Герой обзора - утилита espanso, позволяющая на лету заменять текстовые фрагменты.
Опять прога на Rust. И опять впечатление "ух ты!", как от ruff и uv.

DRY! Не повторяйся при наборе текста!

У людей, которые целый день пишут тесты: кодеры, тестеры, админы, тех-писатели, тех-поддержка, наставники, кураторы и прочие - у всех, кто работает с клавиатурой весь день, часто возникает коллекция повторяющихся фрагментов, заготовок.

Зачем мне самому коллекция заготовок

Я ревьювер в ЯндексПрактикуме с 2020 года, комментирую коды студентов уже пять лет. 95%-90%-80% комментариев повторяются десятки-сотни-тысячи раз.

Расскажу, какие мне попадались организации такой коллекции, расставлю им "за" ➕ и "против" ➖ (имхо оценки). Надеюсь, в комментариях поделятся другими.

Примитивный файл с текстовыми фрагментами

  • ➕ Придумал новый фрагмент, добавил в конец файла - все, можно пользоваться.

  • ➖ Поиск фрагмента примитивный - по его словам средствами редактора.

  • ➖ Вставка фрагмента низкотехнологичная - копипастой.

Личный опыт

Поразительно живучий формат. Мне не удалось от него избавиться - в нем застревают редко применяемые, но явно полезные фрагменты.

Дополнительные возможности утилит другого назначения

Punto предоставляет сервис "замены текста на текст".
AutoHotKey способен замороченным способом реализовывать замены.

  • ➕ Годится для крохотной коллекции.

  • ➖ Не масштабируется по размерам и числу фрагментов.

Личный опыт

Пунто-заменяльщик был моей первой попыткой. После мновенного разрастания коллекции до нескольких десятков замен стало понятно "это не годится". Но рудименты остались - десяток замен все еще выполняются Пунтой.

AHK настолько мало приспособлен для поставленной задачи, что попытки заглохли за пару дней.

Obsidian и подобные

  • ➕ Богатый сервис: поиски, визуализации связей, плагины - красота!

  • ➖ Вставка фрагмента - копипастой.

  • ➖ Высокий порог входа.

Личный опыт

Обсидиан произвел сильное впечатление. Коллеги его применяют. Но мне он не годится, так как мой DRY включает, в том числе, и частые подстановки коротких фраз и даже отдельных слов – ходить в соседнюю прогу "за словами" это уже никакая не "личная продуктивность".

espanso

Специализированная утилита, работающая по юникс-принципу "решай одну задачу, но решай ее максимально хорошо".

  • ➕ Вставка фрагмента автоматическая: демон следит за нажатиями клавиш, распознает фрагменты-триггеры и на лету заменяет их на новые фрагменты.

  • ➕ Начать пользоваться “не просто, а очень просто!

  • ➕ Легко указать место каретки (курсора) после вставки.

  • ➕ Есть подстановки содержимого буфера обмена.

  • ➖ Триггеры нужно придумывать и потом вспоминать/искать.

  • ➖ Триггеры могут срабатывать неуместно, нужна аккуратность при их создании.

Быстрый старт

  1. Скачать и установить утилиту для своей ОС Win/Linux/Mac.

  2. Открыть YAML-файл с триггерами
    в Вин %AppData%\espanso\match\base.yml
    в Лин home/$USER/.config/espanso/match/base.yml
    в Мак /Users/$USER/Library/Application Support/espanso/match/base.yml

  3. Удалить примеры от автора.

  4. Заполнить своими триггерами.

  5. Сохранить. Все! Можно пользоваться.

  6. Замены будут доступны в любом месте, где возможен ввод текста: в редакторе, в браузере, в терминале, в окне выбора файла и т.п.

Два способа применения:

  • Либо набрать триггер-фрагмент – тут же выполнится подстановка.

  • Либо вызвать диалог для поиска по триггерам и заменам

    Найдено пять вариантов
    Найдено пять вариантов

    в котором:
    Набрать текст 1️⃣. Произойдет его фаззи-поиск в триггерах и заменяемых текстах.
    Выбрать из найденных вариантов 2️⃣
    – либо кликом,
    – либо стрелками+ВВОД,
    – либо через ускорители 4️⃣ для первых восьми вариантов.
    Лучше запомнить триггер 3️⃣ примененной замены. Ведь если через две минуты нужна такая же замена, то применение этого триггера первым способом даст большую экономию.

    Вызов диалога происходит
    – либо по сочетанию клавиш (в Вин это Alt+пробел, настраивается),
    – либо по специальному триггеру (можно настроить).

Пример.
Если заполнить файл base.yml так
matches:
- trigger: Дд
replace: Добрый день!
- trigger: ддфио
replace: |
Добрый день!

С уважением
Имя Фамилия
- trigger: ч-т-о
replace: "c:\\users\\me\\Downloads\\что-то-особое\\"

то будут доступны три замены:

  • Дд будет подменяться на Добрый день!.
    Пригодится для общения в чатах и письмах.

  • ддфио будет подменяться на
    Добрый день!

    С уважением
    Имя Фамилия
    Пригодится для начала письма.

  • ч-т-о будет подменяться на c:\users\me\Downloads\что-то-особое\.
    Пригодится для ввода спец-папки при сохранении из браузера.

Вкусности

  1. Через $|$ можно указать место для каретки внутри вставленного фрагмента.

  2. Если описать триггер так
      - trigger: "н" 
    replace: "новость"
    word: true
    то замена произойдет только, если н окажется полным словом.
    Значит все буквы кроме тех, которые являются однобуквенными словами
        а, в, и, к, о, с, у, я,
    можно применять как триггеры. Максимальная экономия!
    Свойство left_word: true мягче - оно проверяет, что фрагмент не находится в начале слова. 

  3. Если описать триггер так
      - trigger: "нвст" 
    replace: "новость"
    propagate_case: true
    то замена будет учитывать регистр при вводе:
        нвст заменится на новость,
        Нвст заменится на Новость,
        НВСТ заменится на НОВОСТЬ.

  4. Через {{clipboard}} можно добавить к тексту содержимое буфера обмена.
    Для этого нужно к описанию триггера еще добавить новый атрибут vars.
    Пример
      - trigger: "змнб" 
        replace: "Замените {{clipboard}} на {{clipboard}}."
        vars: [{"name": "clipboard", "type": "clipboard"}]

  5. Можно вставлять результаты от работы консольных команд или от вызова Питона.

  6. Можно вставить текст одного триггера в другой.
    Это может пригодится, если вставляемый большой и/или нестабильный.

  7. Можно указать регулярку для триггера.
    Автор эспансы рекомендует делать это осторожно и только если нет других способов.

  8. Если в подставляемый текст нужно добавить несколько значений, известных только в момент вставки, то это можно сделать через показ формы-диалога.
    Например, после ввода триггера может появится такая форма

    Если в нее вписать два текстовых поля 1️⃣ и 2️⃣, выбрать вариант в выпадающем списке 3️⃣ и еще вариант в развернутом списке 4️⃣, то после ВВОД триггер заменится на
    Добрый день, Андрей!
    Обратите внимание на неудачный текст.
    Если появятся вопросы, можете
    написать мне в Пачку @me_pch,
    либо наставнику @name2 в Пачке.
    Для оформления такого триггера нужно больше разметки
      - trigger: "реакция1"
        form: |
          Добрый день, [[name]]!
          Обратите внимание на [[fragment]].
          Если появятся вопросы, можете
          написать мне [[to_me]],
          либо наставнику [[pch_tags]] в Пачке.
        form_fields:
          to_me:
            type: choice
            values:
              - "в Пачку @me_pch"
              - "в Телегу @me_tlg"
              - "на почту me@me.ru"
          pch_tags:
            type: list
            values:
              - "@name1"
              - "@name2"
              - "@name3"
    Да, разметки много.
    Но! Если такой триггер-диалог уже создан, то развивать его легко - достаточно только менять текст после form: | и элементы в списках values:.

    Форма может быть минимальной - содержать лишь список вариантов. Так, например, удобно подставлять один из своих мейлов/телефонов/сайтов/...

  9. Можно "подхватывать" через систему плагинов готовые наборы с hub.espanso.org.
    Например, текущие дата/время, эмодзи-символы или эмодзи-фразы, куски TEX-формул и пр.

Проблемы и советы про создание триггеров

Есть противоречия при выборе триггер-фразы:
– Чтобы работала экономия, фраза должна быть как можно короче.
– Чтобы удобно применять триггер, фраза должна состоять из простых символов: в одном регистре, без спец-знаков.
– Но чтобы запомнить фразу, она должна быть содержательной, то есть длинной.
– Но чтобы триггер случайно не срабатывал, выгодно делать фразу сложнее или со спец-знаками.

Поэтому выбор триггеров требует баланса - каждый сам взвешивает риски и выбирает экономию/содержательность и простоту/надежность.
В примерах на сайте самой эспансы все триггеры начинаются с :, то есть ради надежности страдает экономия+простота.

Личный опыт
  1. Создал >1300 триггеров, сейчас в работе ~1000. Помню из них ~400, остальные применяю либо однократно через диалог поиска, либо в диалоге их вспоминаю.

  2. Фразы сложились в почти язык: корни, приставки/суффиксы, глаголы/наречия и прочие связи.
    Примеры.
      (1) Для ревью нужны комментарии про ошибки - триггеры для них начинаются на приставки
          ош - любая ошибка,
          лш - логическая ошибка.
      (2) Еще приставки:
          ли - про что-то лишнее,
          ну/до - про нужно добавить,
          уд - про удачное место,
          не - про неудачное место.
      (3) Суффикс б указывает, что будет вставка из буфера обмена.

  3. Мой приоритет – простота применения.

  4. Самый частый способ создания нового триггера - это обрезание текста до почти аббревиатуры. Удача, если получается яркое сокращение.
    Примеры:
        лиизба = Лишний запрос в базу. Ответ известен заранее.
        нупопа = Мало подсказок для отчета о падении подтестов.
        ужефан = Лишняя функция. Уже есть штатная фикстура для анонима.
        нутаз = Не нужна какая-то случайная запись из базы. Нужна только та, которую редактировали.

  5. Очень удобно применять подмены для вставки нерусских терминов/кодов - почти всегда первые буквы на русской раскладке будут уникальной фразой
        куй = requests
        оы = json
        зкш = print()
        юащ = .format()
        пуещ = get_object()
    Замены эспансой позволяют не переключать раскладку.

  6. Эспансо-замены отлично сочетаются с маркдауном.
    Почти всегда вставляю короткие и многострочные примеры кодов через триггеры, подставляя код из буфера.

Комментарии (0)


  1. MAXH0
    14.09.2025 17:45


  1. evgenyk
    14.09.2025 17:45

    Опять изобрели сниппеты?