summaryrefslogtreecommitdiff
path: root/lisp
diff options
context:
space:
mode:
Diffstat (limited to 'lisp')
-rw-r--r--lisp/erc/erc-button.el93
-rw-r--r--lisp/erc/erc-fill.el10
-rw-r--r--lisp/erc/erc.el17
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))