Wie schreibe ich eine einfache Funktion "Vervollständigung am Punkt"?

9

Ich denke darüber nach, einen Hauptmodus für die Bearbeitung von Magic zu schreiben: die Gathering-Decks.

Das meiste davon scheint ziemlich einfach zu sein, aber ich habe eine Frage. Es sind ungefähr 15 000 einzigartige Magic-Karten verfügbar (Karten mit eindeutigen Namen). Ich möchte in der Lage sein, gegen sie zu vervollständigen, indem ich eine Punkt-zu-Punkt-Funktion schreibe. Ich habe nach einem einfachen, grundlegenden Beispiel für eine Capf-Funktion gesucht, die nur mit einer Reihe von Wörtern abgeschlossen wird, auf denen mein Modus basiert, aber bisher nichts gefunden hat. Kennen Sie ein gutes Beispiel dafür? Und glauben Sie, es wäre einfach, eine gute Leistung zu erzielen, oder müsste ich meine eigene Datenstruktur schreiben (ich denke vielleicht wie ein Trie).

Natürlich müsste ich einen Weg finden, um mit neuen Karten usw. zu synchronisieren und in Zukunft vielleicht sogar nach Karten mit anderen Merkmalen als nur dem Kartennamen suchen zu können, aber das kann warten.

Mattias Bengtsson
quelle

Antworten:

17

Dokumentation

Die API-Vervollständigungsfunktion am Punkt finden Sie in der Dokumentation von completion-at-point-functions

Jede Funktion an diesem Hook wird abwechselnd ohne Argument aufgerufen und sollte entweder nil zurückgeben, um zu bedeuten, dass sie zum Zeitpunkt nicht anwendbar ist, oder eine Funktion ohne Argument, um die Vervollständigung durchzuführen (entmutigt), oder eine Liste des Formulars (START END COLLECTION) PROPS) wobei START und END die zu vervollständigende Entität begrenzen und Punkt enthalten sollten, COLLECTION die Fertigstellungstabelle ist, die zum Vervollständigen verwendet werden soll, und PROPS eine Eigenschaftsliste für zusätzliche Informationen.

start, endUnd propsliegen auf der Hand, aber ich denke , das Format collectionnicht richtig definiert ist. Dazu sehen Sie die Dokumentation von try-completionoderall-completions

Wenn COLLECTION eine Liste ist, sind die Schlüssel (Autos der Elemente) die möglichen Vervollständigungen. Wenn ein Element keine Nachteilezelle ist, ist das Element selbst die mögliche Vervollständigung. Wenn COLLECTION eine Hash-Tabelle ist, sind alle Schlüssel, die Zeichenfolgen oder Symbole sind, mögliche Vervollständigungen. Wenn COLLECTION ein Obarray ist, sind die Namen aller Symbole im Obarray die möglichen Vervollständigungen.

COLLECTION kann auch eine Funktion sein, um die Fertigstellung selbst durchzuführen. Es erhält drei Argumente: die Werte STRING, PREDICATE und nil. Was auch immer es zurückgibt, wird zum Wert von "Try-Completion".

Beispiel

Unten finden Sie ein einfaches Beispiel für die Vervollständigung an der Punktfunktion, bei der die in definierten Wörter verwendet werden /etc/dictionaries-common/words, um die Wörter im Puffer zu vervollständigen

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

Die Vervollständigungsfunktion sucht an einem bestimmten Punkt nach Wörtern (die Bibliothek thingatptwird verwendet, um die Wortgrenzen zu ermitteln) und vervollständigt sie anhand der Wörter in der /etc/dictionaries-common/wordsDatei. Die Eigenschaft :exclusiveist so festgelegt, nodass Emacs andere CapF-Funktionen verwenden können, wenn unsere fehlschlagen. Schließlich werden einige zusätzliche Eigenschaften festgelegt, um die Integration im Unternehmensmodus zu verbessern.

Performance

Die Wortdatei auf meinem System hatte 99171 Einträge und Emacs konnte sie ohne Probleme vervollständigen, daher denke ich, dass 15000 Einträge kein Problem sein sollten.

Integration in den Firmenmodus

Der Unternehmensmodus lässt sich sehr gut in die completion-at-point-functionsVerwendung des company-capfBackends integrieren, sodass er für Sie propssofort einsatzbereit sein sollte. Sie können jedoch die vom Unternehmen angebotenen Vervollständigungen verbessern, indem Sie im Ergebnis der CapF-Funktion zusätzliche Rückgaben zurückgeben . Die derzeit unterstützten Requisiten sind

:company-doc-buffer - Wird vom Unternehmen verwendet, um Metadaten für den aktuellen Kandidaten anzuzeigen

:company-docsig - Wird vom Unternehmen verwendet, um Metadaten über den Kandidaten im Minibuffer wiederzugeben

:company-location - Wird vom Unternehmen verwendet, um zum Standort des aktuellen Kandidaten zu springen

Iqbal Ansari
quelle
Oh mein! Danke für die gründliche Antwort! Ich werde das ein bisschen ausprobieren und danach akzeptieren. Zusätzlicher Dank für die Hinweise des Unternehmens (die ich tatsächlich verwende).
Mattias Bengtsson
Danke, das ist wirklich hilfreich, jetzt kann ich benutzerdefinierte Vervollständigungen einfach konfigurieren :)
Clemera
Ich bin froh, dass ich helfen konnte :)
Iqbal Ansari
0

@ Iqbal Ansari gab eine tolle Antwort. Hier ist eine ergänzende Antwort, hoffe es wird helfen.

Hier ist eine Implementierung mit dem klassischen Abschlussmechanismus von emacs, 2009.

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

Das Folgende ist der Code, der die Vervollständigung ausführt.

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

Es folgt eine Implementierung unter Verwendung der Schnittstelle des Ido-Modus. Viel einfacher.

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

Sie müssen xyz-kwdList als Liste Ihrer Wörter definieren.

Xah Lee
quelle
2
-1 für eine schlechtere Neuerfindung der Vervollständigungsschnittstelle, für eine Überarbeitung und für nulldie notVerwendung von Kamelkennungen und griechischen Symbolen, die nur in Ihren eigenen Modi sinnvoll sind.
Wasamasa
3
-1 für die completion-at-point-functionsNichtbeantwortung der Frage, um die es ging (ich bin nicht einverstanden mit @wasamasa über das nullvs- notDing).
npostavs
3
@XahLee Die Funktionen in completion-at-point-functionssollen Abschlussdaten zurückgeben, nicht den Abschluss selbst ausführen. Die Funktionen in Ihrer Antwort können also nicht als Einträge in verwendet werden completion-at-point-functions.
npostavs
1
@npostavs ah ich verstehe. Du hast recht. Vielen Dank!
Xah Lee
4
@npostavs Diese Art von Funktion würde immer noch funktionieren, aber das Schreiben einer Abschlussfunktion auf diese Weise widerspricht der dokumentierten Schnittstelle und wird dringend davon abgeraten.
Dmitry