]> rtime.felk.cvut.cz Git - sojka/company-mode.git/blob - company.el
6166363927e5056c4cde58ae97257845bad45103
[sojka/company-mode.git] / 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 company-nxml company-css
97                               company-ispell)
98   "*"
99   :group 'company
100   :type '(repeat (function :tag "function" nil)))
101
102 (defcustom company-minimum-prefix-length 3
103   "*"
104   :group 'company
105   :type '(integer :tag "prefix length"))
106
107 (defvar company-timer nil)
108
109 (defun company-timer-set (variable value)
110   (set variable value)
111   (when company-timer (cancel-timer company-timer))
112   (when (numberp value)
113     (setq company-timer (run-with-idle-timer value t 'company-idle-begin))))
114
115 (defcustom company-idle-delay .7
116   "*"
117   :set 'company-timer-set
118   :group 'company
119   :type '(choice (const :tag "never (nil)" nil)
120                  (const :tag "immediate (t)" t)
121                  (number :tag "seconds")))
122
123 ;;; mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
124
125 (defvar company-mode-map
126   (let ((keymap (make-sparse-keymap)))
127     (define-key keymap (kbd "M-n") 'company-select-next)
128     (define-key keymap (kbd "M-p") 'company-select-previous)
129     (define-key keymap (kbd "M-<return>") 'company-complete-selection)
130     (define-key keymap "\t" 'company-complete-common)
131     keymap))
132
133 ;;;###autoload
134 (define-minor-mode company-mode
135   ""
136   nil " comp" company-mode-map
137   (if company-mode
138       (progn
139         (add-hook 'pre-command-hook 'company-pre-command nil t)
140         (add-hook 'post-command-hook 'company-post-command nil t)
141         (company-timer-set 'company-idle-delay
142                            company-idle-delay))
143     (remove-hook 'pre-command-hook 'company-pre-command t)
144     (remove-hook 'post-command-hook 'company-post-command t)
145     (company-cancel)
146     (kill-local-variable 'company-point)))
147
148 ;;; backends ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
149
150 (defun company-grab (regexp &optional expression)
151   (when (looking-back regexp)
152     (or (match-string-no-properties (or expression 0)) "")))
153
154 (defun company-in-string-or-comment (&optional point)
155   (let ((pos (syntax-ppss)))
156     (or (nth 3 pos) (nth 4 pos) (nth 7 pos))))
157
158 ;;; completion mechanism ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
159
160 (defvar company-backend nil)
161 (make-variable-buffer-local 'company-backend)
162
163 (defvar company-prefix nil)
164 (make-variable-buffer-local 'company-prefix)
165
166 (defvar company-candidates nil)
167 (make-variable-buffer-local 'company-candidates)
168
169 (defvar company-common nil)
170 (make-variable-buffer-local 'company-common)
171
172 (defvar company-selection 0)
173 (make-variable-buffer-local 'company-selection)
174
175 (defvar company-selection-changed nil)
176 (make-variable-buffer-local 'company-selection-changed)
177
178 (defvar company-point nil)
179 (make-variable-buffer-local 'company-point)
180
181 (defvar company-disabled-backends nil)
182
183 (defsubst company-strip-prefix (str)
184   (substring str (length company-prefix)))
185
186 (defsubst company-offset (display-limit)
187   (let ((offset (- company-selection display-limit -1)))
188     (max offset 0)))
189
190 (defsubst company-reformat (candidate)
191   ;; company-ispell needs this, because the results are always lower-case
192   ;; It's mory efficient to fix it only when they are displayed.
193   (concat company-prefix (substring candidate (length company-prefix))))
194
195 (defsubst company-should-complete (prefix)
196   (and (eq company-idle-delay t)
197        (>= (length prefix) company-minimum-prefix-length)))
198
199 (defsubst company-call-frontends (command)
200   (dolist (frontend company-frontends)
201     (funcall frontend command)))
202
203 (defun company-idle-begin ()
204   (and company-mode
205        (not company-candidates)
206        (not (equal (point) company-point))
207        (let ((company-idle-delay t))
208          (company-begin)
209          (company-post-command))))
210
211 (defun company-manual-begin ()
212   (and company-mode
213        (not company-candidates)
214        (let ((company-idle-delay t)
215              (company-minimum-prefix-length 0))
216          (company-begin)))
217   ;; Return non-nil if active.
218   company-candidates)
219
220 (defun company-continue-or-cancel ()
221   (when company-candidates
222     (let ((old-point (- company-point (length company-prefix)))
223           (company-idle-delay t)
224           (company-minimum-prefix-length 0))
225       ;; TODO: Make more efficient.
226       (setq company-candidates nil)
227       (company-begin)
228       (unless (and company-candidates
229                    (equal old-point (- company-point (length company-prefix))))
230         (company-cancel))
231       company-candidates)))
232
233 (defun company-begin ()
234   (or (company-continue-or-cancel)
235       (let (prefix)
236         (dolist (backend company-backends)
237           (unless (fboundp backend)
238             (ignore-errors (require backend nil t)))
239           (if (fboundp backend)
240               (when (setq prefix (funcall backend 'prefix))
241                 (when (company-should-complete prefix)
242                   (setq company-backend backend
243                         company-prefix prefix
244                         company-candidates
245                         (funcall company-backend 'candidates prefix)
246                         company-common
247                         (let ((completion-ignore-case (funcall backend
248                                                                'ignore-case)))
249                           (try-completion prefix company-candidates))
250                         company-selection 0
251                         company-point (point))
252                   (unless (funcall company-backend 'sorted)
253                     (setq company-candidates
254                           (sort company-candidates 'string<)))
255                   (company-call-frontends 'update))
256                 (return prefix))
257             (unless (memq backend company-disabled-backends)
258               (push backend company-disabled-backends)
259               (message "Company back-end '%s' could not be initialized"
260                        backend))))
261         (unless (and company-candidates
262                      (not (eq t company-common)))
263           (company-cancel)))))
264
265 (defun company-cancel ()
266   (setq company-backend nil
267         company-prefix nil
268         company-candidates nil
269         company-common nil
270         company-selection 0
271         company-selection-changed nil
272         company-point nil)
273   (company-call-frontends 'hide))
274
275 (defun company-abort ()
276   (company-cancel)
277   ;; Don't start again, unless started manually.
278   (setq company-point (point)))
279
280 (defun company-pre-command ()
281   (when company-candidates
282     (company-call-frontends 'pre-command)))
283
284 (defun company-post-command ()
285   (unless (equal (point) company-point)
286     (company-begin))
287   (when company-candidates
288     (company-call-frontends 'post-command)))
289
290 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
291
292 (defun company-select-next ()
293   (interactive)
294   (when (company-manual-begin)
295     (setq company-selection (min (1- (length company-candidates))
296                                  (1+ company-selection))
297           company-selection-changed t)))
298
299 (defun company-select-previous ()
300   (interactive)
301   (when (company-manual-begin)
302     (setq company-selection (max 0 (1- company-selection))
303           company-selection-changed t)))
304
305 (defun company-complete-selection ()
306   (interactive)
307   (when (company-manual-begin)
308     (insert (company-strip-prefix (nth company-selection company-candidates)))
309     (company-abort)))
310
311 (defun company-complete-common ()
312   (interactive)
313   (when (company-manual-begin)
314     (insert (company-strip-prefix company-common))))
315
316 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
317
318 (defconst company-space-strings-limit 100)
319
320 (defconst company-space-strings
321   (let (lst)
322     (dotimes (i company-space-strings-limit)
323       (push (make-string (- company-space-strings-limit 1 i) ?\  ) lst))
324     (apply 'vector lst)))
325
326 (defsubst company-space-string (len)
327   (if (< len company-space-strings-limit)
328       (aref company-space-strings len)
329     (make-string len ?\ )))
330
331 (defsubst company-safe-substring (str from &optional to)
332   (let ((len (length str)))
333     (if (> from len)
334         ""
335       (if (and to (> to len))
336           (concat (substring str from)
337                   (company-space-string (- to len)))
338         (substring str from to)))))
339
340 ;;; pseudo-tooltip ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
341
342 (defvar company-pseudo-tooltip-overlay nil)
343 (make-variable-buffer-local 'company-pseudo-tooltip-overlay)
344
345 ;;; propertize
346
347 (defun company-fill-propertize (line width selected)
348   (setq line (company-safe-substring line 0 width))
349   (add-text-properties 0 width
350                        (list 'face (if selected
351                                        'company-tooltip-selection
352                                      'company-tooltip)) line)
353   (add-text-properties 0 (length company-common)
354                        (list 'face (if selected
355                                        'company-tooltip-common-selection
356                                      'company-tooltip-common)) line)
357   line)
358
359 (defun company-fill-propertize-lines (column lines selection)
360   (let ((width 0)
361         (lines-copy lines)
362         (len (min company-tooltip-limit (length lines)))
363         new)
364     (dotimes (i len)
365       (setq width (max (length (pop lines-copy)) width)))
366     (setq width (min width (- (window-width) column)))
367     (dotimes (i len)
368       (push (company-fill-propertize (company-reformat (pop lines))
369                                      width (equal i selection))
370             new))
371     (nreverse new)))
372
373 ;;; replace
374
375 (defun company-buffer-lines (beg end)
376   (goto-char beg)
377   (let ((row (cdr (posn-col-row (posn-at-point))))
378         lines)
379     (while (< (point) end)
380       (move-to-window-line (incf row))
381       (push (buffer-substring beg (min end (1- (point)))) lines)
382       (setq beg (point)))
383     (nreverse lines)))
384
385 (defun company-modify-line (old new offset)
386   (concat (company-safe-substring old 0 offset)
387           new
388           (company-safe-substring old (+ offset (length new)))))
389
390 (defun company-modified-substring (beg end lines column nl)
391   (let ((old (company-buffer-lines beg end))
392         new)
393     ;; Inject into old lines.
394     (while old
395       (push (company-modify-line (pop old) (pop lines) column) new))
396     ;; Append whole new lines.
397     (while lines
398       (push (company-modify-line "" (pop lines) column) new))
399     (concat (when nl "\n")
400             (mapconcat 'identity (nreverse new) "\n")
401             "\n")))
402
403 ;; show
404
405 (defun company-pseudo-tooltip-show (row column lines selection)
406   (company-pseudo-tooltip-hide)
407   (unless lines (error "No text provided"))
408   (save-excursion
409
410     ;; Scroll to offset.
411     (let ((offset (company-offset company-tooltip-limit)))
412       (setq lines (nthcdr offset lines))
413       (decf selection offset))
414
415     (setq lines (company-fill-propertize-lines column lines selection))
416
417
418     (move-to-column 0)
419
420     (let ((nl (< (move-to-window-line row) row))
421           (beg (point))
422           (end (save-excursion
423                  (move-to-window-line (min (window-height)
424                                            (+ row company-tooltip-limit)))
425                  (point)))
426           str)
427
428       (setq company-pseudo-tooltip-overlay (make-overlay beg end))
429
430       (overlay-put company-pseudo-tooltip-overlay 'before-string
431                    (company-modified-substring beg end lines column nl))
432       (overlay-put company-pseudo-tooltip-overlay 'invisible t)
433       (overlay-put company-pseudo-tooltip-overlay 'window (selected-window)))))
434
435 (defun company-pseudo-tooltip-show-at-point (pos)
436   (let ((col-row (posn-col-row (posn-at-point pos))))
437     (company-pseudo-tooltip-show (1+ (cdr col-row)) (car col-row)
438                                  company-candidates company-selection)))
439
440 (defun company-pseudo-tooltip-hide ()
441   (when company-pseudo-tooltip-overlay
442     (delete-overlay company-pseudo-tooltip-overlay)
443     (setq company-pseudo-tooltip-overlay nil)))
444
445 (defun company-pseudo-tooltip-frontend (command)
446   (case command
447     ('pre-command (company-pseudo-tooltip-hide))
448     ('post-command (company-pseudo-tooltip-show-at-point
449                     (- (point) (length company-prefix))))
450     ('hide (company-pseudo-tooltip-hide))))
451
452 (defun company-pseudo-tooltip-unless-just-one-frontend (command)
453   (unless (and (eq command 'post-command)
454                (not (cdr company-candidates)))
455     (company-pseudo-tooltip-frontend command)))
456
457 ;;; overlay ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
458
459 (defvar company-preview-overlay nil)
460 (make-variable-buffer-local 'company-preview-overlay)
461
462 (defun company-preview-show-at-point (pos)
463   (company-preview-hide)
464
465   (setq company-preview-overlay (make-overlay pos pos))
466
467   (let ((completion (company-strip-prefix (nth company-selection
468                                                company-candidates))))
469     (and (equal pos (point))
470          (not (equal completion ""))
471          (add-text-properties 0 1 '(cursor t) completion))
472
473     (setq completion (propertize completion 'face 'company-preview))
474     (add-text-properties 0 (- (length company-common) (length company-prefix))
475                          '(face company-preview-common) completion)
476
477     (overlay-put company-preview-overlay 'after-string completion)
478     (overlay-put company-preview-overlay 'window (selected-window))))
479
480 (defun company-preview-hide ()
481   (when company-preview-overlay
482     (delete-overlay company-preview-overlay)
483     (setq company-preview-overlay nil)))
484
485 (defun company-preview-frontend (command)
486   (case command
487     ('pre-command (company-preview-hide))
488     ('post-command (company-preview-show-at-point (point)))
489     ('hide (company-preview-hide))))
490
491 (defun company-preview-if-just-one-frontend (command)
492   (unless (and (eq command 'post-command)
493                (cdr company-candidates))
494     (company-preview-frontend command)))
495
496 ;;; echo ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
497
498 (defvar company-echo-last-msg nil)
499 (make-variable-buffer-local 'company-echo-last-msg)
500
501 (defun company-echo-refresh ()
502   (let ((message-log-max nil))
503     (if company-echo-last-msg
504         (message "%s" company-echo-last-msg)
505       (message ""))))
506
507 (defun company-echo-show (candidates)
508
509   ;; Roll to selection.
510   (setq candidates (nthcdr company-selection candidates))
511
512   (let ((limit (window-width (minibuffer-window)))
513         (len -1)
514         comp msg)
515     (while candidates
516       (setq comp (company-reformat (pop candidates))
517             len (+ len 1 (length comp)))
518       (if (>= len limit)
519           (setq candidates nil)
520         (setq comp (propertize comp 'face 'company-echo))
521         (add-text-properties 0 (length company-common)
522                              '(face company-echo-common) comp)
523         (push comp msg)))
524
525     (setq company-echo-last-msg (mapconcat 'identity (nreverse msg) " "))
526     (company-echo-refresh)))
527
528 (defun company-echo-frontend (command)
529   (case command
530     ('pre-command (company-echo-refresh))
531     ('post-command (company-echo-show company-candidates))
532     ('hide (setq company-echo-last-msg nil))))
533
534 (provide 'company)
535 ;;; company.el ends here