Сегодня, в день рождения
Карамзина, я покажу как в SXEmacs можно сделать быстрый полу-автоматический
ёфикатор текста.
Для начала демонстрация:
Для выполнения такой задачи уже существовал
скрипт и база слов Евгения Миньковского для XEmacs, но у этого решения есть несколько недостатков:
- Время загрузки базы слов в hash-таблицу (serialization) крайне огромное и этому есть объяснения.
- В скрипте содержатся не-ascii символы, это скорее эстетический недостаток, а не реальный.
Я поэкспериментировал немного, посоветовался с разработчиками XEmacs и SXEmacs и пришёл к выводу, что сделать более быструю (чем O(n)) сереализацию базы слов в hash-таблицу пока не представляется возможным.
С другой стороны, я был наслышан, что третья реализация FTS(Full Text Search) в SQLite3 достаточно быстра, к тому же, у SQLite3 хороший и простой API. Поэтому было решено написать ffi-sqlite.el -- FFI(Foreign Function Interface) к библиотеке sqlite3, что и было сделано в течение пары часов. (На данный момент ffi-sqlite.el уже находится в main репозитарии SXEmacs)
После того как в моих руках оказалась связка SXEmacs+SQLite из базы слов Евгения Миньковского была создана
sqlite база с двумя FTS таблицами:
- Для слов, в которых написание ё точно определено
- Для слов, в которых возможно написание ё
Далее дело техники. Немного адаптируем код Евгения и получаем:
(defcustom yo-db-file (expand-file-name "yo.db" user-init-directory)
"File with yo database.")
(defcustom yo-cutting-strings (list "\\-" "\"=" "\"~")
"Words in the text may be splitting by some strings:
for example: hy\\-phe\\-na\\-ti\\-on in TeX")
(defconst yo-cutting-regexp
(concat "\\(?:"
(mapconcat 'regexp-quote
yo-cutting-strings "\\|")
"\\)"))
;; Under Mule: 1493 - little e, 1461 - big E
(defconst yo-e-word-regexp
(concat "\\(?:\\w\\(?:\\w\\|" yo-cutting-regexp "\\)*\\)?"
"\\(?:" (char-to-string (int-to-char 1493)) "\\|"
(char-to-string (int-to-char 1461)) "\\)"
"\\(?:\\w\\(?:\\w\\|" yo-cutting-regexp "\\)*\\)?"))
(defvar yo-db nil)
(defun yo-word (word &optional table)
"Return yo variant of the WORD.
Return value:
nil - no yo variant
string - unambiguous yo variant
(maybe . string) - ambiguous yo variant"
(unless yo-db
(setq yo-db (sqlite-open yo-db-file)))
(let* ((sql (format "select val from %S where key match ?"
(or table 'onlyyo) word))
(eword (encode-coding-string word 'utf-8))
(result (car (car (sqlite-rows yo-db sql (list eword))))))
(if result
(decode-coding-string result 'utf-8)
(when (or (null table) (eq table 'onlyyo))
(let ((mres (yo-word word 'maybeyo)))
(when mres (cons 'maybe mres)))))))
(defun yofy-region (begin end)
"Yofy the region."
(interactive "r")
(save-restriction
(save-excursion
(narrow-to-region begin end)
(beginning-of-buffer)
(let (current-e-word current-yo-word)
(while (re-search-forward yo-e-word-regexp nil t)
(save-match-data
(setq current-e-word
(downcase
(replace-in-string
(match-string 0) yo-cutting-regexp "")))
(setq current-yo-word (yo-word current-e-word)))
(cond ((stringp current-yo-word)
(replace-match current-yo-word nil))
((and (consp current-yo-word)
(eq (car current-yo-word) 'maybe))
(setq current-yo-word (cdr current-yo-word))
(if search-highlight
(isearch-highlight (match-beginning 0) (match-end 0)))
(when (y-or-n-p (format "\"%s\" --> \"%s\"? "
current-e-word current-yo-word))
(undo-boundary)
(replace-match current-yo-word nil))
(isearch-dehighlight))))
))))
(defun yofy-buffer ()
"Yofy whole buffer."
(interactive)
(yofy-region (point-min) (point-max)))
(defun yofy ()
"Yofy region or buffer."
(interactive)
(if (region-active-p)
(yofy-region (region-beginning) (region-end))
(yofy-buffer)))
Кладём
базу слов (5.3M) в
~/.sxemacs/, вставляем код в
init.el и всё. Теперь у нас появилась мощная команда -
M-x yofy RET, которую вы можете забиндить на какую-нибудь кнопку. В моём случае она висит на
C-m e y.
Комментариев нет:
Отправить комментарий