пятница, 2 мая 2014 г.

Fuzzy matching в popup-switcher с использованием flx

Наконец добрался до fuzzy matching в popup-switcher. Есть несколько решений для осуществления нечеткого поиска в Emacs. Например:

Настройка

В popup-switcher используется flx. ИМХО, это просто прекрасный движек для нечеткого поиска, хотя автор и предупреждает о том, что проект достаточно молодой и возможно еще остались ошибки.

В flx-ido есть более высокоуровневые функции, поэтому фактически зависимость именно от flx-ido.

Как и в случае с CEDET и projectile, явно зависимость не указывается (чтобы не превращать popup-switcher в монстра с большим количеством обязательных зависимостей). Чтобы воспользоваться возможностью нечеткого поиска, необходимо установить flx и flx-ido любым удобным вам способом, например через MELPA:

 M-x package-install [RET] flx-ido [RET]
Кроме этого, необходимо установить psw-use-flx в значение t
 (setq psw-use-flx t)

Пример использования

Например, неообходимо найти функцию psw-get-item-by-name. Вызываем psw-switch-function (любым выбранным клавиатурным сочетанием), набираем первые буквы слов "get" и "item" - "gi", flx сузит список поиска и отсортирует по релевантности:
Пример использования flx в popup-switcher.

P.S.

Happy hacking! :)

вторник, 5 ноября 2013 г.

popup-switcher - переключение буферов и навигация по функциям, используя TUI-меню

Эргономика промышленных решений

С некоторого времени для разработки на Java я использую IntelliJ IDEA. Какова там навигация по файлам или по методам! Нажимаешь C-S-n (или C-n для классов) и появляется окошко поиска со списком. Вводишь буквы из названия файла и список сокращается - самый удобный способ навигации по файлам проекта. Аналогично C-F12 для навигации по методам открытого класса - интерактивно изменяющееся меню на основе вводимых символов из имени метода. Ничего эргономичнее до этого не видел.

Изящество и красота текстовых интерфейсов

В emacs это все есть, безусловно, но выглядит это... в emacs вообще все выглядит абсолютно самобытно. Именно самобытно. Думаю, вряд ли кто-то скажет, что emacs предоставляет GUI - интерфейс, возможно лишь местами. В большинстве случаев, emacs не предоставляет и CLI - да, он часто интегрируется и CLI-инструментами: CLI-утилита является back-end-ом, а emacs предоставляет свою обертку, он имеет внутренние оболочки shell-ы и repl-ы, может сам быть запущен из внешних, но этот интерфейс, хоть и играет важную роль, все же является скорее вспомогательным. Выходит TUI? Во-многом, да. Но он зачастую деформируется механизмом окон. Если взять, например, mc, far, ndd, старые, времен DOS, среды разработки, антивирусы, архиваторы, предоставляющие TUI-интерфейс, то там любое диалоговое окно - часть общего текстового рабочего пространства. В emacs же окна жестко механически разделяют текстовые области. Данный подход стал идиоматическим. Скажем, org-mode и magit - примеры эффективного разделения возможностей TUI в рамках основного буфера и добавления вспомогательных окон и буферов. Такие буферы имеют свои специальные режимы с удобными таблицами ключей. Может оно и хорошо, и у такого подхода безусловно есть свои плюсы, но всегда ли это нужно? На каждый чих создается новое окно и в нем свой буфер. Лично мне куда приятнее чаще видеть старый-добрый TUI-интерфейс там, где создание отдельного окна избыточно.

TUI в emacs

Библиотеки popup-el (выпадающие меню) и direx-el (деревья) - примеры расширений, предоставляющих некоторую функциональность TUI. К сожалению, предоставляется лишь некоторая часть возможных виджетов, но тем не менее, имеющиеся весьма удобны.

Так, например, первая из них позволила мне реализовать в TUI некоторые из идей, используемых в IntelliJ IDEA: popup-switcher. Вызов интерактивной функции

  • psw-switch-buffer - приведет к появлению списка открытых буферов. Ввод символов имени буфера будет сокращать список. Именно в качестве (еще одного) механизма переключения буферов и задумывался изначально popup-switcher, однако на самом деле, он может быть использован для переключения чего угодно (отсюда и название).
  • psw-switch-projectile-files - работает со всеми файлами проекта (файлы проекта рассматриваются в контексте расширения projectile).
  • psw-switch-function - работает с функциями/методами в текущем файле.
Пример навигации по буферам.
Пример навигации по функциям в файле с исходным кодом на Clojure.

Установка

Установка расширения довольно проста. Добавьте MELPA в вашу переменную package-archives (если у вас он еще не был добавлен):
 (require 'package)
 (add-to-list 'package-archives
              '("melpa" . "http://melpa.milkbox.net/packages/"))
 (package-initialize)
Далее popup-switcher устанавливается командой:
 M-x package-install [RET] popup-switcher [RET]
Кроме того, в файле инициализации .emacs необходимо указать:
 (require 'popup-switcher)

Настройка

После установки psw-in-window-center в значение t всплывающее меню будет появляться в середине окна. По умолчанию появляется в посередине меду левым краем окна и значением fill-column.
 (setq psw-in-window-center t)
Вы можете установить интерактивные функции переключения буферов и навигации по методам для определенных клавиатурных сочетаний на ваш вкус, например:
 (global-set-key [f2] 'psw-switch-buffer)
 (global-set-key (kbd "C-S-n") 'psw-switch-projectile-files)
Для активации возможности навигации по функциям/методам необходимо установить последнюю версию CEDET и подключить библиотеку eassist. Это не является обязательным, и вы можете использовать popup-switcher без CEDET (и, очевидно, без данной функциональности). Пример настройки:
 ;; Activate semantic
 (semantic-mode 1)
 
 ;; Load contrib library
 (add-to-list 'load-path "~/.emacs.d/cedet/contrib/")
 (require 'eassist)
 
 (eval-after-load "eassist"
   '(global-set-key [f3] 'psw-switch-function))

Добавление переключений

Добавлять новые применения механизма переключения очень просто, так функция psw-switch-buffer выглядит следующим образом:
 (defun psw-switch-buffer ()
    (interactive)
    (psw-switcher
     :items-list (psw-get-buffer-list)
     :item-name-getter 'buffer-name
     :switcher 'switch-to-buffer))
где
  • items-list - список, в котором осуществляется выбор (psw-get-buffer-list - просто использует встроеную функцию buffer-list с некоторыми дополнительными фильтрами).
  • item-name-getter - функция получения текстового обозначения каждого элемента списка в меню выбора.
  • switcher - функция, описывающая что делать с выбранным элементом.

Заключение

Для emacs существуют хоть и не всеобъемлющие, но весьма удобные библиотеки работы с TUI. Например, popup-el уже использующаяся в проектах auto-complete и emacs-jedi, а теперь и в popup-switcher.

Библиотека direx-el используется, например, в проекте emacs-jedi-direx. direx-el потенциально может выступать в качестве основы для более простого и современного аналога ECB. Конечно, в данном случае создание нового окна будет необходимо, но мне кажется, что это как раз тот случай, когда это оправдано.

Надеюсь, вас зантересовала идея более широкого применения TUI в emacs, а также написания и развития расширений, как использующих эти возможности, так и предоставляющих новые возможности для TUI - виджетов.

пятница, 19 октября 2012 г.

Функциональная и информативная mode-line

Давно хотелось сделать отображаемую в emacs mode-line информацию более понятной. В то же время, слишком отходить от стандартного вида тоже не хотелось, т.к. уже есть много всего удобного, например всплывающее меню для основного режима. Конечно, прежде всего хотелось поменять обозначение кодировки, признак read-only и признак того, что буфер был изменен. По этому поводу Xah Lee верно подметил, что начало mode-line выглядит загадочно :). За основу я взял описание конфигурации в emacs-fu.

Несколько ключевых моментов:

  • mode-line-mule-info
    о сдной стороны не слишком красива и информативна, зато позволяет переключать кодировки и символ завершения строк мышкой (и да, я польуюсь мышкой в emacs :)).
  • buffer-file-coding-system
    все что нужно для красивого описания текущей кодировки.
  • mode-line-buffer-identification
    имя буфера (как в стандартой mode-line)
  • mode-line-modes
    еще одна важная деталь из стандартной mode-line, которая отображает список minor-mode, сужения и ассоциированный с буфером процесс.
  • Положение курсора в буфере, идентификация режима Insert/Overwrite взята из emacs-fu.

В итоге получилось так:

Полный вариант данной конфигурации на bitbucket.

понедельник, 8 октября 2012 г.

PageUp/PageDown в меню auto-complete

После длительного сидения на 1.2, обновился до версии 1.4 auto-complete.el (через ELPA). Красота, да еще и со всплывающими подсказками. Первое что захотелось сделать перевесить вызов как в популярных IDE:
 (define-key ac-mode-map (kbd "C-SPC") 'auto-complete)
Уже на списке автодополнений захотелось нажать привычную для IDE PageDown для прокрутки по списку. Ан нет, emacs выполняет прокрутку на странице. Сделаем возможность PageDown/PageUp на меню автодополнений:
 (defun ac-page-next ()
   "Select next completion candidate per `ac-menu-height' range.
 Pages down through completion menu."
   (interactive)
   (let ((counter 0))
     (dotimes (counter (1- ac-menu-height))
       (ac-next))))

 (defun ac-page-previous ()
   "Select previous completion candidate per `ac-menu-height' range.
 Pages up through completion menu."
   (interactive)
   (let ((counter 0))
     (dotimes (counter (1- ac-menu-height))
       (ac-previous))))
(define-key ac-complete-mode-map [next] 'ac-page-next)
(define-key ac-complete-mode-map [prior] 'ac-page-previous)
Поиск в меню давно хотелось перевесить, но все ни как руки не доходили. isearch-forward у меня висит на C-f, соответственно меняем:
(define-key ac-complete-mode-map (kbd "C-f") 'ac-isearch)

среда, 7 октября 2009 г.

Компоновка CLASSPATH с помощью emacs и muCommander

Вообще, CLASSPATH имеет по сути отношение к настоящей заметке как французское полусладкое к физике жидкости, но идея возникла именно в тот момент когда я в очередной раз компоновал CLASSPATH. На самом же деле объединением строк случается пользоваться не только и столько для этого. CLASSPATH - штука капризная. Бывает не любит пробелы или русские буквы в путях. А бывает этот фокус работает "на ура". Конечно, традиционно стараешься этого избежать. Тем не менее, иногда необходимо сформировать строку, в которой должна быть записана "пачка", "jar-ников". Иногда, этот процесс приходится повторять... Иногда и не раз...
Хочется этот процесс автоматизировать. Искать специализированные решения - несколько дико.
В качестве файлового менеджера я использую muCommander. Он реализует минимально необходимый набор функций файлового менеджера, не пытаясь стать системой по управлению ОС (аля TotalCommander), но и не столь аскетичен как dired (хотя, возможно, и менее гибок). Кроме того, cross-platform (java), распространяется под GPL. Настройка muCommander для того, чтобы emacs стал редактором по умолчанию, выглядит следующим образом:
В ~\.mucommander\preferences.xml указывается
<editor>
<use_custom>true</use_custom>
<custom_command>emacsclientw.exe -n &quot;$f&quot;</custom_command>
</editor>

Возвращаемся к CLASSPATH. Находясь в директории библиотеки, например C:\JavaLib\axis\ нажимаем (ключи я описал в стиле emacs, но все это, конечно, выполняется в muCommander):
C-a
M-S-c
Это приведет к тому, что пути к файлам будут скопированы в буфер обмена. Переходим в некоторый буфер emacs, например,temp. Вставляем содержимое буфера обмена (C-v или C-y в зависимости от настроек). Его содержимое принимает следующий вид, например:
C:\JavaLib\axis\axis-ant.jar
C:\JavaLib\axis\axis.jar
C:\JavaLib\axis\commons-discovery-0.2.jar
C:\JavaLib\axis\commons-logging-1.0.4.jar
C:\JavaLib\axis\jaxrpc.jar
C:\JavaLib\axis\log4j-1.2.8.jar
C:\JavaLib\axis\saaj.jar
C:\JavaLib\axis\wsdl4j-1.5.1.jar

Подобным образом дополняем список из библиотек других директорий.
Ниже предлагается ряд функций. Может, это и не очень существенная разработка :), но для меня частота их использования конкурирует разьве только что с к командами движения курсора.
;;=============================================================================
;; Join lines
;;=============================================================================
(defun circle-processing (arg function)
"Circle call 'function' 'arg' times, default - once"
(interactive)
(progn
(if arg
(setq times arg)
(setq times 1))
(let (counter)
(dotimes (counter times)
(apply function nil)))))

(defun join-next-line-space ()
"Joins next line with current with a space between them"
(interactive)
(progn
(end-of-line)
(next-line)
(join-line)))

(defun join-next-line ()
"Joins next line with current without space between them"
(interactive)
(progn
(join-next-line-space)
(delete-char 1)))

(defun join-next-line-semicolon ()
"Joins next line with current with semicolon between them"
(interactive)
(progn
(join-next-line)
(insert ";")))

(defun join-next-line-space-n (&optional arg)
"Joins number of next lines with current with a space between them"
(interactive "P")
(circle-processing arg 'join-next-line-space))
(global-set-key "\C-j" 'join-next-line-space-n)

(defun join-next-line-n (&optional arg)
"Joins number of next lines with current without space between them"
(interactive "P")
(circle-processing arg 'join-next-line))
(global-set-key "\C-cj" 'join-next-line-n)

(defun join-next-line-semicolon-n (&optional arg)
"Joins number of next lines with current with semicolon between them"
(interactive "P")
(circle-processing arg 'join-next-line-semicolon))
(global-set-key "\C-c\C-j" 'join-next-line-semicolon-n)

Так, точка в начале буфера.
M-<число строк> C-c C-j (join-next-line-semicolon-n).
CLASSPATH готов.
Обратная задача - преобразование из строки в список удобный для чтения. Вставив строку CLASSPATH в буфер набираем:
M-x replace-string ; <RET> ; C-q <RET> <RET>
К сожалению, перевод строки и/или возврат каретки будет представлен в буфере символом ^M вместо собственно начала новой строки. Но скопировав этот фрагмент из emacs, текст представляется в том виде, в котором изначально и ожидалось.

P.S. При поддержке emacs@conference.jabber.ru :)
P.P.S. Комментарии приветствуются, прежде всего, по стилю написания на elisp (можно ли что-то упростить, унифицировать, etc).

суббота, 26 сентября 2009 г.

Ограничения размера буфера для imenu

Сегодня обнаружил, что содержимое LaTeX-документа в окне методов ecb вдруг перестало автоматически перестраиваться по сохранению. Долго пытался понять что ж такого наизменял в .emacs, чтобы это перестало работать. На всякий пожарный попробовал старые конфиги, переустановил последовательно ecb, emacs, мир... :)

Но долго сказка сказывается, да скоро проблема решается:
;Imenu auto-rescan is disabled in buffers larger than this size (in bytes).
(setq imenu-auto-rescan-maxout 600000)

вторник, 28 июля 2009 г.

Emacs, Aspell и одновременное использование словарей.

Недавно задался вопросом как осуществлять проверку правописания для буфера, в котором есть как русские, так и английские слова.
На ЛОРе подсказали решение, которое показалось мне вполне приемлемым. Привожу его в том виде, в котором оно осталось в моем .emacs (т.е. почти без изменений):
;;=============================================================================
;;
;;Настройка проверки правописания Ispell
;;
(require 'flyspell)
(require 'ispell)

(setq
; i like aspel, and you?
ispell-program-name "aspell"

; my dictionary-alist, using for redefinition russian dictionary
ispell-dictionary-alist
'(("english" ; English
"[a-zA-Z]"
"[^a-zA-Z]"
"[']"
nil
("-d" "en")
nil iso-8859-1)
("russian" ; Russian
"[АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЫЪЭЮЯабвгдеёжзийклмнопрстуфхцчшщьыъэюя]"
"[^АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЫЪЭЮЯабвгдеёжзийклмнопрстуфхцчшщьыъэюя]"
"[-]"
nil
("-C" "-d" "ru")
nil utf-8)
(nil ; Default
"[A-Za-z]"
"[^A-Za-z]"
"[']"
nil
("-C")
nil iso-8859-1))

ispell-russian-dictionary "russian"
ispell-english-dictionary "english"
flyspell-default-dictionary ispell-russian-dictionary
ispell-dictionary ispell-english-dictionary
ispell-local-dictionary ispell-russian-dictionary
ispell-extra-args '("--sug-mode=ultra"))

(defun flyspell-russian ()
(interactive)
(flyspell-mode t)
(ispell-change-dictionary ispell-russian-dictionary)
(flyspell-buffer)
(message "Russian dictionary - Spell Checking completed."))

; English
(defun flyspell-english ()
(interactive)
(flyspell-mode t)
(ispell-change-dictionary ispell-english-dictionary)
(flyspell-buffer)
(message "English dictionary - Spell Checking completed."))

(setq ispell-highlight-face (quote flyspell-incorrect))
(setq ispell-have-new-look t)
(setq ispell-enable-tex-parser t)
(add-hook 'text-mode-hook 'flyspell-mode)
(setq flyspell-delay 1)
(setq flyspell-always-use-popup t)

(global-set-key [f1] 'ispell-word)
(global-set-key [f7] 'ispell-buffer); проверить орфографию в текущем буфере
(global-set-key [f8] 'ispell-region)
(global-set-key [f9] 'auto-fill-mode); вкл/выкл автозаполнения
(global-set-key [f10] 'flyspell-english)
(global-set-key [f11] 'flyspell-russian)
(global-set-key [f12] 'flyspell-mode); вкл/выкл проверки орфографии "на ходу"
;;
;;============================================================================
P.S. упражняемся c htmlize.el