]> code.delx.au - gnu-emacs-elpa/blob - packages/jumpc/jumpc.el
Merge commit '0cda39255827f283e7578cd469ae42daad9556a2' from js2-mode
[gnu-emacs-elpa] / packages / jumpc / jumpc.el
1 ;;; jumpc.el --- jump to previous insertion points -*- coding: utf-8; lexical-binding: t -*-
2
3 ;; Copyright (C) 2013 Free Software Foundation, Inc.
4
5 ;; Author: Ivan Kanis <ivan@kanis.fr>
6 ;; Version: 3.0
7
8 ;; This file is part of GNU Emacs.
9
10 ;; GNU Emacs is free software: you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation, either version 3 of the License, or
13 ;; (at your option) any later version.
14
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
22
23 ;;; Commentary:
24
25 ;; This implements the jump cursor feature found in vim.
26
27 ;; A jump is added every time you insert a character on a different
28 ;; line.
29
30 ;; Jumps are remembered in a jump list. With the C-o and C-i
31 ;; command you can go to cursor positions before older jumps, and back
32 ;; again. Thus you can move up and down the list.
33
34 ;; Jumps are read and saved in the same configuration file as vim so
35 ;; you can switch back and forth between the two editors
36
37 ;;;; THANKS:
38
39 ;; Bram Moolenaar for writing a fine editor and helping needy children
40 ;; in Uganda
41
42 ;; Stefan Monnier for telling me how to add C-i binding
43
44 ;; Ted Zlatanov for suggesting to use less agressive key bindings
45
46 ;;;; BUGS:
47
48 ;;;; INSTALLATION:
49
50 ;; put this file somewhere in your load path then put the put the
51 ;; following in your .emacs:
52 ;;
53 ;; (require 'jumpc)
54 ;; (jumpc)
55 ;;
56 ;; Then either:
57 ;;
58 ;; (jumpc-bind-vim-key)
59 ;;
60 ;; Or:
61 ;;
62 ;; (global-set-key (kbd "<f8>") 'jumpc-jump-backward)
63 ;; (global-set-key (kbd "<f9>") 'jumpc-jump-forward)
64 ;;
65 ;; The first will bind C-i and C-o just like vim. The second bind
66 ;; function keys 8 and 9. Of course you can pick any keys you like. If
67 ;; you use Emacs on a console you will have to pick the second form as
68 ;; C-i and TAB are the same thing.
69 ;;
70 ;; If you use autoload you don't need to require the file
71
72 ;;;; TODO
73
74 ;; search for TODO within the file
75
76 ;; Add rotate jump list feature in vim
77 ;; defined as JUMPLIST_ROTATE in mark.c
78 ;; "If last used entry is not at the top, put it at the top by
79 ;; rotating the stack until it is (the newer entries will be at
80 ;; the bottom). Keep one entry (the last used one) at the top."
81
82 ;; Add a bit of looseness. For example do not add jump points within
83 ;; three lines of the last one.
84
85 ;; Add jump list displaying each jump with the file and line in the
86 ;; file. Clicking on a line takes you to the jump location.
87
88 ;; Add more commands that will insert jump. Vim does: "'", "`", "G",
89 ;; "/", "?", "n", "N", "%", "(", ")", "[[", "]]", "{", "}", ":s",
90 ;; ":tag", "L", "M", "H"
91
92 ;; Remove files that do not exist when reading and writing from
93 ;; configuration file
94
95 ;;;; VERSION
96
97 ;; version 1
98
99 ;; version 2
100 ;; - don't force vim key bindings
101 ;; - remove debugging message
102 ;; - insert a jump moves the index back to top of list
103 ;; - insert jumps goes back to top of list
104
105 ;; version 3
106 ;; - remove deleted files
107
108 ;;; Code:
109
110 (defvar jumpc-file "~/.viminfo"
111 "File where jump information is written.")
112
113 (defvar jumpc-list nil
114 "List of filenames and position to jump to.")
115
116 (defvar jumpc-index 0
117 "Index of jump, 0 is the newest entry.")
118
119 (defun jumpc-read-list ()
120 "Read jump list from file."
121 (when (file-exists-p jumpc-file)
122 (with-temp-buffer
123 (insert-file-contents jumpc-file)
124 (goto-char (point-min))
125 (when (re-search-forward "# Jumplist (newest first):" nil t)
126 (while (re-search-forward
127 "-' \\([0-9]*\\) \\([0-9]*\\) \\(.*\\)" nil t)
128 (add-to-list 'jumpc-list
129 (list (string-to-number (match-string 1))
130 (string-to-number (match-string 2))
131 (expand-file-name (match-string 3)))
132 jumpc-list)))))
133 jumpc-list)
134
135 (defun jumpc-write-list ()
136 "Write jump list to file."
137 (let (bgn end)
138 (jumpc-remove-deleted-file)
139 (find-file jumpc-file)
140 (goto-char (point-min))
141 (setq bgn (re-search-forward "# Jumplist (newest first):" nil t))
142 (if bgn
143 (progn
144 (setq end (re-search-forward "^$"))
145 (delete-region bgn end))
146 ;; looks like the entry doesn't exist, tack it at the end
147 (goto-char (point-max))
148 (insert "# Jumplist (newest first):"))
149 (insert "\n")
150 (dolist (line jumpc-list)
151 (insert (format "-' %d %d %s\n"
152 (nth 0 line) (nth 1 line)
153 (abbreviate-file-name (nth 2 line)))))
154 (save-buffer)))
155
156 (defun jumpc-jump-backward ()
157 "Jump backward in list of jumps."
158 (interactive)
159 (jumpc-jump 1))
160
161 (defun jumpc-jump-forward ()
162 "Jump forward in list of jumps."
163 (interactive)
164 (jumpc-jump -1))
165
166 ;; TODO make it interactive with COUNT as argument
167 (defun jumpc-jump (count)
168 "Jump COUNT from current index."
169 (jumpc-remove-deleted-file)
170 (let ((length (length jumpc-list)) file-name)
171 ;; first backward motion adds current point in the list
172 (when (and (> count 0) (= jumpc-index 0))
173 (jumpc-insert))
174 (setq jumpc-index (+ jumpc-index count))
175 ;; fix index if it's out of boundary
176 (cond
177 ((< jumpc-index 0)
178 (setq jumpc-index 0))
179 ((> jumpc-index length)
180 (setq jumpc-index length))
181 (t
182 (setq file-name (nth 2 (nth jumpc-index jumpc-list)))
183 (find-file file-name)
184 (goto-char (point-min))
185 (forward-line (1- (nth 0 (nth jumpc-index jumpc-list))))
186 (move-to-column (nth 0 (nth jumpc-index jumpc-list)))))))
187
188 (defun jumpc-insert ()
189 "Insert jump location."
190 ;; It means we are going back to the top of the list
191 (setq jumpc-index 0)
192 (when buffer-file-name
193 (when (not (= (line-number-at-pos) (nth 0 (car jumpc-list))))
194 (setq jumpc-list
195 (cons (list (line-number-at-pos) (current-column) buffer-file-name)
196 jumpc-list)))))
197
198 (defun jumpc-remove-deleted-file ()
199 "Remove deleted file in the list.
200 Returns list minus deleted files."
201 (let ((length (length jumpc-list))
202 (index 0)
203 reduced-list element)
204 (while (< index length)
205 (setq element (nth index jumpc-list))
206 (when (file-exists-p (nth 2 element))
207 (setq reduced-list (cons element reduced-list)))
208 (setq index (1+ index)))
209 (setq jumpc-list reduced-list)))
210
211 (defun jumpc-bind-vim-key ()
212 "Bind keys just like vim."
213 (global-set-key (kbd "C-o") 'jumpc-jump-backward)
214 (define-key input-decode-map [?\C-i] [control-i])
215 (global-set-key [control-i] 'jumpc-jump-forward))
216
217 ;;;###autoload
218 (defun jumpc ()
219 "Initialize jump cursor."
220 (interactive)
221 (setq jumpc-list (jumpc-read-list))
222 (defadvice self-insert-command
223 (after jumpc-insert activate)
224 "Insert jump position after insertion."
225 (jumpc-insert))
226 (add-hook 'kill-emacs-hook 'jumpc-write-list))
227
228
229 ;; vi:et:sw=4:ts=4:
230 ;; Local Variables:
231 ;; compile-command: "make"
232 ;; End:
233
234 (provide 'jumpc)
235 ;;; jumpc.el ends here