Як записати просту функцію "завершення в точці-функції"?


9

Я розглядаю можливість створення основного режиму редагування Magic: The Gathering колоди.

Більшість це здається досить простим, але у мене є одне питання. Є близько 15 000 унікальних магічних карт (картки з унікальними назвами, які є). Мені хотілося б мати змогу виконати проти них, написавши функцію завершення в точку. Я шукав простий, базовий приклад функції capf, який просто завершується проти набору слів на основі мого режиму, але поки що нічого не знайшов. Чи знаєте ви якийсь хороший приклад для цього, щоб почати? І чи вірите ви, що було б легко отримати хороші показники або мені довелося б написати власну структуру даних (я думаю, як Трі, можливо).

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

Відповіді:


17

Документація

Функція завершення API в точці можна знайти в документації completion-at-point-functions

Кожна функція цього гака викликається по черзі без будь-якого аргументу і повинна повертати або нуль, щоб означати, що вона не застосовується в точці, або функцію без аргументу для виконання завершення (відмовляє), або список форми (НАЧАЛО КОНТРОЛЬНА КОЛЕКЦІЯ . PROPS), де START і END обмежують об'єкт для завершення і повинен включати крапку, COLLECTION - це таблиця завершення, яка використовується для її заповнення, а PROPS - список властивостей для додаткової інформації.

start, endі propsочевидні, але я думаю, що формат collectionне визначений належним чином. Для цього ви можете ознайомитися з документацією try-completionабоall-completions

Якщо КОЛЕКЦІЯ є алістом, то ключі (машини елементів) - це можливі доповнення. Якщо елемент не є осередком мінусу, то самим елементом є можливе завершення. Якщо КОЛЕКЦІЯ є хеш-таблицею, усі ключі, що є рядками або символами, є можливими доповненнями. Якщо КОЛЕКЦІЯ - це koarray, імена всіх символів у koarray є можливим доповненням.

COLLECTION також може бути функцією, щоб зробити завершення самостійно. Він отримує три аргументи: значення STRING, PREDICATE та нуль. Все, що вона повертає, стає значенням "спроба завершення".

Приклад

Нижче наводиться простий приклад виконання функції в точці, яка використовує слова, визначені в, /etc/dictionaries-common/wordsдля завершення слів у буфері

(defvar words (split-string (with-temp-buffer
                              (insert-file-contents-literally "/etc/dictionaries-common/words")
                              (buffer-string))
                            "\n"))

(defun words-completion-at-point ()
  (let ((bounds (bounds-of-thing-at-point 'word)))
    (when bounds
      (list (car bounds)
            (cdr bounds)
            words
            :exclusive 'no
            :company-docsig #'identity
            :company-doc-buffer (lambda (cand)
                                  (company-doc-buffer (format "'%s' is defined in '/etc/dictionaries-common/words'" cand)))
            :company-location (lambda (cand)
                                (with-current-buffer (find-file-noselect "/etc/dictionaries-common/words")
                                  (goto-char (point-min))
                                  (cons (current-buffer) (search-forward cand nil t))))))))

Функція завершення шукає слово в точці (бібліотека thingatptвикористовується для пошуку меж слова) і доповнює його до слів у /etc/dictionaries-common/wordsфайлі, властивість :exclusiveвстановлюється noтаким чином, що emacs може використовувати інші функції кефіру, якщо наші не вдаються. Нарешті, деякі додаткові властивості встановлюються для покращення інтеграції в режим компанії.

Продуктивність

У файлі слів у моїй системі було 99171 записів, і emacs зміг їх виконати без жодних проблем, тому, мабуть, 15000 записів не повинно бути проблемою.

Інтеграція з режимом компанії

Режим компанії дуже добре інтегрується з completion-at-point-functionsвикористанням company-capfбекенда, тому він повинен вийти з коробки для вас, але ви можете покращити запропоновані компанією завершення, повернувши додаткові propsв результаті функції кефіру. Наразі підтримуються реквізити

:company-doc-buffer - Використовується компанією для відображення метаданих для поточного кандидата

:company-docsig - Використовується компанією для відображення метаданих про кандидата в мінібуфер

:company-location - Використовується компанією, щоб перейти на місце поточного кандидата


О мій! Дякую за ретельну відповідь! Я спробую це трохи, і після цього прийму. Додаткові подяки за підказки компанії (якими я фактично користуюся).
Маттіас Бенгтссон

Дякую, що зараз дуже корисно, я можу легко налаштувати власні доповнення :)
clemera

Радий, що можу допомогти :)
Ікбал Ансарі

0

@Iqbal Ansari дав чудову відповідь. Ось додаткова відповідь, сподіваюся, що це допоможе.

Ось реалізація за допомогою класичного механізму завершення Emacs, 2009.

;; this is your lang's keywords
(setq xyz-kwdList
      '("touch"
       "touch_start"
       "touch_end"
       "for"
       "foreach"
       "forall"
       ))

Далі йде код, який виконує завершення.

(defun xyz-complete-symbol ()
  "Perform keyword completion on word before cursor."
  (interactive)
  (let ((posEnd (point))
        (meat (thing-at-point 'symbol))
        maxMatchResult)

    ;; when nil, set it to empty string, so user can see all lang's keywords.
    ;; if not done, try-completion on nil result lisp error.
    (when (not meat) (setq meat ""))
    (setq maxMatchResult (try-completion meat xyz-kwdList))

    (cond ((eq maxMatchResult t))
          ((null maxMatchResult)
           (message "Can't find completion for “%s”" meat)
           (ding))
          ((not (string= meat maxMatchResult))
           (delete-region (- posEnd (length meat)) posEnd)
           (insert maxMatchResult))
          (t (message "Making completion list…")
             (with-output-to-temp-buffer "*Completions*"
               (display-completion-list 
                (all-completions meat xyz-kwdList)
                meat))
             (message "Making completion list…%s" "done")))))

Далі йде реалізація за допомогою інтерфейсу режиму ido. Набагато простіше.

(defun abc-complete-symbol ()
  "Perform keyword completion on current symbol.
This uses `ido-mode' user interface for completion."
  (interactive)
  (let* (
         (bds (bounds-of-thing-at-point 'symbol))
         (p1 (car bds))
         (p2 (cdr bds))
         (current-sym
          (if  (or (null p1) (null p2) (equal p1 p2))
              ""
            (buffer-substring-no-properties p1 p2)))
         result-sym)
    (when (not current-sym) (setq current-sym ""))
    (setq result-sym
          (ido-completing-read "" xyz-kwdList nil nil current-sym ))
    (delete-region p1 p2)
    (insert result-sym)))

Вам потрібно буде визначити xyz-kwdList як список своїх слів.


2
-1 для винаходячи інтерфейс завершення в гіршому чином, йдучи nullбільш notі використовуючи camelcased ідентифікаторів і грецькі символи , які тільки мають сенс в ваших власних режимах.
wasamasa

3
-1 за те, що я не відповідав на запитання, про яке йшлося completion-at-point-functions(я не згоден з @wasamasa щодо речі nullvs not).
npostavs

3
@XahLee Функції в completion-at-point-functionsповинні повертати дані про завершення, а не виконувати завершення самостійно. Тож функції у вашій відповіді не можна використовувати як записи completion-at-point-functions.
npostavs

1
@npostavs ах я бачу. ти правий. Дякую!
Xah Lee

4
@npostavs Ця функція все одно буде працювати, але дійсно написання функції завершення таким чином суперечить документованому інтерфейсу, і це сильно не рекомендується.
Дмитро
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.