diff --git a/ChangeLog b/ChangeLog index 4b7774d03..93718a6f1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,39 @@ +2015-07-17 Helmut Eller + + Start using completion-at-point. + + That saves us the complex code to display the list of completions. + Unfortunately, slime-complete-symbol-function doesn't work well to + customize it. Had to introduce a replacment: + slime-completion-at-point-functions. + + * slime.el (slime-complete-symbol): Use completion-at-point. + (slime-completion-at-point-functions): New. + (slime--completion-at-point-functions): New helper. + (slime-complete-symbol-function): Obsolete, but have to keep it + around for some time. + (slime-simple-completion-at-point): Renamed from + slime-simple-complete-symbol to avoid confusion. + (slime-filename-completion): Renamed from + slime-maybe-complete-as-filename. + (slime-simple-complete-symbol): Backward compatible replacement. + + (slime-completions-buffer-name) + (slime-complete-saved-window-configuration) + (slime-completions-window) + (slime-complete-maybe-save-window-configuration) + (slime-complete-delay-restoration) + (slime-complete-forget-window-configuration) + (slime-complete-restore-window-configuration) + (slime-complete-maybe-restore-window-configuration) + (slime-completion-window-active-p, slime-display-completion-list) + (slime-display-or-scroll-completions, slime-scroll-completions) + (slime-minibuffer-respecting-message): Move to + contrib/slime-c-p-c.el + + * slime-tests.el (complete-symbol): Update tests. + * doc/slime.texi: Update documentation for symbol completion. + 2015-07-15 Helmut Eller Use *READ-SUPPRESS* more often while searching source locations. diff --git a/NEWS b/NEWS index 74efc84c0..88e5ba93a 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,11 @@ * SLIME News -*- mode: outline; coding: utf-8 -*- +* 2.15 (July 2015) +** Core +*** Completions are now displayed with `completion-at-point'. +The new variable `slime-completion-at-point-functions' should now be +used to customize completion. The old variable +`slime-complete-symbol-function' still works, but it is considered +obsolete and will be removed eventually. * 2.14 (June 2015) ** Core diff --git a/contrib/slime-c-p-c.el b/contrib/slime-c-p-c.el index d629294a1..8142d4638 100644 --- a/contrib/slime-c-p-c.el +++ b/contrib/slime-c-p-c.el @@ -42,6 +42,128 @@ If false, move point to the end of the inserted text." :group 'slime-mode :type 'boolean) + +;; FIXME: this is the old code to display completions. Remove it once +;; `slime-complete-symbol*' and `slime-fuzzy-complete-symbol' can be +;; used together with `completion-at-point'. + +(defvar slime-completions-buffer-name "*Completions*") + +;; FIXME: can probably use quit-window instead +(make-variable-buffer-local + (defvar slime-complete-saved-window-configuration nil + "Window configuration before we show the *Completions* buffer. +This is buffer local in the buffer where the completion is +performed.")) + +(make-variable-buffer-local + (defvar slime-completions-window nil + "The window displaying *Completions* after saving window configuration. +If this window is no longer active or displaying the completions +buffer then we can ignore `slime-complete-saved-window-configuration'.")) + +(defun slime-complete-maybe-save-window-configuration () + "Maybe save the current window configuration. +Return true if the configuration was saved." + (unless (or slime-complete-saved-window-configuration + (get-buffer-window slime-completions-buffer-name)) + (setq slime-complete-saved-window-configuration + (current-window-configuration)) + t)) + +(defun slime-complete-delay-restoration () + (add-hook 'pre-command-hook + 'slime-complete-maybe-restore-window-configuration + 'append + 'local)) + +(defun slime-complete-forget-window-configuration () + (setq slime-complete-saved-window-configuration nil) + (setq slime-completions-window nil)) + +(defun slime-complete-restore-window-configuration () + "Restore the window config if available." + (remove-hook 'pre-command-hook + 'slime-complete-maybe-restore-window-configuration) + (when (and slime-complete-saved-window-configuration + (slime-completion-window-active-p)) + (save-excursion (set-window-configuration + slime-complete-saved-window-configuration)) + (setq slime-complete-saved-window-configuration nil) + (when (buffer-live-p slime-completions-buffer-name) + (kill-buffer slime-completions-buffer-name)))) + +(defun slime-complete-maybe-restore-window-configuration () + "Restore the window configuration, if the following command +terminates a current completion." + (remove-hook 'pre-command-hook + 'slime-complete-maybe-restore-window-configuration) + (condition-case err + (cond ((cl-find last-command-event "()\"'`,# \r\n:") + (slime-complete-restore-window-configuration)) + ((not (slime-completion-window-active-p)) + (slime-complete-forget-window-configuration)) + (t + (slime-complete-delay-restoration))) + (error + ;; Because this is called on the pre-command-hook, we mustn't let + ;; errors propagate. + (message "Error in slime-complete-restore-window-configuration: %S" + err)))) + +(defun slime-completion-window-active-p () + "Is the completion window currently active?" + (and (window-live-p slime-completions-window) + (equal (buffer-name (window-buffer slime-completions-window)) + slime-completions-buffer-name))) + +(defun slime-display-completion-list (completions base) + (let ((savedp (slime-complete-maybe-save-window-configuration))) + (with-output-to-temp-buffer slime-completions-buffer-name + (display-completion-list completions) + (let ((offset (- (point) 1 (length base)))) + (with-current-buffer standard-output + (setq completion-base-position offset) + (set-syntax-table lisp-mode-syntax-table)))) + (when savedp + (setq slime-completions-window + (get-buffer-window slime-completions-buffer-name))))) + +(defun slime-display-or-scroll-completions (completions base) + (cond ((and (eq last-command this-command) + (slime-completion-window-active-p)) + (slime-scroll-completions)) + (t + (slime-display-completion-list completions base))) + (slime-complete-delay-restoration)) + +(defun slime-scroll-completions () + (let ((window slime-completions-window)) + (with-current-buffer (window-buffer window) + (if (pos-visible-in-window-p (point-max) window) + (set-window-start window (point-min)) + (save-selected-window + (select-window window) + (scroll-up)))))) + +(defun slime-minibuffer-respecting-message (format &rest format-args) + "Display TEXT as a message, without hiding any minibuffer contents." + (let ((text (format " [%s]" (apply #'format format format-args)))) + (if (minibuffer-window-active-p (minibuffer-window)) + (minibuffer-message text) + (message "%s" text)))) + +(defun slime-maybe-complete-as-filename () + "If point is at a string starting with \", complete it as filename. + Return nil if point is not at filename." + (when (save-excursion (re-search-backward "\"[^ \t\n]+\\=" + (max (point-min) + (- (point) 1000)) t)) + (let ((comint-completion-addsuffix '("/" . "\""))) + (comint-replace-by-expanded-filename) + t))) + + (defun slime-complete-symbol* () "Expand abbreviations and complete the symbol at point." ;; NB: It is only the name part of the symbol that we actually want diff --git a/doc/slime.texi b/doc/slime.texi index 81113cd3b..025752f34 100644 --- a/doc/slime.texi +++ b/doc/slime.texi @@ -486,15 +486,8 @@ can add one of the following to your init file: @example (add-hook 'slime-load-hook - #'(lambda () - (define-key slime-prefix-map (kbd "M-h") 'slime-documentation-lookup))) -@end example - -Or just: - -@example -(eval-after-load 'slime - `(define-key slime-prefix-map (kbd "M-h") 'slime-documentation-lookup)) + (lambda () + (define-key slime-prefix-map (kbd "M-h") 'slime-documentation-lookup))) @end example The former technique works only for @SLIME{}'s core keymaps, not it's @@ -951,7 +944,7 @@ completion tries harder. @c @itemx C-M-i Complete the symbol at point. Note that three styles of completion are available in @SLIME{}; the default is similar to normal Emacs -completion (@pxref{slime-complete-symbol-function}). +completion (@pxref{slime-completion-at-point-functions}). @end table @@ -1694,16 +1687,19 @@ buffers popped up by @SLIME{}. This is @code{t} by default, which ensures that lines do not wrap in backtraces, apropos listings, and so on. It can however cause information to spill off the screen. -@anchor{slime-complete-symbol-function} -@vindex slime-complete-symbol-function -@item slime-complete-symbol-function -The function to use for completion of Lisp symbols. Three completion -styles are available: @code{slime-simple-complete-symbol}, +@anchor{slime-completion-at-point-functions} +@vindex slime-completion-at-point-functions +@item slime-completion-at-point-functions +A list of functions used for completion of Lisp symbols. This works +as the standard +@code{completion-at-point-functions} +(@pxref{Completion in Buffers,,,elisp}). Three completion +styles are available: @code{slime-simple-completion-at-point}, @code{slime-complete-symbol*} (@pxref{Compound Completion}), and @code{slime-fuzzy-complete-symbol} (@pxref{Fuzzy Completion}). -The default is @code{slime-simple-complete-symbol}, which completes in -the usual Emacs way. +The default is @code{slime-simple-completion-at-point}, which +completes in the usual Emacs way. @vindex slime-filename-translations @item slime-filename-translations @@ -2608,21 +2604,21 @@ For example, the effect of @code{:nearest-package} can be also achieved by specifying the following custom filter in @file{~/.swank.lisp}: @example (setf *fuzzy-duplicate-symbol-filter* - #'(lambda (cur-package all-packages dedup-table) - (declare (ignore cur-package all-packages)) - #'(lambda (symbol) - (unless (gethash (symbol-name symbol) dedup-table) - (setf (gethash (symbol-name symbol) dedup-table) t))))) + (lambda (cur-package all-packages dedup-table) + (declare (ignore cur-package all-packages)) + (lambda (symbol) + (unless (gethash (symbol-name symbol) dedup-table) + (setf (gethash (symbol-name symbol) dedup-table) t))))) @end example And instead of @code{:home-package}, the following can be used: @example (setf *fuzzy-duplicate-symbol-filter* - #'(lambda (cur-package all-packages dedup-table) - (declare (ignore dedup-table)) - (let ((packages (mapcar #'find-package - (remove cur-package all-packages)))) - #'(lambda (symbol) - (not (member (symbol-package symbol) packages)))))) + (lambda (cur-package all-packages dedup-table) + (declare (ignore dedup-table)) + (let ((packages (mapcar #'find-package + (remove cur-package all-packages)))) + (lambda (symbol) + (not (member (symbol-package symbol) packages)))))) @end example @node slime-autodoc-mode diff --git a/slime-tests.el b/slime-tests.el index fa5ff590d..87f81f13e 100644 --- a/slime-tests.el +++ b/slime-tests.el @@ -586,18 +586,16 @@ confronted with nasty #.-fu." (def-slime-test complete-symbol (prefix expected-completions) "Find the completions of a symbol-name prefix." - '(("cl:compile" (("cl:compile" "cl:compile-file" "cl:compile-file-pathname" - "cl:compiled-function" "cl:compiled-function-p" - "cl:compiler-macro" "cl:compiler-macro-function") - "cl:compile")) - ("cl:foobar" (nil "")) - ("swank::compile-file" (("swank::compile-file" - "swank::compile-file-for-emacs" - "swank::compile-file-if-needed" - "swank::compile-file-output" - "swank::compile-file-pathname") - "swank::compile-file")) - ("cl:m-v-l" (nil ""))) + '(("cl:compile" ("cl:compile" "cl:compile-file" "cl:compile-file-pathname" + "cl:compiled-function" "cl:compiled-function-p" + "cl:compiler-macro" "cl:compiler-macro-function")) + ("cl:foobar" ()) + ("swank::compile-file" ("swank::compile-file" + "swank::compile-file-for-emacs" + "swank::compile-file-if-needed" + "swank::compile-file-output" + "swank::compile-file-pathname")) + ("cl:m-v-l" ())) (let ((completions (slime-simple-completions prefix))) (slime-test-expect "Completion set" expected-completions completions))) diff --git a/slime.el b/slime.el index 5591c9d38..84ba62fe4 100644 --- a/slime.el +++ b/slime.el @@ -275,13 +275,23 @@ argument." (and tags-table-list (slime-etags-definitions name)))))) -(defcustom slime-complete-symbol-function 'slime-simple-complete-symbol - "*Function to perform symbol completion." +;; FIXME: remove one day +(defcustom slime-complete-symbol-function 'nil + "Obsolete. Use `slime-completion-at-point-functions' instead." :group 'slime-mode - :type '(choice (const :tag "Simple" slime-simple-complete-symbol) - (const :tag "Compound" slime-complete-symbol*) + :type '(choice (const :tag "Compound" slime-complete-symbol*) (const :tag "Fuzzy" slime-fuzzy-complete-symbol))) +(defcustom slime-completion-at-point-functions + '(slime-filename-completion + slime-simple-completion-at-point) + "List of functions to perform completion. +Works like `completion-at-point-functions'. + +`slime-complete-symbol' prepends this list to `completion-at-point-functions' +before calling `completion-at-point'." + :group 'slime-mode) + ;;;;; slime-mode-faces (defgroup slime-mode-faces nil @@ -3509,157 +3519,43 @@ more than one space." ;;;; Completion -;; XXX those long names are ugly to read; long names an indicator for -;; bad factoring? - -(defvar slime-completions-buffer-name "*Completions*") - -;; FIXME: can probably use quit-window instead -(make-variable-buffer-local - (defvar slime-complete-saved-window-configuration nil - "Window configuration before we show the *Completions* buffer. -This is buffer local in the buffer where the completion is -performed.")) - -(make-variable-buffer-local - (defvar slime-completions-window nil - "The window displaying *Completions* after saving window configuration. -If this window is no longer active or displaying the completions -buffer then we can ignore `slime-complete-saved-window-configuration'.")) - -(defun slime-complete-maybe-save-window-configuration () - "Maybe save the current window configuration. -Return true if the configuration was saved." - (unless (or slime-complete-saved-window-configuration - (get-buffer-window slime-completions-buffer-name)) - (setq slime-complete-saved-window-configuration - (current-window-configuration)) - t)) - -(defun slime-complete-delay-restoration () - (add-hook 'pre-command-hook - 'slime-complete-maybe-restore-window-configuration - 'append - 'local)) - -(defun slime-complete-forget-window-configuration () - (setq slime-complete-saved-window-configuration nil) - (setq slime-completions-window nil)) - -(defun slime-complete-restore-window-configuration () - "Restore the window config if available." - (remove-hook 'pre-command-hook - 'slime-complete-maybe-restore-window-configuration) - (when (and slime-complete-saved-window-configuration - (slime-completion-window-active-p)) - (save-excursion (set-window-configuration - slime-complete-saved-window-configuration)) - (setq slime-complete-saved-window-configuration nil) - (when (buffer-live-p slime-completions-buffer-name) - (kill-buffer slime-completions-buffer-name)))) - -(defun slime-complete-maybe-restore-window-configuration () - "Restore the window configuration, if the following command -terminates a current completion." - (remove-hook 'pre-command-hook - 'slime-complete-maybe-restore-window-configuration) - (condition-case err - (cond ((cl-find last-command-event "()\"'`,# \r\n:") - (slime-complete-restore-window-configuration)) - ((not (slime-completion-window-active-p)) - (slime-complete-forget-window-configuration)) - (t - (slime-complete-delay-restoration))) - (error - ;; Because this is called on the pre-command-hook, we mustn't let - ;; errors propagate. - (message "Error in slime-complete-restore-window-configuration: %S" - err)))) - -(defun slime-completion-window-active-p () - "Is the completion window currently active?" - (and (window-live-p slime-completions-window) - (equal (buffer-name (window-buffer slime-completions-window)) - slime-completions-buffer-name))) - -(defun slime-display-completion-list (completions base) - (let ((savedp (slime-complete-maybe-save-window-configuration))) - (with-output-to-temp-buffer slime-completions-buffer-name - (display-completion-list completions) - (let ((offset (- (point) 1 (length base)))) - (with-current-buffer standard-output - (setq completion-base-position offset) - (set-syntax-table lisp-mode-syntax-table)))) - (when savedp - (setq slime-completions-window - (get-buffer-window slime-completions-buffer-name))))) - -(defun slime-display-or-scroll-completions (completions base) - (cond ((and (eq last-command this-command) - (slime-completion-window-active-p)) - (slime-scroll-completions)) +(defun slime--completion-at-point-functions () + (cond (slime-complete-symbol-function ; FIXME: for backward compatibilty + (list (lambda () slime-complete-symbol-function))) (t - (slime-display-completion-list completions base))) - (slime-complete-delay-restoration)) - -(defun slime-scroll-completions () - (let ((window slime-completions-window)) - (with-current-buffer (window-buffer window) - (if (pos-visible-in-window-p (point-max) window) - (set-window-start window (point-min)) - (save-selected-window - (select-window window) - (scroll-up)))))) + (append slime-completion-at-point-functions + completion-at-point-functions)))) (defun slime-complete-symbol () "Complete the symbol at point. -Completion is performed by `slime-complete-symbol-function'." +Completion is performed by `slime-completion-at-point-functions'." (interactive) - (funcall slime-complete-symbol-function)) + (let ((completion-at-point-functions (slime--completion-at-point-functions))) + (completion-at-point))) -(defun slime-simple-complete-symbol () +(defun slime-simple-completion-at-point () "Complete the symbol at point. -Perform completion more similar to Emacs' complete-symbol." - (or (slime-maybe-complete-as-filename) - (let* ((end (point)) - (beg (slime-symbol-start-pos)) - (prefix (buffer-substring-no-properties beg end)) - (result (slime-simple-completions prefix))) - (cl-destructuring-bind (completions partial) result - (if (null completions) - (progn (slime-minibuffer-respecting-message - "Can't find completion for \"%s\"" prefix) - (ding) - (slime-complete-restore-window-configuration)) - (insert-and-inherit (substring partial (length prefix))) - (cond ((slime-length= completions 1) - (slime-minibuffer-respecting-message "Sole completion") - (slime-complete-restore-window-configuration)) - ;; Incomplete - (t - (when (member partial completions) - (slime-minibuffer-respecting-message - "Complete but not unique")) - (slime-display-or-scroll-completions completions - partial)))))))) +Perform completion similar to `elisp-completion-at-point'." + (let* ((end (point)) + (beg (slime-symbol-start-pos))) + (list beg end (completion-table-dynamic #'slime-simple-completions)))) -(defun slime-maybe-complete-as-filename () +(defun slime-filename-completion () "If point is at a string starting with \", complete it as filename. Return nil if point is not at filename." (when (save-excursion (re-search-backward "\"[^ \t\n]+\\=" - (max (point-min) - (- (point) 1000)) t)) + (max (point-min) (- (point) 1000)) + t)) (let ((comint-completion-addsuffix '("/" . "\""))) - (comint-replace-by-expanded-filename) - t))) + (comint-filename-completion)))) -(defun slime-minibuffer-respecting-message (format &rest format-args) - "Display TEXT as a message, without hiding any minibuffer contents." - (let ((text (format " [%s]" (apply #'format format format-args)))) - (if (minibuffer-window-active-p (minibuffer-window)) - (minibuffer-message text) - (message "%s" text)))) +;; FIXME: for backward compatibility. Remove it one day +;; together with slime-complete-symbol-function. +(defun slime-simple-complete-symbol () + (let ((completion-at-point-functions '(slime-maybe-complete-as-filename + slime-simple-completion-at-point))) + (completion-at-point))) (defun slime-indent-and-complete-symbol () "Indent the current line and perform symbol completion. @@ -3712,9 +3608,12 @@ alist but ignores CDRs." (mapcar (lambda (x) (cons x nil)) list)) (defun slime-simple-completions (prefix) - (let ((slime-current-thread t)) - (slime-eval - `(swank:simple-completions ,prefix ',(slime-current-package))))) + (cl-destructuring-bind (completions _partial) + (let ((slime-current-thread t)) + (slime-eval + `(swank:simple-completions ,(substring-no-properties prefix) + ',(slime-current-package)))) + completions)) ;;;; Edit definition @@ -5981,7 +5880,7 @@ truly screwed up." (dolist (cmd commands) ;; First wait until gdb was initialized, then wait until current ;; command was processed. - (while (not (looking-back comint-prompt-regexp)) + (while (not (looking-back comint-prompt-regexp nil)) (sit-for 0.01)) ;; We do not use `gud-call' because we want the initial commands ;; to be displayed by the user so he knows what he's got.