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