-
-
Notifications
You must be signed in to change notification settings - Fork 93
/
racket-complete.el
186 lines (163 loc) · 7.2 KB
/
racket-complete.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
;;; racket-complete.el -*- lexical-binding: t -*-
;; Copyright (c) 2013-2024 by Greg Hendershott.
;; Portions Copyright (C) 1985-1986, 1999-2013 Free Software Foundation, Inc.
;; Author: Greg Hendershott
;; URL: https://github.com/greghendershott/racket-mode
;; SPDX-License-Identifier: GPL-3.0-or-later
(require 'racket-common)
(defun racket--call-with-completion-prefix-positions (proc)
(cl-flet ((maybe-call (beg end)
(when (and (<= (+ beg 2) end) ;prefix at least 2 chars
(eq (line-number-at-pos beg)
(line-number-at-pos end)))
(funcall proc beg end))))
(if forward-sexp-function ;not necessarily sexp lang
(condition-case _
(save-excursion
(let ((beg (progn (forward-sexp -1) (point)))
(end (progn (forward-sexp 1) (point))))
(maybe-call beg end)))
(error nil))
(let ((beg (save-excursion (skip-syntax-backward "^-()>") (point))))
(unless (or (eq beg (point-max))
(member (char-syntax (char-after beg)) '(?\" ?\( ?\))))
(condition-case _
(save-excursion
(goto-char beg)
(forward-sexp 1)
(maybe-call beg (point)))
(error nil)))))))
(defun racket--in-require-form-p ()
(unless forward-sexp-function ;not necessarily sexp lang
(save-excursion
(save-match-data
(racket--escape-string-or-comment)
(let ((done nil)
(result nil))
(condition-case _
(while (not done)
(backward-up-list)
(when (looking-at-p (rx ?\( (or "require" "#%require")))
(setq done t)
(setq result t)))
(scan-error nil))
result)))))
;;; Completion tables with "category" metadata
(defconst racket--identifier-category 'racket-identifier
"Value for category metadata of identifier completion tables.")
;; Suggest default; can customize via `completion-category-overrides'.
(add-to-list 'completion-category-defaults
`(,racket--identifier-category (styles basic)))
(defconst racket--module-category 'racket-module
"Value for category metadata of module completion tables.")
;; Suggest default; can customize via `completion-category-overrides'.
(add-to-list 'completion-category-defaults
`(,racket--module-category (styles basic)))
(defun racket--completion-table (completions &optional metadata)
"Like `completion-table-dynamic' but also supplies metadata.
METADATA defaults to `((category . ,`racket--identifier-category')).
Although sometimes completion metadata is specified as properties
in a `completion-at-point-functions' item, sometimes that is
insufficient or irrelevant -- as with category metadata, or, when
CAPF isn't involved and instead the completion table is given
directly to `completing-read'.
Supplying category metadata allows the user to configure a
completion matching style for that category. It also prevents
third party packages like marginalia from misclassifying and
displaying inappropriate annotations."
(lambda (prefix predicate action)
(pcase action
('metadata
(cons 'metadata
(or metadata
`((category . ,racket--identifier-category)))))
(_
(complete-with-action action completions prefix predicate)))))
(defun racket--make-affix (specs &optional prop)
"Make an affixation-function to show completion annotations.
For more information about affixation-function completion
metadata, see Info node `(elisp)Programmed Completion'.
PROP is the symbol name of a text property that must be attached
to each of the completion candidate strings. The value of the
property is a list of strings -- each string is a suffix column
value to show as an annotation. The list length must be the same
for all candidate strings. The property name defaults to
\\='racket-affix.
SPECS is a vector of specs for each column -- one for the
completion candidate string, plus the length of the list of
suffix columns. Each spec may be an integer, which is a minimum
width, or [WIDTH FACE]. Note: The width is N/A for the last
suffix column. The face is N/A for the first column, which shows
the candidate string. For suffix columns, the face defaults to
completions-anntoations. An explicit nil value in the spec means
not to add a face, because the string is already propertized with
one.
The affixation-function arranges for each suffix column to be
aligned, considering the minimum width and the maximum width of
the previous column.
When a candidate string ends with text made invisible by a
\\='display \"\" property -- as is done by
`racket--doc-index-make-alist' -- that text is ignored for
purposes of calculating widths."
;; Note: Below we use `cl-loop' because `seq-do-indexed' and
;; `seq-map-indexed' are unavailable in Emacs 25.
(let ((min-widths (cl-loop
for spec across specs
collect (pcase spec
(`[,width ,_face] width)
((and (pred numberp) width) width)
(_ 0))))
(suffix-faces (cl-loop for spec across (seq-drop specs 1)
collect (pcase spec
(`[,_width ,face] face)
(_ 'completions-annotations))))
(prop (or prop 'racket-affix)))
(lambda (strs)
(let* ((max-widths (apply #'vector min-widths))
(rows
(cl-loop
for str in strs
collect
(let ((visible-str
(substring str
0
(text-property-any 0 (length str)
'display ""
str)))
(suffixes (get-text-property 0 prop str)))
;; Mutate `max-widths'.
(cl-loop
for col in (cons visible-str suffixes)
for ix from 0
do (aset max-widths ix
(max (aref max-widths ix)
(1+ (length col)))))
(cons str suffixes))))
(suffix-offsets
(let ((offset 0))
(cl-loop
for max-width across max-widths
collect
(setq offset (+ offset max-width))))))
(cl-loop
for row in rows
collect
(pcase-let*
((`(,str . ,suffixes) row)
(suffixes-str
(cl-loop
for suffix in suffixes
for offset in suffix-offsets
for face in suffix-faces
concat
(concat
(propertize " "
'display
`(space :align-to ,offset))
(if face
(propertize (or suffix "")
'face face)
(or suffix ""))))))
(list str "" suffixes-str)))))))
(provide 'racket-complete)
;; racket-complete.el ends here