Герой обзора - утилита 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. Эспансо-замены отлично сочетаются с маркдауном.
    Почти всегда вставляю короткие и многострочные примеры кодов через триггеры, подставляя код из буфера.