Stupid goto definition

This commit is contained in:
Aleksey Kladov 2018-08-13 16:35:17 +03:00
parent 7fc91f41d8
commit 8ae56fa6d0
9 changed files with 109 additions and 14 deletions

View file

@ -18,10 +18,14 @@ use std::{
path::{PathBuf, Path}, path::{PathBuf, Path},
}; };
use libsyntax2::ast; use libsyntax2::{
TextUnit,
ast::{self, AstNode},
algo::{find_leaf_at_offset, ancestors},
};
use libeditor::{LineIndex, FileSymbol}; use libeditor::{LineIndex, FileSymbol};
use self::symbol_index::{FileSymbols}; use self::symbol_index::FileSymbols;
pub use self::symbol_index::Query; pub use self::symbol_index::Query;
pub type Result<T> = ::std::result::Result<T, ::failure::Error>; pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
@ -90,8 +94,7 @@ impl World {
Ok(index.clone()) Ok(index.clone())
} }
pub fn world_symbols<'a>(&'a self, query: Query) -> impl Iterator<Item=(&'a Path, &'a FileSymbol)> + 'a pub fn world_symbols<'a>(&'a self, query: Query) -> impl Iterator<Item=(&'a Path, &'a FileSymbol)> + 'a {
{
self.data.file_map.iter() self.data.file_map.iter()
.flat_map(move |(path, data)| { .flat_map(move |(path, data)| {
let path: &'a Path = path.as_path(); let path: &'a Path = path.as_path();
@ -100,6 +103,31 @@ impl World {
}) })
} }
pub fn approximately_resolve_symbol<'a>(
&'a self,
path: &Path,
offset: TextUnit,
) -> Result<Vec<(&'a Path, &'a FileSymbol)>> {
let file = self.file_syntax(path)?;
let syntax = file.syntax();
let syntax = syntax.as_ref();
let name_ref =
find_leaf_at_offset(syntax, offset)
.left_biased()
.into_iter()
.flat_map(|node| ancestors(node))
.flat_map(ast::NameRef::cast)
.next();
let name = match name_ref {
None => return Ok(vec![]),
Some(name_ref) => name_ref.text(),
};
let mut query = Query::new(name.to_string());
query.exact();
Ok(self.world_symbols(query).take(4).collect())
}
fn file_data(&self, path: &Path) -> Result<Arc<FileData>> { fn file_data(&self, path: &Path) -> Result<Arc<FileData>> {
match self.data.file_map.get(path) { match self.data.file_map.get(path) {
Some(data) => Ok(data.clone()), Some(data) => Ok(data.clone()),

View file

@ -123,6 +123,24 @@ impl<R: TreeRoot> AstNode<R> for Name<R> {
impl<R: TreeRoot> Name<R> {} impl<R: TreeRoot> Name<R> {}
// NameRef
#[derive(Debug, Clone, Copy)]
pub struct NameRef<R: TreeRoot = Arc<SyntaxRoot>> {
syntax: SyntaxNode<R>,
}
impl<R: TreeRoot> AstNode<R> for NameRef<R> {
fn cast(syntax: SyntaxNode<R>) -> Option<Self> {
match syntax.kind() {
NAME_REF => Some(NameRef { syntax }),
_ => None,
}
}
fn syntax(&self) -> &SyntaxNode<R> { &self.syntax }
}
impl<R: TreeRoot> NameRef<R> {}
// StaticItem // StaticItem
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct StaticItem<R: TreeRoot = Arc<SyntaxRoot>> { pub struct StaticItem<R: TreeRoot = Arc<SyntaxRoot>> {

View file

@ -73,3 +73,11 @@ impl<R: TreeRoot> Name<R> {
ident.leaf_text().unwrap() ident.leaf_text().unwrap()
} }
} }
impl<R: TreeRoot> NameRef<R> {
pub fn text(&self) -> SmolStr {
let ident = self.syntax().first_child()
.unwrap();
ident.leaf_text().unwrap()
}
}

View file

@ -228,5 +228,6 @@ Grammar(
"StaticItem": ( traits: ["NameOwner"] ), "StaticItem": ( traits: ["NameOwner"] ),
"TypeItem": ( traits: ["NameOwner"] ), "TypeItem": ( traits: ["NameOwner"] ),
"Name": (), "Name": (),
"NameRef": (),
}, },
) )

View file

@ -20,7 +20,7 @@ pub fn server_capabilities() -> ServerCapabilities {
hover_provider: None, hover_provider: None,
completion_provider: None, completion_provider: None,
signature_help_provider: None, signature_help_provider: None,
definition_provider: None, definition_provider: Some(true),
type_definition_provider: None, type_definition_provider: None,
implementation_provider: None, implementation_provider: None,
references_provider: None, references_provider: None,

View file

@ -1,7 +1,11 @@
use languageserver_types::{Range, SymbolKind, Position, TextEdit}; use std::path::Path;
use languageserver_types::{Range, SymbolKind, Position, TextEdit, Location, Url};
use libeditor::{LineIndex, LineCol, Edit, AtomEdit}; use libeditor::{LineIndex, LineCol, Edit, AtomEdit};
use libsyntax2::{SyntaxKind, TextUnit, TextRange}; use libsyntax2::{SyntaxKind, TextUnit, TextRange};
use Result;
pub trait Conv { pub trait Conv {
type Output; type Output;
fn conv(self) -> Self::Output; fn conv(self) -> Self::Output;
@ -13,6 +17,12 @@ pub trait ConvWith {
fn conv_with(self, ctx: &Self::Ctx) -> Self::Output; fn conv_with(self, ctx: &Self::Ctx) -> Self::Output;
} }
pub trait TryConvWith {
type Ctx;
type Output;
fn try_conv_with(self, ctx: &Self::Ctx) -> Result<Self::Output>;
}
impl Conv for SyntaxKind { impl Conv for SyntaxKind {
type Output = SymbolKind; type Output = SymbolKind;
@ -104,6 +114,20 @@ impl ConvWith for AtomEdit {
} }
} }
impl<'a> TryConvWith for (&'a Path, TextRange) {
type Ctx = LineIndex;
type Output = Location;
fn try_conv_with(self, line_index: &LineIndex) -> Result<Location> {
let loc = Location::new(
Url::from_file_path(self.0)
.map_err(|()| format_err!("can't convert path to url: {}", self.0.display()))?,
self.1.conv_with(line_index),
);
Ok(loc)
}
}
pub trait MapConvWith<'a>: Sized { pub trait MapConvWith<'a>: Sized {
type Ctx; type Ctx;

View file

@ -3,7 +3,7 @@ use std::collections::HashMap;
use languageserver_types::{ use languageserver_types::{
Diagnostic, DiagnosticSeverity, Url, DocumentSymbol, Diagnostic, DiagnosticSeverity, Url, DocumentSymbol,
Command, TextDocumentIdentifier, WorkspaceEdit, Command, TextDocumentIdentifier, WorkspaceEdit,
SymbolInformation, Location, SymbolInformation,
}; };
use libanalysis::{World, Query}; use libanalysis::{World, Query};
use libeditor; use libeditor;
@ -13,7 +13,7 @@ use serde_json::{to_value, from_value};
use ::{ use ::{
req::{self, Decoration}, Result, req::{self, Decoration}, Result,
util::FilePath, util::FilePath,
conv::{Conv, ConvWith, MapConvWith}, conv::{Conv, ConvWith, TryConvWith, MapConvWith},
}; };
pub fn handle_syntax_tree( pub fn handle_syntax_tree(
@ -115,15 +115,10 @@ pub fn handle_workspace_symbol(
for (path, symbol) in world.world_symbols(query).take(128) { for (path, symbol) in world.world_symbols(query).take(128) {
let line_index = world.file_line_index(path)?; let line_index = world.file_line_index(path)?;
let info = SymbolInformation { let info = SymbolInformation {
name: symbol.name.to_string(), name: symbol.name.to_string(),
kind: symbol.kind.conv(), kind: symbol.kind.conv(),
location: Location::new( location: (path, symbol.node_range).try_conv_with(&line_index)?,
Url::from_file_path(path)
.map_err(|()| format_err!("invalid url"))?,
symbol.node_range.conv_with(&line_index),
),
container_name: None, container_name: None,
}; };
acc.push(info); acc.push(info);
@ -132,6 +127,22 @@ pub fn handle_workspace_symbol(
Ok(Some(acc)) Ok(Some(acc))
} }
pub fn handle_goto_definition(
world: World,
params: req::TextDocumentPositionParams,
) -> Result<Option<req::GotoDefinitionResponse>> {
let path = params.text_document.file_path()?;
let line_index = world.file_line_index(&path)?;
let offset = params.position.conv_with(&line_index);
let mut res = Vec::new();
for (path, symbol) in world.approximately_resolve_symbol(&path, offset)? {
let line_index = world.file_line_index(path)?;
let location = (path, symbol.node_range).try_conv_with(&line_index)?;
res.push(location)
}
Ok(Some(req::GotoDefinitionResponse::Array(res)))
}
pub fn handle_execute_command( pub fn handle_execute_command(
world: World, world: World,
mut params: req::ExecuteCommandParams, mut params: req::ExecuteCommandParams,

View file

@ -26,6 +26,7 @@ use {
handle_code_action, handle_code_action,
handle_execute_command, handle_execute_command,
handle_workspace_symbol, handle_workspace_symbol,
handle_goto_definition,
}, },
}; };
@ -152,6 +153,9 @@ fn on_request(
handle_request_on_threadpool::<req::WorkspaceSymbol>( handle_request_on_threadpool::<req::WorkspaceSymbol>(
&mut req, pool, world, sender, handle_workspace_symbol, &mut req, pool, world, sender, handle_workspace_symbol,
)?; )?;
handle_request_on_threadpool::<req::GotoDefinition>(
&mut req, pool, world, sender, handle_goto_definition,
)?;
dispatch::handle_request::<req::ExecuteCommand, _>(&mut req, |params, resp| { dispatch::handle_request::<req::ExecuteCommand, _>(&mut req, |params, resp| {
io.send(RawMsg::Response(resp.into_response(Ok(None))?)); io.send(RawMsg::Response(resp.into_response(Ok(None))?));

View file

@ -9,6 +9,7 @@ pub use languageserver_types::{
CodeActionParams, ApplyWorkspaceEditParams, CodeActionParams, ApplyWorkspaceEditParams,
ExecuteCommandParams, ExecuteCommandParams,
WorkspaceSymbolParams, WorkspaceSymbolParams,
TextDocumentPositionParams,
}; };