Bagaimana cara menulis fungsi `selesai-pada-titik-fungsi` sederhana?

9

Saya sedang mempertimbangkan untuk menulis mode utama untuk mengedit Magic: the Gathering decks.

Sebagian besar tampaknya cukup mudah tetapi saya punya satu pertanyaan. Ada sekitar 15.000 kartu Magic unik yang tersedia (kartu dengan nama unik itu). Saya ingin dapat menyelesaikan terhadap mereka dengan menulis fungsi penyelesaian-pada-titik. Saya telah mencari beberapa contoh sederhana dari fungsi capf yang hanya melengkapi satu set kata yang menjadi dasar mode saya tetapi gagal menemukan apa pun sejauh ini. Apakah Anda tahu ada contoh bagus untuk memulai ini? Dan apakah Anda percaya itu akan mudah untuk mendapatkan kinerja yang baik atau saya harus menulis struktur data saya sendiri (saya berpikir seperti Trie mungkin).

Jelas saya perlu menemukan cara untuk menyinkronkan dengan kartu baru dll dan di masa depan bahkan mungkin dapat mencari kartu dengan karakteristik lain selain hanya nama kartu tetapi itu bisa menunggu.

Mattias Bengtsson
sumber

Jawaban:

17

Dokumentasi

Fungsi penyelesaian API pada titik dapat ditemukan dalam dokumentasi completion-at-point-functions

Setiap fungsi pada hook ini dipanggil secara bergantian tanpa argumen dan harus mengembalikan nihil berarti bahwa itu tidak berlaku pada titik, atau fungsi tidak ada argumen untuk melakukan penyelesaian (putus asa), atau daftar formulir (MULAI KOLEKSI MULAI . PROPS) tempat MULAI dan AKHIR membatasi entitas untuk diisi dan harus menyertakan titik, KOLEKSI adalah tabel penyelesaian untuk digunakan untuk melengkapinya, dan PROPS adalah daftar properti untuk informasi tambahan.

start, enddan propssudah jelas, tapi saya pikir formatnya collectiontidak didefinisikan dengan benar. Untuk itu Anda bisa melihat dokumentasi try-completionatauall-completions

Jika KOLEKSI adalah daftar, kunci (mobil elemen) adalah kemungkinan penyelesaian. Jika suatu elemen bukan sel kontra, maka elemen itu sendiri adalah penyelesaian yang mungkin. Jika COLLECTION adalah tabel hash, semua kunci yang merupakan string atau simbol adalah kemungkinan penyelesaian. Jika KOLEKSI adalah obarray, nama-nama semua simbol dalam obarray adalah pelengkap yang mungkin.

KOLEKSI juga bisa menjadi fungsi untuk menyelesaikannya sendiri. Ia menerima tiga argumen: nilai-nilai STRING, PREDICATE dan nil. Apa pun yang dikembalikan menjadi nilai `coba-penyelesaian '.

Contoh

Di bawah ini adalah contoh sederhana penyelesaian pada fungsi titik yang menggunakan kata-kata yang didefinisikan /etc/dictionaries-common/wordsuntuk melengkapi kata-kata dalam 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))))))))

Fungsi penyelesaian mencari kata pada titik (perpustakaan thingatptdigunakan untuk menemukan batas-batas kata) dan melengkapinya dengan kata-kata dalam /etc/dictionaries-common/wordsfile, properti :exclusivediatur ke nosehingga emacs dapat menggunakan fungsi capf lainnya jika kami gagal. Akhirnya beberapa properti tambahan diatur untuk meningkatkan integrasi mode perusahaan.

Performa

File kata-kata di sistem saya memiliki 99171 entri dan emacs dapat menyelesaikannya tanpa masalah, jadi saya kira 15.000 entri seharusnya tidak menjadi masalah.

Integrasi dengan mode perusahaan

Mode perusahaan terintegrasi dengan sangat baik dengan completion-at-point-functionsmenggunakan company-capfbackend, jadi itu harus bekerja di luar kotak untuk Anda, tetapi Anda dapat meningkatkan penyelesaian yang ditawarkan oleh perusahaan dengan mengembalikan tambahan propsdalam hasil fungsi capf. Alat peraga yang saat ini didukung adalah

:company-doc-buffer - Digunakan oleh perusahaan untuk menampilkan metadata untuk kandidat saat ini

:company-docsig - Digunakan oleh perusahaan untuk menggemakan metadata tentang kandidat di minibuffer

:company-location - Digunakan oleh perusahaan untuk melompat ke lokasi kandidat saat ini

Iqbal Ansari
sumber
Astaga! Terima kasih atas jawabannya! Saya akan mencoba ini sedikit dan akan menerima setelah itu. Terima kasih ekstra untuk petunjuk Perusahaan (yang sebenarnya saya gunakan).
Mattias Bengtsson
Terima kasih, itu sangat membantu sekarang saya dapat mengkonfigurasi penyelesaian kustom dengan mudah :)
clemera
Senang saya bisa membantu :)
Iqbal Ansari
0

@Iqbal Ansari memberikan jawaban yang bagus. Ini jawaban tambahan, semoga bisa membantu.

Berikut ini adalah implementasi menggunakan mekanisme penyelesaian klasik emacs, 2009.

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

Berikut ini adalah kode yang menyelesaikannya.

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

Berikut ini adalah implementasi menggunakan antarmuka ido-mode. Jauh lebih sederhana.

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

Anda perlu mendefinisikan xyz-kwdList sebagai daftar kata-kata Anda.

Xah Lee
sumber
2
-1 untuk menemukan kembali antarmuka penyelesaian dengan cara yang lebih buruk, pergi untuk nulllebih notdan menggunakan pengidentifikasi camelc dan simbol yunani yang hanya masuk akal dalam mode Anda sendiri.
wasamasa
3
-1 untuk tidak menjawab pertanyaan tentang completion-at-point-functions(saya tidak setuju dengan @wasamasa tentang hal nullvs not).
npostavs
3
@XahLee Fungsi dalam completion-at-point-functionsseharusnya mengembalikan data penyelesaian, bukan melakukan penyelesaian sendiri. Jadi fungsi dalam jawaban Anda tidak dapat digunakan sebagai entri completion-at-point-functions.
npostavs
1
@npostavs ah saya mengerti. kamu benar. Terima kasih!
Xah Lee
4
@npostavs Fungsi semacam ini masih akan berfungsi, tetapi memang menulis fungsi penyelesaian dengan cara ini bertentangan dengan antarmuka yang terdokumentasi, dan sangat tidak disarankan.
Dmitry