-
-
Notifications
You must be signed in to change notification settings - Fork 93
/
racket-xp-complete.el
151 lines (124 loc) · 5.69 KB
/
racket-xp-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
;;; racket-xp-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 'seq)
(require 'racket-complete)
(require 'racket-describe)
(require 'racket-scribble-anchor)
(defvar-local racket--xp-completion-table-all nil
"A completion table of all bindings; for use by a CAPF.
Includes both imports and lexical bindings. Better for use by
`completion-at-point' in an edit buffer, because in general more
completion candidates offer more opportunities to minimize
typing.
The table includes category and affixation-function metadata; the
latter shows the module from which an identifier was imported,
when not a lexical binding.")
(defvar-local racket--xp-completion-table-imports nil
"A completion table of import bindings; for use in minibuffer.
Includes only imports, not lexical bindings. Definitely better
for use by commands that look up documentation. Sometimes better
for use by `completing-read' in the minibuffer, because that
returns strings stripped of all text properties -- unless a
command is able to find a suitable matching string in the buffer
and use its text properties.
The table includes category and affixation-function metadata.")
(defun racket--set-xp-binding-completions (mods+syms)
;; The back end gives us data optimized for space when serializing:
;;
;; ((modA symA0 symA1 ...)
;; (modB symB0 symB1 ...) ...)
;;
;; Reshape that to a list of strings, each propertized with its mod,
;; for use as completion table.
(let* ((all nil)
(imports nil)
(affixator (racket--make-affix [16 0]))
(metadata `((category . ,racket--identifier-category)
(affixation-function . ,affixator))))
(dolist (mod+syms mods+syms)
(pcase-let ((`(,mod . ,syms) mod+syms))
(dolist (sym syms)
(push (propertize sym 'racket-affix (list mod)) all)
(when mod
(push (propertize sym 'racket-affix (list mod)) imports)))))
(setq racket--xp-completion-table-all
(racket--completion-table all metadata))
(setq racket--xp-completion-table-imports
(racket--completion-table imports metadata))))
(defvar-local racket--xp-module-completions nil
"A completion table for available collection module paths.
Do not `setq' directly; instead call `racket--xp-set-module-completions'.")
(defun racket--set-xp-module-completions (completions)
(setq-local racket--xp-module-completions
(racket--completion-table completions
`((category . ,racket--module-category)))))
(defun racket-xp-complete-at-point ()
"A value for the variable `completion-at-point-functions'.
- Within a textually apparent \"require\" form, when completing:
- A symbol immediately after an opening paren: Candidates are
names of require transformers.
- Another symbol: Candidates are absolute module paths like
\"racket/path\".
- Anything `thing-at-point' thinks is a filename: Candidates
are from `completion-file-name-table'.
- Otherwise, when completing a symbol: Candidates are bindings as
found by drracket/check-syntax plus our own back end analysis
of imported bindings."
(if (racket--in-require-form-p)
(or (racket--call-with-completion-prefix-positions
(lambda (beg end)
(if (eq ?\( (char-syntax (char-before beg)))
(racket--xp-capf-require-transformers beg end)
(racket--xp-capf-absolute-module-paths beg end))))
(racket--xp-capf-relative-module-paths))
(racket--call-with-completion-prefix-positions
#'racket--xp-capf-bindings)))
(defun racket--xp-capf-bindings (beg end)
(list beg
end
racket--xp-completion-table-all
;; ^table metadata already has :affixation-function
:exclusive 'no
:company-location (racket--xp-make-company-location-proc)
:company-doc-buffer (racket--xp-make-company-doc-buffer-proc)))
(defun racket--xp-capf-require-transformers (beg end)
"Note: Currently this returns too many candidates -- all
available bindings, not just those that are require transformers.
Although not ideal, I think it's less-worse than having some
hardwired list of require transformers. In general with
completion candidates, if you have to err, better to err on the
side of too many not too few. Having said that, someday maybe our
back end could give us the exact subset of available bindings
that are require transformers."
(racket--xp-capf-bindings beg end))
(defun racket--xp-capf-absolute-module-paths (beg end)
(list beg
end
racket--xp-module-completions
:exclusive 'no))
(defun racket--xp-capf-relative-module-paths ()
(when-let (bounds (bounds-of-thing-at-point 'filename))
(list (car bounds)
(cdr bounds)
#'completion-file-name-table
:exclusive 'no)))
(defun racket--xp-make-company-location-proc ()
(when (racket--cmd-open-p)
(let ((how (racket-how-front-to-back (buffer-file-name))))
(lambda (str)
(let ((str (substring-no-properties str)))
(pcase (racket--cmd/await nil `(def ,how ,str))
(`(,path ,line ,_)
(cons (racket-file-name-back-to-front path) line))))))))
(defun racket--xp-make-company-doc-buffer-proc ()
(when (racket--cmd-open-p)
(let ((how (racket-how-front-to-back (buffer-file-name))))
(lambda (str)
(let ((str (substring-no-properties str)))
(racket--company-doc-buffer how str))))))
(provide 'racket-xp-complete)
;; racket-xp-complete.el ends here