]> code.delx.au - gnu-emacs-elpa/blob - company.el
Protect against front-end errors.
[gnu-emacs-elpa] / company.el
1 ;;; company.el --- extensible inline text completion mechanism
2 ;;
3 ;; Copyright (C) 2009 Nikolaj Schumacher
4 ;;
5 ;; Author: Nikolaj Schumacher <bugs * nschum de>
6 ;; Version:
7 ;; Keywords: abbrev, convenience, matchis
8 ;; URL: http://nschum.de/src/emacs/company/
9 ;; Compatibility: GNU Emacs 23.x
10 ;;
11 ;; This file is NOT part of GNU Emacs.
12 ;;
13 ;; This program is free software; you can redistribute it and/or
14 ;; modify it under the terms of the GNU General Public License
15 ;; as published by the Free Software Foundation; either version 2
16 ;; of the License, or (at your option) any later version.
17 ;;
18 ;; This program is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;; GNU General Public License for more details.
22 ;;
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
25 ;;
26 ;;; Commentary:
27 ;;
28 ;;; Change Log:
29 ;;
30 ;; Initial release.
31 ;;
32 ;;; Code:
33
34 (eval-when-compile (require 'cl))
35
36 (add-to-list 'debug-ignored-errors
37 "^Pseudo tooltip frontend cannot be used twice$")
38 (add-to-list 'debug-ignored-errors "^Preview frontend cannot be used twice$")
39 (add-to-list 'debug-ignored-errors "^Echo area cannot be used twice$")
40 (add-to-list 'debug-ignored-errors "^No documentation available$")
41
42 (defgroup company nil
43 ""
44 :group 'abbrev
45 :group 'convenience
46 :group 'maching)
47
48 (defface company-tooltip
49 '((t :background "yellow"
50 :foreground "black"))
51 "*"
52 :group 'company)
53
54 (defface company-tooltip-selection
55 '((t :background "orange1"
56 :foreground "black"))
57 "*"
58 :group 'company)
59
60 (defface company-tooltip-common
61 '((t :inherit company-tooltip
62 :foreground "red"))
63 "*"
64 :group 'company)
65
66 (defface company-tooltip-common-selection
67 '((t :inherit company-tooltip-selection
68 :foreground "red"))
69 "*"
70 :group 'company)
71
72 (defcustom company-tooltip-limit 10
73 "*"
74 :group 'company
75 :type 'integer)
76
77 (defface company-preview
78 '((t :background "blue4"
79 :foreground "wheat"))
80 "*"
81 :group 'company)
82
83 (defface company-preview-common
84 '((t :inherit company-preview
85 :foreground "red"))
86 "*"
87 :group 'company)
88
89 (defface company-echo nil
90 "*"
91 :group 'company)
92
93 (defface company-echo-common
94 '((((background dark)) (:foreground "firebrick1"))
95 (((background light)) (:background "firebrick4")))
96 "*"
97 :group 'company)
98
99 (defun company-frontends-set (variable value)
100 ;; uniquify
101 (let ((remainder value))
102 (setcdr remainder (delq (car remainder) (cdr remainder))))
103 (and (memq 'company-pseudo-tooltip-unless-just-one-frontend value)
104 (memq 'company-pseudo-tooltip-frontend value)
105 (error "Pseudo tooltip frontend cannot be used twice"))
106 (and (memq 'company-preview-if-just-one-frontend value)
107 (memq 'company-preview-frontend value)
108 (error "Preview frontend cannot be used twice"))
109 (and (memq 'company-echo value)
110 (memq 'company-echo-metadata-frontend value)
111 (error "Echo area cannot be used twice"))
112 ;; preview must come last
113 (dolist (f '(company-preview-if-just-one-frontend company-preview-frontend))
114 (when (memq f value)
115 (setq value (append (delq f value) (list f)))))
116 (set variable value))
117
118 (defcustom company-frontends '(company-pseudo-tooltip-unless-just-one-frontend
119 company-preview-if-just-one-frontend
120 company-echo-metadata-frontend)
121 "*"
122 :set 'company-frontends-set
123 :group 'company
124 :type '(repeat (choice (const :tag "echo" company-echo-frontend)
125 (const :tag "pseudo tooltip"
126 company-pseudo-tooltip-frontend)
127 (const :tag "pseudo tooltip, multiple only"
128 company-pseudo-tooltip-unless-just-one-frontend)
129 (const :tag "preview" company-preview-frontend)
130 (const :tag "preview, unique only"
131 company-preview-if-just-one-frontend)
132 (function :tag "custom function" nil))))
133
134 (defcustom company-backends '(company-elisp company-nxml company-css
135 company-semantic company-oddmuse
136 company-files company-dabbrev)
137 "*"
138 :group 'company
139 :type '(repeat (function :tag "function" nil)))
140
141 (defcustom company-minimum-prefix-length 3
142 "*"
143 :group 'company
144 :type '(integer :tag "prefix length"))
145
146 (defcustom company-idle-delay .7
147 "*"
148 :group 'company
149 :type '(choice (const :tag "never (nil)" nil)
150 (const :tag "immediate (t)" t)
151 (number :tag "seconds")))
152
153 ;;; mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
154
155 (defvar company-mode-map (make-sparse-keymap))
156
157 (defvar company-active-map
158 (let ((keymap (make-sparse-keymap)))
159 (define-key keymap (kbd "M-n") 'company-select-next)
160 (define-key keymap (kbd "M-p") 'company-select-previous)
161 (define-key keymap "\C-m" 'company-complete-selection)
162 (define-key keymap "\t" 'company-complete-common)
163 (define-key keymap (kbd "<f1>") 'company-show-doc-buffer)
164 (define-key keymap "\C-s" 'company-search-candidates)
165 keymap))
166
167 ;;;###autoload
168 (define-minor-mode company-mode
169 ""
170 nil " comp" company-mode-map
171 (if company-mode
172 (progn
173 (add-hook 'pre-command-hook 'company-pre-command nil t)
174 (add-hook 'post-command-hook 'company-post-command nil t))
175 (remove-hook 'pre-command-hook 'company-pre-command t)
176 (remove-hook 'post-command-hook 'company-post-command t)
177 (company-cancel)
178 (kill-local-variable 'company-point)))
179
180 ;;; keymaps ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
181
182 (defvar company-overriding-keymap-bound nil)
183 (make-variable-buffer-local 'company-overriding-keymap-bound)
184
185 (defvar company-old-keymap nil)
186 (make-variable-buffer-local 'company-old-keymap)
187
188 (defvar company-my-keymap nil)
189 (make-variable-buffer-local 'company-my-keymap)
190
191 (defsubst company-enable-overriding-keymap (keymap)
192 (setq company-my-keymap keymap)
193 (when company-overriding-keymap-bound
194 (company-uninstall-map)))
195
196 (defun company-install-map ()
197 (unless (or company-overriding-keymap-bound
198 (null company-my-keymap))
199 (setq company-old-keymap overriding-terminal-local-map
200 overriding-terminal-local-map company-my-keymap
201 company-overriding-keymap-bound t)))
202
203 (defun company-uninstall-map ()
204 (when (and company-overriding-keymap-bound
205 (eq overriding-terminal-local-map company-my-keymap))
206 (setq overriding-terminal-local-map company-old-keymap
207 company-overriding-keymap-bound nil)))
208
209 ;; Hack:
210 ;; Emacs calculates the active keymaps before reading the event. That means we
211 ;; cannot change the keymap from a timer. So we send a bogus command.
212 (defun company-ignore ()
213 (interactive))
214
215 (global-set-key '[31415926] 'company-ignore)
216
217 (defun company-input-noop ()
218 (push 31415926 unread-command-events))
219
220 ;;; backends ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
221
222 (defun company-grab (regexp &optional expression)
223 (when (looking-back regexp)
224 (or (match-string-no-properties (or expression 0)) "")))
225
226 (defun company-in-string-or-comment (&optional point)
227 (let ((pos (syntax-ppss)))
228 (or (nth 3 pos) (nth 4 pos) (nth 7 pos))))
229
230 ;;; completion mechanism ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
231
232 (defvar company-backend nil)
233 (make-variable-buffer-local 'company-backend)
234
235 (defvar company-prefix nil)
236 (make-variable-buffer-local 'company-prefix)
237
238 (defvar company-candidates nil)
239 (make-variable-buffer-local 'company-candidates)
240
241 (defvar company-candidates-cache nil)
242 (make-variable-buffer-local 'company-candidates-cache)
243
244 (defvar company-candidates-predicate nil)
245 (make-variable-buffer-local 'company-candidates-predicate)
246
247 (defvar company-common nil)
248 (make-variable-buffer-local 'company-common)
249
250 (defvar company-selection 0)
251 (make-variable-buffer-local 'company-selection)
252
253 (defvar company-selection-changed nil)
254 (make-variable-buffer-local 'company-selection-changed)
255
256 (defvar company-point nil)
257 (make-variable-buffer-local 'company-point)
258
259 (defvar company-timer nil)
260
261 (defvar company-disabled-backends nil)
262
263 (defsubst company-strip-prefix (str)
264 (substring str (length company-prefix)))
265
266 (defsubst company-reformat (candidate)
267 ;; company-ispell needs this, because the results are always lower-case
268 ;; It's mory efficient to fix it only when they are displayed.
269 (concat company-prefix (substring candidate (length company-prefix))))
270
271 (defsubst company-should-complete (prefix)
272 (and (eq company-idle-delay t)
273 (>= (length prefix) company-minimum-prefix-length)))
274
275 (defsubst company-call-frontends (command)
276 (dolist (frontend company-frontends)
277 (condition-case err
278 (funcall frontend command)
279 (error (error "Company: Front-end %s error \"%s\" on command %s"
280 frontend (error-message-string err) command)))))
281
282 (defsubst company-set-selection (selection &optional force-update)
283 (setq selection (max 0 (min (1- (length company-candidates)) selection)))
284 (when (or force-update (not (equal selection company-selection)))
285 (setq company-selection selection
286 company-selection-changed t)
287 (company-call-frontends 'update)))
288
289 (defun company-apply-predicate (candidates predicate)
290 (let (new)
291 (dolist (c candidates)
292 (when (funcall predicate c)
293 (push c new)))
294 (nreverse new)))
295
296 (defun company-update-candidates (candidates)
297 (if (> company-selection 0)
298 ;; Try to restore the selection
299 (let ((selected (nth company-selection company-candidates)))
300 (setq company-selection 0
301 company-candidates candidates)
302 (when selected
303 (while (and candidates (string< (pop candidates) selected))
304 (incf company-selection))
305 (unless candidates
306 ;; Make sure selection isn't out of bounds.
307 (setq company-selection (min (1- (length company-candidates))
308 company-selection)))))
309 (setq company-selection 0
310 company-candidates candidates))
311 ;; Calculate common.
312 (let ((completion-ignore-case (funcall company-backend 'ignore-case)))
313 (setq company-common (try-completion company-prefix company-candidates)))
314 (when (eq company-common t)
315 (setq company-candidates nil)))
316
317 (defsubst company-calculate-candidates (prefix)
318 (setq company-prefix prefix)
319 (company-update-candidates
320 (or (cdr (assoc prefix company-candidates-cache))
321 (let ((len (length prefix))
322 (completion-ignore-case (funcall company-backend 'ignore-case))
323 prev)
324 (dotimes (i len)
325 (when (setq prev (cdr (assoc (substring prefix 0 (- len i))
326 company-candidates-cache)))
327 (return (all-completions prefix prev)))))
328 (let ((candidates (funcall company-backend 'candidates prefix)))
329 (and company-candidates-predicate
330 (setq candidates
331 (company-apply-predicate candidates
332 company-candidates-predicate)))
333 (unless (funcall company-backend 'sorted)
334 (setq candidates (sort candidates 'string<)))
335 candidates)))
336 (unless (assoc prefix company-candidates-cache)
337 (push (cons prefix company-candidates) company-candidates-cache))
338 company-candidates)
339
340 (defun company-idle-begin (buf win tick pos)
341 (and company-mode
342 (eq buf (current-buffer))
343 (eq win (selected-window))
344 (eq tick (buffer-chars-modified-tick))
345 (eq pos (point))
346 (not company-candidates)
347 (not (equal (point) company-point))
348 (let ((company-idle-delay t))
349 (company-begin)
350 (when company-candidates
351 (company-input-noop)
352 (company-post-command)))))
353
354 (defun company-manual-begin ()
355 (and company-mode
356 (not company-candidates)
357 (let ((company-idle-delay t)
358 (company-minimum-prefix-length 0))
359 (company-begin)))
360 ;; Return non-nil if active.
361 company-candidates)
362
363 (defun company-continue ()
364 (when company-candidates
365 (when (funcall company-backend 'no-cache)
366 ;; Don't complete existing candidates, fetch new ones.
367 (setq company-candidates-cache nil))
368 (let ((new-prefix (funcall company-backend 'prefix)))
369 (unless (and (= (- (point) (length new-prefix))
370 (- company-point (length company-prefix)))
371 (or (equal company-prefix new-prefix)
372 (company-calculate-candidates new-prefix)))
373 (setq company-candidates nil)))))
374
375 (defun company-begin ()
376 (if (or buffer-read-only overriding-terminal-local-map overriding-local-map)
377 ;; Don't complete in these cases.
378 (setq company-candidates nil)
379 (company-continue)
380 (unless company-candidates
381 (let (prefix)
382 (dolist (backend company-backends)
383 (unless (fboundp backend)
384 (ignore-errors (require backend nil t)))
385 (if (fboundp backend)
386 (when (setq prefix (funcall backend 'prefix))
387 (when (company-should-complete prefix)
388 (setq company-backend backend)
389 (company-calculate-candidates prefix))
390 (return prefix))
391 (unless (memq backend company-disabled-backends)
392 (push backend company-disabled-backends)
393 (message "Company back-end '%s' could not be initialized"
394 backend)))))))
395 (if company-candidates
396 (progn
397 (setq company-point (point))
398 (company-enable-overriding-keymap company-active-map)
399 (company-call-frontends 'update))
400 (company-cancel)))
401
402 (defun company-cancel ()
403 (setq company-backend nil
404 company-prefix nil
405 company-candidates nil
406 company-candidates-cache nil
407 company-candidates-predicate nil
408 company-common nil
409 company-selection 0
410 company-selection-changed nil
411 company-point nil)
412 (when company-timer
413 (cancel-timer company-timer))
414 (company-search-mode 0)
415 (company-call-frontends 'hide)
416 (company-enable-overriding-keymap nil))
417
418 (defun company-abort ()
419 (company-cancel)
420 ;; Don't start again, unless started manually.
421 (setq company-point (point)))
422
423 (defun company-pre-command ()
424 (unless (eq this-command 'company-show-doc-buffer)
425 (condition-case err
426 (when company-candidates
427 (company-call-frontends 'pre-command))
428 (error (message "Company: An error occurred in pre-command")
429 (message "%s" (error-message-string err))
430 (company-cancel))))
431 (when company-timer
432 (cancel-timer company-timer))
433 (company-uninstall-map))
434
435 (defun company-post-command ()
436 (unless (eq this-command 'company-show-doc-buffer)
437 (condition-case err
438 (progn
439 (unless (equal (point) company-point)
440 (company-begin))
441 (when company-candidates
442 (company-call-frontends 'post-command))
443 (when (numberp company-idle-delay)
444 (setq company-timer
445 (run-with-timer company-idle-delay nil 'company-idle-begin
446 (current-buffer) (selected-window)
447 (buffer-chars-modified-tick) (point)))))
448 (error (message "Company: An error occurred in post-command")
449 (message "%s" (error-message-string err))
450 (company-cancel))))
451 (company-install-map))
452
453 ;;; search ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
454
455 (defvar company-search-string nil)
456 (make-variable-buffer-local 'company-search-string)
457
458 (defvar company-search-lighter " Search: \"\"")
459 (make-variable-buffer-local 'company-search-lighter)
460
461 (defvar company-search-old-map nil)
462 (make-variable-buffer-local 'company-search-old-map)
463
464 (defvar company-search-old-selection 0)
465 (make-variable-buffer-local 'company-search-old-selection)
466
467 (defun company-search (text lines)
468 (let ((quoted (regexp-quote text))
469 (i 0))
470 (dolist (line lines)
471 (when (string-match quoted line (length company-prefix))
472 (return i))
473 (incf i))))
474
475 (defun company-search-printing-char ()
476 (interactive)
477 (setq company-search-string
478 (concat (or company-search-string "") (string last-command-event))
479 company-search-lighter (concat " Search: \"" company-search-string
480 "\""))
481 (let ((pos (company-search company-search-string
482 (nthcdr company-selection company-candidates))))
483 (if (null pos)
484 (ding)
485 (company-set-selection (+ company-selection pos) t))))
486
487 (defun company-search-repeat-forward ()
488 (interactive)
489 (let ((pos (company-search company-search-string
490 (cdr (nthcdr company-selection
491 company-candidates)))))
492 (if (null pos)
493 (ding)
494 (company-set-selection (+ company-selection pos 1) t))))
495
496 (defun company-search-repeat-backward ()
497 (interactive)
498 (let ((pos (company-search company-search-string
499 (nthcdr (- (length company-candidates)
500 company-selection)
501 (reverse company-candidates)))))
502 (if (null pos)
503 (ding)
504 (company-set-selection (- company-selection pos 1) t))))
505
506 (defun company-search-kill-others ()
507 (interactive)
508 (let ((predicate `(lambda (candidate)
509 (string-match ,company-search-string candidate))))
510 (setq company-candidates-predicate predicate)
511 (company-update-candidates (company-apply-predicate company-candidates
512 predicate))
513 (company-search-mode 0)
514 (company-call-frontends 'update)))
515
516 (defun company-search-abort ()
517 (interactive)
518 (company-set-selection company-search-old-selection t)
519 (company-search-mode 0))
520
521 (defun company-search-other-char ()
522 (interactive)
523 (company-search-mode 0)
524 (when last-input-event
525 (clear-this-command-keys t)
526 (setq unread-command-events (list last-input-event))))
527
528 (defvar company-search-map
529 (let ((i 0)
530 (keymap (make-keymap)))
531 (set-char-table-range (nth 1 keymap) (cons #x100 (max-char))
532 'company-search-printing-char)
533 (define-key keymap [t] 'company-search-other-char)
534 (while (< i ?\s)
535 (define-key keymap (make-string 1 i) 'company-search-other-char)
536 (incf i))
537 (while (< i 256)
538 (define-key keymap (vector i) 'company-search-printing-char)
539 (incf i))
540 (let ((meta-map (make-sparse-keymap)))
541 (define-key keymap (char-to-string meta-prefix-char) meta-map)
542 (define-key keymap [escape] meta-map))
543 (define-key keymap (vector meta-prefix-char t) 'company-search-other-char)
544 (define-key keymap "\e\e\e" 'company-search-other-char)
545 (define-key keymap [escape escape escape] 'company-search-other-char)
546
547 (define-key keymap "\C-g" 'company-search-abort)
548 (define-key keymap "\C-s" 'company-search-repeat-forward)
549 (define-key keymap "\C-r" 'company-search-repeat-backward)
550 (define-key keymap "\C-o" 'company-search-kill-others)
551 keymap))
552
553 (define-minor-mode company-search-mode
554 ""
555 nil company-search-lighter nil
556 (if company-search-mode
557 (if (company-manual-begin)
558 (progn
559 (setq company-search-old-selection company-selection)
560 (company-enable-overriding-keymap company-search-map)
561 (company-call-frontends 'update))
562 (setq company-search-mode nil))
563 (kill-local-variable 'company-search-string)
564 (kill-local-variable 'company-search-lighter)
565 (kill-local-variable 'company-search-old-selection)
566 (company-enable-overriding-keymap company-active-map)))
567
568 (defun company-search-candidates ()
569 (interactive)
570 (company-search-mode 1))
571
572 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
573
574 (defun company-select-next ()
575 (interactive)
576 (when (company-manual-begin)
577 (company-set-selection (1+ company-selection))))
578
579 (defun company-select-previous ()
580 (interactive)
581 (when (company-manual-begin)
582 (company-set-selection (1- company-selection))))
583
584 (defun company-complete-selection ()
585 (interactive)
586 (when (company-manual-begin)
587 (insert (company-strip-prefix (nth company-selection company-candidates)))
588 (company-abort)))
589
590 (defun company-complete-common ()
591 (interactive)
592 (when (company-manual-begin)
593 (insert (company-strip-prefix company-common))))
594
595 (defun company-complete ()
596 (interactive)
597 (when (company-manual-begin)
598 (if (or company-selection-changed
599 (eq last-command 'company-complete-common))
600 (call-interactively 'company-complete-selection)
601 (call-interactively 'company-complete-common)
602 (setq this-command 'company-complete-common))))
603
604 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
605
606 (defconst company-space-strings-limit 100)
607
608 (defconst company-space-strings
609 (let (lst)
610 (dotimes (i company-space-strings-limit)
611 (push (make-string (- company-space-strings-limit 1 i) ?\ ) lst))
612 (apply 'vector lst)))
613
614 (defsubst company-space-string (len)
615 (if (< len company-space-strings-limit)
616 (aref company-space-strings len)
617 (make-string len ?\ )))
618
619 (defsubst company-safe-substring (str from &optional to)
620 (let ((len (length str)))
621 (if (> from len)
622 ""
623 (if (and to (> to len))
624 (concat (substring str from)
625 (company-space-string (- to len)))
626 (substring str from to)))))
627
628 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
629
630 (defvar company-last-metadata nil)
631 (make-variable-buffer-local 'company-last-metadata)
632
633 (defun company-fetch-metadata ()
634 (let ((selected (nth company-selection company-candidates)))
635 (unless (equal selected (car company-last-metadata))
636 (setq company-last-metadata
637 (cons selected (funcall company-backend 'meta selected))))
638 (cdr company-last-metadata)))
639
640 (defun company-doc-buffer (&optional string)
641 (with-current-buffer (get-buffer-create "*Company meta-data*")
642 (erase-buffer)
643 (current-buffer)))
644
645 (defun company-show-doc-buffer ()
646 (interactive)
647 (when company-candidates
648 (save-window-excursion
649 (let* ((height (window-height))
650 (row (cdr (posn-col-row (posn-at-point))))
651 (selected (nth company-selection company-candidates))
652 (buffer (funcall company-backend 'doc-buffer selected)))
653 (if (not buffer)
654 (error "No documentation available.")
655 (display-buffer buffer)
656 (and (< (window-height) height)
657 (< (- (window-height) row 2) company-tooltip-limit)
658 (recenter (- (window-height) row 2)))
659 (while (eq 'scroll-other-window
660 (key-binding (vector (list (read-event)))))
661 (scroll-other-window))
662 (when last-input-event
663 (clear-this-command-keys t)
664 (setq unread-command-events (list last-input-event))))))))
665
666 ;;; pseudo-tooltip ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
667
668 (defvar company-pseudo-tooltip-overlay nil)
669 (make-variable-buffer-local 'company-pseudo-tooltip-overlay)
670
671 (defvar company-tooltip-offset 0)
672 (make-variable-buffer-local 'company-tooltip-offset)
673
674 (defun company-pseudo-tooltip-update-offset (selection num-lines limit)
675
676 (decf limit 2)
677 (setq company-tooltip-offset
678 (max (min selection company-tooltip-offset)
679 (- selection -1 limit)))
680
681 (when (<= company-tooltip-offset 1)
682 (incf limit)
683 (setq company-tooltip-offset 0))
684
685 (when (>= company-tooltip-offset (- num-lines limit 1))
686 (incf limit)
687 (when (= selection (1- num-lines))
688 (decf company-tooltip-offset)
689 (when (<= company-tooltip-offset 1)
690 (setq company-tooltip-offset 0)
691 (incf limit))))
692
693 limit)
694
695 ;;; propertize
696
697 (defun company-fill-propertize (line width selected)
698 (setq line (company-safe-substring line 0 width))
699 (add-text-properties 0 width (list 'face 'company-tooltip) line)
700 (add-text-properties 0 (length company-common)
701 (list 'face 'company-tooltip-common) line)
702 (when selected
703 (if (and company-search-string
704 (string-match (regexp-quote company-search-string) line
705 (length company-prefix)))
706 (progn
707 (add-text-properties (match-beginning 0) (match-end 0)
708 '(face company-tooltip-selection) line)
709 (when (< (match-beginning 0) (length company-common))
710 (add-text-properties (match-beginning 0) (length company-common)
711 '(face company-tooltip-common-selection)
712 line)))
713 (add-text-properties 0 width '(face company-tooltip-selection) line)
714 (add-text-properties 0 (length company-common)
715 (list 'face 'company-tooltip-common-selection)
716 line)))
717 line)
718
719 ;;; replace
720
721 (defun company-buffer-lines (beg end)
722 (goto-char beg)
723 (let ((row (cdr (posn-col-row (posn-at-point))))
724 lines)
725 (while (and (equal (move-to-window-line (incf row)) row)
726 (<= (point) end))
727 (push (buffer-substring beg (min end (1- (point)))) lines)
728 (setq beg (point)))
729 (unless (eq beg end)
730 (push (buffer-substring beg end) lines))
731 (nreverse lines)))
732
733 (defun company-modify-line (old new offset)
734 (concat (company-safe-substring old 0 offset)
735 new
736 (company-safe-substring old (+ offset (length new)))))
737
738 (defun company-replacement-string (old lines column nl)
739 (let (new)
740 ;; Inject into old lines.
741 (while old
742 (push (company-modify-line (pop old) (pop lines) column) new))
743 ;; Append whole new lines.
744 (while lines
745 (push (company-modify-line "" (pop lines) column) new))
746 (concat (when nl "\n")
747 (mapconcat 'identity (nreverse new) "\n")
748 "\n")))
749
750 (defun company-create-lines (column lines selection limit)
751
752 (let ((len (length lines))
753 width
754 lines-copy
755 previous
756 remainder
757 new)
758
759 ;; Scroll to offset.
760 (setq limit (company-pseudo-tooltip-update-offset selection len limit))
761
762 (when (> company-tooltip-offset 0)
763 (setq previous (format "...(%d)" company-tooltip-offset)))
764
765 (setq remainder (- len limit company-tooltip-offset)
766 remainder (when (> remainder 0)
767 (setq remainder (format "...(%d)" remainder))))
768
769 (decf selection company-tooltip-offset)
770 (setq width (min (length previous) (length remainder))
771 lines (nthcdr company-tooltip-offset lines)
772 len (min limit (length lines))
773 lines-copy lines)
774
775 (dotimes (i len)
776 (setq width (max (length (pop lines-copy)) width)))
777 (setq width (min width (- (window-width) column)))
778
779 (when previous
780 (push (propertize (company-safe-substring previous 0 width)
781 'face 'company-tooltip)
782 new))
783
784 (dotimes (i len)
785 (push (company-fill-propertize (company-reformat (pop lines))
786 width (equal i selection))
787 new))
788
789 (when remainder
790 (push (propertize (company-safe-substring remainder 0 width)
791 'face 'company-tooltip)
792 new))
793
794 (setq lines (nreverse new))))
795
796 ;; show
797
798 (defsubst company-pseudo-tooltip-height ()
799 "Calculate the appropriate tooltip height."
800 (max 3 (min company-tooltip-limit
801 (- (window-height) (cdr (posn-col-row (posn-at-point))) 2))))
802
803 (defun company-pseudo-tooltip-show (row column lines selection)
804 (company-pseudo-tooltip-hide)
805 (unless lines (error "No text provided"))
806 (save-excursion
807
808 (move-to-column 0)
809
810 (let* ((height (company-pseudo-tooltip-height))
811 (lines (company-create-lines column lines selection height))
812 (nl (< (move-to-window-line row) row))
813 (beg (point))
814 (end (save-excursion
815 (move-to-window-line (+ row height))
816 (point)))
817 (old-string (company-buffer-lines beg end))
818 str)
819
820 (setq company-pseudo-tooltip-overlay (make-overlay beg end))
821
822 (overlay-put company-pseudo-tooltip-overlay 'company-old old-string)
823 (overlay-put company-pseudo-tooltip-overlay 'company-column column)
824 (overlay-put company-pseudo-tooltip-overlay 'company-nl nl)
825 (overlay-put company-pseudo-tooltip-overlay 'company-before
826 (company-replacement-string old-string lines column nl))
827 (overlay-put company-pseudo-tooltip-overlay 'company-height height)
828
829 (overlay-put company-pseudo-tooltip-overlay 'window (selected-window)))))
830
831 (defun company-pseudo-tooltip-show-at-point (pos)
832 (let ((col-row (posn-col-row (posn-at-point pos))))
833 (company-pseudo-tooltip-show (1+ (cdr col-row)) (car col-row)
834 company-candidates company-selection)))
835
836 (defun company-pseudo-tooltip-edit (lines selection)
837 (let* ((old-string (overlay-get company-pseudo-tooltip-overlay 'company-old))
838 (column (overlay-get company-pseudo-tooltip-overlay 'company-column))
839 (nl (overlay-get company-pseudo-tooltip-overlay 'company-nl))
840 (height (overlay-get company-pseudo-tooltip-overlay 'company-height))
841 (lines (company-create-lines column lines selection height)))
842 (overlay-put company-pseudo-tooltip-overlay 'company-before
843 (company-replacement-string old-string lines column nl))))
844
845 (defun company-pseudo-tooltip-hide ()
846 (when company-pseudo-tooltip-overlay
847 (delete-overlay company-pseudo-tooltip-overlay)
848 (setq company-pseudo-tooltip-overlay nil)))
849
850 (defun company-pseudo-tooltip-hide-temporarily ()
851 (when (overlayp company-pseudo-tooltip-overlay)
852 (overlay-put company-pseudo-tooltip-overlay 'invisible nil)
853 (overlay-put company-pseudo-tooltip-overlay 'before-string nil)))
854
855 (defun company-pseudo-tooltip-unhide ()
856 (when company-pseudo-tooltip-overlay
857 (overlay-put company-pseudo-tooltip-overlay 'invisible t)
858 (overlay-put company-pseudo-tooltip-overlay 'before-string
859 (overlay-get company-pseudo-tooltip-overlay 'company-before))))
860
861 (defun company-pseudo-tooltip-frontend (command)
862 (case command
863 ('pre-command (company-pseudo-tooltip-hide-temporarily))
864 ('post-command
865 (unless (and (overlayp company-pseudo-tooltip-overlay)
866 (equal (overlay-get company-pseudo-tooltip-overlay
867 'company-height)
868 (company-pseudo-tooltip-height)))
869 ;; Redraw needed.
870 (company-pseudo-tooltip-show-at-point (- (point)
871 (length company-prefix))))
872 (company-pseudo-tooltip-unhide))
873 ('hide (company-pseudo-tooltip-hide)
874 (setq company-tooltip-offset 0))
875 ('update (when (overlayp company-pseudo-tooltip-overlay)
876 (company-pseudo-tooltip-edit company-candidates
877 company-selection)))))
878
879 (defun company-pseudo-tooltip-unless-just-one-frontend (command)
880 (unless (and (eq command 'post-command)
881 (not (cdr company-candidates)))
882 (company-pseudo-tooltip-frontend command)))
883
884 ;;; overlay ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
885
886 (defvar company-preview-overlay nil)
887 (make-variable-buffer-local 'company-preview-overlay)
888
889 (defun company-preview-show-at-point (pos)
890 (company-preview-hide)
891
892 (setq company-preview-overlay (make-overlay pos pos))
893
894 (let ((completion (company-strip-prefix (nth company-selection
895 company-candidates))))
896 (and (equal pos (point))
897 (not (equal completion ""))
898 (add-text-properties 0 1 '(cursor t) completion))
899
900 (setq completion (propertize completion 'face 'company-preview))
901 (add-text-properties 0 (- (length company-common) (length company-prefix))
902 '(face company-preview-common) completion)
903
904 (overlay-put company-preview-overlay 'after-string completion)
905 (overlay-put company-preview-overlay 'window (selected-window))))
906
907 (defun company-preview-hide ()
908 (when company-preview-overlay
909 (delete-overlay company-preview-overlay)
910 (setq company-preview-overlay nil)))
911
912 (defun company-preview-frontend (command)
913 (case command
914 ('pre-command (company-preview-hide))
915 ('post-command (company-preview-show-at-point (point)))
916 ('hide (company-preview-hide))))
917
918 (defun company-preview-if-just-one-frontend (command)
919 (unless (and (eq command 'post-command)
920 (cdr company-candidates))
921 (company-preview-frontend command)))
922
923 ;;; echo ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
924
925 (defvar company-echo-last-msg nil)
926 (make-variable-buffer-local 'company-echo-last-msg)
927
928 (defun company-echo-refresh ()
929 (let ((message-log-max nil))
930 (if company-echo-last-msg
931 (message "%s" company-echo-last-msg)
932 (message ""))))
933
934 (defun company-echo-show (candidates)
935
936 ;; Roll to selection.
937 (setq candidates (nthcdr company-selection candidates))
938
939 (let ((limit (window-width (minibuffer-window)))
940 (len -1)
941 comp msg)
942 (while candidates
943 (setq comp (company-reformat (pop candidates))
944 len (+ len 1 (length comp)))
945 (if (>= len limit)
946 (setq candidates nil)
947 (setq comp (propertize comp 'face 'company-echo))
948 (add-text-properties 0 (length company-common)
949 '(face company-echo-common) comp)
950 (push comp msg)))
951
952 (setq company-echo-last-msg (mapconcat 'identity (nreverse msg) " "))
953 (company-echo-refresh)))
954
955 (defun company-echo-frontend (command)
956 (case command
957 ('pre-command (company-echo-refresh))
958 ('post-command (company-echo-show company-candidates))
959 ('hide (setq company-echo-last-msg nil))))
960
961 (defun company-echo-metadata-frontend (command)
962 (case command
963 ('pre-command (company-echo-refresh))
964 ('post-command (setq company-echo-last-msg (company-fetch-metadata))
965 (company-echo-refresh))
966 ('hide (setq company-echo-last-msg nil))))
967
968
969 (provide 'company)
970 ;;; company.el ends here