GNU EMACS для технических писателей
282 subscribers
11 photos
19 files
87 links
Настройка, использование, хаки
Download Telegram
Маленькая хитрость: в русской раскладке не работает сочетание [M-@], потому что при этом вводится не собака, а двойная кавычка. Но поскольку команда mark-word используется довольно часто, а сочетание [M-"] ни к чему не привязано, имеет смысл добавить в init.el такую строку:

(keymap-global-set "M-\"" 'mark-word) ;; Чтобы в RU раскладке тоже работало
👍1
В продолжение последнего поста надо бы объяснить, что такое метод ввода и чем он отличается от раскладки.

Раскладка — это размещение клавиш на клавиатуре.
Метод ввода — реакция компьютера на нажатие клавиш или их последовательностей. Например, в некоторых методах ввода последовательные нажатия нескольких клавиш приводят к вводу одного символа (или лигатуры).

Для Emacs "родной" является раскладка EN/US QWERTY. Все нажатия клавиш он по умолчанию обрабатывает только в этой раскладке. Если на уровне системы вы включите для окна Emacs раскладку RU, то сочетания клавиш перестанут работать.

Решений несколько:

1. Установите пакет reverse-im. Это костыль, который не всегда работает хорошо. Например, [M-@] (mark-word) работать не будет, ведь в русской раскладке вместо @ вводится двойная кавычка ".


;; 📦 REVERSE-IM
;; https://github.com/a13/reverse-im.el
;; Чтобы сочетания клавиш работали в любой раскладке.
(use-package reverse-im
:ensure t
:custom
(reverse-im-input-methods '("russian-computer"))
:config
(reverse-im-mode 1))


2. Используйте переключение методов ввода внутри Emacs вместо смены раскладки на уровне системы. При этом желательно настроить на уровне системы отдельную раскладку для каждого окна и сделать EN раскладкой по умолчанию.

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

Для смены метода ввода в Emacs нажмите [C-\]. В строке статуса появится дополнительная метка RU. Ещё одно нажатие вернёт метод ввода к значению по умолчанию.

Если вы работаете более чем с двумя методами ввода, для включения нужного используйте команду set-input-method [C-x RET C-\]. По умолчанию для русского языка доступны методы russian-computer и russian-typewriter (как на печатных машинках).
5👍3
В Emacs "из коробки" несколько пакетов для работы с терминалом, пройдёмся по трём из них.


term.el

Команда: term.

Спросит какой интерпретатор вы хотите запустить, запустит его и будет работать как обычный терминал: вы вводите команды, интерпретатор их выполняет, Emacs показывает результаты работы в буфере *term*. Можно даже вместо /bin/bash указать psql или, скажем, python — тоже будет работать.

Минусы? Курсор всегда будет находиться в поле ввода команд. Ну, как в обычном терминале, короче.


shell.el

Команда: shell.

Ничего не спросит, а посмотрит в переменную окружения SHELL. Какой интерпретатор там указан, тот и будет запущен. У меня это /bin/bash.

Работает не как обычный терминал: можно перемещаться по выводу как по обычному буферу (я так примеры для книги делал — очень удобно). Чтобы вернуться ко вводу команд, нажмите [C->]. Переход по истории (при нахождении курсора в поле для ввода команд) — [M-n] и [M-p] соответственно.


eshell.el

Команда: eshell.

Интерпретатор, написанный на Emacs Lisp. Для работы такого терминала не нужны сторонние интерпретаторы, у него всё своё. Работают многие команды, доступные в оболочках типа Bash: ls, cp, diff и кое-какие другие. Полный список не вижу смысла приводить, он большой и лучше описан в документации.

Запомните эту команду: rename-uniquely, [C-x x u]. Она добавляет к названию буфера уникальный суффикс, тем самым позволяя открыть несколько буферов, которые по умолчанию доступны только в одном экземпляре. Например, *term* / *shell* / *eshell*.

1. Переключитесь на буфер с терминалом.
2. Нажмите [C-x x u].
3. Запустите новый терминал: [M-x shell RET].

Поздравляю! Теперь у вас открыто 2 терминала типа shell.

#терминал #shell #eshell #term
👍7
На Tech Writer Days 2 ни одного доклада про Emacs. Но если бы он был, вы бы пошли?
Anonymous Poll
63%
Да
38%
Нет
Пакет Counsel делает очень простую вещь: ускоряет выбор из списков. Он не заменяет собой Ivy, а работает на его основе.

Допустим, как работает обычная команда [M-x]? Вы вводите название команды, помогая себе с помощью [TAB]. То есть вам нужно знать с какого слова начинается название нужной команды.

Counsel предоставляет замену стандартной команде — counsel-M-x. Эта команда использует регулярные выражения для фильтрации записей. А это значит, что вы не обязаны знать с каких слов начинается название нужной команды: достаточно помнить хотя бы одну из частей, а Counsel сделает всё остальное.
Попробуем найти все команды со словом compile в названии:

1. [M-x counsel-M-x RET].
2. Удалите ^, т. к. этот символ в регулярных выражениях означает "начало строки". Другими словами, пока ^ находится в начале строки, Counsel интерпретирует команды так: "найди всё, что начинается с указанной строки", т. е. реализует обычное поведение Emacs.
3. Введите слово compile.

Обратите внимание на то, сколько ещё возможных вариантов предлагает Emacs. В моём случае всего 47 команд, содержащих слово "compile" в своём названии.

Перемещение между записями классическое — [C-n], [C-p].

В Counsel есть не только counsel-M-x, но и другие команды. Рекомендую:
;; 📦 COUNSEL
;; https://elpa.gnu.org/packages/counsel.html
;; Автодополнение на основе Ivy
(use-package counsel
:ensure t
:bind
(:map global-map
("C-c c" . counsel-compile)
("C-c g" . counsel-git)
("C-h f" . counsel-describe-function)
("C-h l" . counsel-find-library)
("C-h v" . counsel-describe-variable)
("C-x 8 RET" . counsel-unicode-char)
("C-x C-f" . counsel-find-file)
("M-x" . counsel-M-x)
("M-y" . counsel-yank-pop)))
👍8
Кто давно читает этот канал, обратил внимание, что я вставляю в код символ 📦, а в книге можно увидеть такую конструкцию: -->. К чему это всё?

Это небольшая хитрость, которую я подсмотрел у кого-то из активных пользователей Emacs: то ли Bozhidar Batsov, то ли Protesilaos Stavrou, уже не помню. Этот маркер не является частью синтаксиса и ни на что не влияет. Зато он позволяет быстро найти нужную настройку в init.el.

1. Нажмите [M-s o].
2. Введите 📦 или --> (смотря чем вы пользуетесь).
3. Нажмите [RET].

В буфере *Occur* вы увидите все нужные строки. Если вы переключитесь на этот буфер [C-x o], то сможете быстро перейти к нужной настройке.
А некоторые комментарии ещё как часть синтаксиса и имеют особое значение! Я про то, что написано в книге в главе "File Local Variables".

Допустим, у вас есть файл на Python, и вы хотите для переменной fill-column (переменной Emacs!) задать значение 120. Добавьте в конец файла такие строки:

# Local Variables:
# fill-column: 120
# End:


При открытии файла Emacs извлечёт значение этой переменной из комментария и задаст настройке указанное значение.

Лучший способ добавлять такие комментарии — использовать команду add-file-local-variable. Однако, я советую вам воздержаться от этого и всё-таки задавать значения настроек на уровне файлов .dir-locals.el.
👍6
Не обновляйте tree-sitter-langs до версии 0.12.268, там ошибка:

⛔️ Error (use-package): tree-sitter-langs/:catch: Eager macro-expansion failure: (error "Error calling (tar -xvzf tree-sitter-grammars.x86_64-unknown-linux-gnu.v0.12.268.tar.gz), exit code is 2")


Посидите пока на старой версии пакета.
👍3
В Emacs 29 разработчики добавили функцию setopt. Она работает так же, как customize-set-variable, но пишется значительно короче. Большого смысла в использовании setopt я не вижу, разве что ваш init.el усыпан setq: тогда, разумеется, лучше сделать замену. Про проблему setq я уже писал: она не вызывает сеттер, если он есть. А без этого значения многих настроек не применяются.
Добрый день, друзья!

Работа над книгой продолжается, недавно на Boosty вышла новая версия. Также часть контента доступна на моём сайте: я добавил описание настроек, которые предоставляет встроенный пакет rst.el: https://dunaevsky-ms.ru/emacs/emacs/packages/builtin/rst.html
👍5
Давайте поговорим про auto-mode-alist. Это такой ассоциированный список, где слева — регулярное выражение для имени файла, а справа — название основного режима. Благодаря auto-mode-alist Emacs понимает, какой режим надо использовать при открытии файлов.
Тут есть пара неочевидных свойств:

1. В auto-mode-alist уже есть какие-то значения. Как их посмотреть? describe-variable: [M-x describe-variable RET auto-mode-alist RET].
2. Нужно ли туда что-то добавлять вручную? Сейчас будет срыв покровов: во многих примерах в этом канале я писал ерунду, и многие примеры кода можно было бы сократить, убрав значения, предусмотренные авторами пакетов.

Допустим, у нас есть yaml-mode. Надо ли писать так?

(use-package yaml-mode
:ensure t
:mode
(("\\.yml\\'"
"\\.yaml\\'") . yaml-mode))


Даю подсказку: надо посмотреть код самого yaml-mode, а именно найти строку с переменной auto-mode-alist. Смотрим:

;;;###autoload
(add-to-list 'auto-mode-alist '("\\.\\(e?ya?\\|ra\\)ml\\'" . yaml-mode))


Ничего себе! Это регулярное выражение сложное, но включает не только .yml и .yaml! А значит, нам и делать ничего не нужно, всё будет работать само собой.

ВЫВОД: заполнение блока :mode в use-package или ручное добавление элементов в auto-mode-alist в императивном стиле имеет смысл только в том случае, когда нужное регулярное выражение не добавлено автором пакета. Например, нужно ассоциировать ruby-mode с файлами Vagrantfile, а вот с расширением .rb — не нужно.
🔥4👍1
Поскольку глава про регулярные выражения ещё не написана, сейчас сделаю разбор выражения, приведённого в предыдущем посте:

\\.\\(e?ya?\\|ra\\)ml\\'


1. \\. означает символ точки, буквально. Т. е. речь идёт о расширении.
2. (e?ya?\\|ra\\) — одно из двух:
2.1. e?ya? — символ e может стоять в первой позиции, но это не обязательно; символ y обязателен, символ a опционален.
2.2. ra — буквально;
3. ml — буквально;
4. \\' — конец имени файла.

Что имеем по итогу? Данному регулярному выражению удовлетворяет целый список расширений:

1. .yml
2. .eyml
3. .yaml
4. .eyaml
5. .raml
👍2
Про парсинг.

Чтобы подсветить ключевые слова как ключевые, а ссылки оформить как ссылки редакторы и IDE используют парсинг — текст программы (в нашем случае — доки) разбирается на отдельные части, а уже они оформляются как нужно.

Существуют разные способы разбора исходного кода. Самый широко распространённый — использование регулярных выражений. Сейчас так работают примерно все IDE и редакторы, и Emacs в том числе. Недостатков у этого подхода море:

1. При изменении одной строки нужно выполнять парсинг всего файла. Разработчики используют различные хитрости, чтобы сузить область для повторного разбора, но работает это плохо.
2. Сложность создания регулярных выражений. Без комментариев.
3. Сложность разбора регулярных выражений. Попробуйте открыть JSON-файл размером ну хотя бы в 5 МБ. Уверен, ваш редактор или IDE подвиснет на несколько секунд.

Автор TreeSitter предложил решение этой проблемы — он создал парсер, который создаёт абстрактное синтаксическое дерево открытого файла, где части исходного кода — это ветви и листья этого дерева. При изменении небольшого фрагмента нет необходимости перестраивать всё дерево — достаточно внести изменения только в нужный фрагмент.

В итоге оказалось, что TreeSitter работает (читай: открывает и раскрашивает файлы) быстрее, чем стандартный парсинг регулярных выражений. Не то чтобы TreeSitter не использовал регулярки (он использует), просто принцип работы там сильно другой.

Сейчас TreeSitter внедряют везде где могут, в том числе в Emacs. Для старых версий есть пакет tree-sitter.el. Начиная с Emacs 29 доступен встроенный пакет treesit.el. При этом авторы tree-sitter.el рекомендуют использовать treesit.el!

Общий порядок установки такой:

1. Установите компиляторы C, C++ и, возможно, Rust. Они нужны для сборки библиотек, которые будут разбирать грамматику ваших файлов.
2. Укажите в настройках treesit.el, откуда загружать библиотеки, нужные для разбора файлов на том или ином языке.
3. Установите и скомпилируйте нужные грамматики.
4. Замените название режима на почти такое же, только с суффиксом -ts. Т. е. было python-mode, а станет python-ts-mode.

Звучит просто, но на самом деле всё довольно сложно. Например, так выглядит код настройки в моём init.el:

;; 📦 TREESIT
;; Встроенный пакет для работы с TreeSitter
(use-package treesit
:init
(progn
;; Создадим каталог для хранения so-файлов с грамматиками
(defvar init-el-tree-sitter-dir (expand-file-name "tree-sitter" user-emacs-directory))
(unless (file-directory-p init-el-tree-sitter-dir)
(make-directory init-el-tree-sitter-dir)))
:config
(add-to-list 'treesit-language-source-alist '(asciidoc "https://github.com/cathaysia/tree-sitter-asciidoc.git" "v0.3.0" "tree-sitter-asciidoc/src/"))
(add-to-list 'treesit-language-source-alist '(bash "https://github.com/tree-sitter/tree-sitter-bash.git" "v0.23.3"))
(add-to-list 'treesit-language-source-alist '(css "https://github.com/tree-sitter/tree-sitter-css.git" "v0.23.2"))
(add-to-list 'treesit-language-source-alist '(dockerfile "https://github.com/camdencheek/tree-sitter-dockerfile" "v0.2.0" "src/"))
(add-to-list 'treesit-language-source-alist '(html "https://github.com/tree-sitter/tree-sitter-html.git" "v0.23.2"))
(add-to-list 'treesit-language-source-alist '(javascript "https://github.com/tree-sitter/tree-sitter-javascript.git" "v0.23.1"))
(add-to-list 'treesit-language-source-alist '(json "https://github.com/tree-sitter/tree-sitter-json.git" "v0.24.8"))
(add-to-list 'treesit-language-source-alist '(make "https://github.com/tree-sitter-grammars/tree-sitter-make.git" "v1.1.1" "src/"))
(add-to-list 'treesit-language-source-alist '(markdown "https://github.com/tree-sitter-grammars/tree-sitter-markdown.git" "v0.3.2" "tree-sitter-markdown/src/"))
(add-to-list 'treesit-language-source-alist '(python "https://github.com/tree-sitter/tree-sitter-python.git" "v0.23.6"))
(add-to-list 'treesit-language-source-alist '(ruby "https://github.com/tree-sitter/tree-sitter-ruby.git" "v0.23.1"))
(add-to-list 'treesit-language-source-alist '(rust "https://github.com/tree-sitter/tree-sitter-rust.git" "v0.23.2"))
(add-to-list 'treesit-language-source-alist '(rst "https://github.com/stsewd/tree-sitter-rst.git" "v0.1.0" "src/"))
(add-to-list 'treesit-language-source-alist '(typescript "https://github.com/tree-sitter/tree-sitter-typescript" "master" "tsx/src"))
(add-to-list 'treesit-language-source-alist '(xml "https://github.com/tree-sitter-grammars/tree-sitter-xml.git" "v0.7.0" "xml/src/"))
(add-to-list 'treesit-language-source-alist '(yaml "https://github.com/tree-sitter-grammars/tree-sitter-yaml.git" "v0.7.0" "src/"))
(add-to-list 'major-mode-remap-alist '(dockerfile-mode . dockerfile-ts-mode))
(add-to-list 'major-mode-remap-alist '(html-mode . html-ts-mode))
(add-to-list 'major-mode-remap-alist '(ruby-mode . ruby-ts-mode))
(add-to-list 'major-mode-remap-alist '(yaml-mode . yaml-ts-mode))
(add-to-list 'major-mode-remap-alist '(typescript-mode . typescript-ts-mode)))


Из всего многообразия я чаще всего использую yaml-ts-mode и ruby-ts-mode. Настроить asciidoc-ts-mode пока не удалось 😊
👍2
Настроили режимы на базе TreeSitter?
Anonymous Poll
48%
Да
37%
Нет, не хочу
15%
Нет, не получается
Про иконочные шрифты.

Раньше автор каждого пакета или библиотеки предпочитал иметь всё своё. Но постепенно этот подход заменил другой: пишем только уникальный код, а общие компоненты выносим в библиотеки. Так появились DLL / so-файлы.

В Emacs авторы многих пакетов пошли тем же путём: «Зачем писать собственную поддержку иконок, если можно использовать другой пакет?»

Раньше для иконок использовали файлы формата ICO. Потом JPG и PNG. В какой-то момент все перешли в вектор – SVG. Однако, самый мощный прорыв случился в момент появления иконочных шрифтов. Вместо букв и цифр их глифы рисуют картинки. Вроде бы сейчас даже появилась спецификация, позволяющая раскрашивать эти картинки разными цветами, а не основным цветом шрифта. Впрочем, вернёмся к Emacs.

Основные «киты» – это пакеты all-the-icons и nerd-icons. В обоих есть команды для загрузки нужных шрифтов – all-the-icons-install-fonts и вы не поверите nerd-icons-install-fonts. В Windows эти шрифты потом надо ещё и установить. В Linux достаточно обновить кеш шрифтов.
👍4
Нужна ли книга "Git простым языком"? Без неё читать главу про Magit сложновато, как мне кажется.
Anonymous Poll
80%
Да
20%
Нет
Как увеличить размер окна по высоте (при условии, что есть куда)? Очень просто: [C-x ^].
Но эта команда увеличивает высоту окна только на один символ! А как увеличить на 5?

Простое и брутальное решение: [C-u 5 C-x ^].

А попроще можно? Конечно! Включите глобальный дополнительный режим repeat-mode!

(use-package repeat
:config (repeat-mode 1))


Что он делает? После первого вызова некоторых команд он переходит в режим повтора. Это значит, что в минибуфере будет выводиться подсказка с названием доступных клавиш, и нажатие каждой из них будет работать так, будто вы нажали всю последовательность целиком.

Ещё раз, попроще. Для изменения размера окна используются эти последовательности:

[C-x ^] — выше
[C-x v] — ниже
[C-x }] — шире
[C-x {] — уже

Но теперь после нажатия любой из этих последовательностей Emacs перейдёт в режим повтора, и для изменения размеров активного окна нужно будет нажать не последовательность, а всего одну клавишу:

[^] — выше
[v] — ниже
[}] — шире
[{] — уже

И пока вы не нажмёте [C-g] или клавишу, не входящую в список для повтора, это поведение будет работать. В следующем посте будет GIF, демонстрирующий это поведение.

Кстати, при изменении размера шрифта repeat-mode тоже крайне полезен!
👍81