Comment puis-je écrire une simple fonction «complétion aux fonctions ponctuelles»?


9

J'envisage d'écrire un mode majeur pour éditer les decks Magic: the Gathering.

La plupart semble assez simple, mais j'ai une question. Il y a environ 15 000 cartes Magic uniques disponibles (cartes avec des noms uniques). Je voudrais pouvoir compléter contre eux en écrivant une fonction d'achèvement au point. Je cherchais un exemple simple et basique d'une fonction capf qui complète simplement un ensemble de mots sur lesquels baser mon mode, mais je n'ai rien trouvé jusqu'à présent. Connaissez-vous un bon exemple pour commencer? Et croyez-vous qu'il serait facile d'obtenir de bonnes performances ou devrais-je écrire ma propre structure de données (je pense peut-être comme un Trie).

Évidemment, je devrais trouver un moyen de synchroniser avec de nouvelles cartes, etc.

Réponses:


17

Documentation

La fonction d'achèvement de l'API au point se trouve dans la documentation de completion-at-point-functions

Chaque fonction de ce hook est appelée tour à tour sans argument et doit renvoyer soit zéro pour signifier qu'elle n'est pas applicable au point, soit une fonction sans argument pour effectuer la complétion (déconseillée), ou une liste du formulaire (START END COLLECTION . PROPS) où START et END délimitent l'entité à compléter et doivent inclure un point, COLLECTION est la table d'achèvement à utiliser pour la compléter et PROPS est une liste de propriétés pour des informations supplémentaires.

start, endet propssont évidents, mais je pense que le format de collectionn'est pas défini correctement. Pour cela, vous pouvez voir la documentation de try-completionouall-completions

Si COLLECTION est une liste, les clés (voitures d'éléments) sont les compléments possibles. Si un élément n'est pas une contre-cellule, alors l'élément lui-même est l'achèvement possible. Si COLLECTION est une table de hachage, toutes les clés qui sont des chaînes ou des symboles sont les compléments possibles. Si COLLECTION est un obarray, les noms de tous les symboles dans l'obarray sont les compléments possibles.

La COLLECTION peut également être une fonction pour effectuer la finition elle-même. Il reçoit trois arguments: les valeurs STRING, PREDICATE et nil. Tout ce qu'il retourne devient la valeur de la «tentative d'achèvement».

Exemple

Vous trouverez ci-dessous un exemple simple de fonction d'achèvement au point qui utilise les mots définis dans /etc/dictionaries-common/wordspour compléter les mots dans le tampon

(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))))))))

La fonction d'achèvement recherche le mot au point (la bibliothèque thingatptest utilisée pour trouver les limites du mot) et le complète par rapport aux mots du /etc/dictionaries-common/wordsfichier, la propriété :exclusiveest définie sur noafin qu'emacs puisse utiliser d'autres fonctions capf en cas d'échec. Enfin, certaines propriétés supplémentaires sont définies pour améliorer l'intégration en mode entreprise.

Performance

Le fichier de mots sur mon système avait 99171 entrées et emacs a pu les compléter sans aucun problème, donc je suppose que 15000 entrées ne devraient pas être un problème.

Intégration avec le mode entreprise

Le mode entreprise s'intègre très bien à l' completion-at-point-functionsutilisation du company-capfbackend, il devrait donc fonctionner dès la sortie de la boîte pour vous, mais vous pouvez améliorer les complétions proposées par l'entreprise en renvoyant des propsrésultats supplémentaires grâce à la fonction capf. Les accessoires actuellement pris en charge sont

:company-doc-buffer - Utilisé par l'entreprise pour afficher les métadonnées du candidat actuel

:company-docsig - Utilisé par l'entreprise pour faire écho aux métadonnées sur le candidat dans le mini-tampon

:company-location - Utilisé par l'entreprise pour accéder à l'emplacement du candidat actuel


Oh mon! Merci pour la réponse complète! Je vais essayer un peu et accepterai après cela. Un merci supplémentaire pour les conseils de la société (que j'utilise actuellement).
Mattias Bengtsson du

Merci, c'est vraiment utile maintenant, je peux facilement configurer des compléments personnalisés :)
clemera

Heureux d'avoir pu aider :)
Iqbal Ansari

0

@Iqbal Ansari a donné une excellente réponse. Voici une réponse supplémentaire, j'espère que cela vous aidera.

Voici une implémentation utilisant le mécanisme d'achèvement classique d'emacs, 2009.

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

Voici le code qui effectue l'achèvement.

(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")))))

Voici une implémentation utilisant l'interface de ido-mode. Beaucoup plus simple.

(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)))

Vous devrez définir xyz-kwdList comme une liste de vos mots.


2
-1 pour réinventer l'interface de complétion d'une manière pire, pour aller au- nulldelà notet utiliser des identifiants camelcasés et des symboles grecs qui n'ont de sens que dans vos propres modes.
wasamasa

3
-1 pour ne pas avoir répondu à la question qui portait sur completion-at-point-functions(je ne suis pas d'accord avec @wasamasa sur le nullvs not).
npostavs du

3
@XahLee Les fonctions de completion-at-point-functionssont censées renvoyer des données d'achèvement, et non effectuer l'achèvement elles-mêmes. Les fonctions de votre réponse ne sont donc pas utilisables comme entrées dans completion-at-point-functions.
npostavs du

1
@npostavs ah je vois. tu as raison. Merci!
Xah Lee

4
@npostavs Ce type de fonction fonctionnerait toujours, mais en effet, l'écriture d'une fonction d'achèvement de cette manière est contraire à l'interface documentée, et elle est fortement déconseillée.
Dmitry
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.