diff options
Diffstat (limited to 'lisp')
-rw-r--r-- | lisp/erc/erc-button.el | 93 | ||||
-rw-r--r-- | lisp/erc/erc-fill.el | 10 | ||||
-rw-r--r-- | lisp/erc/erc.el | 17 |
3 files changed, 94 insertions, 26 deletions
diff --git a/lisp/erc/erc-button.el b/lisp/erc/erc-button.el index 33e69f3b0b8..e2447deecde 100644 --- a/lisp/erc/erc-button.el +++ b/lisp/erc/erc-button.el @@ -55,11 +55,11 @@ ((erc-button--check-nicknames-entry) (add-hook 'erc-insert-modify-hook #'erc-button-add-buttons 'append) (add-hook 'erc-send-modify-hook #'erc-button-add-buttons 'append) - (add-hook 'erc-complete-functions #'erc-button-next-function) + (add-hook 'erc--tab-functions #'erc-button-next) (erc--modify-local-map t "<backtab>" #'erc-button-previous)) ((remove-hook 'erc-insert-modify-hook #'erc-button-add-buttons) (remove-hook 'erc-send-modify-hook #'erc-button-add-buttons) - (remove-hook 'erc-complete-functions #'erc-button-next-function) + (remove-hook 'erc--tab-functions #'erc-button-next) (erc--modify-local-map nil "<backtab>" #'erc-button-previous))) ;;; Variables @@ -529,6 +529,7 @@ call it with the value of the `erc-data' text property." (defun erc-button-next-function () "Pseudo completion function that actually jumps to the next button. For use on `completion-at-point-functions'." + (declare (obsolete erc-nickserv-identify "30.1")) ;; FIXME: This is an abuse of completion-at-point-functions. (when (< (point) (erc-beg-of-input-line)) (let ((start (point))) @@ -546,27 +547,73 @@ For use on `completion-at-point-functions'." (error "No next button")) t))))) -(defun erc-button-next () - "Go to the next button in this buffer." - (interactive) - (let ((f (erc-button-next-function))) - (if f (funcall f)))) - -(defun erc-button-previous () - "Go to the previous button in this buffer." - (interactive) - (let ((here (point))) - (when (< here (erc-beg-of-input-line)) - (while (and (get-text-property here 'erc-callback) - (not (= here (point-min)))) - (setq here (1- here))) - (while (and (not (get-text-property here 'erc-callback)) - (not (= here (point-min)))) - (setq here (1- here))) - (if (> here (point-min)) - (goto-char here) - (error "No previous button")) - t))) +(defvar erc-button--prev-next-predicate-functions + '(erc-button--end-of-button-p) + "Abnormal hook whose members can return non-nil to continue searching. +Otherwise, if all members return nil, point will stay at the +current button. Called with a single arg, a buffer position +greater than `point-min' with a text property of `erc-callback'.") + +(defun erc-button--end-of-button-p (point) + (get-text-property (1- point) 'erc-callback)) + +(defun erc--button-next (arg) + (let* ((nextp (prog1 (>= arg 1) (setq arg (max 1 (abs arg))))) + (search-fn (if nextp + #'next-single-char-property-change + #'previous-single-char-property-change)) + (start (point)) + (p start)) + (while (progn + ;; Break out of current search context. + (when-let ((low (max (point-min) (1- (pos-bol)))) + (high (min (point-max) (1+ (pos-eol)))) + (prop (get-text-property p 'erc-callback)) + (q (if nextp + (text-property-not-all p high + 'erc-callback prop) + (funcall search-fn p 'erc-callback nil low))) + ((< low q high))) + (setq p q)) + ;; Assume that buttons occur frequently enough that + ;; omitting LIMIT is acceptable. + (while + (and (setq p (funcall search-fn p 'erc-callback)) + (if nextp (< p erc-insert-marker) (/= p (point-min))) + (run-hook-with-args-until-success + 'erc-button--prev-next-predicate-functions p))) + (and arg + (< (point-min) p erc-insert-marker) + (goto-char p) + (not (zerop (cl-decf arg)))))) + (when (= (point) start) + (user-error (if nextp "No next button" "No previous button"))) + t)) + +(defun erc-button-next (&optional arg) + "Go to the ARGth next button." + (declare (advertised-calling-convention (arg) "30.1")) + (interactive "p") + (setq arg (pcase arg ((pred listp) (prefix-numeric-value arg)) (_ arg))) + (erc--button-next arg)) + +(defun erc-button-previous (&optional arg) + "Go to ARGth previous button." + (declare (advertised-calling-convention (arg) "30.1")) + (interactive "p") + (setq arg (pcase arg ((pred listp) (prefix-numeric-value arg)) (_ arg))) + (erc--button-next (- arg))) + +(defun erc-button-previous-of-nick (arg) + "Go to ARGth previous button for nick at point." + (interactive "p") + (if-let* ((prop (get-text-property (point) 'erc-data)) + (erc-button--prev-next-predicate-functions + (cons (lambda (p) + (not (equal (get-text-property p 'erc-data) prop))) + erc-button--prev-next-predicate-functions))) + (erc--button-next (- arg)) + (user-error "No nick at point"))) (defun erc-browse-emacswiki (thing) "Browse to THING in the emacs-wiki." diff --git a/lisp/erc/erc-fill.el b/lisp/erc/erc-fill.el index a56134d8188..bf995a5a5e6 100644 --- a/lisp/erc/erc-fill.el +++ b/lisp/erc/erc-fill.el @@ -300,7 +300,9 @@ of the minor-mode toggles as usual." (setq msg (concat msg (and msg " ") (erc-fill--make-module-dependency-msg "button")))) (erc-with-server-buffer - (erc-button-mode +1)))) + (erc-button-mode +1))) + (add-hook 'erc-button--prev-next-predicate-functions + #'erc-fill--wrap-merged-button-p nil t)) ;; Set local value of user option (can we avoid this somehow?) (unless (eq erc-fill-function #'erc-fill-wrap) (setq-local erc-fill-function #'erc-fill-wrap)) @@ -328,6 +330,8 @@ of the minor-mode toggles as usual." (kill-local-variable 'erc-fill--wrap-value) (kill-local-variable 'erc-fill-function) (kill-local-variable 'erc-fill--wrap-visual-keys) + (remove-hook 'erc-button--prev-next-predicate-functions + #'erc-fill--wrap-merged-button-p t) (remove-function (local 'erc-stamp--insert-date-function) #'erc-fill--wrap-stamp-insert-prefixed-date) (visual-line-mode -1)) @@ -414,6 +418,10 @@ See `erc-fill-wrap-mode' for details." `((space :width (- erc-fill--wrap-value ,len)) (space :width erc-fill--wrap-value)))))) +;; FIXME use own text property to avoid false positives. +(defun erc-fill--wrap-merged-button-p (point) + (equal "" (get-text-property point 'display))) + ;; This is an experimental helper for third-party modules. You could, ;; for example, use this to automatically resize the prefix to a ;; fraction of the window's width on some event change. Another use diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index 05b6b5bfd21..a439e2438b0 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -354,7 +354,7 @@ simply because we do not necessarily receive the QUIT event." :type 'hook) (defcustom erc-complete-functions nil - "These functions get called when the user hits TAB in ERC. + "These functions get called when the user hits \\`TAB' in ERC. Each function in turn is called until one returns non-nil to indicate it has handled the input." :group 'erc-hooks @@ -1231,7 +1231,7 @@ which the local user typed." (define-key map "\C-c\C-u" #'erc-kill-input) (define-key map "\C-c\C-x" #'erc-quit-server) (define-key map "\M-\t" #'ispell-complete-word) - (define-key map "\t" #'completion-at-point) + (define-key map "\t" #'erc-tab) ;; Suppress `font-lock-fontify-block' key binding since it ;; destroys face properties. @@ -4675,6 +4675,19 @@ This places `point' just after the prompt, or at the beginning of the line." (setq erc-input-ring-index nil)) (kill-line))) +(defvar erc--tab-functions nil + "Functions to try when user hits \\`TAB' outside of input area. +Called with a numeric prefix arg.") + +(defun erc-tab (&optional arg) + "Call `completion-at-point' when typing in the input area. +Otherwise call members of `erc--tab-functions' with raw prefix +ARG until one of them returns non-nil." + (interactive "P") + (if (>= (point) erc-input-marker) + (completion-at-point) + (run-hook-with-args-until-success 'erc--tab-functions arg))) + (defun erc-complete-word-at-point () (run-hook-with-args-until-success 'erc-complete-functions)) |