]> code.delx.au - gnu-emacs-elpa/blob - company.el
Moved back-ends to separate files.
[gnu-emacs-elpa] / company.el
1 (eval-when-compile (require 'cl))
2
3 (add-to-list 'debug-ignored-errors
4 "^Pseudo tooltip frontend cannot be used twice$")
5 (add-to-list 'debug-ignored-errors "^Preview frontend cannot be used twice$")
6
7 (defgroup company nil
8 ""
9 :group 'abbrev
10 :group 'convenience
11 :group 'maching)
12
13 (defface company-tooltip
14 '((t :background "yellow"
15 :foreground "black"))
16 "*"
17 :group 'company)
18
19 (defface company-tooltip-selection
20 '((t :background "orange1"
21 :foreground "black"))
22 "*"
23 :group 'company)
24
25 (defface company-tooltip-common
26 '((t :inherit company-tooltip
27 :foreground "red"))
28 "*"
29 :group 'company)
30
31 (defface company-tooltip-common-selection
32 '((t :inherit company-tooltip-selection
33 :foreground "red"))
34 "*"
35 :group 'company)
36
37 (defcustom company-tooltip-limit 10
38 "*"
39 :group 'company
40 :type 'integer)
41
42 (defface company-preview
43 '((t :background "blue4"
44 :foreground "wheat"))
45 "*"
46 :group 'company)
47
48 (defface company-preview-common
49 '((t :inherit company-preview
50 :foreground "red"))
51 "*"
52 :group 'company)
53
54 (defface company-echo nil
55 "*"
56 :group 'company)
57
58 (defface company-echo-common
59 '((((background dark)) (:foreground "firebrick1"))
60 (((background light)) (:background "firebrick4")))
61 "*"
62 :group 'company)
63
64 (defun company-frontends-set (variable value)
65 ;; uniquify
66 (let ((remainder value))
67 (setcdr remainder (delq (car remainder) (cdr remainder))))
68 (and (memq 'company-pseudo-tooltip-unless-just-one-frontend value)
69 (memq 'company-pseudo-tooltip-frontend value)
70 (error "Pseudo tooltip frontend cannot be used twice"))
71 (and (memq 'company-preview-if-just-one-frontend value)
72 (memq 'company-preview-frontend value)
73 (error "Preview frontend cannot be used twice"))
74 ;; preview must come last
75 (dolist (f '(company-preview-if-just-one-frontend company-preview-frontend))
76 (when (memq f value)
77 (setq value (append (delq f value) (list f)))))
78 (set variable value))
79
80 (defcustom company-frontends '(company-echo-frontend
81 company-pseudo-tooltip-unless-just-one-frontend
82 company-preview-if-just-one-frontend)
83 "*"
84 :set 'company-frontends-set
85 :group 'company
86 :type '(repeat (choice (const :tag "echo" company-echo-frontend)
87 (const :tag "pseudo tooltip"
88 company-pseudo-tooltip-frontend)
89 (const :tag "pseudo tooltip, multiple only"
90 company-pseudo-tooltip-unless-just-one-frontend)
91 (const :tag "preview" company-preview-frontend)
92 (const :tag "preview, unique only"
93 company-preview-if-just-one-frontend)
94 (function :tag "custom function" nil))))
95
96 (defcustom company-backends '(company-elisp)
97 "*"
98 :group 'company
99 :type '(repeat (function :tag "function" nil)))
100
101 (defcustom company-minimum-prefix-length 3
102 "*"
103 :group 'company
104 :type '(integer :tag "prefix length"))
105
106 (defvar company-timer nil)
107
108 (defun company-timer-set (variable value)
109 (set variable value)
110 (when company-timer (cancel-timer company-timer))
111 (when (numberp value)
112 (setq company-timer (run-with-idle-timer value t 'company-idle-begin))))
113
114 (defcustom company-idle-delay .7
115 "*"
116 :set 'company-timer-set
117 :group 'company
118 :type '(choice (const :tag "never (nil)" nil)
119 (const :tag "immediate (t)" t)
120 (number :tag "seconds")))
121
122 ;;; mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
123
124 (defvar company-mode-map
125 (let ((keymap (make-sparse-keymap)))
126 (define-key keymap (kbd "M-n") 'company-select-next)
127 (define-key keymap (kbd "M-p") 'company-select-previous)
128 (define-key keymap (kbd "M-<return>") 'company-complete-selection)
129 (define-key keymap "\t" 'company-complete-common)
130 keymap))
131
132 ;;;###autoload
133 (define-minor-mode company-mode
134 ""
135 nil " comp" company-mode-map
136 (if company-mode
137 (progn
138 (add-hook 'pre-command-hook 'company-pre-command nil t)
139 (add-hook 'post-command-hook 'company-post-command nil t)
140 (company-timer-set 'company-idle-delay
141 company-idle-delay))
142 (remove-hook 'pre-command-hook 'company-pre-command t)
143 (remove-hook 'post-command-hook 'company-post-command t)
144 (company-cancel)
145 (kill-local-variable 'company-point)))
146
147 ;;; backends ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
148
149 (defun company-grab (regexp &optional expression)
150 (when (looking-back regexp)
151 (or (match-string-no-properties (or expression 0)) "")))
152
153 (defun company-in-string-or-comment (&optional point)
154 (let ((pos (syntax-ppss)))
155 (or (nth 3 pos) (nth 4 pos) (nth 7 pos))))
156
157 ;;; completion mechanism ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
158
159 (defvar company-backend nil)
160 (make-variable-buffer-local 'company-backend)
161
162 (defvar company-prefix nil)
163 (make-variable-buffer-local 'company-prefix)
164
165 (defvar company-candidates nil)
166 (make-variable-buffer-local 'company-candidates)
167
168 (defvar company-common nil)
169 (make-variable-buffer-local 'company-common)
170
171 (defvar company-selection 0)
172 (make-variable-buffer-local 'company-selection)
173
174 (defvar company-selection-changed nil)
175 (make-variable-buffer-local 'company-selection-changed)
176
177 (defvar company-point nil)
178 (make-variable-buffer-local 'company-point)
179
180 (defvar company-disabled-backends nil)
181
182 (defsubst company-strip-prefix (str)
183 (substring str (length company-prefix)))
184
185 (defsubst company-offset (display-limit)
186 (let ((offset (- company-selection display-limit -1)))
187 (max offset 0)))
188
189 (defsubst company-should-complete (prefix)
190 (and (eq company-idle-delay t)
191 (>= (length prefix) company-minimum-prefix-length)))
192
193 (defsubst company-call-frontends (command)
194 (dolist (frontend company-frontends)
195 (funcall frontend command)))
196
197 (defun company-idle-begin ()
198 (and company-mode
199 (not company-candidates)
200 (not (equal (point) company-point))
201 (let ((company-idle-delay t))
202 (company-begin)
203 (company-post-command))))
204
205 (defun company-manual-begin ()
206 (and company-mode
207 (not company-candidates)
208 (let ((company-idle-delay t)
209 (company-minimum-prefix-length 0))
210 (company-begin)))
211 ;; Return non-nil if active.
212 company-candidates)
213
214 (defun company-continue-or-cancel ()
215 (when company-candidates
216 (let ((old-point (- company-point (length company-prefix)))
217 (company-idle-delay t)
218 (company-minimum-prefix-length 0))
219 ;; TODO: Make more efficient.
220 (setq company-candidates nil)
221 (company-begin)
222 (unless (and company-candidates
223 (equal old-point (- company-point (length company-prefix))))
224 (company-cancel))
225 company-candidates)))
226
227 (defun company-begin ()
228 (or (company-continue-or-cancel)
229 (let ((completion-ignore-case nil) ;; TODO: make this optional
230 prefix)
231 (dolist (backend company-backends)
232 (unless (fboundp backend)
233 (ignore-errors (require backend nil t)))
234 (if (fboundp backend)
235 (when (setq prefix (funcall backend 'prefix))
236 (when (company-should-complete prefix)
237 (setq company-backend backend
238 company-prefix prefix
239 company-candidates
240 (funcall company-backend 'candidates prefix)
241 company-common
242 (try-completion prefix company-candidates)
243 company-selection 0
244 company-point (point))
245 (unless (funcall company-backend 'sorted)
246 (setq company-candidates
247 (sort company-candidates 'string<)))
248 (company-call-frontends 'update))
249 (return prefix))
250 (unless (memq backend company-disabled-backends)
251 (push backend company-disabled-backends)
252 (message "Company back-end '%s' could not be initialized"
253 backend))))
254 (unless (and company-candidates
255 (not (eq t company-common)))
256 (company-cancel)))))
257
258 (defun company-cancel ()
259 (setq company-backend nil
260 company-prefix nil
261 company-candidates nil
262 company-common nil
263 company-selection 0
264 company-selection-changed nil
265 company-point nil)
266 (company-call-frontends 'hide))
267
268 (defun company-abort ()
269 (company-cancel)
270 ;; Don't start again, unless started manually.
271 (setq company-point (point)))
272
273 (defun company-pre-command ()
274 (when company-candidates
275 (company-call-frontends 'pre-command)))
276
277 (defun company-post-command ()
278 (unless (equal (point) company-point)
279 (company-begin))
280 (when company-candidates
281 (company-call-frontends 'post-command)))
282
283 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
284
285 (defun company-select-next ()
286 (interactive)
287 (when (company-manual-begin)
288 (setq company-selection (min (1- (length company-candidates))
289 (1+ company-selection))
290 company-selection-changed t)))
291
292 (defun company-select-previous ()
293 (interactive)
294 (when (company-manual-begin)
295 (setq company-selection (max 0 (1- company-selection))
296 company-selection-changed t)))
297
298 (defun company-complete-selection ()
299 (interactive)
300 (when (company-manual-begin)
301 (insert (company-strip-prefix (nth company-selection company-candidates)))
302 (company-abort)))
303
304 (defun company-complete-common ()
305 (interactive)
306 (when (company-manual-begin)
307 (insert (company-strip-prefix company-common))))
308
309 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
310
311 (defconst company-space-strings-limit 100)
312
313 (defconst company-space-strings
314 (let (lst)
315 (dotimes (i company-space-strings-limit)
316 (push (make-string (- company-space-strings-limit 1 i) ?\ ) lst))
317 (apply 'vector lst)))
318
319 (defsubst company-space-string (len)
320 (if (< len company-space-strings-limit)
321 (aref company-space-strings len)
322 (make-string len ?\ )))
323
324 (defsubst company-safe-substring (str from &optional to)
325 (let ((len (length str)))
326 (if (> from len)
327 ""
328 (if (and to (> to len))
329 (concat (substring str from)
330 (company-space-string (- to len)))
331 (substring str from to)))))
332
333 ;;; pseudo-tooltip ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
334
335 (defvar company-pseudo-tooltip-overlay nil)
336 (make-variable-buffer-local 'company-pseudo-tooltip-overlay)
337
338 ;;; propertize
339
340 (defun company-fill-propertize (line width selected)
341 (setq line (company-safe-substring line 0 width))
342 (add-text-properties 0 width
343 (list 'face (if selected
344 'company-tooltip-selection
345 'company-tooltip)) line)
346 (add-text-properties 0 (length company-common)
347 (list 'face (if selected
348 'company-tooltip-common-selection
349 'company-tooltip-common)) line)
350 line)
351
352 (defun company-fill-propertize-lines (column lines selection)
353 (let ((width 0)
354 (lines-copy lines)
355 (len (min company-tooltip-limit (length lines)))
356 new)
357 (dotimes (i len)
358 (setq width (max (length (pop lines-copy)) width)))
359 (setq width (min width (- (window-width) column)))
360 (dotimes (i len)
361 (push (company-fill-propertize (pop lines) width (equal i selection))
362 new))
363 (nreverse new)))
364
365 ;;; replace
366
367 (defun company-buffer-lines (beg end)
368 (goto-char beg)
369 (let ((row (cdr (posn-col-row (posn-at-point))))
370 lines)
371 (while (< (point) end)
372 (move-to-window-line (incf row))
373 (push (buffer-substring beg (min end (1- (point)))) lines)
374 (setq beg (point)))
375 (nreverse lines)))
376
377 (defun company-modify-line (old new offset)
378 (concat (company-safe-substring old 0 offset)
379 new
380 (company-safe-substring old (+ offset (length new)))))
381
382 (defun company-modified-substring (beg end lines column nl)
383 (let ((old (company-buffer-lines beg end))
384 new)
385 ;; Inject into old lines.
386 (while old
387 (push (company-modify-line (pop old) (pop lines) column) new))
388 ;; Append whole new lines.
389 (while lines
390 (push (company-modify-line "" (pop lines) column) new))
391 (concat (when nl "\n")
392 (mapconcat 'identity (nreverse new) "\n")
393 "\n")))
394
395 ;; show
396
397 (defun company-pseudo-tooltip-show (row column lines selection)
398 (company-pseudo-tooltip-hide)
399 (unless lines (error "No text provided"))
400 (save-excursion
401
402 ;; Scroll to offset.
403 (let ((offset (company-offset company-tooltip-limit)))
404 (setq lines (nthcdr offset lines))
405 (decf selection offset))
406
407 (setq lines (company-fill-propertize-lines column lines selection))
408
409
410 (move-to-column 0)
411
412 (let ((nl (< (move-to-window-line row) row))
413 (beg (point))
414 (end (save-excursion
415 (move-to-window-line (min (window-height)
416 (+ row company-tooltip-limit)))
417 (point)))
418 str)
419
420 (setq company-pseudo-tooltip-overlay (make-overlay beg end))
421
422 (overlay-put company-pseudo-tooltip-overlay 'before-string
423 (company-modified-substring beg end lines column nl))
424 (overlay-put company-pseudo-tooltip-overlay 'invisible t)
425 (overlay-put company-pseudo-tooltip-overlay 'window (selected-window)))))
426
427 (defun company-pseudo-tooltip-show-at-point (pos)
428 (let ((col-row (posn-col-row (posn-at-point pos))))
429 (company-pseudo-tooltip-show (1+ (cdr col-row)) (car col-row)
430 company-candidates company-selection)))
431
432 (defun company-pseudo-tooltip-hide ()
433 (when company-pseudo-tooltip-overlay
434 (delete-overlay company-pseudo-tooltip-overlay)
435 (setq company-pseudo-tooltip-overlay nil)))
436
437 (defun company-pseudo-tooltip-frontend (command)
438 (case command
439 ('pre-command (company-pseudo-tooltip-hide))
440 ('post-command (company-pseudo-tooltip-show-at-point
441 (- (point) (length company-prefix))))
442 ('hide (company-pseudo-tooltip-hide))))
443
444 (defun company-pseudo-tooltip-unless-just-one-frontend (command)
445 (unless (and (eq command 'post-command)
446 (not (cdr company-candidates)))
447 (company-pseudo-tooltip-frontend command)))
448
449 ;;; overlay ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
450
451 (defvar company-preview-overlay nil)
452 (make-variable-buffer-local 'company-preview-overlay)
453
454 (defun company-preview-show-at-point (pos)
455 (company-preview-hide)
456
457 (setq company-preview-overlay (make-overlay pos pos))
458
459 (let ((completion (company-strip-prefix (nth company-selection
460 company-candidates))))
461 (and (equal pos (point))
462 (not (equal completion ""))
463 (add-text-properties 0 1 '(cursor t) completion))
464
465 (setq completion (propertize completion 'face 'company-preview))
466 (add-text-properties 0 (- (length company-common) (length company-prefix))
467 '(face company-preview-common) completion)
468
469 (overlay-put company-preview-overlay 'after-string completion)
470 (overlay-put company-preview-overlay 'window (selected-window))))
471
472 (defun company-preview-hide ()
473 (when company-preview-overlay
474 (delete-overlay company-preview-overlay)
475 (setq company-preview-overlay nil)))
476
477 (defun company-preview-frontend (command)
478 (case command
479 ('pre-command (company-preview-hide))
480 ('post-command (company-preview-show-at-point (point)))
481 ('hide (company-preview-hide))))
482
483 (defun company-preview-if-just-one-frontend (command)
484 (unless (and (eq command 'post-command)
485 (cdr company-candidates))
486 (company-preview-frontend command)))
487
488 ;;; echo ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
489
490 (defvar company-echo-last-msg nil)
491 (make-variable-buffer-local 'company-echo-last-msg)
492
493 (defun company-echo-refresh ()
494 (let ((message-log-max nil))
495 (if company-echo-last-msg
496 (message "%s" company-echo-last-msg)
497 (message ""))))
498
499 (defun company-echo-show (candidates)
500
501 ;; Roll to selection.
502 (setq candidates (nthcdr company-selection candidates))
503
504 (let ((limit (window-width (minibuffer-window)))
505 (len -1)
506 comp msg)
507 (while candidates
508 (setq comp (pop candidates)
509 len (+ len 1 (length comp)))
510 (if (>= len limit)
511 (setq candidates nil)
512 (setq comp (propertize comp 'face 'company-echo))
513 (add-text-properties 0 (length company-common)
514 '(face company-echo-common) comp)
515 (push comp msg)))
516
517 (setq company-echo-last-msg (mapconcat 'identity (nreverse msg) " "))
518 (company-echo-refresh)))
519
520 (defun company-echo-frontend (command)
521 (case command
522 ('pre-command (company-echo-refresh))
523 ('post-command (company-echo-show company-candidates))
524 ('hide (setq company-echo-last-msg nil))))
525
526 (provide 'company)
527 ;;; company.el ends here