]> rtime.felk.cvut.cz Git - sojka/company-mode.git/blob - company.el
Added echo front-end.
[sojka/company-mode.git] / 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 (defface company-echo nil
51   "*"
52   :group 'company)
53
54 (defface company-echo-common
55   '((((background dark)) (:foreground "firebrick1"))
56     (((background light)) (:background "firebrick4")))
57   "*"
58   :group 'company)
59
60 (defcustom company-backends '(company-elisp-completion)
61   "*"
62   :group 'company
63   :type '(repeat (function :tag "function" nil)))
64
65 (defcustom company-minimum-prefix-length 3
66   "*"
67   :group 'company
68   :type '(integer :tag "prefix length"))
69
70 (defvar company-timer nil)
71
72 (defun company-timer-set (variable value)
73   (set variable value)
74   (when company-timer (cancel-timer company-timer))
75   (when (numberp value)
76     (setq company-timer (run-with-idle-timer value t 'company-idle-begin))))
77
78 (defcustom company-idle-delay .7
79   "*"
80   :set 'company-timer-set
81   :group 'company
82   :type '(choice (const :tag "never (nil)" nil)
83                  (const :tag "immediate (t)" t)
84                  (number :tag "seconds")))
85
86 ;;; mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
87
88 (defvar company-mode-map
89   (let ((keymap (make-sparse-keymap)))
90     (define-key keymap (kbd "M-n") 'company-select-next)
91     (define-key keymap (kbd "M-p") 'company-select-previous)
92     (define-key keymap (kbd "M-<return>") 'company-complete-selection)
93     (define-key keymap "\t" 'company-complete-common)
94     keymap))
95
96 ;;;###autoload
97 (define-minor-mode company-mode
98   ""
99   nil " comp" company-mode-map
100   (if company-mode
101       (progn
102         (add-hook 'pre-command-hook 'company-pre-command nil t)
103         (add-hook 'post-command-hook 'company-post-command nil t)
104         (company-timer-set 'company-idle-delay
105                            company-idle-delay))
106     (remove-hook 'pre-command-hook 'company-pre-command t)
107     (remove-hook 'post-command-hook 'company-post-command t)
108     (company-cancel)))
109
110 ;;; backends ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
111
112 (defun company-grab (regexp &optional expression)
113   (when (looking-back regexp)
114     (or (match-string-no-properties (or expression 0)) "")))
115
116 (defun company-in-string-or-comment (&optional point)
117   (let ((pos (syntax-ppss)))
118     (or (nth 3 pos) (nth 4 pos) (nth 7 pos))))
119
120 ;;; elisp
121
122 (defvar company-lisp-symbol-regexp
123   "\\_<\\(\\sw\\|\\s_\\)+\\_>\\=")
124
125 (defun company-grab-lisp-symbol ()
126   (let ((prefix (or (company-grab company-lisp-symbol-regexp) "")))
127     (unless (and (company-in-string-or-comment (- (point) (length prefix)))
128                  (/= (char-before (- (point) (length prefix))) ?`))
129       prefix)))
130
131 (defun company-elisp-completion (command &optional arg &rest ignored)
132   (case command
133     ('prefix (and (eq major-mode 'emacs-lisp-mode)
134                   (company-grab-lisp-symbol)))
135     ('candidates (let ((completion-ignore-case nil))
136                    (all-completions arg obarray
137                                     (lambda (symbol) (or (boundp symbol)
138                                                          (fboundp symbol))))))))
139
140 ;;; completion mechanism ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
141
142 (defvar company-backend nil)
143 (make-variable-buffer-local 'company-backend)
144
145 (defvar company-prefix nil)
146 (make-variable-buffer-local 'company-prefix)
147
148 (defvar company-candidates nil)
149 (make-variable-buffer-local 'company-candidates)
150
151 (defvar company-common nil)
152 (make-variable-buffer-local 'company-common)
153
154 (defvar company-selection 0)
155 (make-variable-buffer-local 'company-selection)
156
157 (defvar company-selection-changed nil)
158 (make-variable-buffer-local 'company-selection-changed)
159
160 (defvar company-point nil)
161 (make-variable-buffer-local 'company-point)
162
163 (defsubst company-strip-prefix (str)
164   (substring str (length company-prefix)))
165
166 (defsubst company-offset (display-limit)
167   (let ((offset (- company-selection display-limit -1)))
168     (max offset 0)))
169
170 (defsubst company-should-complete (prefix)
171   (and (eq company-idle-delay t)
172        (>= (length prefix) company-minimum-prefix-length)))
173
174 (defun company-idle-begin ()
175   (and company-mode
176        (not company-candidates)
177        (let ((company-idle-delay t))
178          (company-begin)
179          (company-post-command))))
180
181 (defun company-manual-begin ()
182   (and company-mode
183        (not company-candidates)
184        (let ((company-idle-delay t)
185              (company-minimum-prefix-length 0))
186          (company-begin)))
187   ;; Return non-nil if active.
188   company-candidates)
189
190 (defun company-continue-or-cancel ()
191   (when company-candidates
192     (let ((old-point (- company-point (length company-prefix)))
193           (company-idle-delay t)
194           (company-minimum-prefix-length 0))
195       ;; TODO: Make more efficient.
196       (setq company-candidates nil)
197       (company-begin)
198       (unless (and company-candidates
199                    (equal old-point (- company-point (length company-prefix))))
200         (company-cancel))
201       company-candidates)))
202
203 (defun company-begin ()
204   (or (company-continue-or-cancel)
205       (let ((completion-ignore-case nil) ;; TODO: make this optional
206             prefix)
207         (dolist (backend company-backends)
208           (when (setq prefix (funcall backend 'prefix))
209             (when (company-should-complete prefix)
210               (setq company-backend backend
211                     company-prefix prefix
212                     company-candidates
213                     (funcall company-backend 'candidates prefix)
214                     company-common (try-completion prefix company-candidates)
215                     company-selection 0
216                     company-point (point)))
217             (return prefix)))
218         (unless (and company-candidates
219                      (not (eq t company-common)))
220           (company-cancel)))))
221
222 (defun company-cancel ()
223   (setq company-backend nil
224         company-prefix nil
225         company-candidates nil
226         company-common nil
227         company-selection 0
228         company-selection-changed nil
229         company-point nil)
230   (company-pseudo-tooltip-hide)
231   (company-echo-hide))
232
233 (defun company-pre-command ()
234   (company-preview-hide)
235   (company-pseudo-tooltip-hide)
236   (company-echo-refresh))
237
238 (defun company-post-command ()
239   (unless (equal (point) company-point)
240     (company-begin))
241   (when company-candidates
242     (company-echo-show company-candidates))
243     (company-pseudo-tooltip-show-at-point (- (point) (length company-prefix))
244                                           company-candidates
245                                           company-selection)
246     (company-preview-show-at-point (point) company-candidates
247                                    company-selection))
248
249 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
250
251 (defun company-select-next ()
252   (interactive)
253   (when (company-manual-begin)
254     (setq company-selection (min (1- (length company-candidates))
255                                  (1+ company-selection))
256           company-selection-changed t)))
257
258 (defun company-select-previous ()
259   (interactive)
260   (when (company-manual-begin)
261     (setq company-selection (max 0 (1- company-selection))
262           company-selection-changed t)))
263
264 (defun company-complete-selection ()
265   (interactive)
266   (when (company-manual-begin)
267     (insert (company-strip-prefix (nth company-selection company-candidates)))))
268
269 (defun company-complete-common ()
270   (interactive)
271   (when (company-manual-begin)
272     (insert (company-strip-prefix company-common))))
273
274 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
275
276 (defconst company-space-strings-limit 100)
277
278 (defconst company-space-strings
279   (let (lst)
280     (dotimes (i company-space-strings-limit)
281       (push (make-string (- company-space-strings-limit 1 i) ?\  ) lst))
282     (apply 'vector lst)))
283
284 (defsubst company-space-string (len)
285   (if (< len company-space-strings-limit)
286       (aref company-space-strings len)
287     (make-string len ?\ )))
288
289 (defsubst company-safe-substring (str from &optional to)
290   (let ((len (length str)))
291     (if (> from len)
292         ""
293       (if (and to (> to len))
294           (concat (substring str from)
295                   (company-space-string (- to len)))
296         (substring str from to)))))
297
298 ;;; pseudo-tooltip ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
299
300 (defvar company-pseudo-tooltip-overlay nil)
301 (make-variable-buffer-local 'company-pseudo-tooltip-overlay)
302
303 ;;; propertize
304
305 (defun company-fill-propertize (line width selected)
306   (setq line (company-safe-substring line 0 width))
307   (add-text-properties 0 width
308                        (list 'face (if selected
309                                        'company-tooltip-selection
310                                      'company-tooltip)) line)
311   (add-text-properties 0 (length company-common)
312                        (list 'face (if selected
313                                        'company-tooltip-common-selection
314                                      'company-tooltip-common)) line)
315   line)
316
317 (defun company-fill-propertize-lines (column lines selection)
318   (let ((width 0)
319         (lines-copy lines)
320         (len (min company-tooltip-limit (length lines)))
321         new)
322     (dotimes (i len)
323       (setq width (max (length (pop lines-copy)) width)))
324     (setq width (min width (- (window-width) column)))
325     (dotimes (i len)
326       (push (company-fill-propertize (pop lines) width (equal i selection))
327             new))
328     (nreverse new)))
329
330 ;;; replace
331
332 (defun company-buffer-lines (beg end)
333   (goto-char beg)
334   (let ((row (cdr (posn-col-row (posn-at-point))))
335         lines)
336     (while (< (point) end)
337       (move-to-window-line (incf row))
338       (push (buffer-substring beg (min end (1- (point)))) lines)
339       (setq beg (point)))
340     (nreverse lines)))
341
342 (defun company-modify-line (old new offset)
343   (concat (company-safe-substring old 0 offset)
344           new
345           (company-safe-substring old (+ offset (length new)))))
346
347 (defun company-modified-substring (beg end lines column)
348   (let ((old (company-buffer-lines beg end))
349         new)
350     ;; Inject into old lines.
351     (while old
352       (push (company-modify-line (pop old) (pop lines) column) new))
353     ;; Append whole new lines.
354     (while lines
355       (push (company-modify-line "" (pop lines) column) new))
356     (concat (mapconcat 'identity (nreverse new) "\n")
357             "\n")))
358
359 ;; show
360
361 (defun company-pseudo-tooltip-show (row column lines &optional selection)
362   (company-pseudo-tooltip-hide)
363   (unless lines (error "No text provided"))
364   (save-excursion
365
366     ;; Scroll to offset.
367     (let ((offset (company-offset company-tooltip-limit)))
368       (setq lines (nthcdr offset lines))
369       (decf selection offset))
370
371     (setq lines (company-fill-propertize-lines column lines selection))
372
373
374     (move-to-column 0)
375     (move-to-window-line row)
376     (let ((beg (point))
377           (end (save-excursion
378                  (move-to-window-line (min (window-height)
379                                            (+ row company-tooltip-limit)))
380                  (point)))
381           str)
382
383       (setq company-pseudo-tooltip-overlay (make-overlay beg end))
384
385       (overlay-put company-pseudo-tooltip-overlay 'before-string
386                    (company-modified-substring beg end lines column))
387       (overlay-put company-pseudo-tooltip-overlay 'invisible t)
388       (overlay-put company-pseudo-tooltip-overlay 'window (selected-window)))))
389
390 (defun company-pseudo-tooltip-show-at-point (pos text &optional selection)
391   (let ((col-row (posn-col-row (posn-at-point pos))))
392     (company-pseudo-tooltip-show (1+ (cdr col-row))
393                                  (car col-row) text selection)))
394
395 (defun company-pseudo-tooltip-hide ()
396   (when company-pseudo-tooltip-overlay
397     (delete-overlay company-pseudo-tooltip-overlay)
398     (setq company-pseudo-tooltip-overlay nil)))
399
400 ;;; overlay ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
401
402 (defvar company-preview-overlay nil)
403 (make-variable-buffer-local 'company-preview-overlay)
404
405 (defun company-preview-show-at-point (pos text &optional selection)
406   (company-preview-hide)
407
408   (setq company-preview-overlay (make-overlay pos pos))
409
410   (let ((completion (company-strip-prefix (nth company-selection
411                                                company-candidates))))
412     (and (equal pos (point))
413          (not (equal completion ""))
414          (add-text-properties 0 1 '(cursor t) completion))
415
416     (setq completion (propertize completion 'face 'company-preview))
417     (add-text-properties 0 (- (length company-common) (length company-prefix))
418                          '(face company-preview-common) completion)
419
420     (overlay-put company-preview-overlay 'after-string completion)
421     (overlay-put company-preview-overlay 'window (selected-window))))
422
423 (defun company-preview-hide ()
424   (when company-preview-overlay
425     (delete-overlay company-preview-overlay)
426     (setq company-preview-overlay nil)))
427
428 ;;; echo ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
429
430 (defvar company-echo-last-msg nil)
431 (make-variable-buffer-local 'company-echo-last-msg)
432
433 (defun company-echo-refresh ()
434   (let ((message-log-max nil))
435     (if company-echo-last-msg
436         (message "%s" company-echo-last-msg)
437       (message ""))))
438
439 (defun company-echo-show (candidates)
440
441   ;; Roll to selection.
442   (setq candidates (nthcdr company-selection candidates))
443
444   (let ((limit (window-width (minibuffer-window)))
445         (len 0)
446         comp msg)
447     (while candidates
448       (setq comp (pop candidates)
449             len (+ len 1 (length comp)))
450       (if (>= len limit)
451           (setq candidates nil)
452         (setq comp (propertize comp 'face 'company-echo))
453         (add-text-properties 0 (length company-common)
454                              '(face company-echo-common) comp)
455         (push comp msg)))
456
457     (setq company-echo-last-msg (mapconcat 'identity (nreverse msg) " "))
458     (company-echo-refresh)))
459
460 (defun company-echo-hide ()
461   (setq company-echo-last-msg nil))
462
463 (provide 'company)
464 ;;; company.el ends here