├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── icons ├── icon-class.svg ├── icon-constant.svg ├── icon-converter.svg ├── icon-enum.svg ├── icon-function.svg ├── icon-generics.svg ├── icon-interface.svg ├── icon-iterator.svg ├── icon-macro.svg ├── icon-method.svg ├── icon-namespace.svg ├── icon-struct.svg ├── icon-typedef.svg ├── icon-union.svg ├── icon-variable.svg └── symbol-icons.woff ├── keymaps └── symbols-tree-view.cson ├── lib ├── .ctags ├── main.coffee ├── symbols-context-menu.coffee ├── symbols-tree-view.coffee ├── tag-generator.coffee ├── tag-parser.coffee └── tree-view.coffee ├── menus └── symbols-tree-view.cson ├── package.json ├── screencast.gif ├── screenshot.png ├── styles └── symbols-tree-view.less └── vendor ├── universal-ctags-darwin ├── universal-ctags-linux └── universal-ctags-win32.exe /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | node_modules 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.1.0 - First Release 2 | * Every feature added 3 | * Every bug fixed 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 xndcn 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 | # symbols-tree-view package 2 | 3 | Symbols Tree View for Atom.io, just like taglist or tagbar for VIM. 4 | 5 | tag-generator.coffee comes from http://github.com/atom/symbols-view 6 | 7 | ![screencast](https://raw.githubusercontent.com/xndcn/symbols-tree-view/master/screencast.gif?raw=true) 8 | 9 | ## Settings 10 | 11 | * `Auto Hide` If checked then symbols-tree-view is always hidden unless mouse hover over it. (default=false) 12 | 13 | * `Auto Toggle` If checked the symbols-tree-view is auto toggled on when you open files. (default=false) 14 | 15 | * `Scroll Animation` If checked it will scroll to the destination gradually when you click the item in symbols-tree. (default=true) 16 | 17 | ## TO DO 18 | 19 | * Add specs for test 20 | 21 | * Improve icons 22 | -------------------------------------------------------------------------------- /icons/icon-class.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 38 | 40 | 41 | 43 | image/svg+xml 44 | 46 | 47 | 48 | 49 | 50 | 55 | 60 | 65 | 75 | 78 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /icons/icon-constant.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 17 | 18 | 20 | image/svg+xml 21 | 23 | 24 | 25 | 26 | 27 | 29 | 32 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /icons/icon-converter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 39 | 41 | 42 | 44 | image/svg+xml 45 | 47 | 48 | 49 | 50 | 51 | 55 | 58 | 62 | 66 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /icons/icon-enum.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 38 | 40 | 41 | 43 | image/svg+xml 44 | 46 | 47 | 48 | 49 | 50 | 55 | 65 | 70 | 75 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /icons/icon-function.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 38 | 40 | 41 | 43 | image/svg+xml 44 | 46 | 47 | 48 | 49 | 50 | 55 | 60 | 65 | 70 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /icons/icon-generics.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 17 | 18 | 20 | image/svg+xml 21 | 23 | 24 | 25 | 26 | 27 | 29 | 32 | 36 | 40 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /icons/icon-interface.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 38 | 40 | 41 | 43 | image/svg+xml 44 | 46 | 47 | 48 | 49 | 50 | 55 | 65 | 70 | 75 | 80 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /icons/icon-iterator.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 39 | 41 | 42 | 44 | image/svg+xml 45 | 47 | 48 | 49 | 50 | 51 | 55 | 58 | 62 | 66 | 70 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /icons/icon-macro.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 38 | 40 | 41 | 43 | image/svg+xml 44 | 46 | 47 | 48 | 49 | 50 | 55 | 65 | 68 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /icons/icon-method.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 17 | 18 | 20 | image/svg+xml 21 | 23 | 24 | 25 | 26 | 27 | 29 | 32 | 36 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /icons/icon-namespace.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 38 | 40 | 41 | 43 | image/svg+xml 44 | 46 | 47 | 48 | 49 | 50 | 55 | 60 | 65 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /icons/icon-struct.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 38 | 40 | 41 | 43 | image/svg+xml 44 | 46 | 47 | 48 | 49 | 50 | 55 | 60 | 65 | 75 | 78 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /icons/icon-typedef.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 38 | 40 | 41 | 43 | image/svg+xml 44 | 46 | 47 | 48 | 49 | 50 | 55 | 65 | 70 | 75 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /icons/icon-union.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 38 | 40 | 41 | 43 | image/svg+xml 44 | 46 | 47 | 48 | 49 | 50 | 55 | 65 | 70 | 75 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /icons/icon-variable.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 38 | 40 | 41 | 43 | image/svg+xml 44 | 46 | 47 | 48 | 49 | 50 | 55 | 65 | 70 | 75 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /icons/symbol-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xndcn/symbols-tree-view/9ae8d173351554450077dcabd5f32c0766f8b9e6/icons/symbol-icons.woff -------------------------------------------------------------------------------- /keymaps/symbols-tree-view.cson: -------------------------------------------------------------------------------- 1 | # Keybindings require three things to be fully defined: A selector that is 2 | # matched against the focused element, the keystroke and the command to 3 | # execute. 4 | # 5 | # Below is a basic keybinding which registers on all platforms by applying to 6 | # the root workspace element. 7 | 8 | # For more detailed documentation see 9 | # https://atom.io/docs/latest/advanced/keymaps 10 | 'atom-workspace': 11 | 'ctrl-alt-o': 'symbols-tree-view:toggle' 12 | -------------------------------------------------------------------------------- /lib/.ctags: -------------------------------------------------------------------------------- 1 | --langdef=CoffeeScript 2 | --langmap=CoffeeScript:.coffee 3 | --regex-CoffeeScript=/^[ \t]*(@?[a-zA-Z$_\.0-9]+)[ \t]*=[ \t]*.*/\1/v,variable/ 4 | --regex-CoffeeScript=/(^|=[ \t])*class ([A-Za-z_][A-Za-z0-9_]+\.)*([A-Za-z_][A-Za-z0-9_]+)( extends ([A-Za-z][A-Za-z0-9_.]*)+)?$/\3/c,class/ 5 | --regex-CoffeeScript=/^[ \t]*(module\.)?(exports\.)?@?(([A-Za-z][A-Za-z0-9_.]*)+):.*[-=]>.*$/\3/m,method/ 6 | --regex-CoffeeScript=/^[ \t]*(module\.)?(exports\.)?(([A-Za-z][A-Za-z0-9_.]*)+)[ \t]*=.*[-=]>.*$/\3/f,function/ 7 | --regex-CoffeeScript=/^[ \t]*@(([A-Za-z][A-Za-z0-9_.]*)+)[ \t]*=[^->\n]*$/\1/f,field/ 8 | --regex-CoffeeScript=/^[ \t]*@(([A-Za-z][A-Za-z0-9_.]*)+):[^->\n]*$/\1/f,static field/ 9 | --regex-CoffeeScript=/^[ \t]*(([A-Za-z][A-Za-z0-9_.]*)+):[^->\n]*$/\1/f,field/ 10 | 11 | --langdef=Css 12 | --langmap=Css:.css 13 | --langmap=Css:+.less 14 | --langmap=Css:+.scss 15 | --regex-Css=/^[ \t]*(.+)[ \t]*\{/\1/f,function/ 16 | --regex-Css=/^[ \t]*(.+)[ \t]*,[ \t]*$/\1/f,function/ 17 | 18 | --langdef=Sass 19 | --langmap=Sass:.sass 20 | --regex-Sass=/^[ \t]*([#.]*[a-zA-Z_0-9]+)[ \t]*$/\1/f,function/ 21 | 22 | --langdef=Yaml 23 | --langmap=Yaml:.yaml 24 | --langmap=Yaml:+.yml 25 | --regex-Yaml=/^[ \t]*([a-zA-Z_0-9 ]+)[ \t]*\:[ \t]*/\1/f,function/ 26 | 27 | --regex-Html=/^[ \t]*<([a-zA-Z]+)[ \t]*.*id="([^"]+)".*>/\1#\2/m,member\tclass:id/ 28 | --regex-Html=/^[ \t]*<([a-zA-Z]+)[ \t]*.*class="([^"]+)".*>/\1.\2/m,member\tclass:class/ 29 | --regex-Html=/^[ \t]*<([a-zA-Z]+)[ \t]*>/\1/m,member\tclass:no-attr/ 30 | 31 | --langdef=Markdown 32 | --langmap=Markdown:.md 33 | --langmap=Markdown:+.markdown 34 | --langmap=Markdown:+.mdown 35 | --langmap=Markdown:+.mkd 36 | --langmap=Markdown:+.mkdown 37 | --langmap=Markdown:+.ron 38 | --regex-Markdown=/^#+[ \t]*([^#]+)/\1/f,function/ 39 | 40 | --langdef=Json 41 | --langmap=Json:.json 42 | --regex-Json=/^[ \t]*"([^"]+)"[ \t]*\:/\1/f,function/ 43 | 44 | --langdef=Cson 45 | --langmap=Cson:.cson 46 | --langmap=Cson:+.gyp 47 | --regex-Cson=/^[ \t]*'([^']+)'[ \t]*\:/\1/f,function/ 48 | --regex-Cson=/^[ \t]*"([^"]+)"[ \t]*\:/\1/f,function/ 49 | --regex-Cson=/^[ \t]*([^'"]+)[ \t]*\:/\1/f,function/ 50 | 51 | --langmap=C++:+.mm 52 | 53 | --langmap=Ruby:+(Rakefile) 54 | 55 | --langmap=Php:+.module 56 | 57 | --langdef=Go 58 | --langmap=Go:.go 59 | --regex-Go=/func([ \t]+\([^)]+\))?[ \t]+([a-zA-Z0-9_]+)/\2/f,func/ 60 | --regex-Go=/var[ \t]+([a-zA-Z_][a-zA-Z0-9_]*)/\1/v,var/ 61 | --regex-Go=/type[ \t]+([a-zA-Z_][a-zA-Z0-9_]*)/\1/t,type/ 62 | 63 | --langmap=perl:+.pod 64 | --regex-perl=/with[ \t]+([^;]+)[ \t]*?;/\1/w,role,roles/ 65 | --regex-perl=/extends[ \t]+['"]([^'"]+)['"][ \t]*?;/\1/e,extends/ 66 | --regex-perl=/use[ \t]+base[ \t]+['"]([^'"]+)['"][ \t]*?;/\1/e,extends/ 67 | --regex-perl=/use[ \t]+parent[ \t]+['"]([^'"]+)['"][ \t]*?;/\1/e,extends/ 68 | --regex-perl=/Mojo::Base[ \t]+['"]([^'"]+)['"][ \t]*?;/\1/e,extends/ 69 | --regex-perl=/^[ \t]*?use[ \t]+([^;]+)[ \t]*?;/\1/u,use,uses/ 70 | --regex-perl=/^[ \t]*?require[ \t]+((\w|\:)+)/\1/r,require,requires/ 71 | --regex-perl=/^[ \t]*?has[ \t]+['"]?(\w+)['"]?/\1/a,attribute,attributes/ 72 | --regex-perl=/^[ \t]*?\*(\w+)[ \t]*?=/\1/a,alias,aliases/ 73 | --regex-perl=/->helper\([ \t]?['"]?(\w+)['"]?/\1/h,helper,helpers/ 74 | --regex-perl=/^[ \t]*?our[ \t]*?[\$@%](\w+)/\1/o,our,ours/ 75 | --regex-perl=/^\=head1[ \t]+(.+)/\1/p,pod,Plain Old Documentation/ 76 | --regex-perl=/^\=head2[ \t]+(.+)/-- \1/p,pod,Plain Old Documentation/ 77 | --regex-perl=/^\=head[3-5][ \t]+(.+)/---- \1/p,pod,Plain Old Documentation/ 78 | 79 | 80 | --regex-JavaScript=/(,|(;|^)[ \t]*(var|let|const|([A-Za-z_$][A-Za-z0-9_$.]*\.)+))[ \t]*([A-Za-z_$][A-Za-z0-9_$.]*)[ \t]*=[ \t]*\{/\5/,object/ 81 | --regex-JavaScript=/(,|(;|^)[ \t]*(var|let|const|([A-Za-z_$][A-Za-z0-9_$.]*\.)+))[ \t]*([A-Za-z_$][A-Za-z0-9_$.]*)[ \t]*=[ \t]*(\[|(new[ \t]+)?Array\()/\5/,array/ 82 | --regex-JavaScript=/(,|(;|^)[ \t]*(var|let|const|([A-Za-z_$][A-Za-z0-9_$.]*\.)+))[ \t]*([A-Za-z_$][A-Za-z0-9_$.]*)[ \t]*=[ \t]*[^"]'[^']*/\5/,string/ 83 | --regex-JavaScript=/(,|(;|^)[ \t]*(var|let|const|([A-Za-z_$][A-Za-z0-9_$.]*\.)+))[ \t]*([A-Za-z_$][A-Za-z0-9_$.]*)[ \t]*=[ \t]*(true|false)/\5/,boolean/ 84 | --regex-JavaScript=/(,|(;|^)[ \t]*(var|let|const|([A-Za-z_$][A-Za-z0-9_$.]*\.)+))[ \t]*([A-Za-z_$][A-Za-z0-9_$.]*)[ \t]*=[ \t]*[0-9]+/\5/,number/ 85 | --regex-JavaScript=/(,|(;|^)[ \t]*(var|let|const|([A-Za-z_$][A-Za-z0-9_$.]*\.)+))[ \t]*([A-Za-z_$][A-Za-z0-9_$.]*)[ \t]*=[ \t]*null/\5/,null/ 86 | --regex-JavaScript=/(,|(;|^)[ \t]*(var|let|const|([A-Za-z_$][A-Za-z0-9_$.]*\.)+))[ \t]*([A-Za-z_$][A-Za-z0-9_$.]*)[ \t]*=[ \t]*undefined/\5/,undefined/ 87 | 88 | --regex-JavaScript=/(,|(;|^)[ \t]*(var|let|([A-Za-z_$][A-Za-z0-9_$.]*\.)+))[ \t]*([A-Za-z_$][A-Za-z0-9_$.]*)[ \t]*=[ \t]*[^tfn0-9"'{\[]+([,;=]|$)/\5/,unknown/ 89 | 90 | --regex-JavaScript=/(,|(;|^)[ \t]*(var|let|([A-Za-z_$][A-Za-z0-9_$.]*\.)+))[ \t]*([A-Za-z_$][A-Za-z0-9_$.]*)[ \t]*=[ \t]*.+([,;=]|$)/\5/,variable/ 91 | --regex-JavaScript=/(,|(;|^)[ \t]*(var|let|([A-Za-z_$][A-Za-z0-9_$.]*\.)+))[ \t]*([A-Za-z_$][A-Za-z0-9_$.]*)[ \t]*[ \t]*([,;]|$)/\5/,undefined/ 92 | --regex-JavaScript=/(,|(;|^)[ \t]*)const[ \t]*([A-Za-z_$][A-Za-z0-9_$]*)[ \t]*=[ \t]*.+([,;=]|$)/\3/,constant/ 93 | 94 | 95 | --regex-JavaScript=/(,|(;|^)[ \t]*(var|let|([A-Za-z_$][A-Za-z0-9_$.]*\.)*))[ \t]*([A-Za-z_$][A-Za-z0-9_$]*)[ \t]*=[ \t]*function[ \t]*\(/\5/,function-expression/ 96 | --regex-JavaScript=/(,|(;|^))[ \t]*(([A-Za-z_$][A-Za-z0-9_$.]*\.)+prototype\.)[ \t]*([A-Za-z_$][A-Za-z0-9_$]*)[ \t]*=[ \t]*function[ \t]*\(/\4\5/,prototype-method/ 97 | --regex-JavaScript=/(,|^|\*\/)[ \t]*([A-Za-z_$][A-Za-z0-9_$]*)[ \t]*:[ \t]*function[ \t]*\(/\2/,object-method/ 98 | 99 | 100 | --regex-JavaScript=/function[ \t]+([A-Za-z_$][A-Za-z0-9_$]*)[ \t]*\([^)]*\)/\1/,function-declaration/ 101 | 102 | --regex-JavaScript=/(,|^|\*\/)[ \t]*(while|if|for|switch|function|([A-Za-z_$][A-Za-z0-9_$]*))[ \t]*\([^)]*\)[ \t]*\{/\3/,function/ 103 | 104 | --regex-JavaScript=/(,|^|\*\/|\{)[ \t]*get[ \t]+([A-Za-z_$][A-Za-z0-9_$]*)[ \t]*\([ \t]*\)[ \t]*\{/get \2/,getter/ 105 | --regex-JavaScript=/(,|^|\*\/|\{)[ \t]*set[ \t]+([A-Za-z_$][A-Za-z0-9_$]*)[ \t]*\([ \t]*([A-Za-z_$][A-Za-z0-9_$]*)?[ \t]*\)[ \t]*\{/set \2/,setter/ 106 | 107 | --regex-JavaScript=/(,|^|\*\/)[ \t]*([A-Za-z_$][A-Za-z0-9_$]*)[ \t]*:[ \t]*\{/\2/,object/ 108 | --regex-JavaScript=/(,|^|\*\/)[ \t]*([A-Za-z_$][A-Za-z0-9_$]*)[ \t]*:[ \t]*\[/\2/,array/ 109 | --regex-JavaScript=/(,|^|\*\/)[ \t]*([A-Za-z_$][A-Za-z0-9_$]*)[ \t]*:[ \t]*[^"]'[^']*/\2/,string/ 110 | --regex-JavaScript=/(,|^|\*\/)[ \t]*([A-Za-z_$][A-Za-z0-9_$]*)[ \t]*:[ \t]*(true|false)/\2/,boolean/ 111 | --regex-JavaScript=/(,|^|\*\/)[ \t]*([A-Za-z_$][A-Za-z0-9_$]*)[ \t]*:[ \t]*[0-9]+/\2/,number/ 112 | --regex-JavaScript=/(,|^|\*\/)[ \t]*([A-Za-z_$][A-Za-z0-9_$]*)[ \t]*:[ \t]*[^=]+([,;]|$)/\2/,variable/ 113 | 114 | 115 | --langdef=haxe 116 | --langmap=haxe:.hx 117 | --regex-haxe=/^package[ \t]+([A-Za-z0-9_.]+)/\1/p,package/ 118 | --regex-haxe=/^[ \t]*[(@:macro|private|public|static|override|inline|dynamic)( \t)]*function[ \t]+([A-Za-z0-9_]+)/\1/f,function/ 119 | --regex-haxe=/^[ \t]*([private|public|static|protected|inline][ \t]*)+var[ \t]+([A-Za-z0-9_]+)/\2/v,variable/ 120 | --regex-haxe=/^[ \t]*package[ \t]*([A-Za-z0-9_]+)/\1/p,package/ 121 | --regex-haxe=/^[ \t]*(extern[ \t]*|@:native\([^)]*\)[ \t]*)*class[ \t]+([A-Za-z0-9_]+)[ \t]*[^\{]*/\2/c,class/ 122 | --regex-haxe=/^[ \t]*(extern[ \t]+)?interface[ \t]+([A-Za-z0-9_]+)/\2/i,interface/ 123 | --regex-haxe=/^[ \t]*typedef[ \t]+([A-Za-z0-9_]+)/\1/t,typedef/ 124 | --regex-haxe=/^[ \t]*enum[ \t]+([A-Za-z0-9_]+)/\1/t,typedef/ 125 | --regex-haxe=/^[ \t]*+([A-Za-z0-9_]+)(;|\([^)]*:[^)]*\))/\1/t,enum_field/ 126 | 127 | --langdef=Nim 128 | --langmap=Nim:.nim 129 | --regex-Nim=/^[\t\s]*proc\s+([_A-Za-z0-9]+)\**(\[\w+(\:\s+\w+)?\])?\s*\(/\1/f,function/ 130 | --regex-Nim=/^[\t\s]*iterator\s+([_A-Za-z0-9]+)\**(\[\w+(\:\s+\w+)?\])?\s*\(/\1/i,iterator/ 131 | --regex-Nim=/^[\t\s]*macro\s+([_A-Za-z0-9]+)\**(\[\w+(\:\s+\w+)?\])?\s*\(/\1/m,macro/ 132 | --regex-Nim=/^[\t\s]*method\s+([_A-Za-z0-9]+)\**(\[\w+(\:\s+\w+)?\])?\s*\(/\1/h,method/ 133 | --regex-Nim=/^[\t\s]*template\s+([_A-Za-z0-9]+)\**(\[\w+(\:\s+\w+)?\])?\s*\(/\1/t,generics/ 134 | --regex-Nim=/^[\t\s]*converter\s+([_A-Za-z0-9]+)\**(\[\w+(\:\s+\w+)?\])?\s*\(/\1/c,converter/ 135 | 136 | --langdef=Rust 137 | --langmap=Rust:.rs 138 | --regex-Rust=/^[ \t]*(#\[[^\]]\][ \t]*)*(pub[ \t]+)?(extern[ \t]+)?("[^"]+"[ \t]+)?(unsafe[ \t]+)?fn[ \t]+([a-zA-Z0-9_]+)/\6/f,function/ 139 | --regex-Rust=/^[ \t]*(pub[ \t]+)?type[ \t]+([a-zA-Z0-9_]+)/\2/T,typedef/ 140 | --regex-Rust=/^[ \t]*(pub[ \t]+)?enum[ \t]+([a-zA-Z0-9_]+)/\2/g,enum/ 141 | --regex-Rust=/^[ \t]*(pub[ \t]+)?struct[ \t]+([a-zA-Z0-9_]+)/\2/s,struct/ 142 | --regex-Rust=/^[ \t]*(pub[ \t]+)?mod[ \t]+([a-zA-Z0-9_]+)/\2/m,namespace/ 143 | --regex-Rust=/^[ \t]*(pub[ \t]+)?static[ \t]+([a-zA-Z0-9_]+)/\2/c,constant/ 144 | --regex-Rust=/^[ \t]*(pub[ \t]+)?trait[ \t]+([a-zA-Z0-9_]+)/\2/t,method/ 145 | --regex-Rust=/^[ \t]*(pub[ \t]+)?impl([ \t\n]*<[^>]*>)?[ \t]+(([a-zA-Z0-9_:]+)[ \t]*(<[^>]*>)?[ \t]+(for)[ \t]+)?([a-zA-Z0-9_]+)/\4 \6 \7/i,generic/ 146 | --regex-Rust=/^[ \t]*macro_rules![ \t]+([a-zA-Z0-9_]+)/\1/d,macro/ 147 | 148 | --langdef=LiveCode 149 | --langmap=LiveCode:.livecodescript 150 | --langmap=LiveCode:+.lc 151 | --langmap=LiveCode:+.irev 152 | --regex-LiveCode=/^[ \t]*(private[ \t]+)*(on|command)[ \t]*([A-Za-z0-9_]+)/\3/h,method/ 153 | --regex-LiveCode=/^[ \t]*(private[ \t]+)*function[ \t]+([A-Za-z0-9_]+)/\2/f,function/ 154 | --regex-LiveCode=/^[ \t]*constant[ \t]+([A-Za-z0-9_]+)/\1/c,constant/ 155 | --regex-LiveCode=/^[ \t]*(global|local)[ \t]+([A-Za-z0-9_]+)/\2/v,variable/ 156 | 157 | --langdef=sql 158 | --langmap=sql:.sql 159 | --langmap=sql:+.bdy 160 | --langmap=sql:+.spc 161 | --langmap=sql:+.pls 162 | --langmap=sql:+.plb 163 | --langmap=sql:+.ddl 164 | --langmap=sql:+.pks 165 | --langmap=sql:+.pkb 166 | --regex-sql=/^[ \t]*create[ \t]+([a-zA-Z0-9 \t]*)?(table)[\t]+([^.]+\.)?([a-zA-Z0-9_@.]+)/\4/t,table/i 167 | --regex-sql=/^[ \t]*create[ \t]+([a-zA-Z0-9 \t]*)?(procedure|package)[\t]+([^.]+\.)?"?([a-zA-Z0-9_@.]+)/\4/p,procedure/i 168 | --regex-sql=/^[ \t]*create[ \t]+([a-zA-Z0-9 \t]*)?(function)[\t]+([^.]+\.)?"?([a-zA-Z0-9_@.]+)/\4/f,function/i 169 | --regex-sql=/^[ \t]*create[ \t]+([a-zA-Z0-9 \t]*)?(trigger)[\t]+([^.]+\.)?"?([a-zA-Z0-9_@.]+)/\4/r,trigger/i 170 | --regex-sql=/^[ \t]*create[ \t]+([a-zA-Z0-9 \t]*)?(event)[\t]+([^.]+\.)?"?([a-zA-Z0-9_@.]+)/\4/e,event/i 171 | --regex-sql=/^[ \t]*create[ \t]+([a-zA-Z0-9 \t]*)?(index)[\t]+([^.]+\.)?"?([a-zA-Z0-9_@.]+)/\4/i,index/i 172 | --regex-sql=/^[ \t]*create[ \t]+([a-zA-Z0-9\t]*)?(publication|subscription to|synchronization user)[\t]+([^.]+\.)?"?([a-zA-Z0-9_@.]+)/\4/m,mobilink/i 173 | --regex-sql=/^[ \t]*create[ \t]+([a-zA-Z0-9 \t]*)?(variable)[\t]+([^.]+\.)?"?([a-zA-Z0-9_@.]+)/\4/v,variable/i 174 | --regex-sql=/^[ \t]*create[ \t]+([a-zA-Z0-9\t]*)?(rule|schema|server|datatype|database|message)[\t]+([^.]+\.)?"?([a-zA-Z0-9_@.]+)/\4/o,other/I 175 | 176 | --langdef=Scilab 177 | --langmap=Scilab:.sce 178 | --langmap=Scilab:+.sci 179 | --langmap=Scilab:+.m 180 | --langmap=Scilab:+.kla 181 | --regex-Scilab=#(^///[ \t]*\$begin[ \t])+ScriptHeader#Script-Header#s,package#i 182 | --regex-Scilab=#^[ \t]*function.*[ \t]]*([a-zA-Z_][a-zA-Z0-9_]*)\(#\1#q,function# 183 | 184 | --langdef=ini 185 | --langmap=ini:.ini 186 | --regex-ini=/^[ \t]*(\[.+\])[ \t]*$/\1/t,generics/ 187 | -------------------------------------------------------------------------------- /lib/main.coffee: -------------------------------------------------------------------------------- 1 | SymbolsTreeView = require './symbols-tree-view' 2 | 3 | module.exports = 4 | config: 5 | autoToggle: 6 | type: 'boolean' 7 | default: false 8 | description: 'If this option is enabled then symbols-tree-view will auto open when you open files.' 9 | scrollAnimation: 10 | type: 'boolean' 11 | default: true 12 | description: 'If this option is enabled then when you click the item in symbols-tree it will scroll to the destination gradually.' 13 | autoHide: 14 | type: 'boolean' 15 | default: false 16 | description: 'If this option is enabled then symbols-tree-view is always hidden unless mouse hover over it.' 17 | zAutoHideTypes: 18 | title: 'AutoHideTypes' 19 | type: 'string' 20 | description: 'Here you can specify a list of types that will be hidden by default (ex: "variable class")' 21 | default: '' 22 | sortByNameScopes: 23 | type: 'string' 24 | description: 'Here you can specify a list of scopes that will be sorted by name (ex: "text.html.php")' 25 | default: '' 26 | defaultWidth: 27 | type: 'number' 28 | description: 'Width of the panel (needs Atom restart)' 29 | default: 200 30 | 31 | 32 | symbolsTreeView: null 33 | 34 | activate: (state) -> 35 | @symbolsTreeView = new SymbolsTreeView(state.symbolsTreeViewState) 36 | atom.commands.add 'atom-workspace', 'symbols-tree-view:toggle': => @symbolsTreeView.toggle() 37 | atom.commands.add 'atom-workspace', 'symbols-tree-view:show': => @symbolsTreeView.showView() 38 | atom.commands.add 'atom-workspace', 'symbols-tree-view:hide': => @symbolsTreeView.hideView() 39 | 40 | atom.config.observe 'tree-view.showOnRightSide', (value) => 41 | if @symbolsTreeView.hasParent() 42 | @symbolsTreeView.remove() 43 | @symbolsTreeView.populate() 44 | @symbolsTreeView.attach() 45 | 46 | atom.config.observe "symbols-tree-view.autoToggle", (enabled) => 47 | if enabled 48 | @symbolsTreeView.toggle() unless @symbolsTreeView.hasParent() 49 | else 50 | @symbolsTreeView.toggle() if @symbolsTreeView.hasParent() 51 | 52 | deactivate: -> 53 | @symbolsTreeView.destroy() 54 | 55 | serialize: -> 56 | symbolsTreeViewState: @symbolsTreeView.serialize() 57 | 58 | getProvider: -> 59 | view = @symbolsTreeView 60 | 61 | providerName: 'symbols-tree-view' 62 | getSuggestionForWord: (textEditor, text, range) => 63 | range: range 64 | callback: ()=> 65 | view.focusClickedTag.bind(view)(textEditor, text) 66 | -------------------------------------------------------------------------------- /lib/symbols-context-menu.coffee: -------------------------------------------------------------------------------- 1 | {$, $$, View} = require 'atom-space-pen-views' 2 | 3 | module.exports = 4 | class SymbolsContextMenu extends View 5 | @content: -> 6 | @div class: 'symbols-context-menu', => 7 | @div class: 'select-list popover-list', => 8 | @input type: 'text', class: 'hidden-input', outlet: 'hiddenInput' 9 | @ol class: 'list-group mark-active', outlet: 'menus' 10 | 11 | initialize: -> 12 | @hiddenInput.on 'focusout', => 13 | @hide() 14 | 15 | clear: -> 16 | @menus.empty() 17 | 18 | addMenu: (name, active, callback) -> 19 | menu = $$ -> 20 | @li class: (if active then 'active' else ''), name 21 | 22 | menu.on 'mousedown', => 23 | menu.toggleClass('active') 24 | @hiddenInput.blur() 25 | callback(name) 26 | 27 | @menus.append(menu) 28 | 29 | toggle: (type) -> 30 | for menu in @menus.find('li') 31 | if $(menu).text() == type 32 | $(menu).toggleClass('active') 33 | 34 | addSeparator: -> 35 | @menus.append $$ -> 36 | @li class: 'separator' 37 | 38 | show: -> 39 | if @menus.children().length > 0 40 | super 41 | @hiddenInput.focus() 42 | 43 | attach: -> 44 | atom.views.getView(atom.workspace).appendChild(@element) 45 | -------------------------------------------------------------------------------- /lib/symbols-tree-view.coffee: -------------------------------------------------------------------------------- 1 | {Point, Range} = require 'atom' 2 | {$, jQuery, View} = require 'atom-space-pen-views' 3 | {TreeView} = require './tree-view' 4 | TagGenerator = require './tag-generator' 5 | TagParser = require './tag-parser' 6 | SymbolsContextMenu = require './symbols-context-menu' 7 | 8 | module.exports = 9 | class SymbolsTreeView extends View 10 | @content: -> 11 | @div class: 'symbols-tree-view tool-panel focusable-panel' 12 | 13 | initialize: -> 14 | @treeView = new TreeView 15 | @append(@treeView) 16 | 17 | @cachedStatus = {} 18 | @contextMenu = new SymbolsContextMenu 19 | @autoHideTypes = atom.config.get('symbols-tree-view.zAutoHideTypes') 20 | 21 | @treeView.onSelect ({node, item}) => 22 | if item.position.row >= 0 and editor = atom.workspace.getActiveTextEditor() 23 | screenPosition = editor.screenPositionForBufferPosition(item.position) 24 | screenRange = new Range(screenPosition, screenPosition) 25 | {top, left, height, width} = editor.element.pixelRectForScreenRange(screenRange) 26 | bottom = top + height 27 | desiredScrollCenter = top + height / 2 28 | unless editor.element.getScrollTop() < desiredScrollCenter < editor.element.getScrollBottom() 29 | desiredScrollTop = desiredScrollCenter - editor.element.getHeight() / 2 30 | 31 | from = {top: editor.element.getScrollTop()} 32 | to = {top: desiredScrollTop} 33 | 34 | step = (now) -> 35 | editor.element.setScrollTop(now) 36 | 37 | done = -> 38 | editor.scrollToBufferPosition(item.position, center: true) 39 | editor.setCursorBufferPosition(item.position) 40 | editor.moveToFirstCharacterOfLine() 41 | 42 | jQuery(from).animate(to, duration: @animationDuration, step: step, done: done) 43 | 44 | atom.config.observe 'symbols-tree-view.scrollAnimation', (enabled) => 45 | @animationDuration = if enabled then 300 else 0 46 | 47 | @minimalWidth = 5 48 | @originalWidth = atom.config.get('symbols-tree-view.defaultWidth') 49 | atom.config.observe 'symbols-tree-view.autoHide', (autoHide) => 50 | unless autoHide 51 | @width(@originalWidth) 52 | else 53 | @width(@minimalWidth) 54 | 55 | getEditor: -> atom.workspace.getActiveTextEditor() 56 | getScopeName: -> atom.workspace.getActiveTextEditor()?.getGrammar()?.scopeName 57 | 58 | populate: -> 59 | unless editor = @getEditor() 60 | @hide() 61 | else 62 | filePath = editor.getPath() 63 | @generateTags(filePath) 64 | @show() 65 | 66 | @onEditorSave = editor.onDidSave (state) => 67 | filePath = editor.getPath() 68 | @generateTags(filePath) 69 | 70 | @onChangeRow = editor.onDidChangeCursorPosition ({oldBufferPosition, newBufferPosition}) => 71 | if oldBufferPosition.row != newBufferPosition.row 72 | @focusCurrentCursorTag() 73 | 74 | focusCurrentCursorTag: -> 75 | if (editor = @getEditor()) and @parser? 76 | row = editor.getCursorBufferPosition().row 77 | tag = @parser.getNearestTag(row) 78 | @treeView.select(tag) 79 | 80 | focusClickedTag: (editor, text) -> 81 | console.log "clicked: #{text}" 82 | if editor = @getEditor() 83 | tag = (t for t in @parser.tags when t.name is text)[0] 84 | @treeView.select(tag) 85 | # imho, its a bad idea =( 86 | jQuery('.list-item.list-selectable-item.selected').click() 87 | 88 | updateContextMenu: (types) -> 89 | @contextMenu.clear() 90 | editor = @getEditor()?.id 91 | 92 | toggleTypeVisible = (type) => 93 | @treeView.toggleTypeVisible(type) 94 | @nowTypeStatus[type] = !@nowTypeStatus[type] 95 | 96 | toggleSortByName = => 97 | @nowSortStatus[0] = !@nowSortStatus[0] 98 | if @nowSortStatus[0] 99 | @treeView.sortByName() 100 | else 101 | @treeView.sortByRow() 102 | for type, visible of @nowTypeStatus 103 | @treeView.toggleTypeVisible(type) unless visible 104 | @focusCurrentCursorTag() 105 | 106 | if @cachedStatus[editor] 107 | {@nowTypeStatus, @nowSortStatus} = @cachedStatus[editor] 108 | for type, visible of @nowTypeStatus 109 | @treeView.toggleTypeVisible(type) unless visible 110 | @treeView.sortByName() if @nowSortStatus[0] 111 | else 112 | @cachedStatus[editor] = {nowTypeStatus: {}, nowSortStatus: [false]} 113 | @cachedStatus[editor].nowTypeStatus[type] = true for type in types 114 | @sortByNameScopes = atom.config.get('symbols-tree-view.sortByNameScopes') 115 | if @sortByNameScopes.indexOf(@getScopeName()) != -1 116 | @cachedStatus[editor].nowSortStatus[0] = true 117 | @treeView.sortByName() 118 | {@nowTypeStatus, @nowSortStatus} = @cachedStatus[editor] 119 | 120 | @contextMenu.addMenu(type, @nowTypeStatus[type], toggleTypeVisible) for type in types 121 | @contextMenu.addSeparator() 122 | @contextMenu.addMenu('sort by name', @nowSortStatus[0], toggleSortByName) 123 | 124 | generateTags: (filePath) -> 125 | new TagGenerator(filePath, @getScopeName()).generate().done (tags) => 126 | @parser = new TagParser(tags, @getScopeName()) 127 | {root, types} = @parser.parse() 128 | @treeView.setRoot(root) 129 | @updateContextMenu(types) 130 | @focusCurrentCursorTag() 131 | 132 | if (@autoHideTypes) 133 | for type in types 134 | if(@autoHideTypes.indexOf(type) != -1) 135 | @treeView.toggleTypeVisible(type) 136 | @contextMenu.toggle(type) 137 | 138 | 139 | # Returns an object that can be retrieved when package is activated 140 | serialize: -> 141 | 142 | # Tear down any state and detach 143 | destroy: -> 144 | @element.remove() 145 | 146 | attach: -> 147 | if atom.config.get('tree-view.showOnRightSide') 148 | @panel = atom.workspace.addLeftPanel(item: this) 149 | else 150 | @panel = atom.workspace.addRightPanel(item: this) 151 | @contextMenu.attach() 152 | @contextMenu.hide() 153 | 154 | attached: -> 155 | @onChangeEditor = atom.workspace.onDidChangeActivePaneItem (editor) => 156 | @removeEventForEditor() 157 | @populate() 158 | 159 | @onChangeAutoHide = atom.config.observe 'symbols-tree-view.autoHide', (autoHide) => 160 | unless autoHide 161 | @off('mouseenter mouseleave') 162 | else 163 | @mouseenter (event) => 164 | @stop() 165 | @animate({width: @originalWidth}, duration: @animationDuration) 166 | 167 | @mouseleave (event) => 168 | @stop() 169 | if atom.config.get('tree-view.showOnRightSide') 170 | @animate({width: @minimalWidth}, duration: @animationDuration) if event.offsetX > 0 171 | else 172 | @animate({width: @minimalWidth}, duration: @animationDuration) if event.offsetX <= 0 173 | 174 | @on "contextmenu", (event) => 175 | left = event.pageX 176 | if left + @contextMenu.width() > atom.getSize().width 177 | left = left - @contextMenu.width() 178 | @contextMenu.css({left: left, top: event.pageY}) 179 | @contextMenu.show() 180 | return false #disable original atom context menu 181 | 182 | removeEventForEditor: -> 183 | @onEditorSave?.dispose() 184 | @onChangeRow?.dispose() 185 | 186 | detached: -> 187 | @onChangeEditor?.dispose() 188 | @onChangeAutoHide?.dispose() 189 | @removeEventForEditor() 190 | @off "contextmenu" 191 | 192 | remove: -> 193 | super 194 | @panel.destroy() 195 | 196 | # Toggle the visibility of this view 197 | toggle: -> 198 | if @hasParent() 199 | @remove() 200 | else 201 | @populate() 202 | @attach() 203 | 204 | # Show view if hidden 205 | showView: -> 206 | if not @hasParent() 207 | @populate() 208 | @attach() 209 | 210 | # Hide view if visisble 211 | hideView: -> 212 | if @hasParent() 213 | @remove() 214 | -------------------------------------------------------------------------------- /lib/tag-generator.coffee: -------------------------------------------------------------------------------- 1 | {BufferedProcess, Point} = require 'atom' 2 | Q = require 'q' 3 | path = require 'path' 4 | 5 | module.exports = 6 | class TagGenerator 7 | constructor: (@path, @scopeName) -> 8 | 9 | parseTagLine: (line) -> 10 | sections = line.split('\t') 11 | if sections.length > 3 12 | tag = { 13 | position: new Point(parseInt(sections[2]) - 1) 14 | name: sections[0] 15 | type: sections[3] 16 | parent: null 17 | } 18 | if sections.length > 4 and sections[4].search('signature:') == -1 19 | tag.parent = sections[4] 20 | if @getLanguage() == 'Python' and tag.type == 'member' 21 | tag.type = 'function' 22 | return tag 23 | else 24 | return null 25 | 26 | getLanguage: -> 27 | return 'Cson' if path.extname(@path) in ['.cson', '.gyp'] 28 | 29 | { 30 | 'source.c' : 'C' 31 | 'source.cpp' : 'C++' 32 | 'source.clojure' : 'Lisp' 33 | 'source.coffee' : 'CoffeeScript' 34 | 'source.css' : 'Css' 35 | 'source.css.less' : 'Css' 36 | 'source.css.scss' : 'Css' 37 | 'source.gfm' : 'Markdown' 38 | 'source.go' : 'Go' 39 | 'source.java' : 'Java' 40 | 'source.js' : 'JavaScript' 41 | 'source.js.jsx' : 'JavaScript' 42 | 'source.jsx' : 'JavaScript' 43 | 'source.json' : 'Json' 44 | 'source.makefile' : 'Make' 45 | 'source.objc' : 'C' 46 | 'source.objcpp' : 'C++' 47 | 'source.python' : 'Python' 48 | 'source.ruby' : 'Ruby' 49 | 'source.sass' : 'Sass' 50 | 'source.yaml' : 'Yaml' 51 | 'text.html' : 'Html' 52 | 'text.html.php' : 'Php' 53 | 'source.livecodescript' : 'LiveCode' 54 | 'source.scilab' : 'Scilab' # Scilab 55 | 'source.matlab' : 'Scilab' # Matlab 56 | 'source.octave' : 'Scilab' # GNU Octave 57 | 58 | # For backward-compatibility with Atom versions < 0.166 59 | 'source.c++' : 'C++' 60 | 'source.objc++' : 'C++' 61 | }[@scopeName] 62 | 63 | generate: -> 64 | deferred = Q.defer() 65 | tags = [] 66 | command = path.resolve(__dirname, '..', 'vendor', "universal-ctags-#{process.platform}") 67 | defaultCtagsFile = require.resolve('./.ctags') 68 | args = ["--options=#{defaultCtagsFile}", '--fields=KsS'] 69 | 70 | if atom.config.get('symbols-view.useEditorGrammarAsCtagsLanguage') 71 | if language = @getLanguage() 72 | args.push("--language-force=#{language}") 73 | 74 | args.push('-nf', '-', @path) 75 | 76 | stdout = (lines) => 77 | for line in lines.split('\n') 78 | if tag = @parseTagLine(line.trim()) 79 | tags.push(tag) 80 | stderr = (lines) -> 81 | exit = -> 82 | deferred.resolve(tags) 83 | 84 | new BufferedProcess({command, args, stdout, stderr, exit}) 85 | 86 | deferred.promise 87 | -------------------------------------------------------------------------------- /lib/tag-parser.coffee: -------------------------------------------------------------------------------- 1 | {Point} = require 'atom' 2 | 3 | module.exports = 4 | class TagParser 5 | constructor: (tags, grammar) -> 6 | @tags = tags 7 | @grammar = grammar 8 | 9 | #splitSymbol = '::' for c/c++, and '.' for others. 10 | if @grammar == 'source.c++' or @grammar == 'source.c' or 11 | @grammar == 'source.cpp' 12 | @splitSymbol = '::' 13 | else 14 | @splitSymbol = '.' 15 | 16 | splitParentTag: (parentTag) -> 17 | index = parentTag.indexOf(':') 18 | 19 | type: parentTag.substr(0, index) 20 | parent: parentTag.substr(index+1) 21 | 22 | splitNameTag: (nameTag) -> 23 | index = nameTag.lastIndexOf(@splitSymbol) 24 | if index >= 0 25 | return nameTag.substr(index+@splitSymbol.length) 26 | else 27 | return nameTag 28 | 29 | buildMissedParent: (parents) -> 30 | parentTags = Object.keys(parents) 31 | parentTags.sort (a, b) => 32 | {typeA, parent: nameA} = @splitParentTag(a) 33 | {typeB, parent: nameB} = @splitParentTag(b) 34 | 35 | if nameA < nameB 36 | return -1 37 | else if nameA > nameB 38 | return 1 39 | else 40 | return 0 41 | 42 | for now, i in parentTags 43 | {type, parent: name} = @splitParentTag(now) 44 | 45 | if parents[now] is null 46 | parents[now] = { 47 | name: name, 48 | type: type, 49 | position: null, 50 | parent: null 51 | } 52 | 53 | @tags.push(parents[now]) 54 | 55 | if i >= 1 56 | pre = parentTags[i-1] 57 | {type, parent: name} = @splitParentTag(pre) 58 | if now.indexOf(name) >= 0 59 | parents[now].parent = pre 60 | parents[now].name = @splitNameTag(parents[now].name) 61 | 62 | parse: -> 63 | roots = [] 64 | parents = {} 65 | types = {} 66 | 67 | # sort tags by row number 68 | @tags.sort (a, b) => 69 | return a.position.row - b.position.row 70 | 71 | # try to find out all tags with parent information 72 | for tag in @tags 73 | parents[tag.parent] = null if tag.parent 74 | 75 | # try to build up relationships between parent information and the real tag 76 | for tag in @tags 77 | if tag.parent 78 | {type, parent} = @splitParentTag(tag.parent) 79 | key = tag.type + ':' + parent + @splitSymbol + tag.name 80 | else 81 | key = tag.type + ':' + tag.name 82 | parents[key] = tag 83 | 84 | # try to build up the missed parent 85 | @buildMissedParent(parents) 86 | 87 | for tag in @tags 88 | if tag.parent 89 | parent = parents[tag.parent] 90 | unless parent.position 91 | parent.position = new Point(tag.position.row-1) 92 | 93 | @tags.sort (a, b) => 94 | return a.position.row - b.position.row 95 | 96 | for tag in @tags 97 | tag.label = tag.name 98 | tag.icon = "icon-#{tag.type}" 99 | if tag.parent 100 | parent = parents[tag.parent] 101 | parent.children ?= [] 102 | parent.children.push(tag) 103 | else 104 | roots.push(tag) 105 | types[tag.type] = null 106 | 107 | return {root: {label: 'root', icon: null, children: roots}, types: Object.keys(types)} 108 | 109 | getNearestTag: (row) -> 110 | left = 0 111 | right = @tags.length-1 112 | while left <= right 113 | mid = (left + right) // 2 114 | midRow = @tags[mid].position.row 115 | 116 | if row < midRow 117 | right = mid - 1 118 | else 119 | left = mid + 1 120 | 121 | nearest = left - 1 122 | return @tags[nearest] 123 | -------------------------------------------------------------------------------- /lib/tree-view.coffee: -------------------------------------------------------------------------------- 1 | {$, $$, View, ScrollView} = require 'atom-space-pen-views' 2 | {Emitter} = require 'event-kit' 3 | 4 | module.exports = 5 | TreeNode: class TreeNode extends View 6 | @content: ({label, icon, children}) -> 7 | if children 8 | @li class: 'list-nested-item list-selectable-item', => 9 | @div class: 'list-item', => 10 | @span class: "icon #{icon}", label 11 | @ul class: 'list-tree', => 12 | for child in children 13 | @subview 'child', new TreeNode(child) 14 | else 15 | @li class: 'list-item list-selectable-item', => 16 | @span class: "icon #{icon}", label 17 | 18 | initialize: (item) -> 19 | @emitter = new Emitter 20 | @item = item 21 | @item.view = this 22 | 23 | @on 'dblclick', @dblClickItem 24 | @on 'click', @clickItem 25 | 26 | setCollapsed: -> 27 | @toggleClass('collapsed') if @item.children 28 | 29 | setSelected: -> 30 | @addClass('selected') 31 | 32 | onDblClick: (callback) -> 33 | @emitter.on 'on-dbl-click', callback 34 | if @item.children 35 | for child in @item.children 36 | child.view.onDblClick callback 37 | 38 | onSelect: (callback) -> 39 | @emitter.on 'on-select', callback 40 | if @item.children 41 | for child in @item.children 42 | child.view.onSelect callback 43 | 44 | clickItem: (event) => 45 | if @item.children 46 | selected = @hasClass('selected') 47 | @removeClass('selected') 48 | $target = @find('.list-item:first') 49 | left = $target.position().left 50 | right = $target.children('span').position().left 51 | width = right - left 52 | @toggleClass('collapsed') if event.offsetX <= width 53 | @addClass('selected') if selected 54 | return false if event.offsetX <= width 55 | 56 | @emitter.emit 'on-select', {node: this, item: @item} 57 | return false 58 | 59 | dblClickItem: (event) => 60 | @emitter.emit 'on-dbl-click', {node: this, item: @item} 61 | return false 62 | 63 | 64 | TreeView: class TreeView extends ScrollView 65 | @content: -> 66 | @div class: '-tree-view-', => 67 | @ul class: 'list-tree has-collapsable-children', outlet: 'root' 68 | 69 | initialize: -> 70 | super 71 | @emitter = new Emitter 72 | 73 | deactivate: -> 74 | @remove() 75 | 76 | onSelect: (callback) => 77 | @emitter.on 'on-select', callback 78 | 79 | setRoot: (root, ignoreRoot=true) -> 80 | @rootNode = new TreeNode(root) 81 | 82 | @rootNode.onDblClick ({node, item}) => 83 | node.setCollapsed() 84 | @rootNode.onSelect ({node, item}) => 85 | @clearSelect() 86 | node.setSelected() 87 | @emitter.emit 'on-select', {node, item} 88 | 89 | @root.empty() 90 | @root.append $$ -> 91 | @div => 92 | if ignoreRoot 93 | for child in root.children 94 | @subview 'child', child.view 95 | else 96 | @subview 'root', root.view 97 | 98 | traversal: (root, doing) => 99 | doing(root.item) 100 | if root.item.children 101 | for child in root.item.children 102 | @traversal(child.view, doing) 103 | 104 | toggleTypeVisible: (type) => 105 | @traversal @rootNode, (item) => 106 | if item.type == type 107 | item.view.toggle() 108 | 109 | sortByName: (ascending=true) => 110 | @traversal @rootNode, (item) => 111 | item.children?.sort (a, b) => 112 | if ascending 113 | return a.name.localeCompare(b.name) 114 | else 115 | return b.name.localeCompare(a.name) 116 | @setRoot(@rootNode.item) 117 | 118 | sortByRow: (ascending=true) => 119 | @traversal @rootNode, (item) => 120 | item.children?.sort (a, b) => 121 | if ascending 122 | return a.position.row - b.position.row 123 | else 124 | return b.position.row - a.position.row 125 | @setRoot(@rootNode.item) 126 | 127 | clearSelect: -> 128 | $('.list-selectable-item').removeClass('selected') 129 | 130 | select: (item) -> 131 | @clearSelect() 132 | item?.view.setSelected() 133 | -------------------------------------------------------------------------------- /menus/symbols-tree-view.cson: -------------------------------------------------------------------------------- 1 | # See https://atom.io/docs/latest/creating-a-package#menus for more details 2 | 'context-menu': 3 | 'atom-text-editor': [ 4 | { 5 | 'label': 'Toggle Symbols-Tree-View' 6 | 'command': 'symbols-tree-view:toggle' 7 | } 8 | ] 9 | 'menu': [ 10 | { 11 | 'label': 'View' 12 | 'submenu': [ 13 | {'label': 'Toggle Symbols-Tree-View', 'command': 'symbols-tree-view:toggle'} 14 | ] 15 | } 16 | ] 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "symbols-tree-view", 3 | "main": "./lib/main", 4 | "version": "0.14.0", 5 | "description": "A symbols view like taglist", 6 | "repository": "https://github.com/xndcn/symbols-tree-view", 7 | "providedServices": { 8 | "hyperclick.provider": { 9 | "versions": { 10 | "0.0.0": "getProvider" 11 | } 12 | } 13 | }, 14 | "license": "MIT", 15 | "engines": { 16 | "atom": ">0.50.0" 17 | }, 18 | "dependencies": { 19 | "atom-space-pen-views": "^2.0.0", 20 | "event-kit": "latest", 21 | "q": "~1.0.1" 22 | }, 23 | "keywords": [ 24 | "taglist", 25 | "tagbar", 26 | "symbols" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /screencast.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xndcn/symbols-tree-view/9ae8d173351554450077dcabd5f32c0766f8b9e6/screencast.gif -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xndcn/symbols-tree-view/9ae8d173351554450077dcabd5f32c0766f8b9e6/screenshot.png -------------------------------------------------------------------------------- /styles/symbols-tree-view.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 | @font-face { 8 | font-family: 'symbol-icons'; 9 | src: url('atom://symbols-tree-view/icons/symbol-icons.woff') format('woff'); 10 | font-weight: normal; 11 | font-style: normal; 12 | } 13 | 14 | .symbols-tree-view { 15 | width: 200px; 16 | height: 100%; 17 | padding: 4px; 18 | position: relative; 19 | overflow-y: auto; 20 | overflow-x: hidden; 21 | } 22 | 23 | .symbols-context-menu { 24 | position: absolute; 25 | z-index: 1; 26 | width: 120px; 27 | .hidden-input { 28 | position: absolute; 29 | width: 0; 30 | height: 0; 31 | border: none; 32 | } 33 | .select-list.popover-list { 34 | width: 100%; 35 | min-width: 100%; 36 | ol { 37 | margin-top: 0; 38 | .separator { 39 | background: @background-color-highlight; 40 | height: 1px; 41 | } 42 | li:hover { 43 | background: @background-color-highlight; 44 | } 45 | } 46 | } 47 | } 48 | 49 | @symbol-class: '\e600'; 50 | @symbol-struct: '\e601'; 51 | @symbol-macro: '\e602'; 52 | @symbol-typedef: '\e603'; 53 | @symbol-union: '\e604'; 54 | @symbol-interface:'\e605'; 55 | @symbol-enum: '\e606'; 56 | @symbol-variable: '\e607'; 57 | @symbol-function: '\e608'; 58 | @symbol-namespace:'\e609'; 59 | 60 | .symbol-icon(@name) { 61 | font-family: 'symbol-icons'; 62 | content: @@name; 63 | } 64 | 65 | .symbols-tree-view .icon-function::before { 66 | .symbol-icon(symbol-function); 67 | } 68 | 69 | .symbols-tree-view .icon-class::before { 70 | .symbol-icon(symbol-class); 71 | } 72 | 73 | .symbols-tree-view .icon-namespace::before { 74 | .symbol-icon(symbol-namespace); 75 | } 76 | 77 | .symbols-tree-view .icon-struct::before { 78 | .symbol-icon(symbol-struct); 79 | } 80 | 81 | .symbols-tree-view .icon-variable::before { 82 | .symbol-icon(symbol-variable); 83 | } 84 | 85 | .symbols-tree-view .icon-method::before { 86 | .symbol-icon(symbol-function); 87 | } 88 | 89 | .symbols-tree-view .icon-field::before { 90 | .symbol-icon(symbol-variable); 91 | } 92 | 93 | .symbols-tree-view .icon-member::before { 94 | .symbol-icon(symbol-variable); 95 | } 96 | 97 | .symbols-tree-view .icon-interface::before { 98 | .symbol-icon(symbol-interface); 99 | } 100 | 101 | .symbols-tree-view .icon-enum::before { 102 | .symbol-icon(symbol-enum); 103 | } 104 | 105 | .symbols-tree-view .icon-typedef::before { 106 | .symbol-icon(symbol-typedef); 107 | } 108 | 109 | .symbols-tree-view .icon-macro::before { 110 | .symbol-icon(symbol-macro); 111 | } 112 | 113 | .symbols-tree-view .icon-union::before { 114 | .symbol-icon(symbol-union); 115 | } 116 | -------------------------------------------------------------------------------- /vendor/universal-ctags-darwin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xndcn/symbols-tree-view/9ae8d173351554450077dcabd5f32c0766f8b9e6/vendor/universal-ctags-darwin -------------------------------------------------------------------------------- /vendor/universal-ctags-linux: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xndcn/symbols-tree-view/9ae8d173351554450077dcabd5f32c0766f8b9e6/vendor/universal-ctags-linux -------------------------------------------------------------------------------- /vendor/universal-ctags-win32.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xndcn/symbols-tree-view/9ae8d173351554450077dcabd5f32c0766f8b9e6/vendor/universal-ctags-win32.exe --------------------------------------------------------------------------------