emacs-orgmode
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Orgmode] Integrating ctags & org mode (patch)


From: Carsten Dominik
Subject: Re: [Orgmode] Integrating ctags & org mode (patch)
Date: Tue, 15 Dec 2009 15:29:07 +0100


Another issue is the following:  How should we handle
export of such links?  Maybe we need another function that does
not *jump* to the link location, but just returns the file in
which the target is, so that the exporters can make use of this?

- Carsten


On Dec 15, 2009, at 8:20 AM, Carsten Dominik wrote:

Hi Paul,

I like this very much. But I would like to change the implementation so that there will be a hook. Then people can do different things, including matching tags in source code files etc.

Would you be interested to turn your way of doing things into a little add-on that people could load? I realize that it would be a very small file because the heavy lifting is done by the tags creating file and Emacs ctags searches. But it would keep the way open for other ideas.

If you agree I will make a new hook and interface for this.

I would be very interested to include the new module (if you write it)
at least as a contributed package, or, if you are willing
to sign the papers with the FSF, in the core.

- Carsten

On Dec 15, 2009, at 4:02 AM, Paul Sexton wrote:

Hi,

I have managed to get exuberant ctags working with org mode. This means that plain links [[like this]], instead of defaulting to plain text search when no match is found in the current file, now look for a matching tag <<like this>> in
all your *.org files, and jumps there.

This means your org files all now "seamlessly" talk to each other and interlink. You can split that monolithic file up into smaller files and the plain links still work. You can throw a plain link to a topic you know exists in another file, without having to worry about the format of special inter- file links,
whether you got the directory right, and so on.

Steps:

1. First you need a small patch to org.el. This is necessary because AFAIK there is no easy way to customise or advise org's behaviour when opening a plain link.
The patch is at the end of this post.

2. Next you need to get exuberant ctags from http://ctags.sourceforge.net/
There is a windows executable there (a zip file). Many linux distributions have
'ctags-exuberant' as an installable package in their repositories.

If you are using windows, extract the ctags.zip file into a directory somewhere,
eg C:\emacs23\ctags

3. Now make a new file called .ctags in your HOME directory. If you are not sure
where this is, evaluate (getenv "HOME") in the emacs scratch buffer.

Put the following 3 lines into this file, then save it:

--langdef=orgmode
--langmap=orgmode:.org
--regex-orgmode=/<<([^>]+)>>/\1/d,definition/

The last line is a regular expression that defines what a tag is in orgmode. If you don't like my definition based on angle brackets, or you want to add other destinations as tags, just alter the bit between the first two /...slashes.../

4. Paste the following into your .emacs file:

(defvar home-dir "/home/paul/")     ; replace with your home dir

(defun operating-system ()
(case system-type
  ((windows-nt cygwin) 'windows)
  (darwin 'macos)
  (otherwise 'unix)))

(defvar path-to-ctags
(case (operating-system)
(windows "c:/emacs/ctags/ctags.exe") ; or whereever you extracted it
  (unix "/usr/bin/ctags-exuberant"))    ; wherever it went
"Path to the CTAGS executable.")

(defun create-tags (dir-name)
"Create tags file."
(interactive "DBase directory: ")
(shell-command
 (format "%s --options=%s/.ctags -f %s/TAGS -e -R %s/*"
         path-to-ctags home-dir dir-name dir-name)))

(global-set-key (kbd "<M-kp-multiply>") 'pop-tag-mark)

(defadvice find-tag (before set-org-mark-before-finding-tag
                            activate compile)
"Before trying to find a tag, save our current position on org mark ring."
(save-excursion
  (if (org-mode-p)
      (org-mark-ring-push))))

5. Now run create-tags:

  M-x create-tags ENTER "/path/to/org/files/" ENTER

create-tags searches all subdirectories as well, and will also create tags for all source code files that if finds (*.c, *.lisp, *.el, etc). All these tags will go in one big TAGS file, located in the "base" directory that you specify as an argument to create-tags. Thus, if you have any large source trees in
subdirectories, create-tags may pause for a few seconds.

6. Check that the file 'TAGS' is in the right place and is not an empty file.

Tags is now ready to use. See below for usage. The first time you try and find a tag, you will be asked which tags file to use. The right answer is the file
named TAGS which you created with create-tags.
To avoid being asked this every time you restart emacs, try putting this in your
.emacs:

(add-hook 'org-mode-hook
   (lambda () (visit-tags-table "/path/to/TAGSfile")

Tags are defined in the .ctags file as any text in <<double angle brackets>>.
(triple angle brackets work too)

When you click on a link "[[foo]]" and org cannot find a matching "<<foo>>" in the current buffer, the tags facility will take over. The file TAGS is examined to see if the tags facility knows about "<<foo>>" in any other files. If it does, the matching file will be opened and the cursor will jump to the position
of "<<foo>>" in that file.

Important commands:
* M-.
Press to enter a tag name (default is a string extracted from the
current cursor position) and then try & jump there. No autocompletion yet.
* C-M-.
as above, but search term is a regular expression
* M-x tags-search
Also searches for a regexp, but searches through the *entire text* of
all the files that the tags facility knows about. Jumps to the first match.
Then, press M-, to jump to each successive match.
* M-* "go back" from a tag jump. (note: if you jumped from an org- mode buffer, your previous position will also have been saved on the org mark ring).

Tags mode has no way of knowing when you create new tags. So, any new <<link targets>> you make after running create-tags will not be in the 'TAGS' file & so will be unknown to the tags facility. For new tags to make it into the TAGS
file, you need to re-run (create-tags "path") to refresh the file.

You also might want to put (create-tags "/path/to/org/files") in your .emacs or even "ctags-exuberant -e -R /path/to/org/ &" in your .bashrc or equivalent.

There is probably a clever way to re-run create-tags at sensible times eg when
saving an org file, but I haven't looked into it.

Also if someone clever wants to enable tabbed completion when prompted for a tag
name with find-tag: please feel free.

There are other tags programs that interface with emacs, for example GNU Global. However I believe that to write parsers for new languages like orgmode, you have to write them in C for most of the other tags programs. With exuberant ctags
it's just a matter of writing a regular expression.

Hope some people find this helpful.

Here is the patch. Sadly the dumb gmane web interface thingy forced me to change all '<' to '{' and '>' to '}' before I could post this, as it accused me of top
posting. You will therefore need to change them all back.

----BEGIN PATCH for org.el (delete this line)----
8349,8369c8349,8350
{             (condition-case nil (eval cmd)
{               ;; ORG-TAGS
{               (error
{                (progn
{                  (widen)
{                  (condition-case nil (eval cmd)
{                    (error
{                     ;; No matching link found anywhere in this file
{                     ;; See if we can find a tag
{                     ;; If so, jump to it
{                     (condition-case nil (find-tag path)
{                       (error
{                        (cond
{                         (org-make-new-topics-for-missing-links-p
{                          (if (y-or-n-p
{ (format "Topic `%S' not found; append to current
buffer?"
{                                       path)))
{                          (org-append-new-topic path nil))
{                         (t
{                          (error "No match found"))))))))))))
{
---
}           (condition-case nil (eval cmd)
}             (error (progn (widen) (eval cmd))))))
8592,8595d8572
{      ;; ORG-TAGS
{      ((not org-open-link-defaults-to-normal-string-search-p)
{       ;; We don't want to search for a plain text match.
{       (error "No match."))
8651,8682d8627
{
{
{ ;; ORG-TAGS
{
{ (defvar org-open-link-defaults-to-normal-string-search-p nil
{ "Behaviour when attempting to open a 'thisfile' hyperlink for which no
{ EXACT match can be found (i.e. no match in angled brackets, etc).
{ If true (default), exhibit normal org behaviour of doing a search for a string
{ matching the link name.
{ If nil, abort the attempt to open the link.")
{
{
{ (defvar org-make-new-topics-for-missing-links-p nil
{ "If true, when attempting to follow a 'plain' hyperlink for which no precise { match is found, offer to append a top-level heading with the same name as the
{ hyperlink, to the end of the buffer.")
{
{
{ (defun org-append-new-topic (word)
{   (interactive "s")
{   (widen)
{   (end-of-buffer)
{   (newline 2)
{   (insert (format "* <<%s>>" word))  ; <<<>>> to make radio word
{   (backward-char 4)
{   ;;(org-update-radio-target-regexp)
{   (end-of-line)
{   (newline 2)
{   (next-line 2))
{
{
{
----END PATCH (delete this line)----




_______________________________________________
Emacs-orgmode mailing list
Please use `Reply All' to send replies to the list.
address@hidden
http://lists.gnu.org/mailman/listinfo/emacs-orgmode

- Carsten




- Carsten







reply via email to

[Prev in Thread] Current Thread [Next in Thread]