]> code.delx.au - gnu-emacs-elpa/blob - company.el
Added preview overlay.
[gnu-emacs-elpa] / company.el
1 (eval-when-compile (require 'cl))
2
3 (defgroup company nil
4 ""
5 :group 'abbrev
6 :group 'convenience
7 :group 'maching)
8
9 (defface company-tooltip
10 '((t :background "yellow"
11 :foreground "black"))
12 "*"
13 :group 'company)
14
15 (defface company-tooltip-selection
16 '((t :background "orange1"
17 :foreground "black"))
18 "*"
19 :group 'company)
20
21 (defface company-tooltip-common
22 '((t :inherit company-tooltip
23 :foreground "red"))
24 "*"
25 :group 'company)
26
27 (defface company-tooltip-common-selection
28 '((t :inherit company-tooltip-selection
29 :foreground "red"))
30 "*"
31 :group 'company)
32
33 (defcustom company-tooltip-limit 10
34 "*"
35 :group 'company
36 :type 'integer)
37
38 (defface company-preview
39 '((t :background "blue4"
40 :foreground "wheat"))
41 "*"
42 :group 'company)
43
44 (defface company-preview-common
45 '((t :inherit company-preview
46 :foreground "red"))
47 "*"
48 :group 'company)
49
50 (defcustom company-backends '(company-elisp-completion)
51 "*"
52 :group 'company
53 :type '(repeat (function :tag "function" nil)))
54
55 ;;; mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
56
57 (defvar company-mode-map
58 (let ((keymap (make-sparse-keymap)))
59 (define-key keymap (kbd "M-n") 'company-select-next)
60 (define-key keymap (kbd "M-p") 'company-select-previous)
61 (define-key keymap (kbd "M-<return>") 'company-complete-selection)
62 (define-key keymap "\t" 'company-complete-common)
63 keymap))
64
65 ;;;###autoload
66 (define-minor-mode company-mode
67 ""
68 nil " comp" company-mode-map
69 (if company-mode
70 (progn
71 (add-hook 'pre-command-hook 'company-pre-command nil t)
72 (add-hook 'post-command-hook 'company-post-command nil t))
73 (remove-hook 'pre-command-hook 'company-pre-command t)
74 (remove-hook 'post-command-hook 'company-post-command t)
75 (company-cancel)))
76
77 ;;; backends ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
78
79 (defun company-grab (regexp &optional expression)
80 (when (looking-back regexp)
81 (or (match-string-no-properties (or expression 0)) "")))
82
83 (defun company-in-string-or-comment (&optional point)
84 (let ((pos (syntax-ppss)))
85 (or (nth 3 pos) (nth 4 pos) (nth 7 pos))))
86
87 ;;; elisp
88
89 (defvar company-lisp-symbol-regexp
90 "\\_<\\(\\sw\\|\\s_\\)+\\_>\\=")
91
92 (defun company-grab-lisp-symbol ()
93 (let ((prefix (or (company-grab company-lisp-symbol-regexp) "")))
94 (unless (and (company-in-string-or-comment (- (point) (length prefix)))
95 (/= (char-before (- (point) (length prefix))) ?`))
96 prefix)))
97
98 (defun company-elisp-completion (command &optional arg &rest ignored)
99 (case command
100 ('prefix (and (eq major-mode 'emacs-lisp-mode)
101 (company-grab-lisp-symbol)))
102 ('candidates (let ((completion-ignore-case nil))
103 (all-completions arg obarray
104 (lambda (symbol) (or (boundp symbol)
105 (fboundp symbol))))))))
106
107 ;;; completion mechanism ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
108
109 (defvar company-backend nil)
110 (make-variable-buffer-local 'company-backend)
111
112 (defvar company-prefix nil)
113 (make-variable-buffer-local 'company-prefix)
114
115 (defvar company-candidates nil)
116 (make-variable-buffer-local 'company-candidates)
117
118 (defvar company-common nil)
119 (make-variable-buffer-local 'company-common)
120
121 (defvar company-selection 0)
122 (make-variable-buffer-local 'company-selection)
123
124 (defvar company-selection-changed nil)
125 (make-variable-buffer-local 'company-selection-changed)
126
127 (defvar company-point nil)
128 (make-variable-buffer-local 'company-point)
129
130 (defsubst company-strip-prefix (str)
131 (substring str (length company-prefix)))
132
133 (defsubst company-offset (display-limit)
134 (let ((offset (- company-selection display-limit -1)))
135 (max offset 0)))
136
137 (defun company-begin ()
138 (let ((completion-ignore-case nil) ;; TODO: make this optional
139 prefix)
140 (dolist (backend company-backends)
141 (when (setq prefix (funcall backend 'prefix))
142 (setq company-backend backend
143 company-prefix prefix
144 company-candidates
145 (funcall company-backend 'candidates prefix)
146 company-common (try-completion prefix company-candidates)
147 company-selection 0
148 company-point (point))
149 (return prefix)))
150 (unless (and company-candidates
151 (not (eq t company-common)))
152 (company-cancel))))
153
154 (defun company-cancel ()
155 (setq company-backend nil
156 company-prefix nil
157 company-candidates nil
158 company-common nil
159 company-selection 0
160 company-selection-changed nil
161 company-point nil)
162 (company-pseudo-tooltip-hide))
163
164 (defun company-pre-command ()
165 (company-preview-hide)
166 (company-pseudo-tooltip-hide))
167
168 (defun company-post-command ()
169 (unless (equal (point) company-point)
170 (company-begin))
171 (when company-candidates
172 (company-pseudo-tooltip-show-at-point (- (point) (length company-prefix))
173 company-candidates
174 company-selection)
175 (company-preview-show-at-point (point) company-candidates
176 company-selection)))
177
178 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
179
180 (defun company-select-next ()
181 (interactive)
182 (setq company-selection (min (1- (length company-candidates))
183 (1+ company-selection))
184 company-selection-changed t))
185
186 (defun company-select-previous ()
187 (interactive)
188 (setq company-selection (max 0 (1- company-selection))
189 company-selection-changed t))
190
191 (defun company-complete-selection ()
192 (interactive)
193 (insert (company-strip-prefix (nth company-selection company-candidates))))
194
195 (defun company-complete-common ()
196 (interactive)
197 (insert (company-strip-prefix company-common)))
198
199 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
200
201 (defconst company-space-strings-limit 100)
202
203 (defconst company-space-strings
204 (let (lst)
205 (dotimes (i company-space-strings-limit)
206 (push (make-string (- company-space-strings-limit 1 i) ?\ ) lst))
207 (apply 'vector lst)))
208
209 (defsubst company-space-string (len)
210 (if (< len company-space-strings-limit)
211 (aref company-space-strings len)
212 (make-string len ?\ )))
213
214 (defsubst company-safe-substring (str from &optional to)
215 (let ((len (length str)))
216 (if (> from len)
217 ""
218 (if (and to (> to len))
219 (concat (substring str from)
220 (company-space-string (- to len)))
221 (substring str from to)))))
222
223 ;;; pseudo-tooltip ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
224
225 (defvar company-pseudo-tooltip-overlay nil)
226 (make-variable-buffer-local 'company-pseudo-tooltip-overlay)
227
228 ;;; propertize
229
230 (defun company-fill-propertize (line width selected)
231 (setq line (company-safe-substring line 0 width))
232 (add-text-properties 0 width
233 (list 'face (if selected
234 'company-tooltip-selection
235 'company-tooltip)) line)
236 (add-text-properties 0 (length company-common)
237 (list 'face (if selected
238 'company-tooltip-common-selection
239 'company-tooltip-common)) line)
240 line)
241
242 (defun company-fill-propertize-lines (column lines selection)
243 (let ((width 0)
244 (lines-copy lines)
245 (len (min company-tooltip-limit (length lines)))
246 new)
247 (dotimes (i len)
248 (setq width (max (length (pop lines-copy)) width)))
249 (setq width (min width (- (window-width) column)))
250 (dotimes (i len)
251 (push (company-fill-propertize (pop lines) width (equal i selection))
252 new))
253 (nreverse new)))
254
255 ;;; replace
256
257 (defun company-buffer-lines (beg end)
258 (goto-char beg)
259 (let ((row (cdr (posn-col-row (posn-at-point))))
260 lines)
261 (while (< (point) end)
262 (move-to-window-line (incf row))
263 (push (buffer-substring beg (min end (1- (point)))) lines)
264 (setq beg (point)))
265 (nreverse lines)))
266
267 (defun company-modify-line (old new offset)
268 (concat (company-safe-substring old 0 offset)
269 new
270 (company-safe-substring old (+ offset (length new)))))
271
272 (defun company-modified-substring (beg end lines column)
273 (let ((old (company-buffer-lines beg end))
274 new)
275 ;; Inject into old lines.
276 (while old
277 (push (company-modify-line (pop old) (pop lines) column) new))
278 ;; Append whole new lines.
279 (while lines
280 (push (company-modify-line "" (pop lines) column) new))
281 (concat (mapconcat 'identity (nreverse new) "\n")
282 "\n")))
283
284 ;; show
285
286 (defun company-pseudo-tooltip-show (row column lines &optional selection)
287 (company-pseudo-tooltip-hide)
288 (unless lines (error "No text provided"))
289 (save-excursion
290
291 ;; Scroll to offset.
292 (let ((offset (company-offset company-tooltip-limit)))
293 (setq lines (nthcdr offset lines))
294 (decf selection offset))
295
296 (setq lines (company-fill-propertize-lines column lines selection))
297
298
299 (move-to-column 0)
300 (move-to-window-line row)
301 (let ((beg (point))
302 (end (save-excursion
303 (move-to-window-line (min (window-height)
304 (+ row company-tooltip-limit)))
305 (point)))
306 str)
307
308 (setq company-pseudo-tooltip-overlay (make-overlay beg end))
309
310 (overlay-put company-pseudo-tooltip-overlay 'before-string
311 (company-modified-substring beg end lines column))
312 (overlay-put company-pseudo-tooltip-overlay 'invisible t)
313 (overlay-put company-pseudo-tooltip-overlay 'window (selected-window)))))
314
315 (defun company-pseudo-tooltip-show-at-point (pos text &optional selection)
316 (let ((col-row (posn-col-row (posn-at-point pos))))
317 (company-pseudo-tooltip-show (1+ (cdr col-row))
318 (car col-row) text selection)))
319
320 (defun company-pseudo-tooltip-hide ()
321 (when company-pseudo-tooltip-overlay
322 (delete-overlay company-pseudo-tooltip-overlay)
323 (setq company-pseudo-tooltip-overlay nil)))
324
325 ;;; overlay ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
326
327 (defvar company-preview-overlay nil)
328 (make-variable-buffer-local 'company-preview-overlay)
329
330 (defun company-preview-show-at-point (pos text &optional selection)
331 (company-preview-hide)
332
333 (setq company-preview-overlay (make-overlay pos pos))
334
335 (let ((completion (company-strip-prefix (nth company-selection
336 company-candidates))))
337 (and (equal pos (point))
338 (not (equal completion ""))
339 (add-text-properties 0 1 '(cursor t) completion))
340
341 (setq completion (propertize completion 'face 'company-preview))
342 (add-text-properties 0 (- (length company-common) (length company-prefix))
343 '(face company-preview-common) completion)
344
345 (overlay-put company-preview-overlay 'after-string completion)
346 (overlay-put company-preview-overlay 'window (selected-window))))
347
348 (defun company-preview-hide ()
349 (when company-preview-overlay
350 (delete-overlay company-preview-overlay)
351 (setq company-preview-overlay nil)))
352
353 (provide 'company)
354 ;;; company.el ends here