Como escrevo uma função simples `conclusão-no-ponto-funções`?

9

Estou pensando em escrever um modo importante para editar decks de Magic: the Gathering.

A maioria parece bastante direta, mas eu tenho uma pergunta. Existem cerca de 15 000 cartas mágicas únicas disponíveis (cartas com nomes únicos). Eu gostaria de poder concluir com eles escrevendo uma função de conclusão no ponto. Eu estive procurando por um exemplo simples e básico de uma função capf que apenas é concluída com base em um conjunto de palavras para basear meu modo, mas não conseguiu encontrar nada até agora. Você conhece algum bom exemplo para isso começar? E você acredita que seria fácil obter um bom desempenho ou eu teria que escrever minha própria estrutura de dados (estou pensando como um Trie, talvez).

Obviamente, eu precisaria encontrar uma maneira de sincronizar com novos cartões, etc.

Mattias Bengtsson
fonte

Respostas:

17

Documentação

A função de conclusão no ponto da API pode ser encontrada na documentação de completion-at-point-functions

Cada função desse gancho é chamada alternadamente, sem argumento algum, e deve retornar nulo para significar que não é aplicável no momento, ou uma função sem argumento para executar a conclusão (desencorajada) ou uma lista do formulário (START END COLLECTION PROPS) onde START e END delimitam a entidade a ser concluída e deve incluir o ponto, COLLECTION é a tabela de conclusão a ser usada para concluí-la e PROPS é uma lista de propriedades para obter informações adicionais.

start, ende propssão óbvias, mas acho que o formato de collectionnão está definido corretamente. Para isso, você pode ver a documentação try-completionouall-completions

Se COLLECTION for uma lista, as chaves (carros dos elementos) são as possíveis conclusões. Se um elemento não for uma célula contras, o próprio elemento é a conclusão possível. Se COLLECTION for uma tabela de hash, todas as chaves que são seqüências de caracteres ou símbolos são as possíveis conclusões. Se COLEÇÃO é uma matriz, os nomes de todos os símbolos na matriz são as possíveis conclusões.

COLEÇÃO também pode ser uma função para realizar a conclusão em si. Ele recebe três argumentos: os valores STRING, PREDICATE e nil. O que quer que ele retorne se torna o valor de `tentativa de conclusão '.

Exemplo

Abaixo está um exemplo simples da função de conclusão no ponto que usa as palavras definidas /etc/dictionaries-common/wordspara completar as palavras no buffer

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

A função de conclusão procura a palavra no ponto (a biblioteca thingatpté usada para encontrar os limites da palavra) e a completa com as palavras do /etc/dictionaries-common/wordsarquivo; a propriedade :exclusiveestá configurada para noque o emacs possa usar outras funções capf se falharmos. Finalmente, algumas propriedades adicionais são definidas para aprimorar a integração no modo da empresa.

atuação

O arquivo de palavras no meu sistema tinha 99171 entradas e o emacs conseguiu concluí-las sem problemas, então acho que 15000 entradas não devem ser um problema.

Integração com o modo empresa

O modo Empresa integra-se muito bem ao completion-at-point-functionsuso do company-capfback - end, portanto, ele deve funcionar propsimediatamente , mas você pode aprimorar as conclusões oferecidas pela empresa retornando mais informações no resultado da função capf. Os adereços atualmente suportados são

:company-doc-buffer - Usado pela empresa para exibir metadados do candidato atual

:company-docsig - Usado pela empresa para ecoar metadados sobre o candidato no minibuffer

:company-location - Usado pela empresa para ir para o local do candidato atual

Iqbal Ansari
fonte
Oh meu! Obrigado pela resposta completa! Vou tentar isso um pouco e depois aceito. Agradecimentos extras pelas dicas da empresa (que na verdade estou usando).
Mattias Bengtsson
Obrigado, isso é realmente útil agora eu posso configurar conclusões personalizados facilmente :)
clemera
Ainda bem que pude ajudar :)
Iqbal Ansari
0

@Iqbal Ansari deu uma ótima resposta. Aqui está uma resposta suplementar, espero que ajude.

Aqui está uma implementação usando o mecanismo de conclusão clássico do emacs, 2009.

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

A seguir está o código que faz a conclusão.

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

A seguir, uma implementação usando a interface do modo ido. Muito mais simples.

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

Você precisará definir xyz-kwdList como uma lista de suas palavras.

Xah Lee
fonte
2
-1 para reinventar a interface de conclusão de uma maneira pior, indo para nullcima note usando identificadores CamelCased e símbolos gregos que só fazem sentido em seus próprios modos.
Wasamasa 03/09/2015
3
-1 por não responder à pergunta em questão completion-at-point-functions(discordo de @wasamasa sobre a coisa nullvs not).
npostavs
3
@XahLee As funções em completion-at-point-functionsdevem retornar dados de conclusão, não executar a conclusão por si mesmas. Portanto, as funções na sua resposta não podem ser usadas como entradas no completion-at-point-functions.
npostavs
11
@npostavs ah eu vejo. você está certo. Obrigado!
Xah Lee
4
@npostavs Esse tipo de função ainda funcionaria, mas escrever uma função de conclusão dessa maneira é contra a interface documentada e é fortemente desencorajado.
Dmitry