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},
};
use libsyntax2::ast;
use libsyntax2::{
TextUnit,
ast::{self, AstNode},
algo::{find_leaf_at_offset, ancestors},
};
use libeditor::{LineIndex, FileSymbol};
use self::symbol_index::{FileSymbols};
use self::symbol_index::FileSymbols;
pub use self::symbol_index::Query;
pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
@ -90,8 +94,7 @@ impl World {
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()
.flat_map(move |(path, data)| {
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>> {
match self.data.file_map.get(path) {
Some(data) => Ok(data.clone()),

View file

@ -123,6 +123,24 @@ impl<R: TreeRoot> AstNode<R> for 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
#[derive(Debug, Clone, Copy)]
pub struct StaticItem<R: TreeRoot = Arc<SyntaxRoot>> {

View file

@ -73,3 +73,11 @@ impl<R: TreeRoot> Name<R> {
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"] ),
"TypeItem": ( traits: ["NameOwner"] ),
"Name": (),
"NameRef": (),
},
)

View file

@ -20,7 +20,7 @@ pub fn server_capabilities() -> ServerCapabilities {
hover_provider: None,
completion_provider: None,
signature_help_provider: None,
definition_provider: None,
definition_provider: Some(true),
type_definition_provider: None,
implementation_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 libsyntax2::{SyntaxKind, TextUnit, TextRange};
use Result;
pub trait Conv {
type Output;
fn conv(self) -> Self::Output;
@ -13,6 +17,12 @@ pub trait ConvWith {
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 {
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 {
type Ctx;

View file

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

View file

@ -26,6 +26,7 @@ use {
handle_code_action,
handle_execute_command,
handle_workspace_symbol,
handle_goto_definition,
},
};
@ -152,6 +153,9 @@ fn on_request(
handle_request_on_threadpool::<req::WorkspaceSymbol>(
&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| {
io.send(RawMsg::Response(resp.into_response(Ok(None))?));

View file

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