From 6e251a420b39df1ab77ce60af69905891632d113 Mon Sep 17 00:00:00 2001 From: "R. Steve McKown" Date: Mon, 18 Jan 2016 14:04:10 -0700 Subject: [PATCH] Improve plugin; consolidate unite sources --- autoload/unite/sources/mkbfiles.vim | 30 ----- autoload/unite/sources/mkbuild.vim | 96 +++++++++++++ autoload/unite/sources/xrefs.vim | 56 -------- plugin/mkbuild.vim | 201 +++++++++++++--------------- 4 files changed, 188 insertions(+), 195 deletions(-) delete mode 100644 autoload/unite/sources/mkbfiles.vim create mode 100644 autoload/unite/sources/mkbuild.vim delete mode 100644 autoload/unite/sources/xrefs.vim diff --git a/autoload/unite/sources/mkbfiles.vim b/autoload/unite/sources/mkbfiles.vim deleted file mode 100644 index 1ff92b3..0000000 --- a/autoload/unite/sources/mkbfiles.vim +++ /dev/null @@ -1,30 +0,0 @@ -" unite source: file selection for projects using mkbuild - -let s:save_cpo = &cpo -set cpo&vim - -let s:source = { - \ 'name': 'mkbfiles', - \ 'description': 'select files in an mkbuild based project', - \ 'action_table': { - \ 'open' : { 'is_selectable': 1 }, - \ }, - \ 'default_action': 'open' - \ } - -function! s:source.action_table.open.func(candidates) - for c in a:candidates - execute "e" c.word - endfor -endfunction - -function! s:source.gather_candidates(args, context) - return map(split(mkbuild#filelist(), '\n'), '{ "word": v:val }') -endfunction - -function! unite#sources#mkbfiles#define() - return s:source -endfunction - -let &cpo = s:save_cpo -unlet s:save_cpo diff --git a/autoload/unite/sources/mkbuild.vim b/autoload/unite/sources/mkbuild.vim new file mode 100644 index 0000000..0a293bd --- /dev/null +++ b/autoload/unite/sources/mkbuild.vim @@ -0,0 +1,96 @@ +" unite mkbuild sources + +let s:save_cpo = &cpo +set cpo&vim + +let s:xref_source = { + \ 'name': 'mkbuild/xref', + \ 'description': 'select cross references built from an mkbuild build', + \ 'action_table': { + \ 'setxrefs' : { 'is_selectable': 1 }, + \ }, + \ 'default_action': 'setxrefs' + \ } + +function! s:xref_source.action_table.setxrefs.func(candidates) + call s:select(a:candidates[0].word) +endfunction + +function! s:xref_source.gather_candidates(args, context) + let cmd = 'find . -maxdepth 4 -type f -name tags -o -name cscope.out | ' . + \ 'while read f; do echo $(dirname $f); done | sort -u | sed -e "s|\./||"' + let xrefdirs = split(s:chompsys(cmd), '\n') + + return map(xrefdirs, '{ "word": v:val }') +endfunction + +function! s:select(dirname) + if empty(a:dirname) + return + endif + let file = a:dirname . "/tags" + let &tags='' + if filereadable(file) + let &tags = file + endif + if has('cscope') + let file = a:dirname . "/cscope.out" + execute 'cscope kill -1' + if filereadable(file) + execute 'cscope add ' . file + endif + endif +endfunction + +" Return the results of a command without the ending newline +function! s:chompsys(cmd) + return substitute(system(a:cmd), '\n$', '', '') +endfunction + + +let s:xfile_source = { + \ 'name': 'mkbuild/file_xref', + \ 'description': 'select files used in an mkbuild build', + \ 'action_table': { + \ 'open' : { 'is_selectable': 1 }, + \ }, + \ 'default_action': 'open' + \ } + +function! s:xfile_source.action_table.open.func(candidates) + for c in a:candidates + execute 'e ' . c.word + endfor +endfunction + +function! s:xfile_source.gather_candidates(args, context) + return map(mkbuild#buildFiles(), '{ "word": v:val }') +endfunction + + +let s:file_source = { + \ 'name': 'mkbuild/file', + \ 'description': 'select files in an mkbuild project', + \ 'action_table': { + \ 'open' : { 'is_selectable': 1 }, + \ }, + \ 'default_action': 'open' + \ } + +function! s:file_source.action_table.open.func(candidates) + for c in a:candidates + execute 'e ' . c.word + endfor +endfunction + +function! s:file_source.gather_candidates(args, context) + return map(mkbuild#allFiles(), '{ "word": v:val }') +endfunction + + +function! unite#sources#mkbuild#define() + return [s:xref_source, s:xfile_source, s:file_source] +endfunction + +let &cpo = s:save_cpo +unlet s:save_cpo diff --git a/autoload/unite/sources/xrefs.vim b/autoload/unite/sources/xrefs.vim deleted file mode 100644 index 87f9b53..0000000 --- a/autoload/unite/sources/xrefs.vim +++ /dev/null @@ -1,56 +0,0 @@ -" unite source: tags file and/or cscope database selection for projects using -" mkbuild - -let s:save_cpo = &cpo -set cpo&vim - -let s:source = { - \ 'name': 'xrefs', - \ 'description': 'select cross references from a directory', - \ 'action_table': { - \ 'setxrefs' : { 'is_selectable': 1 }, - \ }, - \ 'default_action': 'setxrefs' - \ } - -function! s:source.action_table.setxrefs.func(candidates) - call s:select(a:candidates[0].word) -endfunction - -function! s:select(dirname) - if empty(a:dirname) - return - endif - let file = a:dirname . "/tags" - let &tags='' - if filereadable(file) - let &tags = file - endif - if has('cscope') - let file = a:dirname . "/cscope.out" - execute 'cscope kill -1' - if filereadable(file) - execute 'cscope add ' . file - endif - endif -endfunction - -" Return the results of a command without the ending newline -function! s:chompsys(cmd) - return substitute(system(a:cmd), '\n$', '', '') -endfunction - -function! s:source.gather_candidates(args, context) - let cmd = 'find . -maxdepth 4 -type f -name tags -o -name cscope.out | ' . - \ 'while read f; do echo $(dirname $f); done | sort -u | sed -e "s|\./||"' - let xrefdirs = split(s:chompsys(cmd), '\n') - - return map(xrefdirs, '{ "word": v:val }') -endfunction - -function! unite#sources#xrefs#define() - return s:source -endfunction - -let &cpo = s:save_cpo -unlet s:save_cpo diff --git a/plugin/mkbuild.vim b/plugin/mkbuild.vim index eb3fd2b..6aac936 100644 --- a/plugin/mkbuild.vim +++ b/plugin/mkbuild.vim @@ -5,48 +5,80 @@ function! s:chompsys(cmd) return substitute(system(a:cmd), '\n$', '', '') endfunction -" Return the top level working directory in a git checkout. If dir is '', -" start from the current directory, else start from dir. -function! s:gittop(dir) - if empty(a:dir) - return s:chompsys('git rev-parse --show-toplevel 2>/dev/null') - else +" Return a list directories contained within an extrefs file at 'dir'. Use +" '.' as dir if wanting to find extrefs in the current directory. The +" returned list may be empty. Directories returned are not validated. +function! s:extrefs(dir) + let extdirs = [] + let extrefs = a:dir . '/extrefs' + if filereadable(extrefs) + for e in split(s:chompsys( + \ "grep ^git " . extrefs . "| awk '{ print $3 }'")) + if !empty(e) + call add(extdirs, a:dir . '/' . e) + endif + endfor + endif + return extdirs +endfunction + +" Return a list of files found using 'find', recursively, starting at 'dir' +function s:filesFromFind(dir) + return split(s:chompsys("cd " . a:dir . " 2>/dev/null && find . -type f"), + \ '\n') +endfunction + +" If 'dir' is empty, assume the current directory. Assume 'dir' is within a +" Git checkout. If true, return a list of tracked files from the Get +" repository. Otherwise, return an empty string. +function! s:gitTopLevel(dir) return s:chompsys("cd " . a:dir . \ " && git rev-parse --show-toplevel 2>/dev/null") endfunction -" Return a shell segment that will output a list of files with full paths -" parsed from working directory's tracked files. -function! s:gitls(workdir) - return "(git --git-dir=" . a:workdir . "/.git ls-files | sed -e s'|^|" . - \ a:workdir . "/|')" +" 'dir' must either be an empty string or a directory within a Git checkout, +" generally its top level directory. Return a list of all tracked files +" within the repository starting at 'dir'. +function! s:filesFromGit(dir) + if empty(a:dir) + return [] + else + return split(s:chompsys("git --git-dir=" . a:dir . "/.git ls-files | " + \ . "sed -e s'|^|" . a:dir . "/|'"), '\n') + endif endfunction -" Return a list of git directories. If the current directory is in a git -" working directory, at least this one will be returned. If the top level of -" this directory has an extrefs file, then the valid git directories listed -" therein are also added to the list. -function! s:gitdirs() - let dirs = [] - let topdir = s:gittop('') - if empty(topdir) +" Return a list of files contained within the directory 'dir'. If 'dir' +" belongs to a Git working directory, return all tracked files in the +" repository. Otherwise, use find to return the files at the filesystem +" subtree starting at 'dir'. +" +" Additionally, if 'dir' (or instead the Git top-level working directory), +" contains an extrefs file, recursively include files from each of its listed +" directories. +function! s:filesFromDir(dir) + let dir = s:gitTopLevel(a:dir) + if !empty(dir) + let files = s:filesFromGit(dir) else - call add(dirs, topdir) - let extrefs = topdir . '/extrefs' + let dir = a:dir + let files = s:filesFromFind(dir) + endif + if !empty(files) + let extrefs = dir . '/extrefs' if filereadable(extrefs) for e in split(s:chompsys( \ "grep ^git " . extrefs . "| awk '{ print $3 }'")) - let dir = s:gittop(topdir . '/' . e) - if empty(dir) - else - call add(dirs, dir) + if !empty(e) + call extend(files, s:filesFromDir(dir . '/' . e)) endif endfor endif endif - return dirs + return files endfunction + " Return a list of readable tag files, as present in &tags. This function " doesn't handle the sometimes-default 'tags:/' value I've seen. function! s:tagfiles() @@ -59,16 +91,16 @@ function! s:tagfiles() return tags endfunction -" Return a shell segment that will output a list of files with full paths -" parsed from the named tags (ctags) file. -function! s:tagls(tagfile) - return "(grep -v \"!\" " . a:tagfile . " | awk -F\\\t '{ print $2 }' | " . - \ "sort -u)" +" Return a list of files with full paths, parsed from the named tags (ctags) +" file. +function! s:filesFromTagsFile(tagfile) + return split(s:chompsys("grep -v \"!\" " . a:tagfile . + \ " | awk -F\\\t '{ print $2 }' | sort -u"), '\n') endfunction " Return a list of readable filelist files, which are looked for in the same " directories where &tags has vim looking for tags files. -function! s:filelists() +function! s:filelistfiles() let files = [] for t in s:tagfiles() let f = substitute(t, '\c\(\.\/\)*tags.*$', 'filelist', '') @@ -79,88 +111,39 @@ function! s:filelists() return files endfunction -" Return a shell segment that will output a list of files with full paths -" parsed from the named filelist file. -function! s:filels(filelist) - return "(cat " . a:filelist . ")" +" Return a list of files with full paths, parsed from the named filelist file. +function! s:filesFromFilelistFile(filelist) + return split(s:chompsys("cat " . a:filelist), '\n') endfunction -" Generate a command to generate a list of files. Return a dictionary -" containing two entries: -" source - The source of the files (generated by executing the sourcecmd) -" sourcecmd - The system command to use to generate the list of files -" -" The source command returned will output files from one of 4 locations, -" searched in priority order: -" 1. All filelist files found in the same directory(ies) vim looks for tags -" files. Files are extracted from the contents of these files. -" 2. Any tags files found in &tags. The file is parsed to get filenames. -" 3. The tracked files in the current git repository, and/or the tracked files -" in those valid git repositories named in the 'extrefs' file, if present. -" 4. Perform a 'find . -type f' in the current directory. -function! mkbuild#flcmd() - let files = s:filelists() - if empty(files) - let tags = s:tagfiles() - if empty(tags) - let gitdirs = s:gitdirs() - if empty(gitdirs) - let source = 'find' - let sourcecmd = 'find . -type f' - else " gitdirs - let source = 'git|extrefs' - let sourcecmd = '(true' - for g in gitdirs - let sourcecmd = sourcecmd . '; ' . s:gitls(g) - endfor - let sourcecmd = sourcecmd . ')' - endif - else " tags - let source = 'tags' - let sourcecmd = '(true' - for t in tags - let sourcecmd = sourcecmd . '; ' . s:tagls(t) - endfor - let sourcecmd = sourcecmd . ')' - endif - else " filelists - let source = 'filelists' - let sourcecmd = '(true' - for f in files - let sourcecmd = sourcecmd . '; ' . s:filels(f) - endfor - let sourcecmd = sourcecmd . ')' - endif - return { 'source': source, 'sourcecmd': sourcecmd } +" Return a list of all project files. Where possible, use tracked files from +" the project Git repository, else collect all files. Also follow 'extrefs' +" files to additional, external sources. +function! mkbuild#allFiles() + return s:filesFromDir('.') endfunction -" Generate a list of files using mkbuild#flcmd() -function! mkbuild#filelist() - let flcmdres = mkbuild#flcmd() - return s:chompsys(flcmdres['sourcecmd'] . ' | cat') -endfunction - -" Use dmenu to select a file to execute with command 'cmd'. If no file is -" selected, no action is taken. The list of files presented to dmenu comes -" from one of four places, in the following priority order: -" 1. All filelist files found in the same directory(ies) vim looks for tags -" files. -" 2. Any tags files found in &tags. The file is parsed to get filenames. -" 3. The tracked files in the current git repository, and/or the tracked files -" in those valid git repositories named in the 'extrefs' file, if present. -" 3. Perform a 'find . -type f' in the current directory. -function! mkbuild#DmenuOpen(cmd) - if empty(system('which dmenu')) - echo "Dmenu is not installed" +" Return the subset of project files used in the build referenced by the +" directory defined in the 'tags' variable. Use the mkbuild filelist, if +" available, or fall back to extracting files from the tags file. If no +" source of filelist or tags files are available, revert to returning all +" proejct files. +function! mkbuild#buildFiles() + let files = [] + let flfiles = s:filelistfiles() + if !empty(flfiles) + for f in flfiles + call extend(files, s:filesFromFilelistFile(f)) + endfor else - let flcmdres = mkbuild#flcmd() - let source = flcmdres['source'] - let sourcecmd = flcmdres['sourcecmd'] - let fname = s:chompsys(sourcecmd . ' | dmenu -i -l 20 -p "' . source . '(' . - \ a:cmd . ')"') - if empty(fname) - return + let tagfiles = s:tagfiles() + if !empty(tagfiles) + for t in tags + call extend(files, s:filesFromTagsFile(t)) + endfor + else + call extend(files, s:filesFromDir('.')) endif - execute a:cmd . " " . fname endif + return files endfunction -- 2.39.2