Klimi's new dotfiles with stow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

298 lines
12 KiB

4 years ago
  1. ;;; swank-c-p-c.lisp -- ILISP style Compound Prefix Completion
  2. ;;
  3. ;; Author: Luke Gorrie <luke@synap.se>
  4. ;; Edi Weitz <edi@agharta.de>
  5. ;; Matthias Koeppe <mkoeppe@mail.math.uni-magdeburg.de>
  6. ;; Tobias C. Rittweiler <tcr@freebits.de>
  7. ;; and others
  8. ;;
  9. ;; License: Public Domain
  10. ;;
  11. (in-package :swank)
  12. (eval-when (:compile-toplevel :load-toplevel :execute)
  13. (swank-require :swank-util))
  14. (defslimefun completions (string default-package-name)
  15. "Return a list of completions for a symbol designator STRING.
  16. The result is the list (COMPLETION-SET COMPLETED-PREFIX), where
  17. COMPLETION-SET is the list of all matching completions, and
  18. COMPLETED-PREFIX is the best (partial) completion of the input
  19. string.
  20. Simple compound matching is supported on a per-hyphen basis:
  21. (completions \"m-v-\" \"COMMON-LISP\")
  22. ==> ((\"multiple-value-bind\" \"multiple-value-call\"
  23. \"multiple-value-list\" \"multiple-value-prog1\"
  24. \"multiple-value-setq\" \"multiple-values-limit\")
  25. \"multiple-value\")
  26. \(For more advanced compound matching, see FUZZY-COMPLETIONS.)
  27. If STRING is package qualified the result list will also be
  28. qualified. If string is non-qualified the result strings are
  29. also not qualified and are considered relative to
  30. DEFAULT-PACKAGE-NAME.
  31. The way symbols are matched depends on the symbol designator's
  32. format. The cases are as follows:
  33. FOO - Symbols with matching prefix and accessible in the buffer package.
  34. PKG:FOO - Symbols with matching prefix and external in package PKG.
  35. PKG::FOO - Symbols with matching prefix and accessible in package PKG.
  36. "
  37. (multiple-value-bind (name package-name package internal-p)
  38. (parse-completion-arguments string default-package-name)
  39. (let* ((symbol-set (symbol-completion-set
  40. name package-name package internal-p
  41. (make-compound-prefix-matcher #\-)))
  42. (package-set (package-completion-set
  43. name package-name package internal-p
  44. (make-compound-prefix-matcher '(#\. #\-))))
  45. (completion-set
  46. (format-completion-set (nconc symbol-set package-set)
  47. internal-p package-name)))
  48. (when completion-set
  49. (list completion-set (longest-compound-prefix completion-set))))))
  50. ;;;;; Find completion set
  51. (defun symbol-completion-set (name package-name package internal-p matchp)
  52. "Return the set of completion-candidates as strings."
  53. (mapcar (completion-output-symbol-converter name)
  54. (and package
  55. (mapcar #'symbol-name
  56. (find-matching-symbols name
  57. package
  58. (and (not internal-p)
  59. package-name)
  60. matchp)))))
  61. (defun package-completion-set (name package-name package internal-p matchp)
  62. (declare (ignore package internal-p))
  63. (mapcar (completion-output-package-converter name)
  64. (and (not package-name)
  65. (find-matching-packages name matchp))))
  66. (defun find-matching-symbols (string package external test)
  67. "Return a list of symbols in PACKAGE matching STRING.
  68. TEST is called with two strings. If EXTERNAL is true, only external
  69. symbols are returned."
  70. (let ((completions '())
  71. (converter (completion-output-symbol-converter string)))
  72. (flet ((symbol-matches-p (symbol)
  73. (and (or (not external)
  74. (symbol-external-p symbol package))
  75. (funcall test string
  76. (funcall converter (symbol-name symbol))))))
  77. (do-symbols* (symbol package)
  78. (when (symbol-matches-p symbol)
  79. (push symbol completions))))
  80. completions))
  81. (defun find-matching-symbols-in-list (string list test)
  82. "Return a list of symbols in LIST matching STRING.
  83. TEST is called with two strings."
  84. (let ((completions '())
  85. (converter (completion-output-symbol-converter string)))
  86. (flet ((symbol-matches-p (symbol)
  87. (funcall test string
  88. (funcall converter (symbol-name symbol)))))
  89. (dolist (symbol list)
  90. (when (symbol-matches-p symbol)
  91. (push symbol completions))))
  92. (remove-duplicates completions)))
  93. (defun find-matching-packages (name matcher)
  94. "Return a list of package names matching NAME with MATCHER.
  95. MATCHER is a two-argument predicate."
  96. (let ((converter (completion-output-package-converter name)))
  97. (remove-if-not (lambda (x)
  98. (funcall matcher name (funcall converter x)))
  99. (mapcar (lambda (pkgname)
  100. (concatenate 'string pkgname ":"))
  101. (loop for package in (list-all-packages)
  102. nconcing (package-names package))))))
  103. ;; PARSE-COMPLETION-ARGUMENTS return table:
  104. ;;
  105. ;; user behaviour | NAME | PACKAGE-NAME | PACKAGE
  106. ;; ----------------+--------+--------------+-----------------------------------
  107. ;; asdf [tab] | "asdf" | NIL | #<PACKAGE "DEFAULT-PACKAGE-NAME">
  108. ;; | | | or *BUFFER-PACKAGE*
  109. ;; asdf: [tab] | "" | "asdf" | #<PACKAGE "ASDF">
  110. ;; | | |
  111. ;; asdf:foo [tab] | "foo" | "asdf" | #<PACKAGE "ASDF">
  112. ;; | | |
  113. ;; as:fo [tab] | "fo" | "as" | NIL
  114. ;; | | |
  115. ;; : [tab] | "" | "" | #<PACKAGE "KEYWORD">
  116. ;; | | |
  117. ;; :foo [tab] | "foo" | "" | #<PACKAGE "KEYWORD">
  118. ;;
  119. (defun parse-completion-arguments (string default-package-name)
  120. "Parse STRING as a symbol designator.
  121. Return these values:
  122. SYMBOL-NAME
  123. PACKAGE-NAME, or nil if the designator does not include an explicit package.
  124. PACKAGE, generally the package to complete in. (However, if PACKAGE-NAME is
  125. NIL, return the respective package of DEFAULT-PACKAGE-NAME instead;
  126. if PACKAGE is non-NIL but a package cannot be found under that name,
  127. return NIL.)
  128. INTERNAL-P, if the symbol is qualified with `::'."
  129. (multiple-value-bind (name package-name internal-p)
  130. (tokenize-symbol string)
  131. (flet ((default-package ()
  132. (or (guess-package default-package-name) *buffer-package*)))
  133. (let ((package (cond
  134. ((not package-name)
  135. (default-package))
  136. ((equal package-name "")
  137. (guess-package (symbol-name :keyword)))
  138. ((find-locally-nicknamed-package
  139. package-name (default-package)))
  140. (t
  141. (guess-package package-name)))))
  142. (values name package-name package internal-p)))))
  143. (defun completion-output-case-converter (input &optional with-escaping-p)
  144. "Return a function to convert strings for the completion output.
  145. INPUT is used to guess the preferred case."
  146. (ecase (readtable-case *readtable*)
  147. (:upcase (cond ((or with-escaping-p
  148. (and (plusp (length input))
  149. (not (some #'lower-case-p input))))
  150. #'identity)
  151. (t #'string-downcase)))
  152. (:invert (lambda (output)
  153. (multiple-value-bind (lower upper) (determine-case output)
  154. (cond ((and lower upper) output)
  155. (lower (string-upcase output))
  156. (upper (string-downcase output))
  157. (t output)))))
  158. (:downcase (cond ((or with-escaping-p
  159. (and (zerop (length input))
  160. (not (some #'upper-case-p input))))
  161. #'identity)
  162. (t #'string-upcase)))
  163. (:preserve #'identity)))
  164. (defun completion-output-package-converter (input)
  165. "Return a function to convert strings for the completion output.
  166. INPUT is used to guess the preferred case."
  167. (completion-output-case-converter input))
  168. (defun completion-output-symbol-converter (input)
  169. "Return a function to convert strings for the completion output.
  170. INPUT is used to guess the preferred case. Escape symbols when needed."
  171. (let ((case-converter (completion-output-case-converter input))
  172. (case-converter-with-escaping (completion-output-case-converter input t)))
  173. (lambda (str)
  174. (if (or (multiple-value-bind (lowercase uppercase)
  175. (determine-case str)
  176. ;; In these readtable cases, symbols with letters from
  177. ;; the wrong case need escaping
  178. (case (readtable-case *readtable*)
  179. (:upcase lowercase)
  180. (:downcase uppercase)
  181. (t nil)))
  182. (some (lambda (el)
  183. (or (member el '(#\: #\Space #\Newline #\Tab))
  184. (multiple-value-bind (macrofun nonterminating)
  185. (get-macro-character el)
  186. (and macrofun
  187. (not nonterminating)))))
  188. str))
  189. (concatenate 'string "|" (funcall case-converter-with-escaping str) "|")
  190. (funcall case-converter str)))))
  191. (defun determine-case (string)
  192. "Return two booleans LOWER and UPPER indicating whether STRING
  193. contains lower or upper case characters."
  194. (values (some #'lower-case-p string)
  195. (some #'upper-case-p string)))
  196. ;;;;; Compound-prefix matching
  197. (defun make-compound-prefix-matcher (delimiter &key (test #'char=))
  198. "Returns a matching function that takes a `prefix' and a
  199. `target' string and which returns T if `prefix' is a
  200. compound-prefix of `target', and otherwise NIL.
  201. Viewing each of `prefix' and `target' as a series of substrings
  202. delimited by DELIMITER, if each substring of `prefix' is a prefix
  203. of the corresponding substring in `target' then we call `prefix'
  204. a compound-prefix of `target'.
  205. DELIMITER may be a character, or a list of characters."
  206. (let ((delimiters (etypecase delimiter
  207. (character (list delimiter))
  208. (cons (assert (every #'characterp delimiter))
  209. delimiter))))
  210. (lambda (prefix target)
  211. (declare (type simple-string prefix target))
  212. (loop with tpos = 0
  213. for ch across prefix
  214. always (and (< tpos (length target))
  215. (let ((delimiter (car (member ch delimiters :test test))))
  216. (if delimiter
  217. (setf tpos (position delimiter target :start tpos))
  218. (funcall test ch (aref target tpos)))))
  219. do (incf tpos)))))
  220. ;;;;; Extending the input string by completion
  221. (defun longest-compound-prefix (completions &optional (delimiter #\-))
  222. "Return the longest compound _prefix_ for all COMPLETIONS."
  223. (flet ((tokenizer (string) (tokenize-completion string delimiter)))
  224. (untokenize-completion
  225. (loop for token-list in (transpose-lists (mapcar #'tokenizer completions))
  226. if (notevery #'string= token-list (rest token-list))
  227. ;; Note that we possibly collect the "" here as well, so that
  228. ;; UNTOKENIZE-COMPLETION will append a delimiter for us.
  229. collect (longest-common-prefix token-list)
  230. and do (loop-finish)
  231. else collect (first token-list))
  232. delimiter)))
  233. (defun tokenize-completion (string delimiter)
  234. "Return all substrings of STRING delimited by DELIMITER."
  235. (loop with end
  236. for start = 0 then (1+ end)
  237. until (> start (length string))
  238. do (setq end (or (position delimiter string :start start) (length string)))
  239. collect (subseq string start end)))
  240. (defun untokenize-completion (tokens &optional (delimiter #\-))
  241. (format nil (format nil "~~{~~A~~^~a~~}" delimiter) tokens))
  242. (defun transpose-lists (lists)
  243. "Turn a list-of-lists on its side.
  244. If the rows are of unequal length, truncate uniformly to the shortest.
  245. For example:
  246. \(transpose-lists '((ONE TWO THREE) (1 2)))
  247. => ((ONE 1) (TWO 2))"
  248. (cond ((null lists) '())
  249. ((some #'null lists) '())
  250. (t (cons (mapcar #'car lists)
  251. (transpose-lists (mapcar #'cdr lists))))))
  252. ;;;; Completion for character names
  253. (defslimefun completions-for-character (prefix)
  254. (let* ((matcher (make-compound-prefix-matcher #\_ :test #'char-equal))
  255. (completion-set (character-completion-set prefix matcher))
  256. (completions (sort completion-set #'string<)))
  257. (list completions (longest-compound-prefix completions #\_))))
  258. (provide :swank-c-p-c)