diff --git a/README.md b/README.md index b154bfe..e1b60f4 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ Display the list of functions/methods in the editor via `cmd-r` in Atom. -If your project has a `tags`/`.tags`/`TAGS`/`.TAGS` file at the root then -following are supported: +If your project has a `tags`/`.tags`/`TAGS`/`.TAGS` file at the root or at a +custom directory defined in `tagsDirectory` then the following are supported: |Command|Description|Keybinding (Linux)|Keybinding (OS X)|Keybinding (Windows)| |-------|-----------|------------------|-----------------|--------------------| diff --git a/lib/load-tags-handler.coffee b/lib/load-tags-handler.coffee index 5ef3bf3..919210f 100644 --- a/lib/load-tags-handler.coffee +++ b/lib/load-tags-handler.coffee @@ -2,16 +2,18 @@ async = require 'async' ctags = require 'ctags' getTagsFile = require './get-tags-file' -module.exports = (directoryPaths) -> +module.exports = (tagDirectoryObjects) -> async.each( - directoryPaths, - (directoryPath, done) -> - tagsFilePath = getTagsFile(directoryPath) + tagDirectoryObjects, + ({tagsPath, projectPath}, done) -> + tagsFilePath = getTagsFile(tagsPath) return done() unless tagsFilePath stream = ctags.createReadStream(tagsFilePath) stream.on 'data', (tags) -> - tag.directory = directoryPath for tag in tags + for tag in tags + tag.projectPath = projectPath + tag.directory = tagsPath emit('tags', tags) stream.on('end', done) stream.on('error', done) diff --git a/lib/main.coffee b/lib/main.coffee index fde3b44..8d2559e 100644 --- a/lib/main.coffee +++ b/lib/main.coffee @@ -4,7 +4,10 @@ module.exports = default: true type: 'boolean' description: 'Force ctags to use the name of the current file\'s language in Atom when generating tags. By default, ctags automatically selects the language of a source file, ignoring those files whose language cannot be determined. This option forces the specified language to be used instead of automatically selecting the language based upon its extension.' - + tagsDirectory: + default: './' + type: 'string' + description: 'A directory relative to your project path where symbols-view will look for your tags file. By default this is set to project root.' activate: -> @stack = [] diff --git a/lib/project-view.coffee b/lib/project-view.coffee index b9a4f4e..d100d48 100644 --- a/lib/project-view.coffee +++ b/lib/project-view.coffee @@ -11,9 +11,13 @@ class ProjectView extends SymbolsView @reloadTags = true @setMaxItems(10) + @relativeTagsDirectoryWatcher = atom.config.onDidChange 'symbols-view.tagsDirectory', (newValue) => + @triggerReloadTags() + destroy: -> @stopTask() @unwatchTagsFiles() + @relativeTagsDirectoryWatcher.dispose() super toggle: -> @@ -59,20 +63,22 @@ class ProjectView extends SymbolsView @watchTagsFiles() + triggerReloadTags: -> + @reloadTags = true + @watchTagsFiles() + watchTagsFiles: -> @unwatchTagsFiles() @tagsFileSubscriptions = new CompositeDisposable() - reloadTags = => - @reloadTags = true - @watchTagsFiles() - for projectPath in atom.project.getPaths() - if tagsFilePath = getTagsFile(projectPath) + relativeTagsDirectory = atom.config.get('symbols-view.tagsDirectory') + for {tagsPath} in TagReader.getTagsPaths(relativeTagsDirectory) + if tagsFilePath = getTagsFile(tagsPath) tagsFile = new File(tagsFilePath) - @tagsFileSubscriptions.add(tagsFile.onDidChange(reloadTags)) - @tagsFileSubscriptions.add(tagsFile.onDidDelete(reloadTags)) - @tagsFileSubscriptions.add(tagsFile.onDidRename(reloadTags)) + @tagsFileSubscriptions.add(tagsFile.onDidChange(=> @triggerReloadTags())) + @tagsFileSubscriptions.add(tagsFile.onDidDelete(=> @triggerReloadTags())) + @tagsFileSubscriptions.add(tagsFile.onDidRename(=> @triggerReloadTags())) return diff --git a/lib/symbols-view.coffee b/lib/symbols-view.coffee index a26797d..6c376c5 100644 --- a/lib/symbols-view.coffee +++ b/lib/symbols-view.coffee @@ -37,12 +37,13 @@ class SymbolsView extends SelectListView getFilterKey: -> 'name' - viewForItem: ({position, name, file, directory}) -> + viewForItem: ({position, name, file, directory, projectPath}) -> # Style matched characters in search results matches = match(name, @getFilterQuery()) + file = path.relative(projectPath, path.join(directory, file)) if atom.project.getPaths().length > 1 - file = path.join(path.basename(directory), file) + file = path.join(path.basename(projectPath), file) $$ -> @li class: 'two-lines', => @@ -63,7 +64,8 @@ class SymbolsView extends SelectListView confirmed: (tag) -> if tag.file and not fs.isFileSync(path.join(tag.directory, tag.file)) - @setError('Selected file does not exist') + @setError("Selected file does not exist. Try running ctags with the " + + "--tag-relative option inside your project directory.") setTimeout((=> @setError()), 2000) else @cancel() diff --git a/lib/tag-reader.coffee b/lib/tag-reader.coffee index 66a9300..9f26662 100644 --- a/lib/tag-reader.coffee +++ b/lib/tag-reader.coffee @@ -1,3 +1,4 @@ +path = require 'path' {Task} = require 'atom' ctags = require 'ctags' async = require 'async' @@ -21,12 +22,14 @@ module.exports = allTags = [] async.each( - atom.project.getPaths(), - (projectPath, done) -> - tagsFile = getTagsFile(projectPath) + @getTagsPaths(atom.config.get('symbols-view.tagsDirectory')) + ({tagsPath, projectPath}, done) -> + tagsFile = getTagsFile(tagsPath) return done() unless tagsFile? ctags.findTags tagsFile, symbol, (err, tags=[]) -> - tag.directory = projectPath for tag in tags + for tag in tags + tag.projectPath = projectPath + tag.directory = tagsPath allTags = allTags.concat(tags) done(err) (err) -> callback(err, allTags) @@ -34,6 +37,16 @@ module.exports = getAllTags: (callback) -> projectTags = [] - task = Task.once handlerPath, atom.project.getPaths(), -> callback(projectTags) + relativeTagsDirectory = atom.config.get('symbols-view.tagsDirectory') + task = Task.once handlerPath, @getTagsPaths(relativeTagsDirectory), -> callback(projectTags) task.on 'tags', (tags) -> projectTags.push(tags...) task + + getTagsPaths: (relativeTagsDirectory) -> + paths = [] + for projectPath in atom.project.getPaths() + paths.push( + projectPath: projectPath + tagsPath: path.join(projectPath, relativeTagsDirectory) + ) + paths diff --git a/spec/fixtures/js/tagsDir/tags b/spec/fixtures/js/tagsDir/tags new file mode 100644 index 0000000..9209300 --- /dev/null +++ b/spec/fixtures/js/tagsDir/tags @@ -0,0 +1,10 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/ +!_TAG_PROGRAM_NAME Exuberant Ctags // +!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ +!_TAG_PROGRAM_VERSION 5.8 // +callMeMaybe ../tagged.js /^function callMeMaybe() {$/;" f +duplicate ../tagged-duplicate.js /^function duplicate() {$/;" f +duplicate ../tagged.js /^function duplicate() {$/;" f +thisIsCrazy ../tagged.js /^var thisIsCrazy = true;$/;" v diff --git a/spec/symbols-view-spec.coffee b/spec/symbols-view-spec.coffee index a75599b..5984195 100644 --- a/spec/symbols-view-spec.coffee +++ b/spec/symbols-view-spec.coffee @@ -397,6 +397,54 @@ describe "SymbolsView", -> runs -> expect(symbolsView.list.children('li').length).toBe 0 + it "displays all tags when the tagsDirectory is set to \"tagsDir\"", -> + + jasmine.unspy(window, 'setTimeout') + atom.config.set('symbols-view.tagsDirectory', 'tagsDir') + + waitsForPromise -> + atom.workspace.open(directory.resolve("tagged.js")) + + runs -> + atom.commands.dispatch(getWorkspaceView(), "symbols-view:toggle-project-symbols") + + waitsForPromise -> + activationPromise + + runs -> + symbolsView = $(getWorkspaceView()).find('.symbols-view').view() + + waitsFor "loading", -> + symbolsView.setLoading.callCount > 1 + + waitsFor -> + symbolsView.list.children('li').length > 0 + + runs -> + directoryBasename = path.basename(directory.getPath()) + expect(symbolsView.loading).toBeEmpty() + expect($(getWorkspaceView()).find('.symbols-view')).toExist() + expect(symbolsView.list.children('li').length).toBe 4 + expect(symbolsView.list.children('li:first').find('.primary-line')).toHaveText 'callMeMaybe' + expect(symbolsView.list.children('li:first').find('.secondary-line')).toHaveText path.join(directoryBasename, 'tagged.js') + expect(symbolsView.list.children('li:last').find('.primary-line')).toHaveText 'thisIsCrazy' + expect(symbolsView.list.children('li:last').find('.secondary-line')).toHaveText path.join(directoryBasename, 'tagged.js') + expect(symbolsView.error).not.toBeVisible() + atom.commands.dispatch(getWorkspaceView(), "symbols-view:toggle-project-symbols") + + fs.removeSync(directory.resolve('tagsDir/tags')) + + waitsFor -> + symbolsView.reloadTags + + runs -> + atom.commands.dispatch(getWorkspaceView(), "symbols-view:toggle-project-symbols") + + waitsFor -> + symbolsView.error.text().length > 0 + + runs -> + expect(symbolsView.list.children('li').length).toBe 0 describe "when there is only one project", -> beforeEach ->