]> code.delx.au - gnu-emacs-elpa/blob - company.el
Added manual start.
[gnu-emacs-elpa] / 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-begin ()
181 (when company-candidates
182 (company-cancel))
183 (let ((completion-ignore-case nil) ;; TODO: make this optional
184 prefix)
185 (dolist (backend company-backends)
186 (when (setq prefix (funcall backend 'prefix))
187 (when (company-should-complete prefix)
188 (setq company-backend backend
189 company-prefix prefix
190 company-candidates
191 (funcall company-backend 'candidates prefix)
192 company-common (try-completion prefix company-candidates)
193 company-selection 0
194 company-point (point)))
195 (return prefix)))
196 (unless (and company-candidates
197 (not (eq t company-common)))
198 (company-cancel))))
199
200 (defun company-cancel ()
201 (setq company-backend nil
202 company-prefix nil
203 company-candidates nil
204 company-common nil
205 company-selection 0
206 company-selection-changed nil
207 company-point nil)
208 (company-pseudo-tooltip-hide))
209
210 (defun company-pre-command ()
211 (company-preview-hide)
212 (company-pseudo-tooltip-hide))
213
214 (defun company-post-command ()
215 (unless (equal (point) company-point)
216 (company-begin))
217 (when company-candidates
218 (company-pseudo-tooltip-show-at-point (- (point) (length company-prefix))
219 company-candidates
220 company-selection)
221 (company-preview-show-at-point (point) company-candidates
222 company-selection)))
223
224 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
225
226 (defun company-select-next ()
227 (interactive)
228 (when (company-manual-begin)
229 (setq company-selection (min (1- (length company-candidates))
230 (1+ company-selection))
231 company-selection-changed t)))
232
233 (defun company-select-previous ()
234 (interactive)
235 (when (company-manual-begin)
236 (setq company-selection (max 0 (1- company-selection))
237 company-selection-changed t)))
238
239 (defun company-complete-selection ()
240 (interactive)
241 (when (company-manual-begin)
242 (insert (company-strip-prefix (nth company-selection company-candidates)))))
243
244 (defun company-complete-common ()
245 (interactive)
246 (when (company-manual-begin)
247 (insert (company-strip-prefix company-common))))
248
249 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
250
251 (defconst company-space-strings-limit 100)
252
253 (defconst company-space-strings
254 (let (lst)
255 (dotimes (i company-space-strings-limit)
256 (push (make-string (- company-space-strings-limit 1 i) ?\ ) lst))
257 (apply 'vector lst)))
258
259 (defsubst company-space-string (len)
260 (if (< len company-space-strings-limit)
261 (aref company-space-strings len)
262 (make-string len ?\ )))
263
264 (defsubst company-safe-substring (str from &optional to)
265 (let ((len (length str)))
266 (if (> from len)
267 ""
268 (if (and to (> to len))
269 (concat (substring str from)
270 (company-space-string (- to len)))
271 (substring str from to)))))
272
273 ;;; pseudo-tooltip ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
274
275 (defvar company-pseudo-tooltip-overlay nil)
276 (make-variable-buffer-local 'company-pseudo-tooltip-overlay)
277
278 ;;; propertize
279
280 (defun company-fill-propertize (line width selected)
281 (setq line (company-safe-substring line 0 width))
282 (add-text-properties 0 width
283 (list 'face (if selected
284 'company-tooltip-selection
285 'company-tooltip)) line)
286 (add-text-properties 0 (length company-common)
287 (list 'face (if selected
288 'company-tooltip-common-selection
289 'company-tooltip-common)) line)
290 line)
291
292 (defun company-fill-propertize-lines (column lines selection)
293 (let ((width 0)
294 (lines-copy lines)
295 (len (min company-tooltip-limit (length lines)))
296 new)
297 (dotimes (i len)
298 (setq width (max (length (pop lines-copy)) width)))
299 (setq width (min width (- (window-width) column)))
300 (dotimes (i len)
301 (push (company-fill-propertize (pop lines) width (equal i selection))
302 new))
303 (nreverse new)))
304
305 ;;; replace
306
307 (defun company-buffer-lines (beg end)
308 (goto-char beg)
309 (let ((row (cdr (posn-col-row (posn-at-point))))
310 lines)
311 (while (< (point) end)
312 (move-to-window-line (incf row))
313 (push (buffer-substring beg (min end (1- (point)))) lines)
314 (setq beg (point)))
315 (nreverse lines)))
316
317 (defun company-modify-line (old new offset)
318 (concat (company-safe-substring old 0 offset)
319 new
320 (company-safe-substring old (+ offset (length new)))))
321
322 (defun company-modified-substring (beg end lines column)
323 (let ((old (company-buffer-lines beg end))
324 new)
325 ;; Inject into old lines.
326 (while old
327 (push (company-modify-line (pop old) (pop lines) column) new))
328 ;; Append whole new lines.
329 (while lines
330 (push (company-modify-line "" (pop lines) column) new))
331 (concat (mapconcat 'identity (nreverse new) "\n")
332 "\n")))
333
334 ;; show
335
336 (defun company-pseudo-tooltip-show (row column lines &optional selection)
337 (company-pseudo-tooltip-hide)
338 (unless lines (error "No text provided"))
339 (save-excursion
340
341 ;; Scroll to offset.
342 (let ((offset (company-offset company-tooltip-limit)))
343 (setq lines (nthcdr offset lines))
344 (decf selection offset))
345
346 (setq lines (company-fill-propertize-lines column lines selection))
347
348
349 (move-to-column 0)
350 (move-to-window-line row)
351 (let ((beg (point))
352 (end (save-excursion
353 (move-to-window-line (min (window-height)
354 (+ row company-tooltip-limit)))
355 (point)))
356 str)
357
358 (setq company-pseudo-tooltip-overlay (make-overlay beg end))
359
360 (overlay-put company-pseudo-tooltip-overlay 'before-string
361 (company-modified-substring beg end lines column))
362 (overlay-put company-pseudo-tooltip-overlay 'invisible t)
363 (overlay-put company-pseudo-tooltip-overlay 'window (selected-window)))))
364
365 (defun company-pseudo-tooltip-show-at-point (pos text &optional selection)
366 (let ((col-row (posn-col-row (posn-at-point pos))))
367 (company-pseudo-tooltip-show (1+ (cdr col-row))
368 (car col-row) text selection)))
369
370 (defun company-pseudo-tooltip-hide ()
371 (when company-pseudo-tooltip-overlay
372 (delete-overlay company-pseudo-tooltip-overlay)
373 (setq company-pseudo-tooltip-overlay nil)))
374
375 ;;; overlay ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
376
377 (defvar company-preview-overlay nil)
378 (make-variable-buffer-local 'company-preview-overlay)
379
380 (defun company-preview-show-at-point (pos text &optional selection)
381 (company-preview-hide)
382
383 (setq company-preview-overlay (make-overlay pos pos))
384
385 (let ((completion (company-strip-prefix (nth company-selection
386 company-candidates))))
387 (and (equal pos (point))
388 (not (equal completion ""))
389 (add-text-properties 0 1 '(cursor t) completion))
390
391 (setq completion (propertize completion 'face 'company-preview))
392 (add-text-properties 0 (- (length company-common) (length company-prefix))
393 '(face company-preview-common) completion)
394
395 (overlay-put company-preview-overlay 'after-string completion)
396 (overlay-put company-preview-overlay 'window (selected-window))))
397
398 (defun company-preview-hide ()
399 (when company-preview-overlay
400 (delete-overlay company-preview-overlay)
401 (setq company-preview-overlay nil)))
402
403 (provide 'company)
404 ;;; company.el ends here