├── .gitignore ├── LICENSE.md ├── README.md ├── keymaps └── goto.cson ├── lib ├── goto-view.coffee ├── index.coffee ├── symbol-generator.coffee ├── symbol-index.coffee └── symbol-utils.coffee ├── menus └── goto.cson ├── package.json ├── spec ├── goto-spec.coffee └── goto-view-spec.coffee └── styles └── goto.less /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | node_modules 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Michael Kleehammer 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # goto package 2 | 3 | Provides “goto symbol” functionality for the current file or the entire project. 4 | 5 | This is a replacement for Atom’s built-in symbols-view package that uses Atom’s own syntax files 6 | to identify symbols rather than ctags. The ctags project is very useful but it is never going 7 | to keep up with all of the new Atom syntaxes that will be created as Atom grows. 8 | 9 | Commands: 10 | 11 | * `cmd-r` - Goto File Symbol 12 | * `cmd-shift-r` - Goto Project Symbol 13 | * `cmd-alt-down` - Goto Declaration 14 | * Rebuild Index 15 | * Invalidate Index 16 | 17 | ## Index 18 | 19 | The symbol index is currently maintained in memory. Goto File Symbol will reindex the current 20 | file if necessary and editing a file will automatically invalidate the symbol cache for it. 21 | 22 | Symbols for the entire project are not indexed until the Goto Project Symbol or Goto 23 | Declaration commands are used. 24 | 25 | While symbols are automatically kept up to date as buffers are modified, the package does not 26 | yet watch for external file modifications. If you change files externally, such as through a 27 | "git pull" or switching branches, you can use run Invalidate Index to clear the current index 28 | so it will be rebuilt when needed or Rebuild Index to rebuild it immediately. 29 | 30 | ## Options 31 | 32 | ### More Ignored Names 33 | 34 | A whitespace and/or comma separated list of globs (filenames or wildcards) to ignore, applied 35 | to both files and directories. This can be useful for speeding up the rebuilding of the index. 36 | 37 | Example: `node_modules, *.sql` 38 | 39 | ### Auto Scroll 40 | 41 | By default the Goto File Symbol command will scroll the selected command into view. Pressing 42 | `Esc` to cancel the command restores the position of the screen. Uncheck this option to 43 | disable the scrolling. 44 | 45 | Note that the Goto Project Symbol does not scroll the editor since it displays choices from 46 | multiple files. 47 | -------------------------------------------------------------------------------- /keymaps/goto.cson: -------------------------------------------------------------------------------- 1 | 2 | # The atom-workspace selector was added to increase specificity so it would override 3 | # symbols-view 4 | 5 | '.platform-darwin atom-workspace atom-text-editor': 6 | 'cmd-r': 'goto:file-symbol' 7 | 'cmd-alt-down': 'goto:declaration' 8 | 9 | '.platform-win32 atom-workspace atom-text-editor': 10 | 'ctrl-r': 'goto:file-symbol' 11 | 'ctrl-alt-down': 'goto:declaration' 12 | 13 | '.platform-darwin atom-workspace': 14 | 'cmd-R': 'goto:project-symbol' 15 | 16 | '.platform-win32 atom-workspace': 17 | 'ctrl-R': 'goto:project-symbol' 18 | 19 | '.platform-linux atom-workspace atom-text-editor': 20 | 'ctrl-r': 'goto:file-symbol' 21 | 'ctrl-shift-r': 'goto:project-symbol' 22 | 'ctrl-alt-down': 'goto:declaration' 23 | -------------------------------------------------------------------------------- /lib/goto-view.coffee: -------------------------------------------------------------------------------- 1 | 2 | path = require 'path' 3 | fs = require 'fs' 4 | {$$, SelectListView} = require 'atom-space-pen-views' 5 | utils = require './symbol-utils' 6 | 7 | module.exports = 8 | class GotoView extends SelectListView 9 | 10 | initialize: -> 11 | super 12 | @addClass('goto-view fuzzy-finder') 13 | # Use fuzzy-finder styles 14 | 15 | @currentEditor = null 16 | # If this is non-null, then the command was 'Goto File Symbol' and this is the current file's 17 | # editor view. Autoscroll to the selected item. 18 | 19 | @cancelPosition = null 20 | # The original position of the screen and selections so they can be restored if the user 21 | # cancels. This is only set by the Goto File Symbol command when auto-scrolling is enabled. 22 | # If set, it is an object containing: 23 | # :firstRow - the editor's first visible row 24 | # :selections - the original selections 25 | 26 | destroy: -> 27 | @cancel() 28 | @panel?.destroy() 29 | 30 | cancel: -> 31 | super 32 | @restoreCancelPosition() 33 | @currentEditor = null 34 | @cancelPosition = null 35 | 36 | populate: (symbols, editor) -> 37 | @rememberCancelPosition(editor) 38 | @setItems(symbols) 39 | @show() 40 | 41 | rememberCancelPosition: (editor) -> 42 | if not editor or not atom.config.get('goto.autoScroll') 43 | return 44 | 45 | @currentEditor = editor 46 | @cancelPosition = 47 | position: editor.getCursorBufferPosition() 48 | selections: editor.getSelectedBufferRanges() 49 | 50 | restoreCancelPosition: -> 51 | if @currentEditor and @cancelPosition 52 | @currentEditor.setCursorBufferPosition(@cancelPosition.position) 53 | if @cancelPosition.selections 54 | @currentEditor.setSelectedBufferRanges(@cancelPosition.selections) 55 | 56 | forgetCancelPosition: -> 57 | @currentEditor = null 58 | @cancelPosition = null 59 | 60 | getFilterKey: -> 'name' 61 | 62 | scrollToItemView: (view) -> 63 | # Hook the selection of an item so we can scroll the current buffer to the item. 64 | super 65 | symbol = @getSelectedItem() 66 | @onItemSelected(symbol) 67 | 68 | onItemSelected: (symbol) -> 69 | @currentEditor?.setCursorBufferPosition(symbol.position) 70 | 71 | viewForItem: (symbol) -> 72 | $$ -> 73 | @li class: 'two-lines', => 74 | @div symbol.name, class: 'primary-line' 75 | dir = path.basename(symbol.path) 76 | text = "#{dir} #{symbol.position.row + 1}" 77 | @div text, class: 'secondary-line' 78 | 79 | getEmptyMessage: (itemCount) -> 80 | if itemCount is 0 81 | 'No symbols found' 82 | else 83 | super 84 | 85 | confirmed: (symbol) -> 86 | @forgetCancelPosition() 87 | 88 | if not fs.existsSync(symbol.path) 89 | @setError('Selected file does not exist') 90 | setTimeout((=> @setError()), 2000) 91 | else if atom.workspace.getActiveTextEditor() 92 | @cancel() 93 | utils.gotoSymbol(symbol) 94 | 95 | show: -> 96 | @storeFocusedElement() 97 | @panel ?= atom.workspace.addModalPanel(item: this) 98 | @panel.show() 99 | @focusFilterEditor() 100 | 101 | hide: -> 102 | @panel?.hide() 103 | 104 | cancelled: -> 105 | @hide() 106 | -------------------------------------------------------------------------------- /lib/index.coffee: -------------------------------------------------------------------------------- 1 | 2 | SymbolIndex = require('./symbol-index') 3 | GotoView = require('./goto-view') 4 | 5 | module.exports = 6 | 7 | config: 8 | logToConsole: 9 | default: false 10 | type: 'boolean' 11 | description: 'Enable debug information logging for goto commands' 12 | moreIgnoredNames: 13 | default: '' 14 | type: 'string' 15 | description: 'Whitespace- or comma-separated list of globs for files that goto should skip. These files are in addition to those specified in the core.ignoredNames setting' 16 | autoScroll: 17 | default: true 18 | type: 'boolean' 19 | description: 'Disable this option to prevent goto from restoring your selection back to your original cursor position after cancelling a goto method' 20 | 21 | index: null 22 | gotoView: null 23 | 24 | activate: (state) -> 25 | @index = new SymbolIndex(state?.entries) 26 | @gotoView = new GotoView() 27 | atom.commands.add 'atom-workspace', { 28 | 'goto:project-symbol': => @gotoProjectSymbol() 29 | 'goto:file-symbol': => @gotoFileSymbol() 30 | 'goto:declaration': => @gotoDeclaration() 31 | 'goto:rebuild-index': => @index.rebuild() 32 | 'goto:invalidate-index': => @index.invalidate() 33 | } 34 | 35 | deactivate: -> 36 | @index?.destroy() 37 | @index = null 38 | @gotoView?.destroy() 39 | @gotoView = null 40 | 41 | serialize: -> { entries: @index.entries } 42 | 43 | gotoDeclaration: -> 44 | symbols = @index.gotoDeclaration() 45 | if symbols and symbols.length 46 | @gotoView.populate(symbols) 47 | 48 | gotoProjectSymbol: -> 49 | symbols = @index.getAllSymbols() 50 | @gotoView.populate(symbols) 51 | 52 | gotoFileSymbol: -> 53 | editor = atom.workspace.getActiveTextEditor() 54 | filePath = editor?.getPath() 55 | if filePath 56 | symbols = @index.getEditorSymbols(editor) 57 | @gotoView.populate(symbols, editor) 58 | -------------------------------------------------------------------------------- /lib/symbol-generator.coffee: -------------------------------------------------------------------------------- 1 | 2 | {Point} = require 'atom' 3 | 4 | # I'm expecting this to grow a lot. We'll also need configuration 5 | # that can be added to dynamically, I think. 6 | 7 | resym = /// ^ ( 8 | entity.name.type.class 9 | | entity.name.function 10 | | entity.other.attribute-name.class 11 | ) /// 12 | 13 | # A simplistic regexp that is used to match the item immediately following. I'll eventually 14 | # need something a bit more complex. 15 | rebefore = /// ^ ( 16 | meta.rspec.behaviour 17 | ) /// 18 | 19 | module.exports = (path, grammar, text) -> 20 | lines = grammar.tokenizeLines(text) 21 | 22 | symbols = [] 23 | 24 | nextIsSymbol = false 25 | 26 | for tokens, lineno in lines 27 | offset = 0 28 | prev = null 29 | for token in tokens 30 | if nextIsSymbol or issymbol(token) 31 | nextIsSymbol = false 32 | 33 | symbol = cleanSymbol(token) 34 | if symbol 35 | if not mergeAdjacent(prev, token, symbols, offset) 36 | symbols.push({ name: token.value, path: path, position: new Point(lineno, offset) }) 37 | prev = token 38 | 39 | nextIsSymbol = isbefore(token) 40 | 41 | offset += token.value.length 42 | 43 | symbols 44 | 45 | cleanSymbol = (token) -> 46 | # Return the token name. Will return null if symbol is not a valid name. 47 | name = token.value.trim().replace(/"/g, '') 48 | name || null 49 | 50 | issymbol = (token) -> 51 | # I'm a little unclear about this :\ so this might be much easier than 52 | # I've made it out to be. If we really can use a single regular expression we can 53 | # switch to array.some() and eliminate this method all together 54 | if token.value.trim().length and token.scopes 55 | for scope in token.scopes 56 | if resym.test(scope) 57 | return true 58 | return false 59 | 60 | isbefore = (token) -> 61 | # Does this token indicate that the following token is a symbol? 62 | if token.value.trim().length and token.scopes 63 | for scope in token.scopes 64 | if rebefore.test(scope) 65 | return true 66 | return false 67 | 68 | mergeAdjacent = (prevToken, thisToken, symbols, offset) -> 69 | # I'm not sure why but first-mate is breaking function names (at least Coffeescript ones) 70 | # into two - the last character is being returned by itself. For now I'll merge any 71 | # two adjacent symbols since I can't see how there could actually ever be two adjacent 72 | # ones. 73 | # 74 | # Returns true if the two symbols are adjacent and will merge `thisToken` into the 75 | # previous symbol. Return false if thisToken is not adjacent to the previous symbol. 76 | 77 | if offset and prevToken 78 | prevSymbol = symbols[symbols.length-1] 79 | if offset is prevSymbol.position.column + prevToken.value.length 80 | prevSymbol.name += thisToken.value 81 | return true 82 | 83 | return false 84 | -------------------------------------------------------------------------------- /lib/symbol-index.coffee: -------------------------------------------------------------------------------- 1 | 2 | fs = require 'fs-plus' 3 | path = require 'path' 4 | minimatch = require 'minimatch' 5 | generate = require './symbol-generator' 6 | utils = require './symbol-utils' 7 | {CompositeDisposable} = require 'atom' 8 | 9 | module.exports = 10 | class SymbolIndex 11 | constructor: (entries)-> 12 | 13 | @entries = {} 14 | # The cache of symbols which maps from fully-qualified-name (absolute path) to either an 15 | # array of symbols or null if the file needs to be rescanned. 16 | # 17 | # If the index needs to be rebuilt, it will contain only individual files that have been 18 | # scanned by the Goto File Symbol command. 19 | 20 | @rescanDirectories = true 21 | # If true we must rescan the directories to provide all project symbols. 22 | # 23 | # The @entries starts out empty and we need to scan the project directories to find all 24 | # symbols. Once we've done this once we have all of the filenames and can mark them as 25 | # invalid when they are modified by setting their @entries value to null. If something 26 | # invalidates our list of filenames (e.g. the project path is changed), directories need 27 | # to be rescanned again. 28 | # 29 | # Note that we may have values in @entries even if directories have not been scaned since 30 | # the Goto File Symbols command will populate it. We allow this so that individual file 31 | # symbols can be cached without requiring a full project scan for cases where the Goto 32 | # Project Symbols command is not used. 33 | 34 | @ignoredNames = atom.config.get('core.ignoredNames') ? [] 35 | if typeof @ignoredNames is 'string' 36 | @ignoredNames = [ @ignoredNames ] 37 | 38 | @logToConsole = atom.config.get('goto.logToConsole') ? false 39 | @moreIgnoredNames = atom.config.get('goto.moreIgnoredNames') ? '' 40 | @moreIgnoredNames = (n for n in @moreIgnoredNames.split(/[, \t]+/) when n?.length) 41 | 42 | @noGrammar = {} 43 | # File extensions that we've found have no grammar. There are probably a lot of files 44 | # such as *.png that we don't have grammars for. Instead of hardcoding them we'll record 45 | # them on the fly. We'll clear this when we rescan directories. 46 | 47 | @disposables = new CompositeDisposable 48 | @subscribe() 49 | 50 | invalidate: -> 51 | @entries = {} 52 | @rescanDirectories = true 53 | 54 | subscribe: () -> 55 | @disposables.add atom.project.onDidChangePaths => 56 | @invalidate() 57 | 58 | atom.config.observe 'core.ignoredNames', => 59 | @ignoredNames = atom.config.get('core.ignoredNames') ? [] 60 | if typeof @ignoredNames is 'string' 61 | @ignoredNames = [ @ignoredNames ] 62 | @invalidate() 63 | 64 | atom.config.observe 'goto.moreIgnoredNames', => 65 | @moreIgnoredNames = atom.config.get('goto.moreIgnoredNames') ? '' 66 | @moreIgnoredNames = (n for n in @moreIgnoredNames.split(/[, \t]+/) when n?.length) 67 | @invalidate() 68 | 69 | atom.workspace.observeTextEditors (editor) => 70 | editor_disposables = new CompositeDisposable 71 | # Editor events 72 | editor_disposables.add editor.onDidChangeGrammar => 73 | @entries[editor.getPath()] = null 74 | 75 | editor_disposables.add editor.onDidStopChanging => 76 | @entries[editor.getPath()] = null 77 | 78 | editor_disposables.add editor.onDidDestroy -> 79 | editor_disposables.dispose() 80 | 81 | destroy: -> 82 | @entries = null 83 | @disposables.dispose() 84 | 85 | getEditorSymbols: (editor) -> 86 | # Return the symbols for the given editor, rescanning the file if necessary. 87 | fqn = editor.getPath() 88 | if not @entries[fqn] and @keepPath(fqn) 89 | @entries[fqn] = generate(fqn, editor.getGrammar(), editor.getText()) 90 | return @entries[fqn] 91 | 92 | getAllSymbols: -> 93 | # Returns the symbols for the entire project. 94 | @update() 95 | 96 | s = [] 97 | for fqn, symbols of @entries 98 | Array::push.apply s, symbols 99 | return s 100 | 101 | update: -> 102 | if @rescanDirectories 103 | @rebuild() 104 | else 105 | for fqn, symbols of @entries 106 | if symbols is null 107 | @processFile(fqn) 108 | 109 | rebuild: -> 110 | for root in atom.project.getDirectories() 111 | fs.traverseTreeSync( 112 | root.path, 113 | (filePath) => @processFile filePath if @keepPath filePath, 114 | (filePath) => @keepPath filePath 115 | ) 116 | @rescanDirectories = false 117 | console.log('No Grammar:', Object.keys(@noGrammar)) if @logToConsole 118 | 119 | gotoDeclaration: -> 120 | editor = atom.workspace.getActiveTextEditor() 121 | # Make a word selection based on current cursor 122 | editor?.selectWordsContainingCursors() 123 | word = editor?.getSelectedText() 124 | if not word?.length 125 | return null 126 | 127 | @update() 128 | 129 | # TODO: I'm quite sure this is not using Coffeescript idioms. Rewrite. 130 | 131 | filePath = editor.getPath() 132 | matches = [] 133 | @matchSymbol(matches, word, @entries[filePath]) 134 | for fqn, symbols of @entries 135 | if fqn isnt filePath 136 | @matchSymbol(matches, word, symbols) 137 | 138 | if matches.length is 0 139 | return null 140 | 141 | if matches.length > 1 142 | return matches 143 | 144 | utils.gotoSymbol(matches[0]) 145 | 146 | matchSymbol: (matches, word, symbols) -> 147 | if symbols 148 | for symbol in symbols 149 | if symbol.name is word 150 | matches.push(symbol) 151 | 152 | processFile: (fqn) -> 153 | console.log('GOTO: file', fqn) if @logToConsole 154 | text = fs.readFileSync(fqn, { encoding: 'utf8' }) 155 | grammar = atom.grammars.selectGrammar(fqn, text) 156 | if grammar?.scopeName isnt 'text.plain.null-grammar' 157 | @entries[fqn] = generate(fqn, grammar, text) 158 | else 159 | @noGrammar[path.extname(fqn)] = true 160 | 161 | keepPath: (filePath) -> 162 | # Should we keep this path in @entries? It is not kept if it is excluded by the 163 | # core ignoredNames setting or if the associated git repo ignore it. 164 | 165 | isFile = fs.isFileSync(filePath) 166 | base = path.basename(filePath) 167 | ext = path.extname(base) 168 | 169 | # files with this extensions are known not to have a grammar. 170 | if isFile and @noGrammar[ext]? 171 | console.log('GOTO: ignore/grammar', filePath) if @logToConsole 172 | return false 173 | 174 | for glob in @moreIgnoredNames 175 | if minimatch(base, glob, { dot: true }) 176 | console.log('GOTO: ignore/core', filePath) if @logToConsole 177 | return false 178 | 179 | if @ignoredNames.includes(base) 180 | console.log('GOTO: ignore/core', filePath) if @logToConsole 181 | return false 182 | 183 | if ext and @ignoredNames.includes('*#{ext}') 184 | console.log('GOTO: ignore/core', filePath) if @logToConsole 185 | return false 186 | 187 | for repo in atom.project.repositories 188 | if repo?.isPathIgnored(filePath) 189 | console.log('GOTO: ignore/git', filePath) if @logToConsole 190 | return false 191 | 192 | return true 193 | -------------------------------------------------------------------------------- /lib/symbol-utils.coffee: -------------------------------------------------------------------------------- 1 | 2 | module.exports.gotoSymbol = (symbol) -> 3 | editor = atom.workspace.getActiveTextEditor() 4 | if editor and symbol.path != editor.getPath() 5 | atom.workspace.open(symbol.path).done -> 6 | moveToPosition(symbol.position) 7 | else 8 | moveToPosition(symbol.position) 9 | 10 | moveToPosition = (position) -> 11 | if editor = atom.workspace.getActiveTextEditor() 12 | editor.setCursorBufferPosition(position) 13 | editor.moveToFirstCharacterOfLine() 14 | -------------------------------------------------------------------------------- /menus/goto.cson: -------------------------------------------------------------------------------- 1 | 'context-menu': 2 | 'atom-text-editor': [ 3 | { label: 'Goto Declaration', command: 'goto:declaration' } 4 | ] 5 | 6 | 'menu': [ 7 | { 8 | 'label': 'Packages' 9 | 'submenu': [ 10 | 'label': 'Goto' 11 | 'submenu': [ 12 | { label: 'Goto File Symbol', command: 'goto:file-symbol' }, 13 | { label: 'Goto Project Symbol', command: 'goto:project-symbol' }, 14 | { label: 'Goto Declaration', command: 'goto:declaration' }, 15 | { label: 'Invalidate Index', command: 'goto:invalidate-index' }, 16 | { label: 'Rebuild Index', command: 'goto:rebuild-index' } 17 | ] 18 | ] 19 | } 20 | ] 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "goto", 3 | "main": "./lib/index", 4 | "version": "1.8.3", 5 | "description": "Provides Go To Symbol based on syntaxes", 6 | "author": "Michael Kleehammer ", 7 | "activationCommands": { 8 | "atom-text-editor": [ 9 | "goto:file-symbol", 10 | "goto:declaration" 11 | ], 12 | "atom-workspace": [ 13 | "goto:project-symbol", 14 | "goto:rebuild-index", 15 | "goto:invalidate-index" 16 | ] 17 | }, 18 | "repository": "https://github.com/v3ss0n/goto", 19 | "bugs": "http://github.com/v3ss0n/goto/issues", 20 | "license": "MIT", 21 | "engines": { 22 | "atom": ">0.50.0" 23 | }, 24 | "dependencies": { 25 | "fs-plus": "^2.9.3", 26 | "minimatch": "^3.0.3", 27 | "atom-space-pen-views": "^2.2.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /spec/goto-spec.coffee: -------------------------------------------------------------------------------- 1 | Goto = require '../lib/goto' 2 | 3 | # Use the command `window:run-package-specs` (cmd-alt-ctrl-p) to run specs. 4 | # 5 | # To run a specific `it` or `describe` block add an `f` to the front (e.g. `fit` 6 | # or `fdescribe`). Remove the `f` to unfocus the block. 7 | 8 | describe "Goto", -> 9 | activationPromise = null 10 | workspaceElement = null 11 | 12 | beforeEach -> 13 | workspaceElement = atom.views.getView(atom.workspace) 14 | activationPromise = atom.packages.activatePackage('goto') 15 | 16 | describe "when the goto:toggle event is triggered", -> 17 | it "attaches and then detaches the view", -> 18 | expect(workspaceElement.find('.goto-view')).not.toExist() 19 | 20 | # This is an activation event, triggering it will cause the package to be 21 | # activated. 22 | #atom.workspaceView.trigger 'goto:toggle' 23 | 24 | waitsForPromise -> 25 | activationPromise 26 | 27 | runs -> 28 | expect(workspaceElement.find('.goto-view')).toExist() 29 | #atom.workspaceView.trigger 'goto:toggle' 30 | #expect(atom.workspaceView.find('.goto')).not.toExist() 31 | -------------------------------------------------------------------------------- /spec/goto-view-spec.coffee: -------------------------------------------------------------------------------- 1 | GotoView = require '../lib/goto-view' 2 | 3 | describe "GotoView", -> 4 | it "has one valid test", -> 5 | expect("life").toBe "easy" 6 | -------------------------------------------------------------------------------- /styles/goto.less: -------------------------------------------------------------------------------- 1 | // The ui-variables file is provided by base themes provided by Atom. 2 | // 3 | // See https://github.com/atom/atom-dark-ui/blob/master/stylesheets/ui-variables.less 4 | // for a full listing of what's available. 5 | @import "ui-variables"; 6 | 7 | .goto { 8 | } 9 | --------------------------------------------------------------------------------