Saturday, February 20, 2010

Text Completions with Ctags in SubEthaEdit

With the infrastructure set up in the last few posts, it is now relatively easy to add Ctags-based text completions to SubEthaEdit (SEE). We use the shell scripts and AppleScript handlers to locate the tag file, determine a search term, get a list of tags matching the search term, and put up a dialog to have the user pick a tag. The only thing we're missing is a handler to actually insert the selected tag.

Here's a handler that does the job:
to insertCompletion of baseText by completionText
-- assumes that the baseText is what was determined from the selection
set {startChar, nextChar} to selectionRange without extendingFront and extendingEnd
if the completionText does not start with the baseText then
error "Invalid completion"
end if
if length of baseText is equal to length of completionText then
-- completion is the same as the existing text, just position the insertion point
setSelectionRange to nextChar
else if startChar is equal to nextChar then
-- empty selection, search term was inferred and only the difference needs to be included
set completion to characters (1 + (length of baseText)) through (length of completionText) of completionText as text
setSelectionText to completion
setSelectionRange to nextChar + (length of completion)
--text selected, just replace it
setSelectionText to completionText
setSelectionRange to startChar + (length of completionText)
end if
end insertCompletion
The handler has parameters corresponding to the base text sought for in the tag file and to the selected tag. These two strings are used, along with the length of the selection in SEE, to determine exactly how much text to insert. It would have been possible to just use the SEE selection, without passing in the base text, but it would have required essentially repeating the entire process of determining the search term; I think the design could be improved here, but I can live with this for now.

Using all these handlers, the logic for the text completion script is now expressible in a compact form:
set tagfilepath to findTagFile()
set searchTerm to determineSearchTerm without userIntervention
--set taglist to (pipeMatches of searchTerm out of tagfilepath thru "awk -F\"\\t\" '{ print $1 }' | sort -u")
set taglist to (pipeMatches of searchTerm out of tagfilepath thru "cut -f1 | sort -u")
set selectedTag to (pickTags from taglist without multipleSelectionsAllowed)
insertCompletion of searchTerm by selectedTag
on error errMsg number errNum
if errNum is equal to 901 then
else if errNum is equal to 902 then
error errMsg number errNum
end if
end try
The try block catches the errors we defined, letting any others go through for SEE to inform us about.

The last component needed is a seescriptsettings handler. I used this:
on seescriptsettings()
{displayName:"Complete using Ctags", shortDisplayName:"Ctags Completion", keyboardShortcut:"@^t", inContextMenu:"yes"}
end seescriptsettings
All this needs to be assembled into a script, which is saved as a compiled script in ~/Library/Application Support/SubEthaEdit/Scripts/. A compiled script is available for download.

No comments: