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