(defcustom company-backends '(company-elisp company-nxml company-css
company-semantic company-oddmuse
- company-dabbrev)
+ company-files company-dabbrev)
"*"
:group 'company
:type '(repeat (function :tag "function" nil)))
;;; mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(defvar company-current-map (make-sparse-keymap))
-
(defvar company-mode-map (make-sparse-keymap))
(defvar company-active-map
(let ((keymap (make-sparse-keymap)))
- (set-keymap-parent keymap company-mode-map)
(define-key keymap (kbd "M-n") 'company-select-next)
(define-key keymap (kbd "M-p") 'company-select-previous)
(define-key keymap "\C-m" 'company-complete-selection)
(define-key keymap "\t" 'company-complete-common)
(define-key keymap (kbd "<f1>") 'company-show-doc-buffer)
+ (define-key keymap "\C-s" 'company-search-candidates)
keymap))
;;;###autoload
(define-minor-mode company-mode
""
- nil " comp" nil
+ nil " comp" company-mode-map
(if company-mode
(progn
- (add-to-list 'minor-mode-overriding-map-alist
- (cons 'company-mode company-current-map))
(add-hook 'pre-command-hook 'company-pre-command nil t)
(add-hook 'post-command-hook 'company-post-command nil t)
(company-timer-set 'company-idle-delay
(company-cancel)
(kill-local-variable 'company-point)))
+;;; keymaps ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar company-overriding-keymap-bound nil)
+(make-variable-buffer-local 'company-overriding-keymap-bound)
+
+(defvar company-old-keymap nil)
+(make-variable-buffer-local 'company-old-keymap)
+
+(defvar company-my-keymap nil)
+(make-variable-buffer-local 'company-my-keymap)
+
+(defsubst company-enable-overriding-keymap (keymap)
+ (setq company-my-keymap keymap)
+ (when company-overriding-keymap-bound
+ (company-uninstall-map)))
+
+(defun company-install-map ()
+ (unless (or company-overriding-keymap-bound
+ (null company-my-keymap))
+ (setq company-old-keymap overriding-terminal-local-map
+ overriding-terminal-local-map company-my-keymap
+ company-overriding-keymap-bound t)))
+
+(defun company-uninstall-map ()
+ (when (and company-overriding-keymap-bound
+ (eq overriding-terminal-local-map company-my-keymap))
+ (setq overriding-terminal-local-map company-old-keymap
+ company-overriding-keymap-bound nil)))
+
+;; Hack:
+;; Emacs calculates the active keymaps before reading the event. That means we
+;; cannot change the keymap from a timer. So we send a bogus command.
+(defun company-ignore ()
+ (interactive))
+
+(global-set-key '[31415926] 'company-ignore)
+
+(defun company-input-noop ()
+ (push 31415926 unread-command-events))
+
;;; backends ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun company-grab (regexp &optional expression)
(defvar company-candidates-cache nil)
(make-variable-buffer-local 'company-candidates-cache)
+(defvar company-candidates-predicate nil)
+(make-variable-buffer-local 'company-candidates-predicate)
+
(defvar company-common nil)
(make-variable-buffer-local 'company-common)
(dolist (frontend company-frontends)
(funcall frontend command)))
-(defsubst company-calculate-candidates (prefix)
- (or (setq company-candidates (cdr (assoc prefix company-candidates-cache)))
- (let ((len (length prefix))
- (completion-ignore-case (funcall company-backend 'ignore-case))
- prev)
- (dotimes (i len)
- (when (setq prev (cdr (assoc (substring prefix 0 (- len i))
- company-candidates-cache)))
- (setq company-candidates (all-completions prefix prev))
- (return t))))
- (progn
- (setq company-candidates (funcall company-backend 'candidates prefix))
- (unless (funcall company-backend 'sorted)
- (setq company-candidates (sort company-candidates 'string<)))))
- (unless (assoc prefix company-candidates-cache)
- (push (cons prefix company-candidates) company-candidates-cache))
- (setq company-selection 0
- company-prefix prefix)
+(defsubst company-set-selection (selection &optional force-update)
+ (setq selection (max 0 (min (1- (length company-candidates)) selection)))
+ (when (or force-update (not (equal selection company-selection)))
+ (setq company-selection selection
+ company-selection-changed t)
+ (company-call-frontends 'update)))
+
+(defun company-apply-predicate (candidates predicate)
+ (let (new)
+ (dolist (c candidates)
+ (when (funcall predicate c)
+ (push c new)))
+ (nreverse new)))
+
+(defun company-update-candidates (candidates)
+ (if (> company-selection 0)
+ ;; Try to restore the selection
+ (let ((selected (nth company-selection company-candidates)))
+ (setq company-selection 0
+ company-candidates candidates)
+ (when selected
+ (while (and candidates (string< (pop candidates) selected))
+ (incf company-selection))
+ (unless candidates
+ ;; Make sure selection isn't out of bounds.
+ (setq company-selection (min (1- (length company-candidates))
+ company-selection)))))
+ (setq company-selection 0
+ company-candidates candidates))
+ ;; Calculate common.
(let ((completion-ignore-case (funcall company-backend 'ignore-case)))
(setq company-common (try-completion company-prefix company-candidates)))
(when (eq company-common t)
- (setq company-candidates nil))
+ (setq company-candidates nil)))
+
+(defsubst company-calculate-candidates (prefix)
+ (setq company-prefix prefix)
+ (company-update-candidates
+ (or (cdr (assoc prefix company-candidates-cache))
+ (let ((len (length prefix))
+ (completion-ignore-case (funcall company-backend 'ignore-case))
+ prev)
+ (dotimes (i len)
+ (when (setq prev (cdr (assoc (substring prefix 0 (- len i))
+ company-candidates-cache)))
+ (return (all-completions prefix prev)))))
+ (let ((candidates (funcall company-backend 'candidates prefix)))
+ (and company-candidates-predicate
+ (setq candidates
+ (company-apply-predicate candidates
+ company-candidates-predicate)))
+ (unless (funcall company-backend 'sorted)
+ (setq candidates (sort candidates 'string<)))
+ candidates)))
+ (unless (assoc prefix company-candidates-cache)
+ (push (cons prefix company-candidates) company-candidates-cache))
company-candidates)
(defun company-idle-begin ()
(not (equal (point) company-point))
(let ((company-idle-delay t))
(company-begin)
- (company-post-command))))
+ (when company-candidates
+ (company-input-noop)
+ (company-post-command)))))
(defun company-manual-begin ()
(and company-mode
(defun company-continue ()
(when company-candidates
+ (when (funcall company-backend 'no-cache)
+ ;; Don't complete existing candidates, fetch new ones.
+ (setq company-candidates-cache nil))
(let ((new-prefix (funcall company-backend 'prefix)))
(unless (and (= (- (point) (length new-prefix))
(- company-point (length company-prefix)))
(setq company-candidates nil)))))
(defun company-begin ()
- (company-continue)
- (unless company-candidates
- (let (prefix)
- (dolist (backend company-backends)
- (unless (fboundp backend)
- (ignore-errors (require backend nil t)))
- (if (fboundp backend)
- (when (setq prefix (funcall backend 'prefix))
- (when (company-should-complete prefix)
- (setq company-backend backend)
- (company-calculate-candidates prefix))
- (return prefix))
- (unless (memq backend company-disabled-backends)
- (push backend company-disabled-backends)
- (message "Company back-end '%s' could not be initialized"
- backend))))))
+ (if (or buffer-read-only overriding-terminal-local-map overriding-local-map)
+ ;; Don't complete in these cases.
+ (setq company-candidates nil)
+ (company-continue)
+ (unless company-candidates
+ (let (prefix)
+ (dolist (backend company-backends)
+ (unless (fboundp backend)
+ (ignore-errors (require backend nil t)))
+ (if (fboundp backend)
+ (when (setq prefix (funcall backend 'prefix))
+ (when (company-should-complete prefix)
+ (setq company-backend backend)
+ (company-calculate-candidates prefix))
+ (return prefix))
+ (unless (memq backend company-disabled-backends)
+ (push backend company-disabled-backends)
+ (message "Company back-end '%s' could not be initialized"
+ backend)))))))
(if company-candidates
(progn
(setq company-point (point))
- (set-keymap-parent company-current-map company-active-map)
+ (company-enable-overriding-keymap company-active-map)
(company-call-frontends 'update))
(company-cancel)))
company-prefix nil
company-candidates nil
company-candidates-cache nil
+ company-candidates-predicate nil
company-common nil
company-selection 0
company-selection-changed nil
company-point nil)
+ (company-search-mode 0)
(company-call-frontends 'hide)
- (set-keymap-parent company-current-map company-mode-map))
+ (company-enable-overriding-keymap nil))
(defun company-abort ()
(company-cancel)
(company-call-frontends 'pre-command))
(error (message "Company: An error occurred in pre-command")
(message "%s" (error-message-string err))
- (company-cancel)))))
+ (company-cancel))))
+ (company-uninstall-map))
(defun company-post-command ()
(unless (eq this-command 'company-show-doc-buffer)
(company-call-frontends 'post-command)))
(error (message "Company: An error occurred in post-command")
(message "%s" (error-message-string err))
- (company-cancel)))))
+ (company-cancel))))
+ (company-install-map))
+
+;;; search ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar company-search-string nil)
+(make-variable-buffer-local 'company-search-string)
+
+(defvar company-search-lighter " Search: \"\"")
+(make-variable-buffer-local 'company-search-lighter)
+
+(defvar company-search-old-map nil)
+(make-variable-buffer-local 'company-search-old-map)
+
+(defvar company-search-old-selection 0)
+(make-variable-buffer-local 'company-search-old-selection)
+
+(defun company-search (text lines)
+ (let ((quoted (regexp-quote text))
+ (i 0))
+ (dolist (line lines)
+ (when (string-match quoted line (length company-prefix))
+ (return i))
+ (incf i))))
+
+(defun company-search-printing-char ()
+ (interactive)
+ (setq company-search-string
+ (concat (or company-search-string "") (string last-command-event))
+ company-search-lighter (concat " Search: \"" company-search-string
+ "\""))
+ (let ((pos (company-search company-search-string
+ (nthcdr company-selection company-candidates))))
+ (if (null pos)
+ (ding)
+ (company-set-selection (+ company-selection pos) t))))
+
+(defun company-search-repeat-forward ()
+ (interactive)
+ (let ((pos (company-search company-search-string
+ (cdr (nthcdr company-selection
+ company-candidates)))))
+ (if (null pos)
+ (ding)
+ (company-set-selection (+ company-selection pos 1) t))))
+
+(defun company-search-repeat-backward ()
+ (interactive)
+ (let ((pos (company-search company-search-string
+ (nthcdr (- (length company-candidates)
+ company-selection)
+ (reverse company-candidates)))))
+ (if (null pos)
+ (ding)
+ (company-set-selection (- company-selection pos 1) t))))
+
+(defun company-search-kill-others ()
+ (interactive)
+ (let ((predicate `(lambda (candidate)
+ (string-match ,company-search-string candidate))))
+ (setq company-candidates-predicate predicate)
+ (company-update-candidates (company-apply-predicate company-candidates
+ predicate))
+ (company-search-mode 0)
+ (company-call-frontends 'update)))
+
+(defun company-search-abort ()
+ (interactive)
+ (company-set-selection company-search-old-selection t)
+ (company-search-mode 0))
+
+(defun company-search-other-char ()
+ (interactive)
+ (company-search-mode 0)
+ (when last-input-event
+ (clear-this-command-keys t)
+ (setq unread-command-events (list last-input-event))))
+
+(defvar company-search-map
+ (let ((i 0)
+ (keymap (make-keymap)))
+ (set-char-table-range (nth 1 keymap) (cons #x100 (max-char))
+ 'company-search-printing-char)
+ (define-key keymap [t] 'company-search-other-char)
+ (while (< i ?\s)
+ (define-key keymap (make-string 1 i) 'company-search-other-char)
+ (incf i))
+ (while (< i 256)
+ (define-key keymap (vector i) 'company-search-printing-char)
+ (incf i))
+ (let ((meta-map (make-sparse-keymap)))
+ (define-key keymap (char-to-string meta-prefix-char) meta-map)
+ (define-key keymap [escape] meta-map))
+ (define-key keymap (vector meta-prefix-char t) 'company-search-other-char)
+ (define-key keymap "\e\e\e" 'company-search-other-char)
+ (define-key keymap [escape escape escape] 'company-search-other-char)
+
+ (define-key keymap "\C-g" 'company-search-abort)
+ (define-key keymap "\C-s" 'company-search-repeat-forward)
+ (define-key keymap "\C-r" 'company-search-repeat-backward)
+ (define-key keymap "\C-o" 'company-search-kill-others)
+ keymap))
+
+(define-minor-mode company-search-mode
+ ""
+ nil company-search-lighter nil
+ (if company-search-mode
+ (if (company-manual-begin)
+ (progn
+ (setq company-search-old-selection company-selection)
+ (company-enable-overriding-keymap company-search-map)
+ (company-call-frontends 'update))
+ (setq company-search-mode nil))
+ (kill-local-variable 'company-search-string)
+ (kill-local-variable 'company-search-lighter)
+ (kill-local-variable 'company-search-old-selection)
+ (company-enable-overriding-keymap company-active-map)))
+
+(defun company-search-candidates ()
+ (interactive)
+ (company-search-mode 1))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun company-select-next ()
(interactive)
(when (company-manual-begin)
- (setq company-selection (min (1- (length company-candidates))
- (1+ company-selection))
- company-selection-changed t))
- (company-call-frontends 'update))
+ (company-set-selection (1+ company-selection))))
(defun company-select-previous ()
(interactive)
(when (company-manual-begin)
- (setq company-selection (max 0 (1- company-selection))
- company-selection-changed t))
- (company-call-frontends 'update))
+ (company-set-selection (1- company-selection))))
(defun company-complete-selection ()
(interactive)
(interactive)
(when company-candidates
(save-window-excursion
- (let* ((selected (nth company-selection company-candidates))
+ (let* ((height (window-height))
+ (row (cdr (posn-col-row (posn-at-point))))
+ (selected (nth company-selection company-candidates))
(buffer (funcall company-backend 'doc-buffer selected)))
(if (not buffer)
(error "No documentation available.")
(display-buffer buffer)
- (read-event)
+ (and (< (window-height) height)
+ (< (- (window-height) row 2) company-tooltip-limit)
+ (recenter (- (window-height) row 2)))
+ (while (eq 'scroll-other-window
+ (key-binding (vector (list (read-event)))))
+ (scroll-other-window))
(when last-input-event
(clear-this-command-keys t)
(setq unread-command-events (list last-input-event))))))))
(when (>= company-tooltip-offset (- num-lines limit 1))
(incf limit)
(when (= selection (1- num-lines))
- (setq company-tooltip-offset (max (1- company-tooltip-offset) 0))))
+ (decf company-tooltip-offset)
+ (when (<= company-tooltip-offset 1)
+ (setq company-tooltip-offset 0)
+ (incf limit))))
limit)
(defun company-fill-propertize (line width selected)
(setq line (company-safe-substring line 0 width))
- (add-text-properties 0 width
- (list 'face (if selected
- 'company-tooltip-selection
- 'company-tooltip)) line)
+ (add-text-properties 0 width (list 'face 'company-tooltip) line)
(add-text-properties 0 (length company-common)
- (list 'face (if selected
- 'company-tooltip-common-selection
- 'company-tooltip-common)) line)
+ (list 'face 'company-tooltip-common) line)
+ (when selected
+ (if (and company-search-string
+ (string-match (regexp-quote company-search-string) line
+ (length company-prefix)))
+ (progn
+ (add-text-properties (match-beginning 0) (match-end 0)
+ '(face company-tooltip-selection) line)
+ (when (< (match-beginning 0) (length company-common))
+ (add-text-properties (match-beginning 0) (length company-common)
+ '(face company-tooltip-common-selection)
+ line)))
+ (add-text-properties 0 width '(face company-tooltip-selection) line)
+ (add-text-properties 0 (length company-common)
+ (list 'face 'company-tooltip-common-selection)
+ line)))
line)
;;; replace
(goto-char beg)
(let ((row (cdr (posn-col-row (posn-at-point))))
lines)
- (while (< (point) end)
- (move-to-window-line (incf row))
+ (while (and (equal (move-to-window-line (incf row)) row)
+ (<= (point) end))
(push (buffer-substring beg (min end (1- (point)))) lines)
(setq beg (point)))
+ (unless (eq beg end)
+ (push (buffer-substring beg end) lines))
(nreverse lines)))
(defun company-modify-line (old new offset)
(mapconcat 'identity (nreverse new) "\n")
"\n")))
-(defun company-create-lines (column lines selection)
+(defun company-create-lines (column lines selection limit)
- (let ((limit (max company-tooltip-limit 3))
- (len (length lines))
+ (let ((len (length lines))
width
lines-copy
previous
;; show
+(defsubst company-pseudo-tooltip-height ()
+ "Calculate the appropriate tooltip height."
+ (max 3 (min company-tooltip-limit
+ (- (window-height) (cdr (posn-col-row (posn-at-point))) 2))))
+
(defun company-pseudo-tooltip-show (row column lines selection)
(company-pseudo-tooltip-hide)
(unless lines (error "No text provided"))
(move-to-column 0)
- (let* ((lines (company-create-lines column lines selection))
+ (let* ((height (company-pseudo-tooltip-height))
+ (lines (company-create-lines column lines selection height))
(nl (< (move-to-window-line row) row))
(beg (point))
(end (save-excursion
- (move-to-window-line (min (window-height)
- (+ row company-tooltip-limit)))
+ (move-to-window-line (+ row height))
(point)))
(old-string (company-buffer-lines beg end))
str)
(overlay-put company-pseudo-tooltip-overlay 'company-nl nl)
(overlay-put company-pseudo-tooltip-overlay 'company-before
(company-replacement-string old-string lines column nl))
+ (overlay-put company-pseudo-tooltip-overlay 'company-height height)
(overlay-put company-pseudo-tooltip-overlay 'window (selected-window)))))
(let* ((old-string (overlay-get company-pseudo-tooltip-overlay 'company-old))
(column (overlay-get company-pseudo-tooltip-overlay 'company-column))
(nl (overlay-get company-pseudo-tooltip-overlay 'company-nl))
- (lines (company-create-lines column lines selection)))
+ (height (overlay-get company-pseudo-tooltip-overlay 'company-height))
+ (lines (company-create-lines column lines selection height)))
(overlay-put company-pseudo-tooltip-overlay 'company-before
(company-replacement-string old-string lines column nl))))
(case command
('pre-command (company-pseudo-tooltip-hide-temporarily))
('post-command
- (unless (overlayp company-pseudo-tooltip-overlay)
+ (unless (and (overlayp company-pseudo-tooltip-overlay)
+ (equal (overlay-get company-pseudo-tooltip-overlay
+ 'company-height)
+ (company-pseudo-tooltip-height)))
+ ;; Redraw needed.
(company-pseudo-tooltip-show-at-point (- (point)
(length company-prefix))))
(company-pseudo-tooltip-unhide))