Skip to content

Commit

Permalink
Merge pull request #5 from UmkaDK/per-buffer-config
Browse files Browse the repository at this point in the history
Implemented support for per-buffer configuration
  • Loading branch information
joonty committed Feb 5, 2014
2 parents c87ce12 + 8a52e17 commit 02f12f1
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 90 deletions.
141 changes: 94 additions & 47 deletions autoload/taggatron.vim
Original file line number Diff line number Diff line change
Expand Up @@ -2,70 +2,117 @@
if exists("g:loaded_taggatron") || &cp
finish
endif

let g:loaded_taggatron= 1

let s:taggatron_cmd_entry = {"cmd":"ctags-exuberant","args":"",'filesappend':'**'}
" Initialise script default values
let s:taggatron_enabled = 1
let s:tagcommands = {}
let s:tagcommands_entry = {
\ "cmd": "ctags-exuberant",
\ "args": "",
\ "filesappend": "**"
\ }

""
" Check command options and initialise tag creation.
"
" Ensure that tags for the current file do need to be created, validate a set
" of current command line options, and create tags based on them.
"
" @param bool forceCreate A switch indicating if we want to update tags for
" the current file only (0), or (re)create tags for all file under the path.
"
function! taggatron#CheckCommandList(forceCreate)
" Exit early if taggatron is disabled
if taggatron#get('taggatron_enabled') == 0
call taggatron#debug('Tag file generation is disabled')
return
endif

function! taggatron#CreateTags(cmdset,forceCreate)
call taggatron#debug("Creating tags for file type ".&filetype)
call taggatron#debug(a:cmdset)
call taggatron#debug(s:taggatron_cmd_entry)
" Validate command options for the current file type
call taggatron#debug('Checking for tag command for this file type')
let l:cmdset = get(taggatron#get('tagcommands'), &filetype)

" Define local support variables
let l:cset = {}
let l:eset = a:cmdset
let l:cwd = fnamemodify(getcwd(), ':p')
" Do nothing if tag command is missing
if l:cmdset is 0
call taggatron#debug('No tag command for filetype ' . &filetype)
return
endif

" Initialise l:cset variable
call extend(l:cset,s:taggatron_cmd_entry)
call extend(l:cset,l:eset)
" Identify project root directory (aka files)
let l:cmdset['files'] = has_key(l:cmdset, 'files')
\ ? fnamemodify(l:cmdset['files'], ':p')
\ : fnamemodify(getcwd(), ':p')
call taggatron#debug('Project root directory: ' . l:cmdset['files'])

" Detect missing tagfile
if !has_key(l:cset,'tagfile')
call taggatron#error("Missing tag file destination from tag commands for file type ".&filetype)
" Do nothing if the file is not inside the project directory
if expand("%:p") !~ '^' . l:cmdset['files']
call taggatron#debug('Not creating tags: file is not inside project root')
return
endif

" Identify files to be scanned
if !has_key(l:cset,'files')
let l:cset['files'] = l:cwd
if has_key(l:cset,'filesappend')
let l:cset['files'] = l:cset['files'].l:cset['filesappend']
endif
" Exit with an error if tag file argument is missing
if !has_key(l:cmdset, 'tagfile')
call taggatron#error('Missing tag file for file type ' . &filetype)
return
endif

" Identify the value for the ctag's --language switch
if !has_key(l:cset,"lang")
let l:cset['lang'] = &filetype
" Sanitize tagfile path
let l:cmdset['tagfile'] = fnamemodify(l:cmdset['tagfile'], ':p')

" Create tag file and ensure that it is in use by the editor
call taggatron#CreateTags(l:cmdset, a:forceCreate)
call taggatron#SetTags(l:cmdset['tagfile'])
endfunction

""
" Create tags as per command options
"
" This function is used to update tags for the current file via UpdateTags()
" or to create a new tag file for tags collected from all files matching
" a current/configured language under a specific path.
"
" @param dictionary cmdset A set of command options
"
function! taggatron#CreateTags(cmdset, forceCreate)
call taggatron#debug('Creating tags for file type ' . &filetype)
call taggatron#debug('Argument: ' . string(a:cmdset))
call taggatron#debug('Default: ' . string(s:tagcommands_entry))

" Initialise local support variables
let l:cmdset = {}

" Build up a set of command line options
call extend(l:cmdset, s:tagcommands_entry)
call extend(l:cmdset, a:cmdset)

" Updated tag file and exit early
if filereadable(l:cmdset['tagfile']) && a:forceCreate == 0
call taggatron#debug('Updating tag file ' . l:cmdset['tagfile'])
call taggatron#UpdateTags(l:cmdset['cmd'], l:cmdset['files'], l:cmdset['tagfile'])
return
endif

" Generate ctags command
let l:cmdstr = l:cset['cmd'] . " " . l:cset["args"] . " --languages=" . l:cset['lang']

" Run ctags to either (re)create or update tag file
if !filereadable(l:cset['tagfile']) || a:forceCreate == 1
let l:cmdstr = l:cmdstr ." -f ".l:cset['tagfile'] . " " .l:cset['files']
call taggatron#debug("Executing command: ".l:cmdstr)
call system(l:cmdstr)
else
call taggatron#debug("Updating tag file ".l:cset['tagfile'])
call taggatron#UpdateTags(l:cset['cmd'],l:cwd,l:cset['tagfile'])
" Append path suffix on top of the existing 'files' path
if has_key(l:cmdset, 'filesappend')
let l:cmdset['files'] = l:cmdset['files'] . l:cmdset['filesappend']
endif

" Ensure that generated tags are picked up by the editor
let l:tagfile = fnamemodify(l:cwd.l:cset['tagfile'], ':p')
call taggatron#SetTags(l:tagfile)
endfunction
" Auto-generate --languages command line argument
let l:cmdset['args'] .= ' --languages='
\ . (has_key(l:cmdset, 'lang') ? l:cmdset['lang'] : &filetype)

function! taggatron#error(str)
echohl Error | echo a:str | echohl None
endfunction
" Auto-generate -f command line argument
let l:cmdset['args'] .= ' -f ' . l:cmdset['tagfile']

function! taggatron#debug(str)
if g:taggatron_verbose == 1
echo a:str
endif
" Create a completed command line string from all available arguments
let l:cmdstr = l:cmdset['cmd']
\ . ' ' . l:cmdset['args']
\ . ' ' . l:cmdset['files']

" Execute tag creation command by passing it over to the system
call taggatron#debug('Executing command: ' . l:cmdstr)
call system(l:cmdstr)
endfunction

""""""""""""""""""
Expand Down
129 changes: 86 additions & 43 deletions plugin/taggatron.vim
Original file line number Diff line number Diff line change
@@ -1,31 +1,28 @@
if !exists("g:tagcommands")
let g:tagcommands = {}
endif
if !exists("g:tagdefaults")
let g:tagdefaults = ""
endif
if !exists("g:taggatron_verbose")
let g:taggatron_verbose = 0
endif
if !exists("g:taggatron_enabled")
let g:taggatron_enabled = 1
endif

" Include all default tags
if len(g:tagdefaults) > 0
call taggatron#debug("Adding default tags: ".g:tagdefaults)
exec "setlocal tags+=".g:tagdefaults
endif

autocmd BufWritePost * call taggatron#CheckCommandList(0)
command! TagUpdate call taggatron#CheckCommandList(1)
command! -nargs=1 SetTags call taggatron#SetTags(<f-args>)
" Initialise script default values
let s:taggatron_verbose = 0
let s:tagdefaults = ''

" Function Declarations
" =====================
"
" This section is used minimize memory foot print of the idle plugin and to
" speed up loading times. All functions required to initialise the plugin are
" declared below, delaying the load of `autoload/taggatron.vim` file until the
" first time the plugin is used.

""
" Add a file to the list of local tags
"
" This function is used to add user supplied file to the list of local tags.
" Before being added, the filename is converted to the absolute path to file
" and is only added if that file is not already on the list.
"
" @param list|string files A file or a list of files to be added
"
function! taggatron#SetTags(files)
" Define local support variables
let l:files = type(a:files) == 1 ? [a:files] : a:files
let l:tagfiles = []
let l:cwd = fnamemodify(getcwd(), ':p')

" Fail if l:files is not a list
if type(l:files) != 3
Expand All @@ -40,14 +37,14 @@ function! taggatron#SetTags(files)

" Create a list of all tag files currently loaded (absolute path)
for l:tagfile in tagfiles()
call add(l:tagfiles, fnamemodify(l:cwd.l:tagfile, ':p'))
call add(l:tagfiles, fnamemodify(l:tagfile, ':p'))
endfor

" Process all tag files one by one
for l:file in l:files
" Skip non-existent and unreadable file
if !filereadable(l:file)
call taggatron#debug("Skipping non-existent or unreadable tag file: ".l:file)
call taggatron#debug("Skipping unreadable tag file: " . l:file)
continue
endif

Expand All @@ -56,29 +53,75 @@ function! taggatron#SetTags(files)

" Only add current file to tags if it hasn't been already found
if index(l:tagfiles, l:file) == -1
call taggatron#debug("Adding tag file: ".l:file)
exec "setlocal tags+=".l:file
call taggatron#debug("Adding tag file:" . l:file)
exec "setlocal tags+=" . l:file
endif
endfor
endfunction

function! taggatron#CheckCommandList(forceCreate)
if g:taggatron_enabled != 1
call taggatron#debug("Tag file generation disabled (taggatron_enabled: " . g:taggatron_enabled . ")")
return
""
" Fetch a scoped value of an option
"
" Determine a value of an option based on user configuration or pre-configured
" defaults. A user can configure an option by defining it as a buffer variable
" or as a global (buffer vars override globals). Default value can be provided
" by defining a script variable for the whole file or a function local (local
" vars override script vars). When all else fails, falls back the supplied
" default value, if one is supplied.
"
" @param string option Scope-less name of the option
" @param mixed a:1 An option default value for the option
"
function! taggatron#get(option, ...)
for l:scope in ['b', 'g', 'l', 's']
if exists(l:scope . ':'. a:option)
return eval(l:scope . ':'. a:option)
endif
endfor

if a:0 > 0
return a:1
endif

let l:cwd = getcwd()
call taggatron#debug("Current directory: ".l:cwd)
if expand("%:p:h") =~ l:cwd . ".*"
call taggatron#debug("Checking for tag command for this file type")
let l:cmdset = get(g:tagcommands,&filetype)
if l:cmdset is 0
call taggatron#debug("No tag command for filetype " . &filetype)
else
call taggatron#CreateTags(l:cmdset,a:forceCreate)
endif
else
call taggatron#debug("Not creating tags: file is not in current directory")
call taggatron#error('Invalid or undefined option: ' . a:option)
endfunction

""
" Show user an error message
"
" Pre-format supplied message as an Error and display it to the user. All
" messages are saved to message-history and are accessible via `:messages`.
"
" @param string message A message to be displayed to the user
"
function! taggatron#error(message)
echohl Error | echomsg a:message | echohl None
endfunction

""
" Show user a debug message
"
" Echo supplied message to the user if verbose mode is enabled. All messages
" are saved to message-history and can be reviewed with :messages command.
"
" @param string message A message to be displayed to the user
"
function! taggatron#debug(message)
" Do nothing if verbose mode is disabled
if taggatron#get('taggatron_verbose') == 0
return
endif

echomsg a:message
endfunction

" Executable code
" ===============

" Initialise taggatron auto-commands
autocmd! BufNew,BufRead * call taggatron#SetTags(taggatron#get('tagdefaults', []))
autocmd! BufWritePost * call taggatron#CheckCommandList(0)

" Initialise taggatron commands
command! TagUpdate call taggatron#CheckCommandList(1)
command! -nargs=1 SetTags call taggatron#SetTags(<f-args>)

0 comments on commit 02f12f1

Please sign in to comment.