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