;;; wrap-search --- wrapped, non-interactive search ;;; This file: http://user.it.uu.se/~embe8573/conf/emacs-init/wrap-search.el ;;; Commentary: ;;; `wrap-search' is non-incremental. The purpose is ;;; to get a disruption-free search that won't show ;;; anything of the buffer which you don't want to ;;; see, just because some prefix of your search ;;; matches some other thing that isn't what you ;;; look for. ;;; `wrap-search' searches forward by default, but it ;;; wraps and continues up and until the search start ;;; position, if necessary (or if there isn't a single ;;; hit). This is so you don't have to wonder if ;;; something is north or south - just search. ;;; The philosophy is that `wrap-search' mostly ;;; shouldn't used for "searching" - tho that is ;;; possible as well - rather, it should be used to ;;; *navigate* a buffer: to quickly jump to a certain ;;; place, which you already know exist. ;;; So, use of this package encourages (and is ;;; benefited from) good code style with short entity ;;; names that are easy to remember and type, and thus ;;; fast and easy to jump to with this package. ;;; To use it without keyboard shortcuts isn't good. ;;; I use: ;;; (global-set-key "\C-s" #'wrap-search) ;;; (global-set-key "\C-r" #'wrap-search-again) ;;; Those are the only commands - `wrap-search' and ;;; `wrap-search-again' - but they involve a somewhat ;;; complicated interface which is based on the prefix ;;; argument to set the search options. For the ;;; details, see the docstrings below or use the ;;; on-line help. ;;; Note: Probably nine out of ten searches will be ;;; plain ones using the default (implicit) options. ;;; In certain cases tho it can be irritating not ;;; being able to - in particular - do a *backward* ;;; search, which is why the functionality was added. ;;; Code: (require 'cl-lib) (defun wrap-search-again (&optional new-options) "Search again for what the user most recently searched for. By default, options from the previous search are preserved. However, the user can supply NEW-OPTIONS with \\[universal-argument]. The options are set and put to effect in the same way as in `wrap-search'." (interactive "p") (let ((cmd (cl-dolist (cmd command-history) (if (and (eq (car cmd) #'wrap-search) (not (string= (cl-caddr cmd) "")) ) (cl-return cmd) )))) (if cmd (if (not (= new-options 1)) (let*((search-str (cl-caddr cmd)) (final-cmd (list #'wrap-search new-options search-str)) ) (setq command-history (cons final-cmd command-history)) (apply final-cmd) ) (eval cmd) ) (message " No previous search.") ))) (defun wrap-search (options search-string) "Do a non-interactive, wrapped search.\n OPTIONS are for interactive calls set by \\[universal-argument] keystrokes.\n 0 strokes - or OPTIONS being 1 - case insensitive, search forward 1 stroke - 4 - case sensitive, -\"- 2 strokes - 16 - case insensitive, search backward 3 strokes - 64 - case sensitive, -\"-\n Search for SEARCH-STRING." (interactive "p\nssearch: ") (if (string= "" search-string) (wrap-search-again options) (let*((options-data (cl-case options ( (1) '(t nil)) ( (4) '(nil nil)) ((16) '(t t)) ((64) '(nil t))) ) (case-fold-search (car options-data)) (backward (cadr options-data)) (pos (point)) (fun-start-end (if backward (list #'search-backward (point-max) (point-min)) (list #'search-forward (point-min) (point-max)) )) (search-f (car fun-start-end)) (start (cadr fun-start-end)) (end (cl-caddr fun-start-end)) ) (if (not (apply (list search-f search-string end t))) ; NOERROR (progn (goto-char start) (if (apply (list search-f search-string (+ pos (length search-string)) t)) (if (= pos (point)) (message " This is the one occurrence.") (message " hit: %s (wrap)" (point))) (progn (goto-char pos) (message "No hit.") ))) (message " hit: %s" (point)) )))) (provide 'wrap-search) ;;; wrap-search.el ends here