@@ -4255,6 +4255,7 @@ a copy of the local keymap, and sets `header-line-format'."
4255
4255
(setq-local yank-excluded-properties
4256
4256
(append '(line-prefix wrap-prefix)
4257
4257
(default-value 'yank-excluded-properties)))
4258
+ (add-hook 'isearch-mode-hook 'ement-room-compose-history-isearch-setup nil t)
4258
4259
;; FIXME: Compose with local map?
4259
4260
(use-local-map (if (current-local-map)
4260
4261
(copy-keymap (current-local-map))
@@ -4270,8 +4271,10 @@ a copy of the local keymap, and sets `header-line-format'."
4270
4271
cmd))))
4271
4272
(local-set-key [remap save-buffer] #'ement-room-dispatch-send-message)
4272
4273
(local-set-key (kbd "C-c C-k") #'ement-room-compose-abort)
4273
- (local-set-key (kbd "M-n") #'ement-room-compose-history-next-message)
4274
4274
(local-set-key (kbd "M-p") #'ement-room-compose-history-prev-message)
4275
+ (local-set-key (kbd "M-n") #'ement-room-compose-history-next-message)
4276
+ (local-set-key (kbd "M-r") #'ement-room-compose-history-isearch-backward)
4277
+ (local-set-key (kbd "C-M-r") #'ement-room-compose-history-isearch-backward-regexp)
4275
4278
(setq header-line-format
4276
4279
(concat (substitute-command-keys
4277
4280
(format " Press \\[save-buffer] to send message to room (%s), or \\[ement-room-compose-abort] to cancel."
@@ -4358,16 +4361,35 @@ Used with `dabbrev-friend-buffer-function'."
4358
4361
(with-current-buffer buffer
4359
4362
(derived-mode-p 'ement-room-mode)))
4360
4363
4361
- ;;; Message history for compose buffers.
4364
+ ;;; Message history for compose buffers. Isearch code is derived from comint.el.
4362
4365
4363
4366
(defvar-local ement-room--compose-message-history-index -1)
4364
4367
(defvar-local ement-room--compose-message-history-initial "")
4368
+ (defvar-local ement-room--compose-history-isearch nil)
4369
+
4370
+ (defun ement-room-compose-message-history-insert (pos)
4371
+ "Insert text of the absolute history position POS."
4372
+ ;; Store the not-from-history buffer message.
4373
+ (when (< ement-room--compose-message-history-index 0)
4374
+ (setq ement-room--compose-message-history-initial
4375
+ (ement-room-compose-buffer-string-trimmed)))
4376
+ ;; Update the index.
4377
+ (setq ement-room--compose-message-history-index (or pos -1))
4378
+ ;; Update the buffer.
4379
+ (erase-buffer)
4380
+ (insert (if (< ement-room--compose-message-history-index 0)
4381
+ ement-room--compose-message-history-initial
4382
+ (or (nth ement-room--compose-message-history-index
4383
+ ement-room-message-history)
4384
+ (format "[invalid ement message history element %d]"
4385
+ ement-room--compose-message-history-index)))))
4365
4386
4366
4387
(defun ement-room-compose-history-prev-message (arg)
4367
4388
"Cycle backward through message history, after saving current message.
4368
4389
With a numeric prefix ARG, go back ARG messages."
4369
4390
(interactive "*p")
4370
4391
(let ((len (length ement-room-message-history)))
4392
+ ;; Valid index values: -1 <= idx < len.
4371
4393
(cond ((<= len 0)
4372
4394
(user-error "Empty message history"))
4373
4395
((eql arg 0)) ;; No-op.
@@ -4376,29 +4398,136 @@ With a numeric prefix ARG, go back ARG messages."
4376
4398
((and (< arg 0) (< ement-room--compose-message-history-index 0))
4377
4399
(user-error "End of history; no next item"))
4378
4400
(t
4379
- ;; Store the not-from-history buffer message.
4380
- (when (< ement-room--compose-message-history-index 0)
4381
- (setq ement-room--compose-message-history-initial
4382
- (ement-room-compose-buffer-string-trimmed)))
4383
- ;; Update the index.
4384
- (setq ement-room--compose-message-history-index
4385
- (let ((newidx (+ arg ement-room--compose-message-history-index)))
4386
- (cond ((>= newidx len) (1- len))
4387
- ((< newidx -1) -1)
4388
- (t newidx))))
4389
- ;; Update the buffer.
4390
- (erase-buffer)
4391
- (insert (if (< ement-room--compose-message-history-index 0)
4392
- ement-room--compose-message-history-initial
4393
- (nth ement-room--compose-message-history-index
4394
- ement-room-message-history)))))))
4401
+ ;; It's still possible to move in the specified direction.
4402
+ (ement-room-compose-message-history-insert
4403
+ (let ((pos (+ arg ement-room--compose-message-history-index)))
4404
+ (cond ((>= pos len) (1- len))
4405
+ ((< pos -1) -1)
4406
+ (t pos))))))))
4395
4407
4396
4408
(defun ement-room-compose-history-next-message (arg)
4397
4409
"Cycle forward through message history, after saving current message.
4398
4410
With a numeric prefix ARG, go forward ARG messages."
4399
4411
(interactive "*p")
4400
4412
(ement-room-compose-history-prev-message (- arg)))
4401
4413
4414
+ (defun ement-room-compose-history-isearch-backward ()
4415
+ "Search for a string in the message history using Isearch.
4416
+ Use \\[isearch-backward] and \\[isearch-forward] to continue searching."
4417
+ (interactive)
4418
+ (setq ement-room--compose-history-isearch t)
4419
+ (isearch-backward nil t))
4420
+
4421
+ (defun ement-room-compose-history-isearch-backward-regexp ()
4422
+ "Search for a regular expression in the message history using Isearch.
4423
+ Use \\[isearch-backward] and \\[isearch-forward] to continue searching."
4424
+ (interactive)
4425
+ (setq ement-room--compose-history-isearch t)
4426
+ (isearch-backward-regexp nil t))
4427
+
4428
+ (defun ement-room-compose-history-isearch-setup ()
4429
+ "Set up Isearch to search `ement-room-message-history'.
4430
+ Intended to be added to `isearch-mode-hook' in an ement compose buffer."
4431
+ (when (eq ement-room--compose-history-isearch t)
4432
+ (setq isearch-message-prefix-add "history ")
4433
+ (setq-local isearch-search-fun-function
4434
+ #'ement-room-compose-history-isearch-search)
4435
+ (setq-local isearch-message-function
4436
+ #'ement-room-compose-history-isearch-message)
4437
+ (setq-local isearch-wrap-function
4438
+ #'ement-room-compose-history-isearch-wrap)
4439
+ (setq-local isearch-push-state-function
4440
+ #'ement-room-compose-history-isearch-push-state)
4441
+ (setq-local isearch-lazy-count nil)
4442
+ (add-hook 'isearch-mode-end-hook 'ement-room-compose-history-isearch-end nil t)))
4443
+
4444
+ (defun ement-room-compose-history-isearch-end ()
4445
+ "Clean up the buffer after terminating Isearch."
4446
+ (setq isearch-message-prefix-add nil)
4447
+ (setq isearch-search-fun-function 'isearch-search-fun-default)
4448
+ (setq isearch-wrap-function nil)
4449
+ (setq isearch-push-state-function nil)
4450
+ ;; Force isearch to not change mark.
4451
+ (setq isearch-opoint (point))
4452
+ (kill-local-variable 'isearch-lazy-count)
4453
+ (remove-hook 'isearch-mode-end-hook 'ement-room-compose-history-isearch-end t)
4454
+ (unless isearch-suspended
4455
+ (setq ement-room--compose-history-isearch nil)))
4456
+
4457
+ (defun ement-room-compose-history-isearch-search ()
4458
+ "Return the search function for Isearch in message history."
4459
+ #'ement-room-compose-history-isearch-function)
4460
+
4461
+ (defun ement-room-compose-history-isearch-function (string bound noerror)
4462
+ "Isearch in message history."
4463
+ (let ((search-fun
4464
+ ;; Use standard functions to search within message text
4465
+ (isearch-search-fun-default))
4466
+ found)
4467
+ (or
4468
+ ;; 1. First try searching in the initial message
4469
+ (funcall search-fun string nil noerror)
4470
+ ;; 2. If the above search fails, start putting next/prev history elements in the
4471
+ ;; buffer successively, and search the string in them. Do this only when bound is
4472
+ ;; nil (i.e. not while lazy-highlighting search strings in the current message).
4473
+ (unless bound
4474
+ (condition-case nil
4475
+ (progn
4476
+ (while (not found)
4477
+ (if isearch-forward
4478
+ (ement-room-compose-history-next-message 1)
4479
+ (ement-room-compose-history-prev-message 1))
4480
+ (goto-char (if isearch-forward (point-min) (point-max)))
4481
+ (setq isearch-barrier (point)
4482
+ isearch-opoint (point))
4483
+ ;; After putting the next/prev history element, search the string in
4484
+ ;; them again, until `ement-room-compose-history-next-message' or
4485
+ ;; `ement-room-compose-history-prev-message' raises an error at the
4486
+ ;; beginning/end of history.
4487
+ (setq found (funcall search-fun string nil noerror)))
4488
+ ;; Return point of the new search result.
4489
+ (point))
4490
+ ;; Return nil on any isearch errors, including the "no next/preceding item"
4491
+ ;; user-errors signalled from `ement-room-compose-history-prev-message'.
4492
+ (error nil))))))
4493
+
4494
+ (defun ement-room-compose-history-isearch-message (&optional c-q-hack ellipsis)
4495
+ "Display the isearch message."
4496
+ (setq isearch-message-prefix-add
4497
+ (if (and isearch-success
4498
+ (not isearch-error)
4499
+ (>= ement-room--compose-message-history-index 0))
4500
+ (format "history item %d: "
4501
+ ement-room--compose-message-history-index)
4502
+ "history "))
4503
+ (isearch-message c-q-hack ellipsis))
4504
+
4505
+ (defun ement-room-compose-history-isearch-wrap ()
4506
+ "Wrap the history search when search fails.
4507
+ Move point to the first history element for a forward search,
4508
+ or to the last history element for a backward search."
4509
+ ;; When `ement-room-compose-history-isearch-search' fails on reaching the
4510
+ ;; beginning/end of the history, wrap the search to the first/last
4511
+ ;; input history element.
4512
+ (ement-room-compose-message-history-insert
4513
+ (if isearch-forward
4514
+ (1- (length ement-room-message-history))
4515
+ -1))
4516
+ (goto-char (if isearch-forward (point-min) (point-max))))
4517
+
4518
+ (defun ement-room-compose-history-isearch-push-state ()
4519
+ "Save a function restoring the state of input history search.
4520
+ Save `ement-room--compose-message-history-index' to the additional state parameter
4521
+ in the search status stack."
4522
+ (let ((index ement-room--compose-message-history-index))
4523
+ (lambda (cmd)
4524
+ (ement-room-compose-history-isearch-pop-state cmd index))))
4525
+
4526
+ (defun ement-room-compose-history-isearch-pop-state (_cmd hist-pos)
4527
+ "Restore the input history search state.
4528
+ Go to the history element by the absolute history position HIST-POS."
4529
+ (ement-room-compose-message-history-insert hist-pos))
4530
+
4402
4531
;;;;; Widgets
4403
4532
4404
4533
(require 'widget)
0 commit comments