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