commit | author | age
|
76bbd0
|
1 |
;;; org-ctags.el - Integrate Emacs "tags" Facility with Org -*- lexical-binding: t; -*- |
C |
2 |
;; |
|
3 |
;; Copyright (C) 2007-2018 Free Software Foundation, Inc. |
|
4 |
|
|
5 |
;; Author: Paul Sexton <eeeickythump@gmail.com> |
|
6 |
|
|
7 |
|
|
8 |
;; Keywords: org, wp |
|
9 |
;; |
|
10 |
;; This file is part of GNU Emacs. |
|
11 |
;; |
|
12 |
;; GNU Emacs is free software: you can redistribute it and/or modify |
|
13 |
;; it under the terms of the GNU General Public License as published by |
|
14 |
;; the Free Software Foundation, either version 3 of the License, or |
|
15 |
;; (at your option) any later version. |
|
16 |
|
|
17 |
;; GNU Emacs is distributed in the hope that it will be useful, |
|
18 |
;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 |
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
20 |
;; GNU General Public License for more details. |
|
21 |
|
|
22 |
;; You should have received a copy of the GNU General Public License |
|
23 |
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. |
|
24 |
|
|
25 |
;; |
|
26 |
;; Synopsis |
|
27 |
;; ======== |
|
28 |
;; |
|
29 |
;; Allows Org mode to make use of the Emacs `etags' system. Defines |
|
30 |
;; tag destinations in Org files as any text between <<double angled |
|
31 |
;; brackets>>. This allows the tags-generation program `exuberant |
|
32 |
;; ctags' to parse these files and create tag tables that record where |
|
33 |
;; these destinations are found. Plain [[links]] in org mode files |
|
34 |
;; which do not have <<matching destinations>> within the same file |
|
35 |
;; will then be interpreted as links to these 'tagged' destinations, |
|
36 |
;; allowing seamless navigation between multiple Org files. Topics |
|
37 |
;; can be created in any org mode file and will always be found by |
|
38 |
;; plain links from other files. Other file types recognized by ctags |
|
39 |
;; (source code files, latex files, etc) will also be available as |
|
40 |
;; destinations for plain links, and similarly, Org links will be |
|
41 |
;; available as tags from source files. Finally, the function |
|
42 |
;; `org-ctags-find-tag-interactive' lets you choose any known tag, |
|
43 |
;; using autocompletion, and quickly jump to it. |
|
44 |
;; |
|
45 |
;; Installation |
|
46 |
;; ============ |
|
47 |
;; |
|
48 |
;; Install org mode |
|
49 |
;; Ensure org-ctags.el is somewhere in your emacs load path. |
|
50 |
;; Download and install Exuberant ctags -- "http://ctags.sourceforge.net/" |
|
51 |
;; Edit your .emacs file (see next section) and load emacs. |
|
52 |
|
|
53 |
;; To put in your init file (.emacs): |
|
54 |
;; ================================== |
|
55 |
;; |
|
56 |
;; Assuming you already have org mode installed and set up: |
|
57 |
;; |
|
58 |
;; (setq org-ctags-path-to-ctags "/path/to/ctags/executable") |
|
59 |
;; (add-hook 'org-mode-hook |
|
60 |
;; (lambda () |
|
61 |
;; (define-key org-mode-map "\C-co" 'org-ctags-find-tag-interactive))) |
|
62 |
;; |
|
63 |
;; By default, with org-ctags loaded, org will first try and visit the tag |
|
64 |
;; with the same name as the link; then, if unsuccessful, ask the user if |
|
65 |
;; he/she wants to rebuild the 'TAGS' database and try again; then ask if |
|
66 |
;; the user wishes to append 'tag' as a new toplevel heading at the end of |
|
67 |
;; the buffer; and finally, defer to org's default behavior which is to |
|
68 |
;; search the entire text of the current buffer for 'tag'. |
|
69 |
;; |
|
70 |
;; This behavior can be modified by changing the value of |
|
71 |
;; ORG-CTAGS-OPEN-LINK-FUNCTIONS. For example I have the following in my |
|
72 |
;; .emacs, which describes the same behavior as the above paragraph with |
|
73 |
;; one difference: |
|
74 |
;; |
|
75 |
;; (setq org-ctags-open-link-functions |
|
76 |
;; '(org-ctags-find-tag |
|
77 |
;; org-ctags-ask-rebuild-tags-file-then-find-tag |
|
78 |
;; org-ctags-ask-append-topic |
|
79 |
;; org-ctags-fail-silently)) ; <-- prevents org default behavior |
|
80 |
;; |
|
81 |
;; |
|
82 |
;; Usage |
|
83 |
;; ===== |
|
84 |
;; |
|
85 |
;; When you click on a link "[[foo]]" and org cannot find a matching "<<foo>>" |
|
86 |
;; in the current buffer, the tags facility will take over. The file TAGS in |
|
87 |
;; the active directory is examined to see if the tags facility knows about |
|
88 |
;; "<<foo>>" in any other files. If it does, the matching file will be opened |
|
89 |
;; and the cursor will jump to the position of "<<foo>>" in that file. |
|
90 |
;; |
|
91 |
;; User-visible functions: |
|
92 |
;; - `org-ctags-find-tag-interactive': type a tag (plain link) name and visit |
|
93 |
;; it. With autocompletion. Bound to ctrl-O in the above setup. |
|
94 |
;; - All the etags functions should work. These include: |
|
95 |
;; |
|
96 |
;; M-. `find-tag' -- finds the tag at point |
|
97 |
;; |
|
98 |
;; C-M-. find-tag based on regular expression |
|
99 |
;; |
|
100 |
;; M-x tags-search RET -- like C-M-. but searches through ENTIRE TEXT |
|
101 |
;; of ALL the files referenced in the TAGS file. A quick way to |
|
102 |
;; search through an entire 'project'. |
|
103 |
;; |
|
104 |
;; M-* "go back" from a tag jump. Like `org-mark-ring-goto'. |
|
105 |
;; You may need to bind this key yourself with (eg) |
|
106 |
;; (global-set-key (kbd "<M-kp-multiply>") 'pop-tag-mark) |
|
107 |
;; |
|
108 |
;; (see etags chapter in Emacs manual for more) |
|
109 |
;; |
|
110 |
;; |
|
111 |
;; Keeping the TAGS file up to date |
|
112 |
;; ================================ |
|
113 |
;; |
|
114 |
;; Tags mode has no way of knowing that you have created new tags by |
|
115 |
;; typing in your Org buffer. New tags make it into the TAGS file in |
|
116 |
;; 3 ways: |
|
117 |
;; |
|
118 |
;; 1. You re-run (org-ctags-create-tags "directory") to rebuild the file. |
|
119 |
;; 2. You put the function `org-ctags-ask-rebuild-tags-file-then-find-tag' in |
|
120 |
;; your `org-open-link-functions' list, as is done in the setup |
|
121 |
;; above. This will cause the TAGS file to be rebuilt whenever a link |
|
122 |
;; cannot be found. This may be slow with large file collections however. |
|
123 |
;; 3. You run the following from the command line (all 1 line): |
|
124 |
;; |
|
125 |
;; ctags --langdef=orgmode --langmap=orgmode:.org |
|
126 |
;; --regex-orgmode="/<<([^>]+)>>/\1/d,definition/" |
|
127 |
;; -f /your/path/TAGS -e -R /your/path/*.org |
|
128 |
;; |
|
129 |
;; If you are paranoid, you might want to run (org-ctags-create-tags |
|
130 |
;; "/path/to/org/files") at startup, by including the following toplevel form |
|
131 |
;; in .emacs. However this can cause a pause of several seconds if ctags has |
|
132 |
;; to scan lots of files. |
|
133 |
;; |
|
134 |
;; (progn |
|
135 |
;; (message "-- rebuilding tags tables...") |
|
136 |
;; (mapc 'org-ctags-create-tags tags-table-list)) |
|
137 |
|
|
138 |
;;; Code: |
|
139 |
|
|
140 |
(require 'org) |
|
141 |
|
|
142 |
(defgroup org-ctags nil |
|
143 |
"Options concerning use of ctags within org mode." |
|
144 |
:tag "Org-Ctags" |
|
145 |
:group 'org-link) |
|
146 |
|
|
147 |
(defvar org-ctags-enabled-p t |
|
148 |
"Activate ctags support in org mode?") |
|
149 |
|
|
150 |
(defvar org-ctags-tag-regexp "/<<([^>]+)>>/\\1/d,definition/" |
|
151 |
"Regexp expression used by ctags external program. |
|
152 |
The regexp matches tag destinations in Org files. |
|
153 |
Format is: /REGEXP/TAGNAME/FLAGS,TAGTYPE/ |
|
154 |
See the ctags documentation for more information.") |
|
155 |
|
|
156 |
(defcustom org-ctags-path-to-ctags |
|
157 |
(if (executable-find "ctags-exuberant") "ctags-exuberant" "ctags") |
|
158 |
"Name of the ctags executable file." |
|
159 |
:group 'org-ctags |
|
160 |
:version "24.1" |
|
161 |
:type 'file) |
|
162 |
|
|
163 |
(defcustom org-ctags-open-link-functions |
|
164 |
'(org-ctags-find-tag |
|
165 |
org-ctags-ask-rebuild-tags-file-then-find-tag |
|
166 |
org-ctags-ask-append-topic) |
|
167 |
"List of functions to be prepended to ORG-OPEN-LINK-FUNCTIONS when ORG-CTAGS is active." |
|
168 |
:group 'org-ctags |
|
169 |
:version "24.1" |
|
170 |
:type 'hook |
|
171 |
:options '(org-ctags-find-tag |
|
172 |
org-ctags-ask-rebuild-tags-file-then-find-tag |
|
173 |
org-ctags-rebuild-tags-file-then-find-tag |
|
174 |
org-ctags-ask-append-topic |
|
175 |
org-ctags-append-topic |
|
176 |
org-ctags-ask-visit-buffer-or-file |
|
177 |
org-ctags-visit-buffer-or-file |
|
178 |
org-ctags-fail-silently)) |
|
179 |
|
|
180 |
|
|
181 |
(defvar org-ctags-tag-list nil |
|
182 |
"List of all tags in the active TAGS file. |
|
183 |
Created as a local variable in each buffer.") |
|
184 |
|
|
185 |
(defcustom org-ctags-new-topic-template |
|
186 |
"* <<%t>>\n\n\n\n\n\n" |
|
187 |
"Text to insert when creating a new org file via opening a hyperlink. |
|
188 |
The following patterns are replaced in the string: |
|
189 |
`%t' - replaced with the capitalized title of the hyperlink" |
|
190 |
:group 'org-ctags |
|
191 |
:version "24.1" |
|
192 |
:type 'string) |
|
193 |
|
|
194 |
|
|
195 |
(add-hook 'org-mode-hook |
|
196 |
(lambda () |
|
197 |
(when (and org-ctags-enabled-p |
|
198 |
(buffer-file-name)) |
|
199 |
;; Make sure this file's directory is added to default |
|
200 |
;; directories in which to search for tags. |
|
201 |
(let ((tags-filename |
|
202 |
(expand-file-name |
|
203 |
(concat (file-name-directory (buffer-file-name)) |
|
204 |
"/TAGS")))) |
|
205 |
(when (file-exists-p tags-filename) |
|
206 |
(visit-tags-table tags-filename)))))) |
|
207 |
|
|
208 |
|
|
209 |
(defadvice visit-tags-table (after org-ctags-load-tag-list activate compile) |
|
210 |
(when (and org-ctags-enabled-p tags-file-name) |
|
211 |
(setq-local org-ctags-tag-list |
|
212 |
(org-ctags-all-tags-in-current-tags-table)))) |
|
213 |
|
|
214 |
|
|
215 |
(defun org-ctags-enable () |
|
216 |
(put 'org-mode 'find-tag-default-function 'org-ctags-find-tag-at-point) |
|
217 |
(setq org-ctags-enabled-p t) |
|
218 |
(dolist (fn org-ctags-open-link-functions) |
|
219 |
(add-hook 'org-open-link-functions fn t))) |
|
220 |
|
|
221 |
|
|
222 |
;;; General utility functions. =============================================== |
|
223 |
;; These work outside org-ctags mode. |
|
224 |
|
|
225 |
(defun org-ctags-get-filename-for-tag (tag) |
|
226 |
"TAG is a string. Search the active TAGS file for a matching tag. |
|
227 |
If the tag is found, return a list containing the filename, line number, and |
|
228 |
buffer position where the tag is found." |
|
229 |
(interactive "sTag: ") |
|
230 |
(unless tags-file-name |
|
231 |
(call-interactively (visit-tags-table))) |
|
232 |
(save-excursion |
|
233 |
(visit-tags-table-buffer 'same) |
|
234 |
(when tags-file-name |
|
235 |
(with-current-buffer (get-file-buffer tags-file-name) |
|
236 |
(goto-char (point-min)) |
|
237 |
(cond |
|
238 |
((re-search-forward (format "^.*%s\\([0-9]+\\),\\([0-9]+\\)$" |
|
239 |
(regexp-quote tag)) nil t) |
|
240 |
(let ((line (string-to-number (match-string 1))) |
|
241 |
(pos (string-to-number (match-string 2)))) |
|
242 |
(cond |
|
243 |
((re-search-backward "\n\\(.*\\),[0-9]+\n") |
|
244 |
(list (match-string 1) line pos)) |
|
245 |
(t ; can't find a file name preceding the matched |
|
246 |
; tag?? |
|
247 |
(error "Malformed TAGS file: %s" (buffer-name)))))) |
|
248 |
(t ; tag not found |
|
249 |
nil)))))) |
|
250 |
|
|
251 |
|
|
252 |
(defun org-ctags-all-tags-in-current-tags-table () |
|
253 |
"Read all tags defined in the active TAGS file, into a list of strings. |
|
254 |
Return the list." |
|
255 |
(interactive) |
|
256 |
(let ((taglist nil)) |
|
257 |
(unless tags-file-name |
|
258 |
(call-interactively (visit-tags-table))) |
|
259 |
(save-excursion |
|
260 |
(visit-tags-table-buffer 'same) |
|
261 |
(with-current-buffer (get-file-buffer tags-file-name) |
|
262 |
(goto-char (point-min)) |
|
263 |
(while (re-search-forward "^.*\\(.*\\)\\([0-9]+\\),\\([0-9]+\\)$" |
|
264 |
nil t) |
|
265 |
(push (substring-no-properties (match-string 1)) taglist))) |
|
266 |
taglist))) |
|
267 |
|
|
268 |
|
|
269 |
(defun org-ctags-string-search-and-replace (search replace string) |
|
270 |
"Replace all instances of SEARCH with REPLACE in STRING." |
|
271 |
(replace-regexp-in-string (regexp-quote search) replace string t t)) |
|
272 |
|
|
273 |
|
|
274 |
;;; Internal functions ======================================================= |
|
275 |
|
|
276 |
|
|
277 |
(defun org-ctags-open-file (name &optional title) |
|
278 |
"Visit or create a file called `NAME.org', and insert a new topic. |
|
279 |
The new topic will be titled NAME (or TITLE if supplied)." |
|
280 |
(interactive "sFile name: ") |
|
281 |
(condition-case v |
|
282 |
(progn |
|
283 |
(org-open-file name t) |
|
284 |
(message "Opened file OK") |
|
285 |
(goto-char (point-max)) |
|
286 |
(insert (org-ctags-string-search-and-replace |
|
287 |
"%t" (capitalize (or title name)) |
|
288 |
org-ctags-new-topic-template)) |
|
289 |
(message "Inserted new file text OK") |
|
290 |
(org-mode-restart)) |
|
291 |
(error (error "Error %S in org-ctags-open-file" v)))) |
|
292 |
|
|
293 |
|
|
294 |
;;;; Misc interoperability with etags system ================================= |
|
295 |
|
|
296 |
|
|
297 |
(defadvice xref-find-definitions |
|
298 |
(before org-ctags-set-org-mark-before-finding-tag activate compile) |
|
299 |
"Before trying to find a tag, save our current position on org mark ring." |
|
300 |
(save-excursion |
|
301 |
(when (and (derived-mode-p 'org-mode) org-ctags-enabled-p) |
|
302 |
(org-mark-ring-push)))) |
|
303 |
|
|
304 |
|
|
305 |
|
|
306 |
(defun org-ctags-find-tag-at-point () |
|
307 |
"Determine default tag to search for, based on text at point. |
|
308 |
If there is no plausible default, return nil." |
|
309 |
(let (from to bound) |
|
310 |
(when (or (ignore-errors |
|
311 |
;; Look for hyperlink around `point'. |
|
312 |
(save-excursion |
|
313 |
(search-backward "[[") (setq from (+ 2 (point)))) |
|
314 |
(save-excursion |
|
315 |
(goto-char from) |
|
316 |
(search-forward "]") (setq to (- (point) 1))) |
|
317 |
(and (> to from) (>= (point) from) (<= (point) to))) |
|
318 |
(progn |
|
319 |
;; Look at text around `point'. |
|
320 |
(save-excursion |
|
321 |
(skip-syntax-backward "w_") (setq from (point))) |
|
322 |
(save-excursion |
|
323 |
(skip-syntax-forward "w_") (setq to (point))) |
|
324 |
(> to from)) |
|
325 |
;; Look between `line-beginning-position' and `point'. |
|
326 |
(save-excursion |
|
327 |
(and (setq bound (line-beginning-position)) |
|
328 |
(skip-syntax-backward "^w_" bound) |
|
329 |
(> (setq to (point)) bound) |
|
330 |
(skip-syntax-backward "w_") |
|
331 |
(setq from (point)))) |
|
332 |
;; Look between `point' and `line-end-position'. |
|
333 |
(save-excursion |
|
334 |
(and (setq bound (line-end-position)) |
|
335 |
(skip-syntax-forward "^w_" bound) |
|
336 |
(< (setq from (point)) bound) |
|
337 |
(skip-syntax-forward "w_") |
|
338 |
(setq to (point))))) |
|
339 |
(buffer-substring-no-properties from to)))) |
|
340 |
|
|
341 |
|
|
342 |
;;; Functions for use with 'org-open-link-functions' hook ================= |
|
343 |
|
|
344 |
|
|
345 |
(defun org-ctags-find-tag (name) |
|
346 |
"This function is intended to be used in ORG-OPEN-LINK-FUNCTIONS. |
|
347 |
Look for a tag called `NAME' in the current TAGS table. If it is found, |
|
348 |
visit the file and location where the tag is found." |
|
349 |
(interactive "sTag: ") |
|
350 |
(let ((old-buf (current-buffer)) |
|
351 |
(old-pnt (point-marker)) |
|
352 |
(old-mark (copy-marker (mark-marker)))) |
|
353 |
(condition-case nil |
|
354 |
(progn (xref-find-definitions name) |
|
355 |
t) |
|
356 |
(error |
|
357 |
;; only restore old location if find-tag raises error |
|
358 |
(set-buffer old-buf) |
|
359 |
(goto-char old-pnt) |
|
360 |
(set-marker (mark-marker) old-mark) |
|
361 |
nil)))) |
|
362 |
|
|
363 |
|
|
364 |
(defun org-ctags-visit-buffer-or-file (name &optional create) |
|
365 |
"This function is intended to be used in ORG-OPEN-LINK-FUNCTIONS. |
|
366 |
Visit buffer named `NAME.org'. If there is no such buffer, visit the file |
|
367 |
with the same name if it exists. If the file does not exist, then behavior |
|
368 |
depends on the value of CREATE. |
|
369 |
|
|
370 |
If CREATE is nil (default), then return nil. Do not create a new file. |
|
371 |
If CREATE is t, create the new file and visit it. |
|
372 |
If CREATE is the symbol `ask', then ask the user if they wish to create |
|
373 |
the new file." |
|
374 |
(interactive) |
|
375 |
(let ((filename (concat (substitute-in-file-name |
|
376 |
(expand-file-name name)) |
|
377 |
".org"))) |
|
378 |
(cond |
|
379 |
((get-buffer (concat name ".org")) |
|
380 |
;; Buffer is already open |
|
381 |
(pop-to-buffer-same-window (get-buffer (concat name ".org")))) |
|
382 |
((file-exists-p filename) |
|
383 |
;; File exists but is not open --> open it |
|
384 |
(message "Opening existing org file `%S'..." |
|
385 |
filename) |
|
386 |
(org-open-file filename t)) |
|
387 |
((or (eql create t) |
|
388 |
(and (eql create 'ask) |
|
389 |
(y-or-n-p (format-message |
|
390 |
"File `%s.org' not found; create?" name)))) |
|
391 |
(org-ctags-open-file filename name)) |
|
392 |
(t ;; File does not exist, and we don't want to create it. |
|
393 |
nil)))) |
|
394 |
|
|
395 |
|
|
396 |
(defun org-ctags-ask-visit-buffer-or-file (name) |
|
397 |
"This function is intended to be used in ORG-OPEN-LINK-FUNCTIONS. |
|
398 |
Wrapper for org-ctags-visit-buffer-or-file, which ensures the user is |
|
399 |
asked before creating a new file." |
|
400 |
(org-ctags-visit-buffer-or-file name 'ask)) |
|
401 |
|
|
402 |
|
|
403 |
(defun org-ctags-append-topic (name &optional narrowp) |
|
404 |
"This function is intended to be used in ORG-OPEN-LINK-FUNCTIONS. |
|
405 |
Append a new toplevel heading to the end of the current buffer. The |
|
406 |
heading contains NAME surrounded by <<angular brackets>>, thus making |
|
407 |
the heading a destination for the tag `NAME'." |
|
408 |
(interactive "sTopic: ") |
|
409 |
(widen) |
|
410 |
(goto-char (point-max)) |
|
411 |
(newline 2) |
|
412 |
(message "Adding topic in buffer %s" (buffer-name)) |
|
413 |
(insert (org-ctags-string-search-and-replace |
|
414 |
"%t" (capitalize name) org-ctags-new-topic-template)) |
|
415 |
(backward-char 4) |
|
416 |
(end-of-line) |
|
417 |
(forward-line 2) |
|
418 |
(when narrowp |
|
419 |
;;(org-tree-to-indirect-buffer 1) ;; opens new frame |
|
420 |
(org-narrow-to-subtree)) |
|
421 |
t) |
|
422 |
|
|
423 |
|
|
424 |
(defun org-ctags-ask-append-topic (name &optional narrowp) |
|
425 |
"This function is intended to be used in ORG-OPEN-LINK-FUNCTIONS. |
|
426 |
Wrapper for org-ctags-append-topic, which first asks the user if they want |
|
427 |
to append a new topic." |
|
428 |
(if (y-or-n-p (format-message |
|
429 |
"Topic `%s' not found; append to end of buffer?" name)) |
|
430 |
(org-ctags-append-topic name narrowp) |
|
431 |
nil)) |
|
432 |
|
|
433 |
|
|
434 |
(defun org-ctags-rebuild-tags-file-then-find-tag (name) |
|
435 |
"This function is intended to be used in ORG-OPEN-LINK-FUNCTIONS. |
|
436 |
Like ORG-CTAGS-FIND-TAG, but calls the external ctags program first, |
|
437 |
to rebuild (update) the TAGS file." |
|
438 |
(unless tags-file-name |
|
439 |
(call-interactively (visit-tags-table))) |
|
440 |
(when (buffer-file-name) |
|
441 |
(org-ctags-create-tags)) |
|
442 |
(org-ctags-find-tag name)) |
|
443 |
|
|
444 |
|
|
445 |
(defun org-ctags-ask-rebuild-tags-file-then-find-tag (name) |
|
446 |
"This function is intended to be used in ORG-OPEN-LINK-FUNCTIONS. |
|
447 |
Wrapper for org-ctags-rebuild-tags-file-then-find-tag." |
|
448 |
(if (and (buffer-file-name) |
|
449 |
(y-or-n-p |
|
450 |
(format-message |
|
451 |
"Tag `%s' not found. Rebuild table `%s/TAGS' and look again?" |
|
452 |
name |
|
453 |
(file-name-directory (buffer-file-name))))) |
|
454 |
(org-ctags-rebuild-tags-file-then-find-tag name) |
|
455 |
nil)) |
|
456 |
|
|
457 |
|
|
458 |
(defun org-ctags-fail-silently (_name) |
|
459 |
"This function is intended to be used in ORG-OPEN-LINK-FUNCTIONS. |
|
460 |
Put as the last function in the list if you want to prevent Org's |
|
461 |
default behavior of free text search." |
|
462 |
t) |
|
463 |
|
|
464 |
|
|
465 |
;;; User-visible functions =================================================== |
|
466 |
|
|
467 |
|
|
468 |
(defun org-ctags-create-tags (&optional directory-name) |
|
469 |
"(Re)create tags file in the directory of the active buffer. |
|
470 |
The file will contain tag definitions for all the files in the |
|
471 |
directory and its subdirectories which are recognized by ctags. |
|
472 |
This will include files ending in `.org' as well as most other |
|
473 |
source files (.C, .H, .EL, .LISP, etc). All the resulting tags |
|
474 |
end up in one file, called TAGS, located in the directory. This |
|
475 |
function may take several seconds to finish if the directory or |
|
476 |
its subdirectories contain large numbers of taggable files." |
|
477 |
(interactive) |
|
478 |
(cl-assert (buffer-file-name)) |
|
479 |
(let ((dir-name (or directory-name |
|
480 |
(file-name-directory (buffer-file-name)))) |
|
481 |
(exitcode nil)) |
|
482 |
(save-excursion |
|
483 |
(setq exitcode |
|
484 |
(shell-command |
|
485 |
(format (concat "%s --langdef=orgmode --langmap=orgmode:.org " |
|
486 |
"--regex-orgmode=\"%s\" -f \"%s\" -e -R \"%s\"") |
|
487 |
org-ctags-path-to-ctags |
|
488 |
org-ctags-tag-regexp |
|
489 |
(expand-file-name (concat dir-name "/TAGS")) |
|
490 |
(expand-file-name (concat dir-name "/*"))))) |
|
491 |
(cond |
|
492 |
((eql 0 exitcode) |
|
493 |
(setq-local org-ctags-tag-list |
|
494 |
(org-ctags-all-tags-in-current-tags-table))) |
|
495 |
(t |
|
496 |
;; This seems to behave differently on Linux, so just ignore |
|
497 |
;; error codes for now |
|
498 |
;;(error "Calling ctags executable resulted in error code: %s" |
|
499 |
;; exitcode) |
|
500 |
nil))))) |
|
501 |
|
|
502 |
|
|
503 |
(defvar org-ctags-find-tag-history nil |
|
504 |
"History of tags visited by org-ctags-find-tag-interactive.") |
|
505 |
|
|
506 |
(defun org-ctags-find-tag-interactive () |
|
507 |
"Prompt for the name of a tag, with autocompletion, then visit the named tag. |
|
508 |
Uses `ido-mode' if available. |
|
509 |
If the user enters a string that does not match an existing tag, create |
|
510 |
a new topic." |
|
511 |
(interactive) |
|
512 |
(let* ((completing-read-fn (if (fboundp 'ido-completing-read) |
|
513 |
'ido-completing-read |
|
514 |
'completing-read)) |
|
515 |
(tag (funcall completing-read-fn "Topic: " org-ctags-tag-list |
|
516 |
nil 'confirm nil 'org-ctags-find-tag-history))) |
|
517 |
(when tag |
|
518 |
(cond |
|
519 |
((member tag org-ctags-tag-list) |
|
520 |
;; Existing tag |
|
521 |
(push tag org-ctags-find-tag-history) |
|
522 |
(xref-find-definitions tag)) |
|
523 |
(t |
|
524 |
;; New tag |
|
525 |
(run-hook-with-args-until-success |
|
526 |
'org-open-link-functions tag)))))) |
|
527 |
|
|
528 |
|
|
529 |
(org-ctags-enable) |
|
530 |
|
|
531 |
(provide 'org-ctags) |
|
532 |
|
|
533 |
;;; org-ctags.el ends here |