mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-09-11 19:17:09 +03:00
Bundled symbols-view
This commit is contained in:
parent
763e41e48e
commit
6e78ee9c47
@ -156,7 +156,7 @@
|
||||
"status-bar": "file:packages/status-bar",
|
||||
"styleguide": "file:./packages/styleguide",
|
||||
"superstring": "^2.4.4",
|
||||
"symbols-view": "https://codeload.github.com/atom/symbols-view/legacy.tar.gz/refs/tags/v0.118.4",
|
||||
"symbols-view": "file:./packages/symbols-view",
|
||||
"tabs": "file:packages/tabs",
|
||||
"temp": "0.9.4",
|
||||
"text-buffer": "^13.18.6",
|
||||
@ -224,7 +224,7 @@
|
||||
"spell-check": "0.77.1",
|
||||
"status-bar": "file:./packages/status-bar",
|
||||
"styleguide": "file:./packages/styleguide",
|
||||
"symbols-view": "0.118.4",
|
||||
"symbols-view": "file:./packages/symbols-view",
|
||||
"tabs": "file:./packages/tabs",
|
||||
"timecop": "file:./packages/timecop",
|
||||
"tree-view": "0.229.1",
|
||||
|
1
packages/symbols-view/.eslintignore
Normal file
1
packages/symbols-view/.eslintignore
Normal file
@ -0,0 +1 @@
|
||||
**/fixtures/**/*.js
|
7
packages/symbols-view/.eslintrc.js
Normal file
7
packages/symbols-view/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
parser: 'babel-eslint',
|
||||
extends: 'fbjs',
|
||||
globals: {
|
||||
atom: true
|
||||
}
|
||||
};
|
15
packages/symbols-view/.github/no-response.yml
vendored
Normal file
15
packages/symbols-view/.github/no-response.yml
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
# Configuration for probot-no-response - https://github.com/probot/no-response
|
||||
|
||||
# Number of days of inactivity before an issue is closed for lack of response
|
||||
daysUntilClose: 28
|
||||
|
||||
# Label requiring a response
|
||||
responseRequiredLabel: more-information-needed
|
||||
|
||||
# Comment to post when closing an issue for lack of response. Set to `false` to disable.
|
||||
closeComment: >
|
||||
This issue has been automatically closed because there has been no response
|
||||
to our request for more information from the original author. With only the
|
||||
information that is currently in the issue, we don't have enough information
|
||||
to take action. Please reach out if you have or find the answers we need so
|
||||
that we can investigate further.
|
1
packages/symbols-view/.gitignore
vendored
Normal file
1
packages/symbols-view/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
node_modules
|
16
packages/symbols-view/.pairs
Normal file
16
packages/symbols-view/.pairs
Normal file
@ -0,0 +1,16 @@
|
||||
pairs:
|
||||
ns: Nathan Sobo; nathan
|
||||
cj: Corey Johnson; cj
|
||||
dg: David Graham; dgraham
|
||||
ks: Kevin Sawicki; kevin
|
||||
jc: Jerry Cheung; jerry
|
||||
bl: Brian Lopez; brian
|
||||
jp: Justin Palmer; justin
|
||||
gt: Garen Torikian; garen
|
||||
mc: Matt Colyer; mcolyer
|
||||
bo: Ben Ogle; benogle
|
||||
jr: Jason Rudolph; jasonrudolph
|
||||
jl: Jessica Lord; jlord
|
||||
email:
|
||||
domain: github.com
|
||||
#global: true
|
15
packages/symbols-view/.travis.yml
Normal file
15
packages/symbols-view/.travis.yml
Normal file
@ -0,0 +1,15 @@
|
||||
language: objective-c
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: never
|
||||
on_failure: change
|
||||
|
||||
script: 'curl -s https://raw.githubusercontent.com/atom/ci/master/build-package.sh | sh'
|
||||
|
||||
git:
|
||||
depth: 10
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
1
packages/symbols-view/CONTRIBUTING.md
Normal file
1
packages/symbols-view/CONTRIBUTING.md
Normal file
@ -0,0 +1 @@
|
||||
[See how you can contribute](https://github.com/pulsar-edit/.github/blob/main/CONTRIBUTING.md)
|
20
packages/symbols-view/LICENSE.md
Normal file
20
packages/symbols-view/LICENSE.md
Normal file
@ -0,0 +1,20 @@
|
||||
Copyright (c) 2014 GitHub Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
16
packages/symbols-view/README.md
Normal file
16
packages/symbols-view/README.md
Normal file
@ -0,0 +1,16 @@
|
||||
# Symbols View package
|
||||
|
||||
Display the list of functions/methods in the editor.
|
||||
|
||||
If your project has a `tags`/`.tags`/`TAGS`/`.TAGS` file at the root then following are supported:
|
||||
|
||||
|Command|Description|Keybinding (Linux)|Keybinding (macOS)|Keybinding (Windows)|
|
||||
|-------|-----------|------------------|-----------------|--------------------|
|
||||
|`symbols-view:toggle-file-symbols`|Show all symbols in current file|<kbd>ctrl-r</kbd>|<kbd>cmd-r</kbd>|<kbd>ctrl-r</kbd>|
|
||||
|`symbols-view:toggle-project-symbols`|Show all symbols in the project|<kbd>ctrl-shift-r</kbd>|<kbd>cmd-shift-r</kbd>|<kbd>ctrl-shift-r</kbd>|
|
||||
|`symbols-view:go-to-declaration`|Jump to the symbol under the cursor|<kbd>ctrl-alt-down</kbd>|<kbd>cmd-alt-down</kbd>||
|
||||
|`symbols-view:return-from-declaration`|Return from the jump|<kbd>ctrl-alt-up</kbd>|<kbd>cmd-alt-up</kbd>||
|
||||
|
||||
This package uses [ctags](http://ctags.sourceforge.net).
|
||||
|
||||
![](https://f.cloud.github.com/assets/671378/2241860/30ef0b2e-9ce8-11e3-86e2-2c17c0885fa4.png)
|
29
packages/symbols-view/appveyor.yml
Normal file
29
packages/symbols-view/appveyor.yml
Normal file
@ -0,0 +1,29 @@
|
||||
image: Visual Studio 2015
|
||||
|
||||
version: "{build}"
|
||||
|
||||
platform: x64
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
clone_depth: 10
|
||||
|
||||
skip_tags: true
|
||||
|
||||
environment:
|
||||
APM_TEST_PACKAGES:
|
||||
|
||||
matrix:
|
||||
- ATOM_CHANNEL: stable
|
||||
- ATOM_CHANNEL: beta
|
||||
|
||||
install:
|
||||
- ps: Install-Product node 4
|
||||
|
||||
build_script:
|
||||
- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/atom/ci/master/build-package.ps1'))
|
||||
|
||||
test: off
|
||||
deploy: off
|
18
packages/symbols-view/keymaps/symbols-view.cson
Normal file
18
packages/symbols-view/keymaps/symbols-view.cson
Normal file
@ -0,0 +1,18 @@
|
||||
'.platform-darwin atom-text-editor':
|
||||
'cmd-r': 'symbols-view:toggle-file-symbols'
|
||||
'cmd-alt-down': 'symbols-view:go-to-declaration'
|
||||
'cmd-alt-up': 'symbols-view:return-from-declaration'
|
||||
|
||||
'.platform-win32 atom-text-editor':
|
||||
'ctrl-r': 'symbols-view:toggle-file-symbols'
|
||||
|
||||
'.platform-linux atom-text-editor':
|
||||
'ctrl-r': 'symbols-view:toggle-file-symbols'
|
||||
'ctrl-alt-down': 'symbols-view:go-to-declaration'
|
||||
'ctrl-alt-up': 'symbols-view:return-from-declaration'
|
||||
|
||||
'.platform-darwin':
|
||||
'cmd-shift-r': 'symbols-view:toggle-project-symbols'
|
||||
|
||||
'.platform-win32, .platform-linux':
|
||||
'ctrl-shift-r': 'symbols-view:toggle-project-symbols'
|
198
packages/symbols-view/lib/ctags-config
Normal file
198
packages/symbols-view/lib/ctags-config
Normal file
@ -0,0 +1,198 @@
|
||||
--langdef=CoffeeScript
|
||||
--langmap=CoffeeScript:.coffee
|
||||
--regex-CoffeeScript=/^[ \t]*(@?[a-zA-Z$_\.0-9]+)[ \t]*(=|\:)[ \t]*(\(.*\))?[ \t]*(-|=)>/\1/f,function/
|
||||
--regex-CoffeeScript=/^[ \t]*([a-zA-Z$_0-9]+\:\:[a-zA-Z$_\.0-9]+)[ \t]*(=|\:)[ \t]*(\(.*\))?[ \t]*(-|=)>/\1/f,function/
|
||||
--regex-CoffeeScript=/^[ \t]*describe[ \t]"(.+)"[ \t]*,[ \t]+[-=]>/\1/f,function/
|
||||
--regex-CoffeeScript=/^[ \t]*describe[ \t]'(.+)'[ \t]*,[ \t]+[-=]>/\1/f,function/
|
||||
--regex-CoffeeScript=/^[ \t]*it[ \t]"([^"]+)"[ \t]*,[ \t]+[-=]>/\1/f,function/
|
||||
--regex-CoffeeScript=/^[ \t]*it[ \t]'([^']+)'[ \t]*,[ \t]+[-=]>/\1/f,function/
|
||||
--regex-CoffeeScript=/^[ \t]*f+describe[ \t]"(.+)"[ \t]*,[ \t]+[-=]>/focused\: \1/f,function/
|
||||
--regex-CoffeeScript=/^[ \t]*f+describe[ \t]'(.+)'[ \t]*,[ \t]+[-=]>/focused: \1/f,function/
|
||||
--regex-CoffeeScript=/^[ \t]*f+it[ \t]"([^"]+)"[ \t]*,[ \t]+[-=]>/focused: \1/f,function/
|
||||
--regex-CoffeeScript=/^[ \t]*f+it[ \t]'([^']+)'[ \t]*,[ \t]+[-=]>/focused: \1/f,function/
|
||||
--regex-CoffeeScript=/^[ \t]*xdescribe[ \t]"(.+)"[ \t]*,[ \t]+[-=]>/disabled\: \1/f,function/
|
||||
--regex-CoffeeScript=/^[ \t]*xdescribe[ \t]'(.+)'[ \t]*,[ \t]+[-=]>/disabled: \1/f,function/
|
||||
--regex-CoffeeScript=/^[ \t]*xit[ \t]"([^"]+)"[ \t]*,[ \t]+[-=]>/disabled: \1/f,function/
|
||||
--regex-CoffeeScript=/^[ \t]*xit[ \t]'([^']+)'[ \t]*,[ \t]+[-=]>/disabled: \1/f,function/
|
||||
--regex-CoffeeScript=/^[ \t]*class[ \t]*([a-zA-Z$_\.0-9]+)[ \t]*/\1/f,function/
|
||||
|
||||
--langdef=ColdFusion
|
||||
--langmap=ColdFusion:.cfc
|
||||
--langmap=ColdFusion:+.cfm
|
||||
--langmap=ColdFusion:+.cfml
|
||||
--regex-ColdFusion=/(,|(;|^)[ \t]*(var|([A-Za-z_$][A-Za-z0-9_$.]*\.)*))[ \t]*([A-Za-z0-9_$]+)[ \t]*=[ \t]*function[ \t]*\(/\5/,function/
|
||||
--regex-ColdFusion=/function[ \t]+([A-Za-z0-9_$]+)[ \t]*\([^)]*\)/\1/,function/
|
||||
--regex-ColdFusion=/cffunction[ \t]+([A-Za-z0-9_$]+)[ \t]*\([^)]*\)/\1/,cffunction/
|
||||
--regex-ColdFusion=/(,|^|\*\/)[ \t]*([A-Za-z_$][A-Za-z0-9_$]+)[ \t]*:[ \t]*function[ \t]*\(/\2/,function/
|
||||
--regex-ColdFusion=/(,|^|\*\/)[ \t]*(static[ \t]+)?(while|if|for|function|switch|with|([A-Za-z_$][A-Za-z0-9_$]+))[ \t]*\(.*\)[ \t]*\{/\2\4/,function/
|
||||
--regex-ColdFusion=/(,|^|\*\/)[ \t]*get[ \t]+([A-Za-z_$][A-Za-z0-9_$]+)[ \t]*\([ \t]*\)[ \t]*\{/get \2/,function/
|
||||
--regex-ColdFusion=/(,|^|\*\/)[ \t]*set[ \t]+([A-Za-z_$][A-Za-z0-9_$]+)[ \t]*\([ \t]*([A-Za-z_$][A-Za-z0-9_$]+)?[ \t]*\)[ \t]*\{/set \2/,function/
|
||||
--regex-ColdFusion=/(,|^|\*\/)[ \t]*async[ \t]+([A-Za-z_$][A-Za-z0-9_$]+)[ \t]*\([ \t]*([A-Za-z_$].+)?[ \t]*\)[ \t]*\{/\2/,function/
|
||||
--regex-ColdFusion=/component[ \t]+([A-Za-z0-9._$]+)[ \t]*/\1/c,component/
|
||||
--regex-ColdFusion=/^[ \t]*given[ \t]"(.+)"[ \t]*,[ \t]+[-=]>/\1/f,function/
|
||||
--regex-ColdFusion=/^[ \t]*given[ \t]'(.+)'[ \t]*,[ \t]+[-=]>/\1/f,function/
|
||||
--regex-ColdFusion=/^[ \t]*story[ \t]"(.+)"[ \t]*,[ \t]+[-=]>/\1/f,function/
|
||||
--regex-ColdFusion=/^[ \t]*story[ \t]"(.+)"[ \t]*,[ \t]+[-=]>/\1/f,function/
|
||||
--regex-ColdFusion=/^[ \t]*feature[ \t]'(.+)'[ \t]*,[ \t]+[-=]>/\1/f,function/
|
||||
--regex-ColdFusion=/^[ \t]*feature[ \t]'(.+)'[ \t]*,[ \t]+[-=]>/\1/f,function/
|
||||
--regex-ColdFusion=/^[ \t]*when[ \t]"(.+)"[ \t]*,[ \t]+[-=]>/\1/f,function/
|
||||
--regex-ColdFusion=/^[ \t]*when[ \t]'(.+)'[ \t]*,[ \t]+[-=]>/\1/f,function/
|
||||
--regex-ColdFusion=/^[ \t]*then[ \t]"(.+)"[ \t]*,[ \t]+[-=]>/\1/f,function/
|
||||
--regex-ColdFusion=/^[ \t]*then[ \t]'(.+)'[ \t]*,[ \t]+[-=]>/\1/f,function/
|
||||
--regex-ColdFusion=/^[ \t]*describe[ \t]"(.+)"[ \t]*,[ \t]+[-=]>/\1/f,function/
|
||||
--regex-ColdFusion=/^[ \t]*describe[ \t]'(.+)'[ \t]*,[ \t]+[-=]>/\1/f,function/
|
||||
--regex-ColdFusion=/^[ \t]*it[ \t]"([^"]+)"[ \t]*,[ \t]+[-=]>/\1/f,function/
|
||||
--regex-ColdFusion=/^[ \t]*it[ \t]'([^']+)'[ \t]*,[ \t]+[-=]>/\1/f,function/
|
||||
--regex-ColdFusion=/^[ \t]*xdescribe[ \t]"(.+)"[ \t]*,[ \t]+[-=]>/disabled\: \1/f,function/
|
||||
--regex-ColdFusion=/^[ \t]*xdescribe[ \t]'(.+)'[ \t]*,[ \t]+[-=]>/disabled: \1/f,function/
|
||||
--regex-ColdFusion=/^[ \t]*xit[ \t]"([^"]+)"[ \t]*,[ \t]+[-=]>/disabled: \1/f,function/
|
||||
--regex-ColdFusion=/^[ \t]*xit[ \t]'([^']+)'[ \t]*,[ \t]+[-=]>/disabled: \1/f,function/
|
||||
|
||||
--langdef=Css
|
||||
--langmap=Css:.css
|
||||
--langmap=Css:+.less
|
||||
--langmap=Css:+.scss
|
||||
--regex-Css=/^[ \t]*(.+)[ \t]*\{/\1/f,function/
|
||||
--regex-Css=/^[ \t]*(.+)[ \t]*,[ \t]*$/\1/f,function/
|
||||
--regex-Css=/^[ \t]*[@$]([a-zA-Z$_][-a-zA-Z$_0-9]*)[ \t]*:/\1/f,function/
|
||||
|
||||
--langdef=Sass
|
||||
--langmap=Sass:.sass
|
||||
--regex-Sass=/^[ \t]*([#.]*[a-zA-Z_0-9]+)[ \t]*$/\1/f,function/
|
||||
|
||||
--langdef=Yaml
|
||||
--langmap=Yaml:.yaml
|
||||
--langmap=Yaml:+.yml
|
||||
--regex-Yaml=/^[ \t]*([a-zA-Z_0-9 ]+)[ \t]*\:[ \t]*/\1/f,function/
|
||||
|
||||
--regex-Html=/^[ \t]*<([a-zA-Z]+)[ \t]*.*>/\1/f,function/
|
||||
|
||||
--langdef=Markdown
|
||||
--langmap=Markdown:.md
|
||||
--langmap=Markdown:+.markdown
|
||||
--langmap=Markdown:+.mdown
|
||||
--langmap=Markdown:+.mkd
|
||||
--langmap=Markdown:+.mkdown
|
||||
--langmap=Markdown:+.ron
|
||||
--regex-Markdown=/^#+[ \t]*([^#]+)/\1/f,function/
|
||||
|
||||
--langdef=Json
|
||||
--langmap=Json:.json
|
||||
--regex-Json=/^[ \t]*"([^"]+)"[ \t]*\:/\1/f,function/
|
||||
|
||||
--langdef=Cson
|
||||
--langmap=Cson:.cson
|
||||
--langmap=Cson:+.gyp
|
||||
--regex-Cson=/^[ \t]*'([^']+)'[ \t]*\:/\1/f,function/
|
||||
--regex-Cson=/^[ \t]*"([^"]+)"[ \t]*\:/\1/f,function/
|
||||
--regex-Cson=/^[ \t]*([^'"]+)[ \t]*\:/\1/f,function/
|
||||
|
||||
--langmap=C++:+.mm
|
||||
|
||||
--langmap=Ruby:+(Rakefile)
|
||||
|
||||
--langmap=Php:+.module
|
||||
|
||||
--langdef=Go
|
||||
--langmap=Go:.go
|
||||
--regex-Go=/func([ \t]+\([^)]+\))?[ \t]+([a-zA-Z0-9_]+)/\2/f,func/
|
||||
--regex-Go=/var[ \t]+([a-zA-Z_][a-zA-Z0-9_]*)/\1/v,var/
|
||||
--regex-Go=/type[ \t]+([a-zA-Z_][a-zA-Z0-9_]*)/\1/t,type/
|
||||
|
||||
--langdef=Capnp
|
||||
--langmap=Capnp:.capnp
|
||||
--regex-Capnp=/struct[ \t]+([A-Za-z]+)/\1/s,struct/
|
||||
--regex-Capnp=/enum[ \t]+([A-Za-z]+)/\1/e,enum/
|
||||
--regex-Capnp=/using[ \t]+([A-Za-z]+)[ \t]+=[ \t]+import/\1/u,using/
|
||||
--regex-Capnp=/const[ \t]+([A-Za-z]+)/\1/c,const/
|
||||
|
||||
--langmap=perl:+.pod
|
||||
--regex-perl=/with[ \t]+([^;]+)[ \t]*?;/\1/w,role,roles/
|
||||
--regex-perl=/extends[ \t]+['"]([^'"]+)['"][ \t]*?;/\1/e,extends/
|
||||
--regex-perl=/use[ \t]+base[ \t]+['"]([^'"]+)['"][ \t]*?;/\1/e,extends/
|
||||
--regex-perl=/use[ \t]+parent[ \t]+['"]([^'"]+)['"][ \t]*?;/\1/e,extends/
|
||||
--regex-perl=/Mojo::Base[ \t]+['"]([^'"]+)['"][ \t]*?;/\1/e,extends/
|
||||
--regex-perl=/^[ \t]*?use[ \t]+([^;]+)[ \t]*?;/\1/u,use,uses/
|
||||
--regex-perl=/^[ \t]*?require[ \t]+((\w|\:)+)/\1/r,require,requires/
|
||||
--regex-perl=/^[ \t]*?has[ \t]+['"]?(\w+)['"]?/\1/a,attribute,attributes/
|
||||
--regex-perl=/^[ \t]*?\*(\w+)[ \t]*?=/\1/a,alias,aliases/
|
||||
--regex-perl=/->helper\([ \t]?['"]?(\w+)['"]?/\1/h,helper,helpers/
|
||||
--regex-perl=/^[ \t]*?our[ \t]*?[\$@%](\w+)/\1/o,our,ours/
|
||||
--regex-perl=/^\=head1[ \t]+(.+)/\1/p,pod,Plain Old Documentation/
|
||||
--regex-perl=/^\=head2[ \t]+(.+)/-- \1/p,pod,Plain Old Documentation/
|
||||
--regex-perl=/^\=head[3-5][ \t]+(.+)/---- \1/p,pod,Plain Old Documentation/
|
||||
|
||||
--regex-JavaScript=/(,|(;|^)[ \t]*(var|let|([A-Za-z_$][A-Za-z0-9_$.]*\.)*))[ \t]*([A-Za-z0-9_$]+)[ \t]*=[ \t]*function[ \t]*\(/\5/,function/
|
||||
--regex-JavaScript=/function[ \t]+([A-Za-z0-9_$]+)[ \t]*\([^)]*\)/\1/,function/
|
||||
--regex-JavaScript=/(,|^|\*\/)[ \t]*([A-Za-z_$][A-Za-z0-9_$]+)[ \t]*:[ \t]*function[ \t]*\(/\2/,function/
|
||||
--regex-JavaScript=/(,|^|\*\/)[ \t]*(static[ \t]+)?(while|if|for|function|switch|with|([A-Za-z_$][A-Za-z0-9_$]+))[ \t]*\(.*\)[ \t]*\{/\2\4/,function/
|
||||
--regex-JavaScript=/(,|^|\*\/)[ \t]*get[ \t]+([A-Za-z_$][A-Za-z0-9_$]+)[ \t]*\([ \t]*\)[ \t]*\{/get \2/,function/
|
||||
--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/,function/
|
||||
--regex-JavaScript=/(,|^|\*\/)[ \t]*async[ \t]+([A-Za-z_$][A-Za-z0-9_$]+)[ \t]*\([ \t]*([A-Za-z_$].+)?[ \t]*\)[ \t]*\{/\2/,function/
|
||||
--regex-JavaScript=/class[ \t]+([A-Za-z0-9._$]+)[ \t]*/\1/c,class/
|
||||
--regex-JavaScript=/^[ \t]*describe\("([^"]+)"[ \t]*,/\1/f,function/
|
||||
--regex-JavaScript=/^[ \t]*describe\('([^']+)'[ \t]*,/\1/f,function/
|
||||
--regex-JavaScript=/^[ \t]*it\("([^"]+)"[ \t]*,/\1/f,function/
|
||||
--regex-JavaScript=/^[ \t]*it\('([^']+)'[ \t]*,/\1/f,function/
|
||||
--regex-JavaScript=/^[ \t]*f+describe\('([^']+)'[ \t]*,/focused: \1/f,function/
|
||||
--regex-JavaScript=/^[ \t]*f+describe\("([^"]+)"[ \t]*,/focused: \1/f,function/
|
||||
--regex-JavaScript=/^[ \t]*f+it\('([^']+)'[ \t]*,/focused: \1/f,function/
|
||||
--regex-JavaScript=/^[ \t]*f+it\("([^"]+)"[ \t]*,/focused: \1/f,function/
|
||||
--regex-JavaScript=/^[ \t]*xdescribe\('([^']+)'[ \t]*,/disabled: \1/f,function/
|
||||
--regex-JavaScript=/^[ \t]*xdescribe\("([^"]+)"[ \t]*,/disabled: \1/f,function/
|
||||
--regex-JavaScript=/^[ \t]*xit\('([^']+)'[ \t]*,/disabled: \1/f,function/
|
||||
--regex-JavaScript=/^[ \t]*xit\("([^"]+)"[ \t]*,/disabled: \1/f,function/
|
||||
|
||||
--langdef=haxe
|
||||
--langmap=haxe:.hx
|
||||
--regex-haxe=/^package[ \t]+([A-Za-z0-9_.]+)/\1/p,package/
|
||||
--regex-haxe=/^[ \t]*[(@:macro|private|public|static|override|inline|dynamic)( \t)]*function[ \t]+([A-Za-z0-9_]+)/\1/f,function/
|
||||
--regex-haxe=/^[ \t]*([private|public|static|protected|inline][ \t]*)+var[ \t]+([A-Za-z0-9_]+)/\2/v,variable/
|
||||
--regex-haxe=/^[ \t]*package[ \t]*([A-Za-z0-9_]+)/\1/p,package/
|
||||
--regex-haxe=/^[ \t]*(extern[ \t]*|@:native\([^)]*\)[ \t]*)*class[ \t]+([A-Za-z0-9_]+)[ \t]*[^\{]*/\2/c,class/
|
||||
--regex-haxe=/^[ \t]*(extern[ \t]+)?interface[ \t]+([A-Za-z0-9_]+)/\2/i,interface/
|
||||
--regex-haxe=/^[ \t]*typedef[ \t]+([A-Za-z0-9_]+)/\1/t,typedef/
|
||||
--regex-haxe=/^[ \t]*enum[ \t]+([A-Za-z0-9_]+)/\1/t,typedef/
|
||||
--regex-haxe=/^[ \t]*+([A-Za-z0-9_]+)(;|\([^)]*:[^)]*\))/\1/t,enum_field/
|
||||
|
||||
--langdef=Elixir
|
||||
--langmap=Elixir:.ex.exs
|
||||
--regex-Elixir=/^[ \t]*def(p?)[ \t]+([a-z_][a-zA-Z0-9_?!]*)/\2/f,functions,functions (def ...)/
|
||||
--regex-Elixir=/^[ \t]*defcallback[ \t]+([a-z_][a-zA-Z0-9_?!]*)/\1/c,callbacks,callbacks (defcallback ...)/
|
||||
--regex-Elixir=/^[ \t]*defdelegate[ \t]+([a-z_][a-zA-Z0-9_?!]*)/\1/d,delegates,delegates (defdelegate ...)/
|
||||
--regex-Elixir=/^[ \t]*defexception[ \t]+([A-Z][a-zA-Z0-9_]*\.)*([A-Z][a-zA-Z0-9_?!]*)/\2/e,exceptions,exceptions (defexception ...)/
|
||||
--regex-Elixir=/^[ \t]*defimpl[ \t]+([A-Z][a-zA-Z0-9_]*\.)*([A-Z][a-zA-Z0-9_?!]*)/\2/i,implementations,implementations (defimpl ...)/
|
||||
--regex-Elixir=/^[ \t]*defmacro(p?)[ \t]+([a-z_][a-zA-Z0-9_?!]*)\(/\2/a,macros,macros (defmacro ...)/
|
||||
--regex-Elixir=/^[ \t]*defmacro(p?)[ \t]+([a-zA-Z0-9_?!]+)?[ \t]+([^ \tA-Za-z0-9_]+)[ \t]*[a-zA-Z0-9_!?!]/\3/o,operators,operators (e.g. "defmacro a <<< b")/
|
||||
--regex-Elixir=/^[ \t]*defmodule[ \t]+([A-Z][a-zA-Z0-9_]*\.)*([A-Z][a-zA-Z0-9_?!]*)/\2/m,modules,modules (defmodule ...)/
|
||||
--regex-Elixir=/^[ \t]*defprotocol[ \t]+([A-Z][a-zA-Z0-9_]*\.)*([A-Z][a-zA-Z0-9_?!]*)/\2/p,protocols,protocols (defprotocol...)/
|
||||
--regex-Elixir=/^[ \t]*Record\.defrecord[ \t]+:([a-zA-Z0-9_]+)/\1/r,records,records (defrecord...)/
|
||||
|
||||
--langdef=Nim
|
||||
--langmap=Nim:.nim
|
||||
--regex-Nim=/^[\t\s]*proc\s+([_A-Za-z0-9]+)\**(\[\w+(\:\s+\w+)?\])?\s*\(/\1/f,function/
|
||||
--regex-Nim=/^[\t\s]*iterator\s+([_A-Za-z0-9]+)\**(\[\w+(\:\s+\w+)?\])?\s*\(/\1/i,iterator/
|
||||
--regex-Nim=/^[\t\s]*macro\s+([_A-Za-z0-9]+)\**(\[\w+(\:\s+\w+)?\])?\s*\(/\1/m,macro/
|
||||
--regex-Nim=/^[\t\s]*method\s+([_A-Za-z0-9]+)\**(\[\w+(\:\s+\w+)?\])?\s*\(/\1/h,method/
|
||||
--regex-Nim=/^[\t\s]*template\s+([_A-Za-z0-9]+)\**(\[\w+(\:\s+\w+)?\])?\s*\(/\1/t,generics/
|
||||
--regex-Nim=/^[\t\s]*converter\s+([_A-Za-z0-9]+)\**(\[\w+(\:\s+\w+)?\])?\s*\(/\1/c,converter/
|
||||
|
||||
--langdef=Fountain
|
||||
--langmap=Fountain:.fountain
|
||||
--langmap=Fountain:+.ftn
|
||||
--regex-Fountain=/^(([iI][nN][tT]|[eE][xX][tT]|[^\w][eE][sS][tT]|\.|[iI]\.?\/[eE]\.?)([^\n]+))/\1/f,function/
|
||||
|
||||
--langdef=Julia
|
||||
--langmap=Julia:.jl
|
||||
--regex-Julia=/^[ \t]*(function|macro|abstract|type|typealias|immutable)[ \t]+([^ \t({[]+).*$/\2/f,function/
|
||||
--regex-Julia=/^[ \t]*(([^@#$ \t({[]+)|\(([^@#$ \t({[]+)\)|\((\$)\))[ \t]*(\{.*\})?[ \t]*\([^#]*\)[ \t]*=([^=].*$|$)/\2\3\4/f,function/
|
||||
|
||||
--langdef=Latex
|
||||
--langmap=latex:.tex
|
||||
--regex-latex=/\\label\{([^}]*)\}/\1/l,label/
|
||||
--regex-latex=/\\section\{([^}]*)\}/\1/s,section/
|
||||
--regex-latex=/\\subsection\{([^}]*)\}/\1/t,subsection/
|
||||
--regex-latex=/\\subsubsection\{([^}]*)\}/\1/u,subsubsection/
|
||||
--regex-latex=/\\section\*\{([^}]*)\}/\1/s,section/
|
||||
--regex-latex=/\\subsection\*\{([^}]*)\}/\1/t,subsection/
|
||||
--regex-latex=/\\subsubsection\*\{([^}]*)\}/\1/u,subsubsection/
|
146
packages/symbols-view/lib/file-view.js
Normal file
146
packages/symbols-view/lib/file-view.js
Normal file
@ -0,0 +1,146 @@
|
||||
/** @babel */
|
||||
|
||||
import { CompositeDisposable } from 'atom';
|
||||
import SymbolsView from './symbols-view';
|
||||
import TagGenerator from './tag-generator';
|
||||
import { match } from 'fuzzaldrin';
|
||||
|
||||
export default class FileView extends SymbolsView {
|
||||
constructor(stack) {
|
||||
super(stack);
|
||||
this.cachedTags = {};
|
||||
this.watchedEditors = new WeakSet();
|
||||
|
||||
this.editorsSubscription = atom.workspace.observeTextEditors(editor => {
|
||||
if (this.watchedEditors.has(editor)) return;
|
||||
|
||||
const removeFromCache = () => {
|
||||
delete this.cachedTags[editor.getPath()];
|
||||
};
|
||||
const editorSubscriptions = new CompositeDisposable();
|
||||
editorSubscriptions.add(editor.onDidChangeGrammar(removeFromCache));
|
||||
editorSubscriptions.add(editor.onDidSave(removeFromCache));
|
||||
editorSubscriptions.add(editor.onDidChangePath(removeFromCache));
|
||||
editorSubscriptions.add(editor.getBuffer().onDidReload(removeFromCache));
|
||||
editorSubscriptions.add(editor.getBuffer().onDidDestroy(removeFromCache));
|
||||
editor.onDidDestroy(() => {
|
||||
this.watchedEditors.delete(editor);
|
||||
editorSubscriptions.dispose();
|
||||
});
|
||||
|
||||
this.watchedEditors.add(editor);
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.editorsSubscription.dispose();
|
||||
return super.destroy();
|
||||
}
|
||||
|
||||
elementForItem({position, name}) {
|
||||
// Style matched characters in search results
|
||||
const matches = match(name, this.selectListView.getFilterQuery());
|
||||
|
||||
const li = document.createElement('li');
|
||||
li.classList.add('two-lines');
|
||||
|
||||
const primaryLine = document.createElement('div');
|
||||
primaryLine.classList.add('primary-line');
|
||||
primaryLine.appendChild(SymbolsView.highlightMatches(this, name, matches));
|
||||
li.appendChild(primaryLine);
|
||||
|
||||
const secondaryLine = document.createElement('div');
|
||||
secondaryLine.classList.add('secondary-line');
|
||||
secondaryLine.textContent = `Line ${position.row + 1}`;
|
||||
li.appendChild(secondaryLine);
|
||||
|
||||
return li;
|
||||
}
|
||||
|
||||
didChangeSelection(item) {
|
||||
if (atom.config.get('symbols-view.quickJumpToFileSymbol') && item) {
|
||||
this.openTag(item);
|
||||
}
|
||||
}
|
||||
|
||||
async didCancelSelection() {
|
||||
await this.cancel();
|
||||
const editor = this.getEditor();
|
||||
if (this.initialState && editor) {
|
||||
this.deserializeEditorState(editor, this.initialState);
|
||||
}
|
||||
this.initialState = null;
|
||||
}
|
||||
|
||||
async toggle() {
|
||||
if (this.panel.isVisible()) {
|
||||
await this.cancel();
|
||||
}
|
||||
const filePath = this.getPath();
|
||||
if (filePath) {
|
||||
const editor = this.getEditor();
|
||||
if (atom.config.get('symbols-view.quickJumpToFileSymbol') && editor) {
|
||||
this.initialState = this.serializeEditorState(editor);
|
||||
}
|
||||
this.populate(filePath);
|
||||
this.attach();
|
||||
}
|
||||
}
|
||||
|
||||
serializeEditorState(editor) {
|
||||
const editorElement = atom.views.getView(editor);
|
||||
const scrollTop = editorElement.getScrollTop();
|
||||
|
||||
return {
|
||||
bufferRanges: editor.getSelectedBufferRanges(),
|
||||
scrollTop,
|
||||
};
|
||||
}
|
||||
|
||||
deserializeEditorState(editor, {bufferRanges, scrollTop}) {
|
||||
const editorElement = atom.views.getView(editor);
|
||||
|
||||
editor.setSelectedBufferRanges(bufferRanges);
|
||||
editorElement.setScrollTop(scrollTop);
|
||||
}
|
||||
|
||||
getEditor() {
|
||||
return atom.workspace.getActiveTextEditor();
|
||||
}
|
||||
|
||||
getPath() {
|
||||
if (this.getEditor()) {
|
||||
return this.getEditor().getPath();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getScopeName() {
|
||||
if (this.getEditor() && this.getEditor().getGrammar()) {
|
||||
return this.getEditor().getGrammar().scopeName;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async populate(filePath) {
|
||||
const tags = this.cachedTags[filePath];
|
||||
if (tags) {
|
||||
await this.selectListView.update({items: tags});
|
||||
} else {
|
||||
await this.selectListView.update({
|
||||
items: [],
|
||||
loadingMessage: 'Generating symbols\u2026',
|
||||
});
|
||||
await this.selectListView.update({
|
||||
items: await this.generateTags(filePath),
|
||||
loadingMessage: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async generateTags(filePath) {
|
||||
const generator = new TagGenerator(filePath, this.getScopeName());
|
||||
this.cachedTags[filePath] = await generator.generate();
|
||||
return this.cachedTags[filePath];
|
||||
}
|
||||
}
|
20
packages/symbols-view/lib/get-tags-file.js
Normal file
20
packages/symbols-view/lib/get-tags-file.js
Normal file
@ -0,0 +1,20 @@
|
||||
/** @babel */
|
||||
|
||||
import path from 'path';
|
||||
import fs from 'fs-plus';
|
||||
|
||||
const files = ['tags', 'TAGS', '.tags', '.TAGS', path.join('.git', 'tags'), path.join('.git', 'TAGS')];
|
||||
export default function(directoryPath) {
|
||||
if (!directoryPath) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
const tagsFile = path.join(directoryPath, file);
|
||||
if (fs.isFileSync(tagsFile)) {
|
||||
return tagsFile;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
28
packages/symbols-view/lib/go-back-view.js
Normal file
28
packages/symbols-view/lib/go-back-view.js
Normal file
@ -0,0 +1,28 @@
|
||||
/** @babel */
|
||||
|
||||
import SymbolsView from './symbols-view';
|
||||
|
||||
export default class GoBackView extends SymbolsView {
|
||||
toggle() {
|
||||
const previousTag = this.stack.pop();
|
||||
if (!previousTag) {
|
||||
return;
|
||||
}
|
||||
|
||||
const restorePosition = () => {
|
||||
if (previousTag.position) {
|
||||
this.moveToPosition(previousTag.position, false);
|
||||
}
|
||||
};
|
||||
|
||||
const previousEditor = atom.workspace.getTextEditors().find(e => e.id === previousTag.editorId);
|
||||
|
||||
if (previousEditor) {
|
||||
const pane = atom.workspace.paneForItem(previousEditor);
|
||||
pane.setActiveItem(previousEditor);
|
||||
restorePosition();
|
||||
} else if (previousTag.file) {
|
||||
atom.workspace.open(previousTag.file).then(restorePosition);
|
||||
}
|
||||
}
|
||||
}
|
65
packages/symbols-view/lib/go-to-view.js
Normal file
65
packages/symbols-view/lib/go-to-view.js
Normal file
@ -0,0 +1,65 @@
|
||||
/** @babel */
|
||||
|
||||
import path from 'path';
|
||||
import SymbolsView from './symbols-view';
|
||||
import TagReader from './tag-reader';
|
||||
|
||||
export default class GoToView extends SymbolsView {
|
||||
toggle() {
|
||||
if (this.panel.isVisible()) {
|
||||
this.cancel();
|
||||
} else {
|
||||
this.populate();
|
||||
}
|
||||
}
|
||||
|
||||
detached() {
|
||||
if (this.resolveFindTagPromise) {
|
||||
this.resolveFindTagPromise([]);
|
||||
}
|
||||
}
|
||||
|
||||
findTag(editor) {
|
||||
if (this.resolveFindTagPromise) {
|
||||
this.resolveFindTagPromise([]);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.resolveFindTagPromise = resolve;
|
||||
TagReader.find(editor, (error, matches) => {
|
||||
if (!matches) {
|
||||
matches = [];
|
||||
}
|
||||
if (error) {
|
||||
return reject(error);
|
||||
} else {
|
||||
return resolve(matches);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async populate() {
|
||||
let editor = atom.workspace.getActiveTextEditor();
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.findTag(editor).then(async matches => {
|
||||
let tags = [];
|
||||
for (let match of Array.from(matches)) {
|
||||
let position = this.getTagLine(match);
|
||||
if (!position) { continue; }
|
||||
match.name = path.basename(match.file);
|
||||
tags.push(match);
|
||||
}
|
||||
|
||||
if (tags.length === 1) {
|
||||
this.openTag(tags[0]);
|
||||
} else if (tags.length > 0) {
|
||||
await this.selectListView.update({items: tags});
|
||||
this.attach();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
25
packages/symbols-view/lib/load-tags-handler.js
Normal file
25
packages/symbols-view/lib/load-tags-handler.js
Normal file
@ -0,0 +1,25 @@
|
||||
/** @babel */
|
||||
/* global emit*/
|
||||
|
||||
import async from 'async';
|
||||
import ctags from 'ctags';
|
||||
import getTagsFile from './get-tags-file';
|
||||
|
||||
export default function(directoryPaths) {
|
||||
return async.each(
|
||||
directoryPaths,
|
||||
(directoryPath, done) => {
|
||||
let tagsFilePath = getTagsFile(directoryPath);
|
||||
if (!tagsFilePath) { return done(); }
|
||||
|
||||
let stream = ctags.createReadStream(tagsFilePath);
|
||||
stream.on('data', function(tags) {
|
||||
for (const tag of Array.from(tags)) { tag.directory = directoryPath; }
|
||||
return emit('tags', tags);
|
||||
});
|
||||
stream.on('end', done);
|
||||
return stream.on('error', done);
|
||||
}
|
||||
, this.async()
|
||||
);
|
||||
}
|
93
packages/symbols-view/lib/main.js
Normal file
93
packages/symbols-view/lib/main.js
Normal file
@ -0,0 +1,93 @@
|
||||
/** @babel */
|
||||
|
||||
export default {
|
||||
activate() {
|
||||
this.stack = [];
|
||||
|
||||
this.workspaceSubscription = atom.commands.add('atom-workspace', {
|
||||
'symbols-view:toggle-project-symbols': () => {
|
||||
this.createProjectView().toggle();
|
||||
},
|
||||
});
|
||||
|
||||
this.editorSubscription = atom.commands.add('atom-text-editor', {
|
||||
'symbols-view:toggle-file-symbols': () => {
|
||||
this.createFileView().toggle();
|
||||
},
|
||||
'symbols-view:go-to-declaration': () => {
|
||||
this.createGoToView().toggle();
|
||||
},
|
||||
'symbols-view:return-from-declaration': () => {
|
||||
this.createGoBackView().toggle();
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
deactivate() {
|
||||
if (this.fileView != null) {
|
||||
this.fileView.destroy();
|
||||
this.fileView = null;
|
||||
}
|
||||
|
||||
if (this.projectView != null) {
|
||||
this.projectView.destroy();
|
||||
this.projectView = null;
|
||||
}
|
||||
|
||||
if (this.goToView != null) {
|
||||
this.goToView.destroy();
|
||||
this.goToView = null;
|
||||
}
|
||||
|
||||
if (this.goBackView != null) {
|
||||
this.goBackView.destroy();
|
||||
this.goBackView = null;
|
||||
}
|
||||
|
||||
if (this.workspaceSubscription != null) {
|
||||
this.workspaceSubscription.dispose();
|
||||
this.workspaceSubscription = null;
|
||||
}
|
||||
|
||||
if (this.editorSubscription != null) {
|
||||
this.editorSubscription.dispose();
|
||||
this.editorSubscription = null;
|
||||
}
|
||||
},
|
||||
|
||||
createFileView() {
|
||||
if (this.fileView) {
|
||||
return this.fileView;
|
||||
}
|
||||
const FileView = require('./file-view');
|
||||
this.fileView = new FileView(this.stack);
|
||||
return this.fileView;
|
||||
},
|
||||
|
||||
createProjectView() {
|
||||
if (this.projectView) {
|
||||
return this.projectView;
|
||||
}
|
||||
const ProjectView = require('./project-view');
|
||||
this.projectView = new ProjectView(this.stack);
|
||||
return this.projectView;
|
||||
},
|
||||
|
||||
createGoToView() {
|
||||
if (this.goToView) {
|
||||
return this.goToView;
|
||||
}
|
||||
const GoToView = require('./go-to-view');
|
||||
this.goToView = new GoToView(this.stack);
|
||||
return this.goToView;
|
||||
},
|
||||
|
||||
createGoBackView() {
|
||||
if (this.goBackView) {
|
||||
return this.goBackView;
|
||||
}
|
||||
const GoBackView = require('./go-back-view');
|
||||
this.goBackView = new GoBackView(this.stack);
|
||||
return this.goBackView;
|
||||
},
|
||||
};
|
105
packages/symbols-view/lib/project-view.js
Normal file
105
packages/symbols-view/lib/project-view.js
Normal file
@ -0,0 +1,105 @@
|
||||
/** @babel */
|
||||
|
||||
import { CompositeDisposable, File } from 'atom';
|
||||
import humanize from 'humanize-plus';
|
||||
import SymbolsView from './symbols-view';
|
||||
import TagReader from './tag-reader';
|
||||
import getTagsFile from './get-tags-file';
|
||||
|
||||
export default class ProjectView extends SymbolsView {
|
||||
constructor(stack) {
|
||||
super(stack, 'Project has no tags file or it is empty', 10);
|
||||
this.reloadTags = true;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.stopTask();
|
||||
this.unwatchTagsFiles();
|
||||
return super.destroy();
|
||||
}
|
||||
|
||||
toggle() {
|
||||
if (this.panel.isVisible()) {
|
||||
this.cancel();
|
||||
} else {
|
||||
this.populate();
|
||||
this.attach();
|
||||
}
|
||||
}
|
||||
|
||||
async populate() {
|
||||
if (this.tags) {
|
||||
await this.selectListView.update({items: this.tags});
|
||||
}
|
||||
|
||||
if (this.reloadTags) {
|
||||
this.reloadTags = false;
|
||||
this.startTask();
|
||||
|
||||
if (this.tags) {
|
||||
await this.selectListView.update({
|
||||
loadingMessage: 'Reloading project symbols\u2026',
|
||||
});
|
||||
} else {
|
||||
await this.selectListView.update({
|
||||
loadingMessage: 'Loading project symbols\u2026',
|
||||
loadingBadge: 0,
|
||||
});
|
||||
let tagsRead = 0;
|
||||
this.loadTagsTask.on('tags', tags => {
|
||||
tagsRead += tags.length;
|
||||
this.selectListView.update({loadingBadge: humanize.intComma(tagsRead)});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stopTask() {
|
||||
if (this.loadTagsTask) {
|
||||
this.loadTagsTask.terminate();
|
||||
}
|
||||
}
|
||||
|
||||
startTask() {
|
||||
this.stopTask();
|
||||
|
||||
this.loadTagsTask = TagReader.getAllTags(tags => {
|
||||
this.tags = tags;
|
||||
this.reloadTags = this.tags.length === 0;
|
||||
this.selectListView.update({
|
||||
loadingMessage: null,
|
||||
loadingBadge: null,
|
||||
items: this.tags,
|
||||
});
|
||||
});
|
||||
|
||||
this.watchTagsFiles();
|
||||
}
|
||||
|
||||
watchTagsFiles() {
|
||||
this.unwatchTagsFiles();
|
||||
|
||||
this.tagsFileSubscriptions = new CompositeDisposable();
|
||||
let reloadTags = () => {
|
||||
this.reloadTags = true;
|
||||
this.watchTagsFiles();
|
||||
};
|
||||
|
||||
for (const projectPath of Array.from(atom.project.getPaths())) {
|
||||
const tagsFilePath = getTagsFile(projectPath);
|
||||
if (tagsFilePath) {
|
||||
const tagsFile = new File(tagsFilePath);
|
||||
this.tagsFileSubscriptions.add(tagsFile.onDidChange(reloadTags));
|
||||
this.tagsFileSubscriptions.add(tagsFile.onDidDelete(reloadTags));
|
||||
this.tagsFileSubscriptions.add(tagsFile.onDidRename(reloadTags));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unwatchTagsFiles() {
|
||||
if (this.tagsFileSubscriptions) {
|
||||
this.tagsFileSubscriptions.dispose();
|
||||
}
|
||||
this.tagsFileSubscriptions = null;
|
||||
}
|
||||
}
|
224
packages/symbols-view/lib/symbols-view.js
Normal file
224
packages/symbols-view/lib/symbols-view.js
Normal file
@ -0,0 +1,224 @@
|
||||
/** @babel */
|
||||
|
||||
import path from 'path';
|
||||
import { Point } from 'atom';
|
||||
import SelectListView from 'atom-select-list';
|
||||
import fs from 'fs-plus';
|
||||
import { match } from 'fuzzaldrin';
|
||||
|
||||
export default class SymbolsView {
|
||||
static highlightMatches(context, name, matches, offsetIndex) {
|
||||
if (!offsetIndex) {
|
||||
offsetIndex = 0;
|
||||
}
|
||||
let lastIndex = 0;
|
||||
let matchedChars = []; // Build up a set of matched chars to be more semantic
|
||||
const fragment = document.createDocumentFragment();
|
||||
|
||||
for (let matchIndex of Array.from(matches)) {
|
||||
matchIndex -= offsetIndex;
|
||||
if (matchIndex < 0) {
|
||||
continue; // If marking up the basename, omit name matches
|
||||
}
|
||||
const unmatched = name.substring(lastIndex, matchIndex);
|
||||
if (unmatched) {
|
||||
if (matchedChars.length) {
|
||||
const span = document.createElement('span');
|
||||
span.classList.add('character-match');
|
||||
span.textContent = matchedChars.join('');
|
||||
fragment.appendChild(span);
|
||||
}
|
||||
matchedChars = [];
|
||||
fragment.appendChild(document.createTextNode(unmatched));
|
||||
}
|
||||
matchedChars.push(name[matchIndex]);
|
||||
lastIndex = matchIndex + 1;
|
||||
}
|
||||
|
||||
if (matchedChars.length) {
|
||||
const span = document.createElement('span');
|
||||
span.classList.add('character-match');
|
||||
span.textContent = matchedChars.join('');
|
||||
fragment.appendChild(span);
|
||||
}
|
||||
|
||||
// Remaining characters are plain text
|
||||
fragment.appendChild(document.createTextNode(name.substring(lastIndex)));
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
constructor(stack, emptyMessage = 'No symbols found', maxResults = null) {
|
||||
this.stack = stack;
|
||||
this.selectListView = new SelectListView({
|
||||
maxResults,
|
||||
emptyMessage,
|
||||
items: [],
|
||||
filterKeyForItem: (item) => item.name,
|
||||
elementForItem: this.elementForItem.bind(this),
|
||||
didChangeSelection: this.didChangeSelection.bind(this),
|
||||
didConfirmSelection: this.didConfirmSelection.bind(this),
|
||||
didConfirmEmptySelection: this.didConfirmEmptySelection.bind(this),
|
||||
didCancelSelection: this.didCancelSelection.bind(this),
|
||||
});
|
||||
this.element = this.selectListView.element;
|
||||
this.element.classList.add('symbols-view');
|
||||
this.panel = atom.workspace.addModalPanel({item: this, visible: false});
|
||||
}
|
||||
|
||||
async destroy() {
|
||||
await this.cancel();
|
||||
this.panel.destroy();
|
||||
return this.selectListView.destroy();
|
||||
}
|
||||
|
||||
getFilterKey() {
|
||||
return 'name';
|
||||
}
|
||||
|
||||
elementForItem({position, name, file, directory}) {
|
||||
// Style matched characters in search results
|
||||
const matches = match(name, this.selectListView.getFilterQuery());
|
||||
|
||||
if (atom.project.getPaths().length > 1) {
|
||||
file = path.join(path.basename(directory), file);
|
||||
}
|
||||
|
||||
const li = document.createElement('li');
|
||||
li.classList.add('two-lines');
|
||||
|
||||
const primaryLine = document.createElement('div');
|
||||
primaryLine.classList.add('primary-line');
|
||||
if (position) {
|
||||
primaryLine.textContent = `${name}:${position.row + 1}`;
|
||||
} else {
|
||||
primaryLine.appendChild(SymbolsView.highlightMatches(this, name, matches));
|
||||
}
|
||||
li.appendChild(primaryLine);
|
||||
|
||||
const secondaryLine = document.createElement('div');
|
||||
secondaryLine.classList.add('secondary-line');
|
||||
secondaryLine.textContent = file;
|
||||
li.appendChild(secondaryLine);
|
||||
|
||||
return li;
|
||||
}
|
||||
|
||||
async cancel() {
|
||||
if (!this.isCanceling) {
|
||||
this.isCanceling = true;
|
||||
await this.selectListView.update({items: []});
|
||||
this.panel.hide();
|
||||
if (this.previouslyFocusedElement) {
|
||||
this.previouslyFocusedElement.focus();
|
||||
this.previouslyFocusedElement = null;
|
||||
}
|
||||
this.isCanceling = false;
|
||||
}
|
||||
}
|
||||
|
||||
didCancelSelection() {
|
||||
this.cancel();
|
||||
}
|
||||
|
||||
didConfirmEmptySelection() {
|
||||
this.cancel();
|
||||
}
|
||||
|
||||
async didConfirmSelection(tag) {
|
||||
if (tag.file && !fs.isFileSync(path.join(tag.directory, tag.file))) {
|
||||
await this.selectListView.update({errorMessage: 'Selected file does not exist'});
|
||||
setTimeout(() => {
|
||||
this.selectListView.update({errorMessage: null});
|
||||
}, 2000);
|
||||
} else {
|
||||
await this.cancel();
|
||||
this.openTag(tag);
|
||||
}
|
||||
}
|
||||
|
||||
didChangeSelection(tag) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
openTag(tag) {
|
||||
const editor = atom.workspace.getActiveTextEditor();
|
||||
let previous;
|
||||
if (editor) {
|
||||
previous = {
|
||||
editorId: editor.id,
|
||||
position: editor.getCursorBufferPosition(),
|
||||
file: editor.getURI(),
|
||||
};
|
||||
}
|
||||
|
||||
let {position} = tag;
|
||||
if (!position) { position = this.getTagLine(tag); }
|
||||
if (tag.file) {
|
||||
atom.workspace.open(path.join(tag.directory, tag.file)).then(() => {
|
||||
if (position) {
|
||||
return this.moveToPosition(position);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
} else if (position && previous && !previous.position.isEqual(position)) {
|
||||
this.moveToPosition(position);
|
||||
}
|
||||
|
||||
this.stack.push(previous);
|
||||
}
|
||||
|
||||
moveToPosition(position, beginningOfLine) {
|
||||
const editor = atom.workspace.getActiveTextEditor();
|
||||
if (beginningOfLine == null) {
|
||||
beginningOfLine = true;
|
||||
}
|
||||
if (editor) {
|
||||
editor.setCursorBufferPosition(position, {autoscroll: false});
|
||||
if (beginningOfLine) {
|
||||
editor.moveToFirstCharacterOfLine();
|
||||
}
|
||||
editor.scrollToCursorPosition({center: true});
|
||||
}
|
||||
}
|
||||
|
||||
attach() {
|
||||
this.previouslyFocusedElement = document.activeElement;
|
||||
this.panel.show();
|
||||
this.selectListView.reset();
|
||||
this.selectListView.focus();
|
||||
}
|
||||
|
||||
getTagLine(tag) {
|
||||
if (!tag) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (tag.lineNumber) {
|
||||
return new Point(tag.lineNumber - 1, 0);
|
||||
}
|
||||
|
||||
// Remove leading /^ and trailing $/
|
||||
if (!tag.pattern) {
|
||||
return undefined;
|
||||
}
|
||||
const pattern = tag.pattern.replace(/(^\/\^)|(\$\/$)/g, '').trim();
|
||||
|
||||
if (!pattern) {
|
||||
return undefined;
|
||||
}
|
||||
const file = path.join(tag.directory, tag.file);
|
||||
if (!fs.isFileSync(file)) {
|
||||
return undefined;
|
||||
}
|
||||
const iterable = fs.readFileSync(file, 'utf8').split('\n');
|
||||
for (let index = 0; index < iterable.length; index++) {
|
||||
let line = iterable[index];
|
||||
if (pattern === line.trim()) {
|
||||
return new Point(index, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
128
packages/symbols-view/lib/tag-generator.js
Normal file
128
packages/symbols-view/lib/tag-generator.js
Normal file
@ -0,0 +1,128 @@
|
||||
/** @babel */
|
||||
|
||||
import { BufferedProcess, Point } from 'atom';
|
||||
import path from 'path';
|
||||
import fs from 'fs-plus';
|
||||
|
||||
export default class TagGenerator {
|
||||
constructor(path1, scopeName) {
|
||||
this.path = path1;
|
||||
this.scopeName = scopeName;
|
||||
}
|
||||
|
||||
getPackageRoot() {
|
||||
const {resourcePath} = atom.getLoadSettings();
|
||||
const currentFileWasRequiredFromSnapshot = !fs.isAbsolute(__dirname);
|
||||
const packageRoot = currentFileWasRequiredFromSnapshot
|
||||
? path.join(resourcePath, 'node_modules', 'symbols-view')
|
||||
: path.resolve(__dirname, '..');
|
||||
|
||||
if (path.extname(resourcePath) === '.asar' && packageRoot.indexOf(resourcePath) === 0) {
|
||||
return path.join(`${resourcePath}.unpacked`, 'node_modules', 'symbols-view');
|
||||
} else {
|
||||
return packageRoot;
|
||||
}
|
||||
}
|
||||
|
||||
parseTagLine(line) {
|
||||
let sections = line.split('\t');
|
||||
if (sections.length > 3) {
|
||||
return {
|
||||
position: new Point(parseInt(sections[2], 10) - 1),
|
||||
name: sections[0],
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getLanguage() {
|
||||
if (['.cson', '.gyp'].includes(path.extname(this.path))) {
|
||||
return 'Cson';
|
||||
}
|
||||
|
||||
switch (this.scopeName) {
|
||||
case 'source.c': return 'C';
|
||||
case 'source.cpp': return 'C++';
|
||||
case 'source.clojure': return 'Lisp';
|
||||
case 'source.capnp': return 'Capnp';
|
||||
case 'source.cfscript': return 'ColdFusion';
|
||||
case 'source.cfscript.embedded': return 'ColdFusion';
|
||||
case 'source.coffee': return 'CoffeeScript';
|
||||
case 'source.css': return 'Css';
|
||||
case 'source.css.less': return 'Css';
|
||||
case 'source.css.scss': return 'Css';
|
||||
case 'source.elixir': return 'Elixir';
|
||||
case 'source.fountain': return 'Fountain';
|
||||
case 'source.gfm': return 'Markdown';
|
||||
case 'source.go': return 'Go';
|
||||
case 'source.java': return 'Java';
|
||||
case 'source.js': return 'JavaScript';
|
||||
case 'source.js.jsx': return 'JavaScript';
|
||||
case 'source.jsx': return 'JavaScript';
|
||||
case 'source.json': return 'Json';
|
||||
case 'source.julia': return 'Julia';
|
||||
case 'source.makefile': return 'Make';
|
||||
case 'source.objc': return 'C';
|
||||
case 'source.objcpp': return 'C++';
|
||||
case 'source.python': return 'Python';
|
||||
case 'source.ruby': return 'Ruby';
|
||||
case 'source.sass': return 'Sass';
|
||||
case 'source.yaml': return 'Yaml';
|
||||
case 'text.html': return 'Html';
|
||||
case 'text.html.php': return 'Php';
|
||||
case 'text.tex.latex': return 'Latex';
|
||||
case 'text.html.cfml': return 'ColdFusion';
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
generate() {
|
||||
let tags = {};
|
||||
const packageRoot = this.getPackageRoot();
|
||||
const command = path.join(packageRoot, 'vendor', `ctags-${process.platform}`);
|
||||
const defaultCtagsFile = path.join(packageRoot, 'lib', 'ctags-config');
|
||||
const args = [`--options=${defaultCtagsFile}`, '--fields=+KS'];
|
||||
|
||||
if (atom.config.get('symbols-view.useEditorGrammarAsCtagsLanguage')) {
|
||||
const language = this.getLanguage();
|
||||
if (language) {
|
||||
args.push(`--language-force=${language}`);
|
||||
}
|
||||
}
|
||||
|
||||
args.push('-nf', '-', this.path);
|
||||
|
||||
return new Promise((resolve) => {
|
||||
let result, tag;
|
||||
return new BufferedProcess({
|
||||
command: command,
|
||||
args: args,
|
||||
stdout: (lines) => {
|
||||
return (() => {
|
||||
result = [];
|
||||
for (const line of Array.from(lines.split('\n'))) {
|
||||
let item;
|
||||
if (tag = this.parseTagLine(line)) {
|
||||
item = tags[tag.position.row] ? tags[tag.position.row] : (tags[tag.position.row] = tag);
|
||||
}
|
||||
result.push(item);
|
||||
}
|
||||
return result;
|
||||
})();
|
||||
},
|
||||
stderr() {},
|
||||
exit() {
|
||||
tags = ((() => {
|
||||
result = [];
|
||||
for (const row in tags) {
|
||||
tag = tags[row];
|
||||
result.push(tag);
|
||||
}
|
||||
return result;
|
||||
})());
|
||||
return resolve(tags);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
124
packages/symbols-view/lib/tag-reader.js
Normal file
124
packages/symbols-view/lib/tag-reader.js
Normal file
@ -0,0 +1,124 @@
|
||||
/** @babel */
|
||||
|
||||
import { Task } from 'atom';
|
||||
import ctags from 'ctags';
|
||||
import async from 'async';
|
||||
import getTagsFile from './get-tags-file';
|
||||
import _ from 'underscore-plus';
|
||||
|
||||
let handlerPath = require.resolve('./load-tags-handler');
|
||||
|
||||
let wordAtCursor = (text, cursorIndex, wordSeparator, noStripBefore) => {
|
||||
const beforeCursor = text.slice(0, cursorIndex);
|
||||
const afterCursor = text.slice(cursorIndex);
|
||||
const beforeCursorWordBegins = noStripBefore ? 0 : beforeCursor.lastIndexOf(wordSeparator) + 1;
|
||||
let afterCursorWordEnds = afterCursor.indexOf(wordSeparator);
|
||||
if (afterCursorWordEnds === -1) {
|
||||
afterCursorWordEnds = afterCursor.length;
|
||||
}
|
||||
return beforeCursor.slice(beforeCursorWordBegins) + afterCursor.slice(0, afterCursorWordEnds);
|
||||
};
|
||||
|
||||
export default {
|
||||
find(editor, callback) {
|
||||
let symbol;
|
||||
const symbols = [];
|
||||
|
||||
if (symbol = editor.getSelectedText()) {
|
||||
symbols.push(symbol);
|
||||
}
|
||||
|
||||
if (!symbols.length) {
|
||||
let nonWordCharacters;
|
||||
const cursor = editor.getLastCursor();
|
||||
const cursorPosition = cursor.getBufferPosition();
|
||||
const scope = cursor.getScopeDescriptor();
|
||||
const rubyScopes = scope.getScopesArray().filter(s => /^source\.ruby($|\.)/.test(s));
|
||||
|
||||
const wordRegExp = rubyScopes.length ?
|
||||
(nonWordCharacters = atom.config.get('editor.nonWordCharacters', {scope}),
|
||||
// Allow special handling for fully-qualified ruby constants
|
||||
nonWordCharacters = nonWordCharacters.replace(/:/g, ''),
|
||||
new RegExp(`[^\\s${_.escapeRegExp(nonWordCharacters)}]+([!?]|\\s*=>?)?|[<=>]+`, 'g'))
|
||||
:
|
||||
cursor.wordRegExp();
|
||||
|
||||
const addSymbol = (symbol) => {
|
||||
if (rubyScopes.length) {
|
||||
// Normalize assignment syntax
|
||||
if (/\s+=?$/.test(symbol)) { symbols.push(symbol.replace(/\s+=$/, '=')); }
|
||||
// Strip away assignment & hashrocket syntax
|
||||
symbols.push(symbol.replace(/\s+=>?$/, ''));
|
||||
} else {
|
||||
symbols.push(symbol);
|
||||
}
|
||||
};
|
||||
|
||||
// Can't use `getCurrentWordBufferRange` here because we want to select
|
||||
// the last match of the potential 2 matches under cursor.
|
||||
editor.scanInBufferRange(wordRegExp, cursor.getCurrentLineBufferRange(), ({range, match}) => {
|
||||
if (range.containsPoint(cursorPosition)) {
|
||||
symbol = match[0];
|
||||
if (rubyScopes.length && symbol.indexOf(':') > -1) {
|
||||
const cursorWithinSymbol = cursorPosition.column - range.start.column;
|
||||
// Add fully-qualified ruby constant up until the cursor position
|
||||
addSymbol(wordAtCursor(symbol, cursorWithinSymbol, ':', true));
|
||||
// Additionally, also look up the bare word under cursor
|
||||
addSymbol(wordAtCursor(symbol, cursorWithinSymbol, ':'));
|
||||
} else {
|
||||
addSymbol(symbol);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!symbols.length) {
|
||||
process.nextTick(() => {
|
||||
callback(null, []);
|
||||
});
|
||||
}
|
||||
|
||||
async.map(atom.project.getPaths(), (projectPath, done) => {
|
||||
const tagsFile = getTagsFile(projectPath);
|
||||
let foundTags = [];
|
||||
let foundErr = null;
|
||||
const detectCallback = () => {
|
||||
done(foundErr, foundTags);
|
||||
};
|
||||
if (!tagsFile) {
|
||||
return detectCallback();
|
||||
}
|
||||
// Find the first symbol in the list that matches a tag
|
||||
return async.detectSeries(symbols, (symbol, doneDetect) => {
|
||||
ctags.findTags(tagsFile, symbol, (err, tags) => {
|
||||
if (!tags) {
|
||||
tags = [];
|
||||
}
|
||||
if (err) {
|
||||
foundErr = err;
|
||||
doneDetect(false);
|
||||
} else if (tags.length) {
|
||||
for (const tag of Array.from(tags)) {
|
||||
tag.directory = projectPath;
|
||||
}
|
||||
foundTags = tags;
|
||||
doneDetect(true);
|
||||
} else {
|
||||
doneDetect(false);
|
||||
}
|
||||
});
|
||||
}, detectCallback);
|
||||
}, (err, foundTags) => {
|
||||
callback(err, _.flatten(foundTags));
|
||||
});
|
||||
},
|
||||
|
||||
getAllTags(callback) {
|
||||
const projectTags = [];
|
||||
const task = Task.once(handlerPath, atom.project.getPaths(), () => callback(projectTags));
|
||||
task.on('tags', (tags) => {
|
||||
projectTags.push(...tags);
|
||||
});
|
||||
return task;
|
||||
},
|
||||
};
|
17
packages/symbols-view/menus/symbols-view.cson
Normal file
17
packages/symbols-view/menus/symbols-view.cson
Normal file
@ -0,0 +1,17 @@
|
||||
'menu': [
|
||||
{
|
||||
'label': 'Packages'
|
||||
'submenu': [
|
||||
'label': 'Symbols'
|
||||
'submenu': [
|
||||
{ 'label': 'File Symbols', 'command': 'symbols-view:toggle-file-symbols' }
|
||||
{ 'label': 'Project Symbols', 'command': 'symbols-view:toggle-project-symbols' }
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
'context-menu':
|
||||
'atom-text-editor:not([mini])': [
|
||||
{ 'label': 'Go to Declaration', 'command': 'symbols-view:go-to-declaration' }
|
||||
]
|
1748
packages/symbols-view/package-lock.json
generated
Normal file
1748
packages/symbols-view/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
53
packages/symbols-view/package.json
Normal file
53
packages/symbols-view/package.json
Normal file
@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "symbols-view",
|
||||
"version": "0.118.4",
|
||||
"main": "./lib/main",
|
||||
"description": "Jump to a function/method in the current editor with `cmd-r`.",
|
||||
"license": "MIT",
|
||||
"activationCommands": {
|
||||
"atom-workspace": [
|
||||
"symbols-view:toggle-project-symbols"
|
||||
],
|
||||
"atom-text-editor": [
|
||||
"symbols-view:go-to-declaration",
|
||||
"symbols-view:return-from-declaration",
|
||||
"symbols-view:toggle-file-symbols"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"async": "^0.2.6",
|
||||
"atom-select-list": "^0.7.0",
|
||||
"ctags": "^3.1.0",
|
||||
"fs-plus": "^3.0.0",
|
||||
"fuzzaldrin": "^2.1.0",
|
||||
"humanize-plus": "^1.8.2",
|
||||
"temp": "^0.8.3",
|
||||
"underscore-plus": "^1.6.6"
|
||||
},
|
||||
"configSchema": {
|
||||
"useEditorGrammarAsCtagsLanguage": {
|
||||
"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."
|
||||
},
|
||||
"quickJumpToFileSymbol": {
|
||||
"default": true,
|
||||
"type": "boolean",
|
||||
"description": "Automatically visit selected file-symbols"
|
||||
}
|
||||
},
|
||||
"repository": "https://github.com/pulsar-edit/symbols-view",
|
||||
"engines": {
|
||||
"atom": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-eslint": "^6.1.2",
|
||||
"eslint": "^3.12.2",
|
||||
"eslint-config-fbjs": "^1.1.1",
|
||||
"eslint-plugin-babel": "^3.3.0",
|
||||
"eslint-plugin-flowtype": "^2.29.1",
|
||||
"eslint-plugin-jasmine": "^2.2.0",
|
||||
"eslint-plugin-prefer-object-spread": "^1.1.0",
|
||||
"eslint-plugin-react": "^5.2.2"
|
||||
}
|
||||
}
|
67
packages/symbols-view/spec/async-spec-helpers.js
Normal file
67
packages/symbols-view/spec/async-spec-helpers.js
Normal file
@ -0,0 +1,67 @@
|
||||
/** @babel */
|
||||
|
||||
export function beforeEach(fn) {
|
||||
global.beforeEach(function() {
|
||||
const result = fn();
|
||||
if (result instanceof Promise) {
|
||||
waitsForPromise(() => result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function afterEach(fn) {
|
||||
global.afterEach(function() {
|
||||
const result = fn();
|
||||
if (result instanceof Promise) {
|
||||
waitsForPromise(() => result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
['it', 'fit', 'ffit', 'fffit'].forEach(function(name) {
|
||||
module.exports[name] = function(description, fn) {
|
||||
global[name](description, function() {
|
||||
const result = fn();
|
||||
if (result instanceof Promise) {
|
||||
waitsForPromise(() => result);
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
export async function conditionPromise(condition) {
|
||||
const startTime = Date.now();
|
||||
|
||||
while (true) {
|
||||
await timeoutPromise(100);
|
||||
|
||||
let conditionResult = condition();
|
||||
if (condition instanceof Promise) {
|
||||
conditionResult = await conditionResult;
|
||||
}
|
||||
|
||||
if (conditionResult) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Date.now() - startTime > 5000) {
|
||||
throw new Error('Timed out waiting on condition');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function timeoutPromise(timeout) {
|
||||
return new Promise(function(resolve) {
|
||||
global.setTimeout(resolve, timeout);
|
||||
});
|
||||
}
|
||||
|
||||
function waitsForPromise(fn) {
|
||||
const promise = fn();
|
||||
global.waitsFor('spec promise to resolve', function(done) {
|
||||
promise.then(done, function(error) {
|
||||
jasmine.getEnv().currentSpec.fail(error);
|
||||
done();
|
||||
});
|
||||
});
|
||||
}
|
6
packages/symbols-view/spec/fixtures/c/sample.c
vendored
Normal file
6
packages/symbols-view/spec/fixtures/c/sample.c
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
#define UNUSED(x) (void)(x)
|
||||
|
||||
static void f(int x)
|
||||
{
|
||||
UNUSED(x);
|
||||
}
|
13
packages/symbols-view/spec/fixtures/js/sample.js
vendored
Normal file
13
packages/symbols-view/spec/fixtures/js/sample.js
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
var quicksort = function () {
|
||||
var sort = function(items) {
|
||||
if (items.length <= 1) return items;
|
||||
var pivot = items.shift(), current, left = [], right = [];
|
||||
while(items.length > 0) {
|
||||
current = items.shift();
|
||||
current < pivot ? left.push(current) : right.push(current);
|
||||
}
|
||||
return sort(left).concat(pivot).concat(sort(right));
|
||||
};
|
||||
|
||||
return sort(Array.apply(this, arguments));
|
||||
};
|
3
packages/symbols-view/spec/fixtures/js/tagged-duplicate.js
vendored
Normal file
3
packages/symbols-view/spec/fixtures/js/tagged-duplicate.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
function duplicate() {
|
||||
return false;
|
||||
}
|
11
packages/symbols-view/spec/fixtures/js/tagged.js
vendored
Normal file
11
packages/symbols-view/spec/fixtures/js/tagged.js
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
var thisIsCrazy = true;
|
||||
|
||||
function callMeMaybe() {
|
||||
return "here's my number";
|
||||
}
|
||||
|
||||
var iJustMetYou = callMeMaybe();
|
||||
|
||||
function duplicate() {
|
||||
return true;
|
||||
}
|
33
packages/symbols-view/spec/fixtures/ruby/file1.rb
vendored
Normal file
33
packages/symbols-view/spec/fixtures/ruby/file1.rb
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
module A::Foo
|
||||
B = 'b'
|
||||
|
||||
def bar!
|
||||
|
||||
end
|
||||
|
||||
def bar?
|
||||
|
||||
end
|
||||
|
||||
def baz
|
||||
end
|
||||
|
||||
def baz=(*)
|
||||
end
|
||||
end
|
||||
|
||||
if bar?
|
||||
baz
|
||||
bar!
|
||||
elsif !bar!
|
||||
baz= 1
|
||||
baz = 2
|
||||
Foo = 3
|
||||
{ :baz => 4 }
|
||||
A::Foo::B
|
||||
C::Foo::B
|
||||
D::Foo::E
|
||||
end
|
||||
|
||||
module D::Foo
|
||||
end
|
532
packages/symbols-view/spec/symbols-view-spec.js
Normal file
532
packages/symbols-view/spec/symbols-view-spec.js
Normal file
@ -0,0 +1,532 @@
|
||||
/** @babel */
|
||||
/* eslint-env jasmine */
|
||||
|
||||
import path from 'path';
|
||||
import etch from 'etch';
|
||||
import fs from 'fs-plus';
|
||||
import temp from 'temp';
|
||||
import SymbolsView from '../lib/symbols-view';
|
||||
import TagGenerator from '../lib/tag-generator';
|
||||
|
||||
import {it, fit, ffit, fffit, beforeEach, afterEach, conditionPromise} from './async-spec-helpers';
|
||||
|
||||
describe('SymbolsView', () => {
|
||||
let [symbolsView, activationPromise, editor, directory] = [];
|
||||
|
||||
const getWorkspaceView = () => atom.views.getView(atom.workspace);
|
||||
const getEditorView = () => atom.views.getView(atom.workspace.getActiveTextEditor());
|
||||
|
||||
beforeEach(async () => {
|
||||
jasmine.unspy(global, 'setTimeout');
|
||||
|
||||
atom.project.setPaths([
|
||||
temp.mkdirSync('other-dir-'),
|
||||
temp.mkdirSync('atom-symbols-view-'),
|
||||
]);
|
||||
|
||||
directory = atom.project.getDirectories()[1];
|
||||
fs.copySync(path.join(__dirname, 'fixtures', 'js'), atom.project.getPaths()[1]);
|
||||
|
||||
activationPromise = atom.packages.activatePackage('symbols-view');
|
||||
jasmine.attachToDOM(getWorkspaceView());
|
||||
});
|
||||
|
||||
describe('when tags can be generated for a file', () => {
|
||||
beforeEach(async () => {
|
||||
await atom.workspace.open(directory.resolve('sample.js'));
|
||||
});
|
||||
|
||||
it('initially displays all JavaScript functions with line numbers', async () => {
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:toggle-file-symbols');
|
||||
await activationPromise;
|
||||
symbolsView = atom.workspace.getModalPanels()[0].item;
|
||||
await conditionPromise(() => symbolsView.element.querySelectorAll('li').length > 0);
|
||||
expect(symbolsView.selectListView.refs.loadingMessage).toBeUndefined();
|
||||
expect(document.body.contains(symbolsView.element)).toBe(true);
|
||||
expect(symbolsView.element.querySelectorAll('li').length).toBe(2);
|
||||
expect(symbolsView.element.querySelector('li:first-child .primary-line')).toHaveText('quicksort');
|
||||
expect(symbolsView.element.querySelector('li:first-child .secondary-line')).toHaveText('Line 1');
|
||||
expect(symbolsView.element.querySelector('li:last-child .primary-line')).toHaveText('quicksort.sort');
|
||||
expect(symbolsView.element.querySelector('li:last-child .secondary-line')).toHaveText('Line 2');
|
||||
expect(symbolsView.selectListView.refs.errorMessage).toBeUndefined();
|
||||
});
|
||||
|
||||
it('caches tags until the editor changes', async () => {
|
||||
editor = atom.workspace.getActiveTextEditor();
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:toggle-file-symbols');
|
||||
await activationPromise;
|
||||
symbolsView = atom.workspace.getModalPanels()[0].item;
|
||||
await conditionPromise(() => symbolsView.element.querySelectorAll('li').length > 0);
|
||||
await symbolsView.cancel();
|
||||
|
||||
spyOn(symbolsView, 'generateTags').andCallThrough();
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:toggle-file-symbols');
|
||||
await conditionPromise(() => symbolsView.element.querySelectorAll('li').length > 0);
|
||||
expect(symbolsView.selectListView.refs.loadingMessage).toBeUndefined();
|
||||
expect(symbolsView.element.querySelectorAll('li').length).toBe(2);
|
||||
expect(symbolsView.generateTags).not.toHaveBeenCalled();
|
||||
await symbolsView.cancel();
|
||||
|
||||
await editor.save();
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:toggle-file-symbols');
|
||||
await conditionPromise(() => symbolsView.element.querySelectorAll('li').length > 0);
|
||||
expect(symbolsView.selectListView.refs.loadingMessage).toBeUndefined();
|
||||
expect(symbolsView.element.querySelectorAll('li').length).toBe(2);
|
||||
expect(symbolsView.generateTags).toHaveBeenCalled();
|
||||
editor.destroy();
|
||||
expect(symbolsView.cachedTags).toEqual({});
|
||||
});
|
||||
|
||||
it('displays an error when no tags match text in mini-editor', async () => {
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:toggle-file-symbols');
|
||||
await activationPromise;
|
||||
symbolsView = atom.workspace.getModalPanels()[0].item;
|
||||
await conditionPromise(() => symbolsView.element.querySelectorAll('li').length > 0);
|
||||
|
||||
symbolsView.selectListView.refs.queryEditor.setText('nothing will match this');
|
||||
await conditionPromise(() => symbolsView.selectListView.refs.emptyMessage);
|
||||
expect(document.body.contains(symbolsView.element)).toBe(true);
|
||||
expect(symbolsView.element.querySelectorAll('li').length).toBe(0);
|
||||
expect(symbolsView.selectListView.refs.emptyMessage.textContent.length).toBeGreaterThan(0);
|
||||
|
||||
// Should remove error
|
||||
symbolsView.selectListView.refs.queryEditor.setText('');
|
||||
await conditionPromise(() => symbolsView.element.querySelectorAll('li').length > 0);
|
||||
expect(symbolsView.element.querySelectorAll('li').length).toBe(2);
|
||||
expect(symbolsView.selectListView.refs.emptyMessage).toBeUndefined();
|
||||
});
|
||||
|
||||
it('moves the cursor to the selected function', async () => {
|
||||
expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([0, 0]);
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:toggle-file-symbols');
|
||||
await activationPromise;
|
||||
symbolsView = atom.workspace.getModalPanels()[0].item;
|
||||
await conditionPromise(() => symbolsView.element.querySelectorAll('li').length > 0);
|
||||
|
||||
symbolsView.element.querySelectorAll('li')[1].click();
|
||||
expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([1, 2]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when tags can't be generated for a file", () => {
|
||||
beforeEach(async () => {
|
||||
await atom.workspace.open('sample.txt');
|
||||
});
|
||||
|
||||
it('shows an error message when no matching tags are found', async () => {
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:toggle-file-symbols');
|
||||
await activationPromise;
|
||||
symbolsView = atom.workspace.getModalPanels()[0].item;
|
||||
|
||||
await conditionPromise(() => symbolsView.selectListView.refs.emptyMessage);
|
||||
expect(document.body.contains(symbolsView.element));
|
||||
expect(symbolsView.element.querySelectorAll('li').length).toBe(0);
|
||||
expect(symbolsView.selectListView.refs.emptyMessage).toBeVisible();
|
||||
expect(symbolsView.selectListView.refs.emptyMessage.textContent.length).toBeGreaterThan(0);
|
||||
expect(symbolsView.selectListView.refs.loadingMessage).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
describe('TagGenerator', () => {
|
||||
it('generates tags for all JavaScript functions', async () => {
|
||||
let tags = [];
|
||||
const sampleJsPath = directory.resolve('sample.js');
|
||||
await new TagGenerator(sampleJsPath).generate().then(o => tags = o);
|
||||
expect(tags.length).toBe(2);
|
||||
expect(tags[0].name).toBe('quicksort');
|
||||
expect(tags[0].position.row).toBe(0);
|
||||
expect(tags[1].name).toBe('quicksort.sort');
|
||||
expect(tags[1].position.row).toBe(1);
|
||||
});
|
||||
|
||||
it('generates no tags for text file', async () => {
|
||||
let tags = [];
|
||||
const sampleJsPath = directory.resolve('sample.txt');
|
||||
await new TagGenerator(sampleJsPath).generate().then(o => tags = o);
|
||||
expect(tags.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('go to declaration', () => {
|
||||
it("doesn't move the cursor when no declaration is found", async () => {
|
||||
await atom.workspace.open(directory.resolve('tagged.js'));
|
||||
editor = atom.workspace.getActiveTextEditor();
|
||||
editor.setCursorBufferPosition([0, 2]);
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration');
|
||||
|
||||
await activationPromise;
|
||||
|
||||
expect(editor.getCursorBufferPosition()).toEqual([0, 2]);
|
||||
});
|
||||
|
||||
it('moves the cursor to the declaration when there is a single matching declaration', async () => {
|
||||
await atom.workspace.open(directory.resolve('tagged.js'));
|
||||
editor = atom.workspace.getActiveTextEditor();
|
||||
editor.setCursorBufferPosition([6, 24]);
|
||||
spyOn(SymbolsView.prototype, 'moveToPosition').andCallThrough();
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration');
|
||||
|
||||
await conditionPromise(() => SymbolsView.prototype.moveToPosition.callCount === 1);
|
||||
expect(editor.getCursorBufferPosition()).toEqual([2, 0]);
|
||||
});
|
||||
|
||||
it('correctly moves the cursor to the declaration of a C preprocessor macro', async () => {
|
||||
atom.project.setPaths([temp.mkdirSync('atom-symbols-view-c-')]);
|
||||
fs.copySync(path.join(__dirname, 'fixtures', 'c'), atom.project.getPaths()[0]);
|
||||
|
||||
await atom.packages.activatePackage('language-c');
|
||||
await atom.workspace.open('sample.c');
|
||||
|
||||
editor = atom.workspace.getActiveTextEditor();
|
||||
editor.setCursorBufferPosition([4, 4]);
|
||||
spyOn(SymbolsView.prototype, 'moveToPosition').andCallThrough();
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration');
|
||||
|
||||
await conditionPromise(() => SymbolsView.prototype.moveToPosition.callCount === 1);
|
||||
expect(editor.getCursorBufferPosition()).toEqual([0, 0]);
|
||||
});
|
||||
|
||||
it('displays matches when more than one exists and opens the selected match', async () => {
|
||||
await atom.workspace.open(directory.resolve('tagged.js'));
|
||||
editor = atom.workspace.getActiveTextEditor();
|
||||
editor.setCursorBufferPosition([8, 14]);
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration');
|
||||
symbolsView = atom.workspace.getModalPanels()[0].item;
|
||||
|
||||
await conditionPromise(() => symbolsView.element.querySelectorAll('li').length > 0);
|
||||
expect(symbolsView.element.querySelectorAll('li').length).toBe(2);
|
||||
expect(symbolsView.element).toBeVisible();
|
||||
spyOn(SymbolsView.prototype, 'moveToPosition').andCallThrough();
|
||||
symbolsView.selectListView.confirmSelection();
|
||||
|
||||
await conditionPromise(() => SymbolsView.prototype.moveToPosition.callCount === 1);
|
||||
expect(atom.workspace.getActiveTextEditor().getPath()).toBe(directory.resolve('tagged-duplicate.js'));
|
||||
expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([0, 4]);
|
||||
});
|
||||
|
||||
it('includes ? and ! characters in ruby symbols', async () => {
|
||||
atom.project.setPaths([temp.mkdirSync('atom-symbols-view-ruby-')]);
|
||||
fs.copySync(path.join(__dirname, 'fixtures', 'ruby'), atom.project.getPaths()[0]);
|
||||
|
||||
await atom.packages.activatePackage('language-ruby');
|
||||
await atom.workspace.open('file1.rb');
|
||||
|
||||
spyOn(SymbolsView.prototype, 'moveToPosition').andCallThrough();
|
||||
atom.workspace.getActiveTextEditor().setCursorBufferPosition([18, 4]);
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration');
|
||||
|
||||
await activationPromise;
|
||||
await conditionPromise(() => SymbolsView.prototype.moveToPosition.callCount === 1);
|
||||
expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([7, 2]);
|
||||
SymbolsView.prototype.moveToPosition.reset();
|
||||
atom.workspace.getActiveTextEditor().setCursorBufferPosition([19, 2]);
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration');
|
||||
|
||||
await conditionPromise(() => SymbolsView.prototype.moveToPosition.callCount === 1);
|
||||
expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([11, 2]);
|
||||
SymbolsView.prototype.moveToPosition.reset();
|
||||
atom.workspace.getActiveTextEditor().setCursorBufferPosition([20, 5]);
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration');
|
||||
|
||||
await conditionPromise(() => SymbolsView.prototype.moveToPosition.callCount === 1);
|
||||
expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([3, 2]);
|
||||
SymbolsView.prototype.moveToPosition.reset();
|
||||
atom.workspace.getActiveTextEditor().setCursorBufferPosition([21, 7]);
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration');
|
||||
|
||||
await conditionPromise(() => SymbolsView.prototype.moveToPosition.callCount === 1);
|
||||
expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([3, 2]);
|
||||
});
|
||||
|
||||
it('handles jumping to assignment ruby method definitions', async () => {
|
||||
atom.project.setPaths([temp.mkdirSync('atom-symbols-view-ruby-')]);
|
||||
fs.copySync(path.join(__dirname, 'fixtures', 'ruby'), atom.project.getPaths()[0]);
|
||||
|
||||
await atom.packages.activatePackage('language-ruby');
|
||||
await atom.workspace.open('file1.rb');
|
||||
spyOn(SymbolsView.prototype, 'moveToPosition').andCallThrough();
|
||||
atom.workspace.getActiveTextEditor().setCursorBufferPosition([22, 5]);
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration');
|
||||
|
||||
await conditionPromise(() => SymbolsView.prototype.moveToPosition.callCount === 1);
|
||||
expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([14, 2]);
|
||||
SymbolsView.prototype.moveToPosition.reset();
|
||||
atom.workspace.getActiveTextEditor().setCursorBufferPosition([23, 5]);
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration');
|
||||
|
||||
await conditionPromise(() => SymbolsView.prototype.moveToPosition.callCount === 1);
|
||||
expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([14, 2]);
|
||||
SymbolsView.prototype.moveToPosition.reset();
|
||||
atom.workspace.getActiveTextEditor().setCursorBufferPosition([24, 5]);
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration');
|
||||
|
||||
await conditionPromise(() => SymbolsView.prototype.moveToPosition.callCount === 1);
|
||||
expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([0, 0]);
|
||||
SymbolsView.prototype.moveToPosition.reset();
|
||||
atom.workspace.getActiveTextEditor().setCursorBufferPosition([25, 5]);
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration');
|
||||
|
||||
await conditionPromise(() => SymbolsView.prototype.moveToPosition.callCount === 1);
|
||||
expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([11, 2]);
|
||||
});
|
||||
|
||||
it('handles jumping to fully qualified ruby constant definitions', async () => {
|
||||
atom.project.setPaths([temp.mkdirSync('atom-symbols-view-ruby-')]);
|
||||
fs.copySync(path.join(__dirname, 'fixtures', 'ruby'), atom.project.getPaths()[0]);
|
||||
await atom.packages.activatePackage('language-ruby');
|
||||
await atom.workspace.open('file1.rb');
|
||||
spyOn(SymbolsView.prototype, 'moveToPosition').andCallThrough();
|
||||
atom.workspace.getActiveTextEditor().setCursorBufferPosition([26, 10]);
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration');
|
||||
|
||||
await conditionPromise(() => SymbolsView.prototype.moveToPosition.callCount === 1);
|
||||
expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([1, 2]);
|
||||
SymbolsView.prototype.moveToPosition.reset();
|
||||
atom.workspace.getActiveTextEditor().setCursorBufferPosition([27, 5]);
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration');
|
||||
|
||||
await conditionPromise(() => SymbolsView.prototype.moveToPosition.callCount === 1);
|
||||
expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([0, 0]);
|
||||
SymbolsView.prototype.moveToPosition.reset();
|
||||
atom.workspace.getActiveTextEditor().setCursorBufferPosition([28, 5]);
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration');
|
||||
|
||||
await conditionPromise(() => SymbolsView.prototype.moveToPosition.callCount === 1);
|
||||
expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([31, 0]);
|
||||
});
|
||||
|
||||
describe('return from declaration', () => {
|
||||
it("doesn't do anything when no go-to have been triggered", async () => {
|
||||
await atom.workspace.open(directory.resolve('tagged.js'));
|
||||
editor = atom.workspace.getActiveTextEditor();
|
||||
editor.setCursorBufferPosition([6, 0]);
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:return-from-declaration');
|
||||
|
||||
await activationPromise;
|
||||
expect(editor.getCursorBufferPosition()).toEqual([6, 0]);
|
||||
});
|
||||
|
||||
it('returns to previous row and column', async () => {
|
||||
await atom.workspace.open(directory.resolve('tagged.js'));
|
||||
editor = atom.workspace.getActiveTextEditor();
|
||||
editor.setCursorBufferPosition([6, 24]);
|
||||
spyOn(SymbolsView.prototype, 'moveToPosition').andCallThrough();
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration');
|
||||
|
||||
await activationPromise;
|
||||
await conditionPromise(() => SymbolsView.prototype.moveToPosition.callCount === 1);
|
||||
expect(editor.getCursorBufferPosition()).toEqual([2, 0]);
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:return-from-declaration');
|
||||
|
||||
await conditionPromise(() => SymbolsView.prototype.moveToPosition.callCount === 2);
|
||||
expect(editor.getCursorBufferPosition()).toEqual([6, 24]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the tag is in a file that doesn't exist", () => {
|
||||
it("doesn't display the tag", async () => {
|
||||
fs.removeSync(directory.resolve('tagged-duplicate.js'));
|
||||
await atom.workspace.open(directory.resolve('tagged.js'));
|
||||
|
||||
editor = atom.workspace.getActiveTextEditor();
|
||||
editor.setCursorBufferPosition([8, 14]);
|
||||
spyOn(SymbolsView.prototype, 'moveToPosition').andCallThrough();
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration');
|
||||
|
||||
await conditionPromise(() => SymbolsView.prototype.moveToPosition.callCount === 1);
|
||||
expect(editor.getCursorBufferPosition()).toEqual([8, 0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('project symbols', () => {
|
||||
it('displays all tags', async () => {
|
||||
await atom.workspace.open(directory.resolve('tagged.js'));
|
||||
expect(getWorkspaceView().querySelector('.symbols-view')).toBeNull();
|
||||
atom.commands.dispatch(getWorkspaceView(), 'symbols-view:toggle-project-symbols');
|
||||
|
||||
await activationPromise;
|
||||
symbolsView = atom.workspace.getModalPanels()[0].item;
|
||||
|
||||
await conditionPromise(() => symbolsView.element.querySelectorAll('li').length > 0);
|
||||
const directoryBasename = path.basename(directory.getPath());
|
||||
const taggedFile = path.join(directoryBasename, 'tagged.js');
|
||||
expect(symbolsView.selectListView.refs.loadingMessage).toBeUndefined();
|
||||
expect(document.body.contains(symbolsView.element)).toBe(true);
|
||||
expect(symbolsView.element.querySelectorAll('li').length).toBe(4);
|
||||
expect(symbolsView.element.querySelector('li:first-child .primary-line')).toHaveText('callMeMaybe');
|
||||
expect(symbolsView.element.querySelector('li:first-child .secondary-line')).toHaveText(taggedFile);
|
||||
expect(symbolsView.element.querySelector('li:last-child .primary-line')).toHaveText('thisIsCrazy');
|
||||
expect(symbolsView.element.querySelector('li:last-child .secondary-line')).toHaveText(taggedFile);
|
||||
atom.commands.dispatch(getWorkspaceView(), 'symbols-view:toggle-project-symbols');
|
||||
fs.removeSync(directory.resolve('tags'));
|
||||
|
||||
await conditionPromise(() => symbolsView.reloadTags);
|
||||
atom.commands.dispatch(getWorkspaceView(), 'symbols-view:toggle-project-symbols');
|
||||
|
||||
await conditionPromise(() => symbolsView.selectListView.refs.loadingMessage);
|
||||
await conditionPromise(() => symbolsView.element.querySelectorAll('li').length === 0);
|
||||
});
|
||||
|
||||
describe('when there is only one project', () => {
|
||||
beforeEach(async () => atom.project.setPaths([directory.getPath()]));
|
||||
|
||||
it("does not include the root directory's name when displaying the tag's filename", async () => {
|
||||
await atom.workspace.open(directory.resolve('tagged.js'));
|
||||
expect(getWorkspaceView().querySelector('.symbols-view')).toBeNull();
|
||||
atom.commands.dispatch(getWorkspaceView(), 'symbols-view:toggle-project-symbols');
|
||||
|
||||
await activationPromise;
|
||||
symbolsView = atom.workspace.getModalPanels()[0].item;
|
||||
await conditionPromise(() => symbolsView.element.querySelectorAll('li').length > 0);
|
||||
expect(symbolsView.element.querySelector('li:first-child .primary-line')).toHaveText('callMeMaybe');
|
||||
expect(symbolsView.element.querySelector('li:first-child .secondary-line')).toHaveText('tagged.js');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when selecting a tag', () => {
|
||||
describe("when the file doesn't exist", () => {
|
||||
beforeEach(async () => fs.removeSync(directory.resolve('tagged.js')));
|
||||
|
||||
it("doesn't open the editor", async () => {
|
||||
atom.commands.dispatch(getWorkspaceView(), 'symbols-view:toggle-project-symbols');
|
||||
|
||||
await activationPromise;
|
||||
|
||||
symbolsView = atom.workspace.getModalPanels()[0].item;
|
||||
|
||||
await conditionPromise(() => symbolsView.element.querySelectorAll('li').length > 0);
|
||||
spyOn(atom.workspace, 'open').andCallThrough();
|
||||
symbolsView.element.querySelector('li:first-child').click();
|
||||
await conditionPromise(() => symbolsView.selectListView.refs.errorMessage);
|
||||
expect(atom.workspace.open).not.toHaveBeenCalled();
|
||||
expect(symbolsView.selectListView.refs.errorMessage.textContent.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when useEditorGrammarAsCtagsLanguage is set to true', () => {
|
||||
it("uses the language associated with the editor's grammar", async () => {
|
||||
atom.config.set('symbols-view.useEditorGrammarAsCtagsLanguage', true);
|
||||
|
||||
await atom.packages.activatePackage('language-javascript');
|
||||
await atom.workspace.open('sample.javascript');
|
||||
atom.workspace.getActiveTextEditor().setText('var test = function() {}');
|
||||
await atom.workspace.getActiveTextEditor().save();
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:toggle-file-symbols');
|
||||
await activationPromise;
|
||||
|
||||
symbolsView = atom.workspace.getModalPanels()[0].item;
|
||||
await conditionPromise(() => symbolsView.selectListView.refs.emptyMessage);
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:toggle-file-symbols');
|
||||
|
||||
atom.workspace.getActiveTextEditor().setGrammar(atom.grammars.grammarForScopeName('source.js'));
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:toggle-file-symbols');
|
||||
await conditionPromise(() => symbolsView.element.querySelectorAll('li').length === 1);
|
||||
expect(document.body.contains(symbolsView.element)).toBe(true);
|
||||
expect(symbolsView.element.querySelector('li:first-child .primary-line')).toHaveText('test');
|
||||
expect(symbolsView.element.querySelector('li:first-child .secondary-line')).toHaveText('Line 1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('match highlighting', () => {
|
||||
beforeEach(async () => {
|
||||
await atom.workspace.open(directory.resolve('sample.js'));
|
||||
});
|
||||
|
||||
it('highlights an exact match', async () => {
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:toggle-file-symbols');
|
||||
|
||||
await activationPromise;
|
||||
symbolsView = atom.workspace.getModalPanels()[0].item;
|
||||
await conditionPromise(() => symbolsView.element.querySelectorAll('li').length > 0);
|
||||
symbolsView.selectListView.refs.queryEditor.setText('quicksort');
|
||||
await getOrScheduleUpdatePromise();
|
||||
const resultView = symbolsView.element.querySelector('.selected');
|
||||
const matches = resultView.querySelectorAll('.character-match');
|
||||
expect(matches.length).toBe(1);
|
||||
expect(matches[0].textContent).toBe('quicksort');
|
||||
});
|
||||
|
||||
it('highlights a partial match', async () => {
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:toggle-file-symbols');
|
||||
await activationPromise;
|
||||
symbolsView = atom.workspace.getModalPanels()[0].item;
|
||||
await conditionPromise(() => symbolsView.element.querySelectorAll('li').length > 0);
|
||||
symbolsView.selectListView.refs.queryEditor.setText('quick');
|
||||
await getOrScheduleUpdatePromise();
|
||||
const resultView = symbolsView.element.querySelector('.selected');
|
||||
const matches = resultView.querySelectorAll('.character-match');
|
||||
expect(matches.length).toBe(1);
|
||||
expect(matches[0].textContent).toBe('quick');
|
||||
});
|
||||
|
||||
it('highlights multiple matches in the symbol name', async () => {
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:toggle-file-symbols');
|
||||
await activationPromise;
|
||||
symbolsView = atom.workspace.getModalPanels()[0].item;
|
||||
await conditionPromise(() => symbolsView.element.querySelectorAll('li').length > 0);
|
||||
symbolsView.selectListView.refs.queryEditor.setText('quicort');
|
||||
await getOrScheduleUpdatePromise();
|
||||
const resultView = symbolsView.element.querySelector('.selected');
|
||||
const matches = resultView.querySelectorAll('.character-match');
|
||||
expect(matches.length).toBe(2);
|
||||
expect(matches[0].textContent).toBe('quic');
|
||||
expect(matches[1].textContent).toBe('ort');
|
||||
});
|
||||
});
|
||||
|
||||
describe('quickjump to symbol', () => {
|
||||
beforeEach(async () => {
|
||||
await atom.workspace.open(directory.resolve('sample.js'));
|
||||
});
|
||||
|
||||
it('jumps to the selected function', async () => {
|
||||
expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([0, 0]);
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:toggle-file-symbols');
|
||||
await activationPromise;
|
||||
symbolsView = atom.workspace.getModalPanels()[0].item;
|
||||
await conditionPromise(() => symbolsView.element.querySelectorAll('li').length > 0);
|
||||
symbolsView.selectListView.selectNext();
|
||||
expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([1, 2]);
|
||||
});
|
||||
|
||||
it('restores previous editor state on cancel', async () => {
|
||||
const bufferRanges = [{start: {row: 0, column: 0}, end: {row: 0, column: 3}}];
|
||||
atom.workspace.getActiveTextEditor().setSelectedBufferRanges(bufferRanges);
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:toggle-file-symbols');
|
||||
|
||||
await activationPromise;
|
||||
symbolsView = atom.workspace.getModalPanels()[0].item;
|
||||
await conditionPromise(() => symbolsView.element.querySelectorAll('li').length > 0);
|
||||
|
||||
symbolsView.selectListView.selectNext();
|
||||
expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([1, 2]);
|
||||
await symbolsView.cancel();
|
||||
expect(atom.workspace.getActiveTextEditor().getSelectedBufferRanges()).toEqual(bufferRanges);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when quickJumpToSymbol is set to false', async () => {
|
||||
beforeEach(async () => {
|
||||
atom.config.set('symbols-view.quickJumpToFileSymbol', false);
|
||||
await atom.workspace.open(directory.resolve('sample.js'));
|
||||
});
|
||||
|
||||
it("won't jumps to the selected function", async () => {
|
||||
expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([0, 0]);
|
||||
atom.commands.dispatch(getEditorView(), 'symbols-view:toggle-file-symbols');
|
||||
|
||||
await activationPromise;
|
||||
symbolsView = atom.workspace.getModalPanels()[0].item;
|
||||
await conditionPromise(() => symbolsView.element.querySelectorAll('li').length > 0);
|
||||
symbolsView.selectListView.selectNext();
|
||||
expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([0, 0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function getOrScheduleUpdatePromise () {
|
||||
return new Promise((resolve) => etch.getScheduler().updateDocument(resolve))
|
||||
}
|
7
packages/symbols-view/styles/symbols-view.less
Normal file
7
packages/symbols-view/styles/symbols-view.less
Normal file
@ -0,0 +1,7 @@
|
||||
@import "ui-variables";
|
||||
|
||||
// Highlight matched text
|
||||
.symbols-view .list-group .character-match {
|
||||
color: @text-color-highlight;
|
||||
font-weight: bold;
|
||||
}
|
BIN
packages/symbols-view/vendor/ctags-darwin
vendored
Executable file
BIN
packages/symbols-view/vendor/ctags-darwin
vendored
Executable file
Binary file not shown.
BIN
packages/symbols-view/vendor/ctags-linux
vendored
Executable file
BIN
packages/symbols-view/vendor/ctags-linux
vendored
Executable file
Binary file not shown.
BIN
packages/symbols-view/vendor/ctags-win32.exe
vendored
Normal file
BIN
packages/symbols-view/vendor/ctags-win32.exe
vendored
Normal file
Binary file not shown.
@ -9087,9 +9087,8 @@ sylvester@^0.0.12:
|
||||
resolved "https://registry.yarnpkg.com/sylvester/-/sylvester-0.0.12.tgz#5a884415cd2d002c57e7a3aac99462a75ce9fdb4"
|
||||
integrity sha512-SzRP5LQ6Ts2G5NyAa/jg16s8e3R7rfdFjizy1zeoecYWw+nGL+YA1xZvW/+iJmidBGSdLkuvdwTYEyJEb+EiUw==
|
||||
|
||||
"symbols-view@https://codeload.github.com/atom/symbols-view/legacy.tar.gz/refs/tags/v0.118.4":
|
||||
"symbols-view@file:./packages/symbols-view":
|
||||
version "0.118.4"
|
||||
resolved "https://codeload.github.com/atom/symbols-view/legacy.tar.gz/refs/tags/v0.118.4#cd1b515d4a3d720402b85301ea8e4d6c8815aaf2"
|
||||
dependencies:
|
||||
async "^0.2.6"
|
||||
atom-select-list "^0.7.0"
|
||||
|
Loading…
Reference in New Issue
Block a user