ctags is an old-school UNIX program that indexes language objects such as functions, methods, classes etc. in source code files. Text editors read the index (called a “tags” file) to locate these language objects quickly.
When BBEdit finds a tags file, it enables these features:
- opening the contextual menu for a selected language object shows a submenu with all definitions of that object
- the completion list is populated with language objects that match what you typed so far
- your custom language objects are colorized in the source code
- the “Find Definition” menu command jumps directly to the definition of the selected word, if there’s only one match, or shows a list with possible matches if there are several
Let’s look at these four features with and without a tags file.
First the contextual menu for a selection. Here I selected the rootdir method call and opened the contextual menu, without a tags file present:
Here’s the same menu with a tags file:
The rootdir method is defined in four files and selecting an entry from the menu brings you directly to that definition.
On to the completion list. I type the word “root” and hit the completion shortcut without a tags file present:
BBEdit offers no completions because what I’m looking for, the rootdir method, does not occur in the current file.
With a tags file, it looks like this:
Here’s how your own symbols look without a tags file:
There’s no difference between the language objects and any other text, they’re all black. With a tags file, the language objects are highlighted with a separate color:
Finally, the “Find Definition” command in the Search menu. You select a word and invoke this command to show the definition of the selected language object. Without a tags file, nothing happens. With a tags file present, BBEdit either opens the definition directly if there’s only one candidate or it presents a list with all matches if there are several:
Creating the Tags File
To create the tags file, open the Terminal and cd to the toplevel directory of your project, the one that contains all related source code. Issue this command:/Applications/BBEdit.app/Contents/MacOS/ctags --excmd=number --tag-relative=no --fields=+a+m+n+S -f tags -R "$PWD"
I made myself an alias called “bbtags” with some Perl-specific excludes and added it to my $HOME/.bashrc file:
alias bbtags='/Applications/BBEdit.app/Contents/MacOS/ctags --excmd=number --tag-relative=no --fields=+a+m+n+S -f tags --exclude=blib --exclude=pod -R "$PWD"
Now I can just type bbtags and I’m all set.
As you edit and save your files, the index gets out of sync. You need to rebuild the tags file from time to time so it matches the changed files. If you work in a programming language that uses a build/compilation phase such as C or Java, you can run the ctags update command during the build process, as Seth notes in the comments below.
If you work in a dynamic programming language like Perl or PHP, you can try the following BBEdit menu AppleScript:
on MenuSelect(mycommand, myname)
return false
end MenuSelect
on PostMenuSelect()
tell application "BBEdit"
if not my isKnownLanguage(source language of text document 1) then return
set docFile to file of text document 1
end tell
set docPath to POSIX path of docFile
repeat while docPath is not equal to "/"
set docPath to (do shell script "dirname " & (quoted form of docPath))
if checkAndRebuildTagsFile(docPath) then return
end repeat
end PostMenuSelect
on isKnownLanguage(sourceLanguage)
return {"PHP", "Perl"} contains sourceLanguage
end isKnownLanguage
on checkAndRebuildTagsFile(tagsParentPath)
set tagsPath to tagsParentPath & "/tags"
set tagsFile to POSIX file tagsPath
tell application "Finder"
if not (exists tagsFile) then return false
end tell
if folder of (info for tagsFile) then return false
set updateCommand to "/Applications/BBEdit.app/Contents/MacOS/ctags 2>&1 --excmd=number --tag-relative=no --fields=+a+m+n+S+l+K+i -f tags --exclude=blib -R " & quoted form of tagsParentPath
set ctagsResult to do shell script updateCommand
do shell script "syslog -l notice -s rebuilt tags file with command " & updateCommand & ", result: " & quoted form of ctagsResult
return true
end checkAndRebuildTagsFile
Save it in your $HOME/Library/Application Support/BBEdit/Menu Scripts folder with the name “File•Save” to run it whenever you save the document. It looks for a tags file in all parent directories of the file you just saved, and if it finds one it runs the ctags update command. It seems to be a bit slow on the big source code trees I work on, but just give it a try, it might work better for you.
If you want to exclude certain directories permanently in all projects, I suggest you create a $HOME/.ctags file with exclude lines in it:
--exclude=blib --exclude=unittest
See the ctags documentation for the details.
In conclusion, if you use BBEdit in multifile projects, you should try the ctags feature. I suspect that it is seriously under-used and unknown even among experienced BBEdit users.
This is a fantastic tip, Marc. I've been using BBEdit since version 4 and I hadn't known about the ctags feature. I'm not sure what version of BBEdit first started using it, but it definitely works in BBEdit 8.7 which I'm currently on (the ctags executable is in the "Resources" directory in this version instead of a MacOS directory).
The AppleScript works but is a bit too slow with the codebases I use because it locks up BBEdit while it's running. I also had to add HTML as a known type because some of my PHP files use that format because it enhances the syntax highlighting.
Thanks again for the great tip!
-Paul Burney
|



If you're using some sort of a build, auto-test, or deployment system with your code, you can usually run ctags from there so that your tags file is always up to date.