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