doc symbols

This commit is contained in:
Aleksey Kladov 2018-08-11 14:44:12 +03:00
parent 7afd84febc
commit f2291d6a76
11 changed files with 2436 additions and 2413 deletions

View file

@ -1,8 +1,8 @@
// Place your settings in this file to overwrite default and user settings. // Place your settings in this file to overwrite default and user settings.
{ {
"files.exclude": { "files.exclude": {
"out": true, "out": true
"node_modules": true // "node_modules": true
}, },
"search.exclude": { "search.exclude": {
"out": true // set this to false to include "out" folder in search results "out": true // set this to false to include "out" folder in search results

41
code/package-lock.json generated
View file

@ -858,7 +858,7 @@
"requires": { "requires": {
"event-stream": "3.3.4", "event-stream": "3.3.4",
"node.extend": "1.1.6", "node.extend": "1.1.6",
"request": "2.87.0", "request": "2.88.0",
"through2": "2.0.3", "through2": "2.0.3",
"vinyl": "2.2.0" "vinyl": "2.2.0"
}, },
@ -1038,9 +1038,9 @@
"dev": true "dev": true
}, },
"har-validator": { "har-validator": {
"version": "5.0.3", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz",
"integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==",
"dev": true, "dev": true,
"requires": { "requires": {
"ajv": "5.5.2", "ajv": "5.5.2",
@ -1538,9 +1538,9 @@
} }
}, },
"oauth-sign": { "oauth-sign": {
"version": "0.8.2", "version": "0.9.0",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
"integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
"dev": true "dev": true
}, },
"object-assign": { "object-assign": {
@ -1705,6 +1705,12 @@
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
"dev": true "dev": true
}, },
"psl": {
"version": "1.1.29",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz",
"integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==",
"dev": true
},
"punycode": { "punycode": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
@ -1821,9 +1827,9 @@
"dev": true "dev": true
}, },
"request": { "request": {
"version": "2.87.0", "version": "2.88.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
"integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
"dev": true, "dev": true,
"requires": { "requires": {
"aws-sign2": "0.7.0", "aws-sign2": "0.7.0",
@ -1833,17 +1839,17 @@
"extend": "3.0.2", "extend": "3.0.2",
"forever-agent": "0.6.1", "forever-agent": "0.6.1",
"form-data": "2.3.2", "form-data": "2.3.2",
"har-validator": "5.0.3", "har-validator": "5.1.0",
"http-signature": "1.2.0", "http-signature": "1.2.0",
"is-typedarray": "1.0.0", "is-typedarray": "1.0.0",
"isstream": "0.1.2", "isstream": "0.1.2",
"json-stringify-safe": "5.0.1", "json-stringify-safe": "5.0.1",
"mime-types": "2.1.19", "mime-types": "2.1.19",
"oauth-sign": "0.8.2", "oauth-sign": "0.9.0",
"performance-now": "2.1.0", "performance-now": "2.1.0",
"qs": "6.5.2", "qs": "6.5.2",
"safe-buffer": "5.1.2", "safe-buffer": "5.1.2",
"tough-cookie": "2.3.4", "tough-cookie": "2.4.3",
"tunnel-agent": "0.6.0", "tunnel-agent": "0.6.0",
"uuid": "3.3.2" "uuid": "3.3.2"
} }
@ -2069,11 +2075,12 @@
} }
}, },
"tough-cookie": { "tough-cookie": {
"version": "2.3.4", "version": "2.4.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
"integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"psl": "1.1.29",
"punycode": "1.4.1" "punycode": "1.4.1"
} }
}, },
@ -2300,7 +2307,7 @@
"gulp-untar": "0.0.7", "gulp-untar": "0.0.7",
"gulp-vinyl-zip": "2.1.0", "gulp-vinyl-zip": "2.1.0",
"mocha": "4.1.0", "mocha": "4.1.0",
"request": "2.87.0", "request": "2.88.0",
"semver": "5.5.0", "semver": "5.5.0",
"source-map-support": "0.5.8", "source-map-support": "0.5.8",
"url-parse": "1.4.3", "url-parse": "1.4.3",

View file

@ -14,7 +14,7 @@
"postinstall": "node ./node_modules/vscode/bin/install" "postinstall": "node ./node_modules/vscode/bin/install"
}, },
"dependencies": { "dependencies": {
"vscode-languageclient": "^4.3.0" "vscode-languageclient": "^4.4.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^7.0.56", "@types/node": "^7.0.56",

View file

@ -10,7 +10,7 @@ use std::{
}; };
use clap::{App, Arg, SubCommand}; use clap::{App, Arg, SubCommand};
use tools::collect_tests; use tools::collect_tests;
use libeditor::{File, syntax_tree, symbols}; use libeditor::{File, syntax_tree, file_symbols};
type Result<T> = ::std::result::Result<T, failure::Error>; type Result<T> = ::std::result::Result<T, failure::Error>;
@ -51,7 +51,7 @@ fn main() -> Result<()> {
} }
("symbols", _) => { ("symbols", _) => {
let file = file()?; let file = file()?;
for s in symbols(&file) { for s in file_symbols(&file) {
println!("{:?}", s); println!("{:?}", s);
} }
} }

View file

@ -7,7 +7,7 @@ mod line_index;
use libsyntax2::{ use libsyntax2::{
ast::{self, NameOwner}, ast::{self, NameOwner},
SyntaxNodeRef, AstNode, AstNode,
algo::walk, algo::walk,
SyntaxKind::*, SyntaxKind::*,
}; };
@ -100,19 +100,6 @@ pub fn syntax_tree(file: &ast::File) -> String {
::libsyntax2::utils::dump_tree(&file.syntax()) ::libsyntax2::utils::dump_tree(&file.syntax())
} }
pub fn symbols(file: &ast::File) -> Vec<Symbol> {
let syntax = file.syntax();
let res: Vec<Symbol> = walk::preorder(syntax.as_ref())
.filter_map(Declaration::cast)
.filter_map(|decl| {
let name = decl.name()?;
let range = decl.range();
Some(Symbol { name, range })
})
.collect();
res // NLL :-(
}
pub fn runnables(file: &ast::File) -> Vec<Runnable> { pub fn runnables(file: &ast::File) -> Vec<Runnable> {
file file
.functions() .functions()
@ -134,27 +121,3 @@ pub fn runnables(file: &ast::File) -> Vec<Runnable> {
}) })
.collect() .collect()
} }
struct Declaration<'f> (SyntaxNodeRef<'f>);
impl<'f> Declaration<'f> {
fn cast(node: SyntaxNodeRef<'f>) -> Option<Declaration<'f>> {
match node.kind() {
| STRUCT | ENUM | FUNCTION | TRAIT
| CONST_ITEM | STATIC_ITEM | MODULE | NAMED_FIELD
| TYPE_ITEM => Some(Declaration(node)),
_ => None
}
}
fn name(&self) -> Option<String> {
let name = self.0.children()
.find(|child| child.kind() == NAME)?;
Some(name.text())
}
fn range(&self) -> TextRange {
self.0.range()
}
}

View file

@ -5,7 +5,6 @@ authors = ["Aleksey Kladov <aleksey.kladov@gmail.com>"]
[dependencies] [dependencies]
failure = "0.1.2" failure = "0.1.2"
languageserver-types = "0.48.0"
serde_json = "1.0.24" serde_json = "1.0.24"
serde = "1.0.71" serde = "1.0.71"
serde_derive = "1.0.71" serde_derive = "1.0.71"
@ -14,8 +13,13 @@ crossbeam-channel = "0.2.4"
threadpool = "1.7.1" threadpool = "1.7.1"
flexi_logger = "0.9.0" flexi_logger = "0.9.0"
log = "0.4.3" log = "0.4.3"
url = "1.1.0"
url_serde = "0.2.0" url_serde = "0.2.0"
libsyntax2 = { path = "../libsyntax2" }
libeditor = { path = "../libeditor" } libeditor = { path = "../libeditor" }
libanalysis = { path = "../libanalysis" } libanalysis = { path = "../libanalysis" }
[dependencies.languageserver-types]
git = "https://github.com/matklad/languageserver-types"
rev = "70e6bf548b901f01dc249c20378d26dd4996c25f"

View file

@ -23,7 +23,7 @@ pub const SERVER_CAPABILITIES: ServerCapabilities = ServerCapabilities {
implementation_provider: None, implementation_provider: None,
references_provider: None, references_provider: None,
document_highlight_provider: None, document_highlight_provider: None,
document_symbol_provider: None, document_symbol_provider: Some(true),
workspace_symbol_provider: None, workspace_symbol_provider: None,
code_action_provider: None, code_action_provider: None,
code_lens_provider: None, code_lens_provider: None,

View file

@ -1,5 +1,5 @@
use url::Url; use languageserver_types::{Range, Position, Diagnostic, DiagnosticSeverity, Url, DocumentSymbol, SymbolKind};
use languageserver_types::{Range, Position, Diagnostic, DiagnosticSeverity}; use libsyntax2::SyntaxKind;
use libanalysis::World; use libanalysis::World;
use libeditor::{self, LineIndex, LineCol, TextRange, TextUnit}; use libeditor::{self, LineIndex, LineCol, TextRange, TextUnit};
@ -34,6 +34,50 @@ pub fn handle_extend_selection(
Ok(req::ExtendSelectionResult { selections }) Ok(req::ExtendSelectionResult { selections })
} }
pub fn handle_document_symbol(
world: World,
params: req::DocumentSymbolParams,
) -> Result<Option<req::DocumentSymbolResponse>> {
let path = params.text_document.file_path()?;
let file = world.file_syntax(&path)?;
let line_index = world.file_line_index(&path)?;
let mut res: Vec<DocumentSymbol> = Vec::new();
for symbol in libeditor::file_symbols(&file) {
let doc_symbol = DocumentSymbol {
name: symbol.name.clone(),
detail: Some(symbol.name),
kind: to_symbol_kind(symbol.kind),
deprecated: None,
range: to_vs_range(&line_index, symbol.node_range),
selection_range: to_vs_range(&line_index, symbol.name_range),
children: None,
};
if let Some(idx) = symbol.parent {
let children = &mut res[idx].children;
if children.is_none() {
*children = Some(Vec::new());
}
children.as_mut().unwrap().push(doc_symbol);
} else {
res.push(doc_symbol);
}
}
Ok(Some(req::DocumentSymbolResponse::Nested(res)))
}
fn to_symbol_kind(kind: SyntaxKind) -> SymbolKind {
match kind {
SyntaxKind::FUNCTION => SymbolKind::Function,
SyntaxKind::STRUCT => SymbolKind::Struct,
SyntaxKind::ENUM => SymbolKind::Enum,
SyntaxKind::TRAIT => SymbolKind::Interface,
SyntaxKind::MODULE => SymbolKind::Module,
_ => SymbolKind::Variable,
}
}
pub fn publish_diagnostics(world: World, uri: Url) -> Result<req::PublishDiagnosticsParams> { pub fn publish_diagnostics(world: World, uri: Url) -> Result<req::PublishDiagnosticsParams> {
let path = uri.file_path()?; let path = uri.file_path()?;
let file = world.file_syntax(&path)?; let file = world.file_syntax(&path)?;

View file

@ -11,11 +11,11 @@ extern crate crossbeam_channel;
extern crate threadpool; extern crate threadpool;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
extern crate url;
extern crate url_serde; extern crate url_serde;
extern crate flexi_logger; extern crate flexi_logger;
extern crate libeditor; extern crate libeditor;
extern crate libanalysis; extern crate libanalysis;
extern crate libsyntax2;
mod io; mod io;
mod caps; mod caps;
@ -27,12 +27,13 @@ mod util;
use threadpool::ThreadPool; use threadpool::ThreadPool;
use crossbeam_channel::{bounded, Sender, Receiver}; use crossbeam_channel::{bounded, Sender, Receiver};
use flexi_logger::Logger; use flexi_logger::Logger;
use url::Url; use languageserver_types::Url;
use libanalysis::{WorldState, World}; use libanalysis::{WorldState, World};
use ::{ use ::{
io::{Io, RawMsg, RawRequest}, io::{Io, RawMsg, RawRequest},
handlers::{handle_syntax_tree, handle_extend_selection, publish_diagnostics, publish_decorations}, handlers::{handle_syntax_tree, handle_extend_selection, publish_diagnostics, publish_decorations,
handle_document_symbol},
util::{FilePath, FnBox} util::{FilePath, FnBox}
}; };
@ -178,6 +179,9 @@ fn main_loop(
handle_request_on_threadpool::<req::ExtendSelection>( handle_request_on_threadpool::<req::ExtendSelection>(
&mut req, pool, world, &sender, handle_extend_selection &mut req, pool, world, &sender, handle_extend_selection
)?; )?;
handle_request_on_threadpool::<req::DocumentSymbolRequest>(
&mut req, pool, world, &sender, handle_document_symbol
)?;
let mut shutdown = false; let mut shutdown = false;
dispatch::handle_request::<req::Shutdown, _>(&mut req, |(), resp| { dispatch::handle_request::<req::Shutdown, _>(&mut req, |(), resp| {
resp.result(io, ())?; resp.result(io, ())?;

View file

@ -1,11 +1,11 @@
use serde::{ser::Serialize, de::DeserializeOwned}; use serde::{ser::Serialize, de::DeserializeOwned};
use url::Url; use languageserver_types::{TextDocumentIdentifier, Range, Url};
use languageserver_types::{TextDocumentIdentifier, Range};
use url_serde; use url_serde;
pub use languageserver_types::{ pub use languageserver_types::{
request::*, notification::*, request::*, notification::*,
InitializeResult, PublishDiagnosticsParams, InitializeResult, PublishDiagnosticsParams,
DocumentSymbolParams, DocumentSymbolResponse
}; };

View file

@ -1,5 +1,6 @@
use std::path::PathBuf; use std::path::PathBuf;
use languageserver_types::{TextDocumentItem, VersionedTextDocumentIdentifier, TextDocumentIdentifier}; use languageserver_types::{TextDocumentItem, VersionedTextDocumentIdentifier,
TextDocumentIdentifier, Url};
use ::{Result}; use ::{Result};
pub trait FnBox<A, R>: Send { pub trait FnBox<A, R>: Send {
@ -34,7 +35,7 @@ impl FilePath for TextDocumentIdentifier {
} }
} }
impl FilePath for ::url::Url { impl FilePath for Url {
fn file_path(&self) -> Result<PathBuf> { fn file_path(&self) -> Result<PathBuf> {
self.to_file_path() self.to_file_path()
.map_err(|()| format_err!("invalid uri: {}", self)) .map_err(|()| format_err!("invalid uri: {}", self))