lotsa changes and inclusion of elpy
[emacs.git] / .emacs.d / elisp / local / smartscan.el
1 ;;; smartscan.el --- Jumps between other symbols found at point
2
3 ;; Copyright (C) 2011-2013 Mickey Petersen
4
5 ;; Author: Mickey Petersen <mickey@masteringemacs.org>
6 ;; Keywords: extensions
7
8 ;; This program is free software; you can redistribute it and/or modify
9 ;; it under the terms of the GNU General Public License as published by
10 ;; the Free Software Foundation, either version 3 of the License, or
11 ;; (at your option) any later version.
12
13 ;; This program is distributed in the hope that it will be useful,
14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ;; GNU General Public License for more details.
17
18 ;; You should have received a copy of the GNU General Public License
19 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
20
21 ;;; Commentary:
22 ;; This code comes from my article Effective Editing I: Movement"
23 ;; article on
24 ;; http://www.masteringemacs.org/articles/2011/01/14/effective-editing-movement/
25 ;;
26 ;; Smart Scan let's you jump between symbols in your buffer, based on
27 ;; the initial symbol your point was on when you started the
28 ;; search. Incremental calls will still respect the original search
29 ;; query so you can move up or down in your buffer, quickly, to find
30 ;; other matches without having to resort to `isearch' to find things
31 ;; first. The main advantage over isearch is speed: Smart Scan will
32 ;; guess the symbol point is on and immediately find other symbols
33 ;; matching it, in an unintrusive way.
34 ;;; HOW TO USE IT
35 ;;
36 ;; Simply type `smart-symbol-go-forward' (or press M-n) to go forward;
37 ;; or `smart-symbol-go-backward' (M-p) to go back.
38
39 ;;; Customizations
40
41 ;; You can customize `smart-use-extended-syntax' to alter
42 ;; (temporarily, when you search) the syntax table used by Smart Scan
43 ;; to find matches in your buffer.
44
45 ;;; Code:
46
47 (provide 'smartscan)
48
49
50 ;;; Default Keybindings
51 (global-set-key (kbd "M-n") 'smart-symbol-go-forward)
52 (global-set-key (kbd "M-p") 'smart-symbol-go-backward)
53
54 (defvar smart-use-extended-syntax nil
55 "If t the smart symbol functionality will consider extended
56 syntax in finding matches, if such matches exist.")
57
58 (defvar smart-last-symbol-name ""
59 "Contains the current symbol name.
60
61 This is only refreshed when `last-command' does not contain
62 either `smart-symbol-go-forward' or `smart-symbol-go-backward'")
63
64 (defvar smart-symbol-old-pt nil
65 "Contains the location of the old point")
66
67 (make-local-variable 'smart-use-extended-syntax)
68
69 (defun smart-symbol-goto (name direction)
70 "Jumps to the next NAME in DIRECTION in the current buffer.
71
72 DIRECTION must be either `forward' or `backward'; no other option
73 is valid."
74
75 ;; if `last-command' did not contain
76 ;; `smart-symbol-go-forward/backward' then we assume it's a
77 ;; brand-new command and we re-set the search term.
78 (unless (memq last-command '(smart-symbol-go-forward
79 smart-symbol-go-backward))
80 (setq smart-last-symbol-name name))
81 (setq smart-symbol-old-pt (point))
82 (message (format "%s scan for symbol \"%s\""
83 (capitalize (symbol-name direction))
84 smart-last-symbol-name))
85 (with-smart-symbol
86 (unless (catch 'done
87 (while (funcall (cond
88 ((eq direction 'forward) ; forward
89 're-search-forward)
90 ((eq direction 'backward) ; backward
91 're-search-backward)
92 (t (error "Invalid direction"))) ; all others
93 (concat "\\<" smart-last-symbol-name "\\>") nil t)
94 (unless (memq (syntax-ppss-context
95 (syntax-ppss (point))) '(string comment))
96 (throw 'done t))))
97 (goto-char smart-symbol-old-pt))))
98
99 (defun smart-symbol-go-forward ()
100 "Jumps forward to the next symbol at point"
101 (interactive)
102 (smart-symbol-goto (smart-symbol-at-pt 'end) 'forward))
103
104 (defun smart-symbol-go-backward ()
105 "Jumps backward to the previous symbol at point"
106 (interactive)
107 (smart-symbol-goto (smart-symbol-at-pt 'beginning) 'backward))
108
109 (defmacro with-smart-symbol (body)
110 "Macro that initialises the syntax table"
111 (declare (indent defun))
112 `(with-syntax-table (make-syntax-table)
113 (if smart-use-extended-syntax
114 (modify-syntax-entry ?. "w"))
115 ;; we need this outside the if-statement as using the word
116 ;; parameter with `thing-at-point' will treat underscore as a word
117 ;; separator.
118 (modify-syntax-entry ?_ "w")
119 (modify-syntax-entry ?- "w")
120 ,body))
121
122
123 (defun smart-symbol-at-pt (&optional dir)
124 "Returns the symbol at point and moves point to DIR (either `beginning' or `end') of the symbol.
125
126 If `smart-use-extended-syntax' is t then that symbol is returned
127 instead."
128 ;; we need a quick-and-dirty syntax table hack here to make
129 ;; `thing-at-point' pick up on the fact that '.', '_', etc. are all
130 ;; part of a single expression.
131 (with-smart-symbol
132 ;; grab the word and return it
133 (let ((word (thing-at-point 'word))
134 (bounds (bounds-of-thing-at-point 'word)))
135 (if word
136 (progn
137 (cond
138 ((eq dir 'beginning) (goto-char (car bounds)))
139 ((eq dir 'end) (goto-char (cdr bounds)))
140 (t (error "Invalid direction")))
141 word)
142 (error "No symbol found")))))
143
144
145 (provide 'smartscan)
146 ;;; smartscan.el ends here