Add lsp/definition

This commit is contained in:
ef3d0c3e 2024-11-19 12:37:20 +01:00
parent cf6ec12afb
commit 4c5e888bd9
8 changed files with 203 additions and 14 deletions

View file

@ -307,7 +307,7 @@ impl RegexRule for ScriptRule {
});
if !label.is_empty() {
label.pop();
hints.add(matches.get(0).unwrap().end() - 1, label);
hints.add(matches.get(0).unwrap().end(), label);
}
}

View file

@ -12,6 +12,8 @@ use crate::parser::reports::*;
use crate::parser::rule::RegexRule;
use crate::parser::source::Token;
use ariadne::Fmt;
use lsp::definition;
use lsp::hints::Hints;
use mlua::Function;
use mlua::Lua;
use regex::Regex;
@ -430,6 +432,16 @@ impl RegexRule for VariableSubstitutionRule {
sems.add(name.end..name.end + 1, tokens.variable_sub_sep);
}
if let Some(hints) = Hints::from_source(token.source(), &state.shared.lsp) {
let label = variable.to_string();
if !label.is_empty() {
hints.add(matches.get(0).unwrap().end(), label);
}
}
// Add definition
definition::from_source(token, variable.location(), &state.shared.lsp);
reports
}
}

View file

@ -3,6 +3,7 @@ use std::rc::Rc;
use crate::parser::source::Source;
use super::definition::DefinitionData;
use super::hints::HintsData;
use super::semantic::SemanticsData;
use super::semantic::Tokens;
@ -12,6 +13,7 @@ pub struct LSPData {
pub semantic_tokens: Tokens,
pub semantic_data: HashMap<Rc<dyn Source>, SemanticsData>,
pub inlay_hints: HashMap<Rc<dyn Source>, HintsData>,
pub definitions: HashMap<Rc<dyn Source>, DefinitionData>,
}
impl LSPData {
@ -20,6 +22,23 @@ impl LSPData {
semantic_tokens: Tokens::new(),
semantic_data: HashMap::new(),
inlay_hints: HashMap::new(),
definitions: HashMap::new(),
}
}
/// Method that must be called when a source is added
pub fn new_source(&mut self, source: Rc<dyn Source>) {
if !self.semantic_data.contains_key(&source) {
self.semantic_data
.insert(source.clone(), SemanticsData::new(source.clone()));
}
if !self.inlay_hints.contains_key(&source) {
self.inlay_hints
.insert(source.clone(), HintsData::new(source.clone()));
}
if !self.definitions.contains_key(&source) {
self.definitions
.insert(source.clone(), DefinitionData::new());
}
}
}

113
src/lsp/definition.rs Normal file
View file

@ -0,0 +1,113 @@
use std::cell::RefCell;
use std::rc::Rc;
use tower_lsp::lsp_types::Location;
use tower_lsp::lsp_types::Position;
use tower_lsp::lsp_types::Range;
use tower_lsp::lsp_types::Url;
use crate::parser::source::LineCursor;
use crate::parser::source::Source;
use crate::parser::source::SourceFile;
use crate::parser::source::SourcePosition;
use crate::parser::source::Token;
use crate::parser::source::VirtualSource;
use super::data::LSPData;
/// Per file definitions
#[derive(Debug)]
pub struct DefinitionData {
/// The definitions
pub definitions: RefCell<Vec<(Location, Range)>>,
}
impl DefinitionData {
pub fn new() -> Self {
Self {
definitions: RefCell::new(vec![]),
}
}
}
fn from_source_impl(
source: Rc<dyn Source>,
target: &Token,
lsp: &Option<RefCell<LSPData>>,
original: Token,
) {
if (source.name().starts_with(":LUA:") || source.name().starts_with(":VAR:"))
&& source.downcast_ref::<VirtualSource>().is_some()
{
return;
}
if let Some(location) = source
.clone()
.downcast_rc::<VirtualSource>()
.ok()
.as_ref()
.map(|parent| parent.location())
.unwrap_or(None)
{
return from_source_impl(location.source(), target, lsp, original);
} else if let Ok(sourcefile) = source.downcast_rc::<SourceFile>() {
let borrow = lsp.as_ref().unwrap().borrow();
let definitions = borrow.definitions.get(&original.source()).unwrap();
let mut db = definitions.definitions.borrow_mut();
{
let token = original.source().original_range(original.range).1;
// Resolve target
let mut target_cursor = LineCursor::new(target.source());
let orignal_target = target.source().original_range(target.range.clone());
target_cursor.move_to(orignal_target.1.start);
let target_start = Position {
line: target_cursor.line as u32,
character: target_cursor.line_pos as u32,
};
target_cursor.move_to(orignal_target.1.end);
let target_end = Position {
line: target_cursor.line as u32,
character: target_cursor.line_pos as u32,
};
// Resolve source
let mut source_cursor = LineCursor::new(sourcefile);
source_cursor.move_to(token.start);
let source_start = Position {
line: source_cursor.line as u32,
character: source_cursor.line_pos as u32,
};
source_cursor.move_to(token.end);
let source_end = Position {
line: source_cursor.line as u32,
character: source_cursor.line_pos as u32,
};
// Add definition
let target_path = std::fs::canonicalize(orignal_target.0.name().as_str()).unwrap();
let uri = Url::from_file_path(target_path).unwrap();
db.push((
Location {
uri,
range: Range {
start: target_start,
end: target_end,
},
},
Range {
start: source_start,
end: source_end,
},
))
}
}
}
pub fn from_source(source: Token, target: &Token, lsp: &Option<RefCell<LSPData>>) {
if lsp.is_none() {
return;
}
from_source_impl(source.source(), target, lsp, source)
}

View file

@ -1,3 +1,4 @@
pub mod data;
pub mod definition;
pub mod hints;
pub mod semantic;

View file

@ -435,10 +435,6 @@ impl<'a> Semantics<'a> {
/// Add a semantic token to be processed instantly
pub fn add(&self, range: Range<usize>, token: (u32, u32)) {
let range = self.original_source.original_range(range).1;
eprintln!(
"Added {token:#?} range={range:#?} source={:#?}",
self.original_source
);
self.process_queue(range.start);
self.add_impl(range, token);
}

View file

@ -6,6 +6,7 @@ use crate::document::document::Document;
use crate::document::element::DocumentEnd;
use crate::document::langdocument::LangDocument;
use crate::elements::text::Text;
use crate::lsp::definition::DefinitionData;
use crate::lsp::hints::HintsData;
use crate::lsp::semantic::Semantics;
use crate::lsp::semantic::SemanticsData;
@ -141,15 +142,7 @@ impl<'b> Parser for LangParser<'b> {
source.clone().downcast_rc::<SourceFile>().ok(),
state.shared.lsp.as_ref(),
) {
let mut b = lsp.borrow_mut();
if !b.semantic_data.contains_key(&source) {
b.semantic_data
.insert(source.clone(), SemanticsData::new(source.clone()));
}
if !b.inlay_hints.contains_key(&source) {
b.inlay_hints
.insert(source.clone(), HintsData::new(source.clone()));
}
lsp.borrow_mut().new_source(source.clone());
}
let content = source.content();

View file

@ -25,6 +25,7 @@ use tower_lsp::Server;
struct Backend {
client: Client,
document_map: DashMap<String, String>,
definition_map: DashMap<String, Vec<(Location, Range)>>,
semantic_token_map: DashMap<String, Vec<SemanticToken>>,
diagnostic_map: DashMap<String, Vec<Diagnostic>>,
hints_map: DashMap<String, Vec<InlayHint>>,
@ -95,6 +96,22 @@ impl Backend {
}
}
}
// Definitions
if let Some(lsp) = state.shared.lsp.as_ref() {
let borrow = lsp.borrow();
for (source, definitions) in &borrow.definitions {
if let Some(path) = source
.clone()
.downcast_rc::<SourceFile>()
.ok()
.map(|source| source.path().to_owned())
{
self.definition_map
.insert(path, definitions.definitions.replace(vec![]));
}
}
}
}
}
@ -109,6 +126,7 @@ impl LanguageServer for Backend {
text_document_sync: Some(TextDocumentSyncCapability::Kind(
TextDocumentSyncKind::FULL,
)),
definition_provider: Some(OneOf::Left(true)),
completion_provider: Some(CompletionOptions {
resolve_provider: Some(false),
trigger_characters: Some(vec!["%".to_string()]),
@ -186,6 +204,42 @@ impl LanguageServer for Backend {
.await
}
async fn goto_definition(
&self,
params: GotoDefinitionParams,
) -> tower_lsp::jsonrpc::Result<Option<GotoDefinitionResponse>> {
let uri = &params.text_document_position_params.text_document.uri;
let pos = &params.text_document_position_params.position;
if let Some(definitions) = self.definition_map.get(uri.as_str()) {
let index = definitions.binary_search_by(|(_, range)| {
if range.start.line > pos.line {
std::cmp::Ordering::Greater
} else if range.end.line <= pos.line {
if range.start.line == pos.line && range.start.character <= pos.character {
std::cmp::Ordering::Equal
} else if range.end.line == pos.line && range.end.character >= pos.character {
std::cmp::Ordering::Equal
} else if range.start.line < pos.line && range.end.line > pos.line {
std::cmp::Ordering::Equal
} else {
std::cmp::Ordering::Less
}
} else {
std::cmp::Ordering::Less
}
});
if let Ok(index) = index {
let loc = self.definition_map.get(uri.as_str()).as_ref().unwrap()[index]
.0
.clone();
return Ok(Some(GotoDefinitionResponse::Scalar(loc)));
}
}
Err(tower_lsp::jsonrpc::Error::method_not_found())
}
async fn completion(
&self,
_params: CompletionParams,
@ -262,6 +316,7 @@ async fn main() {
let (service, socket) = LspService::new(|client| Backend {
client,
document_map: DashMap::new(),
definition_map: DashMap::new(),
semantic_token_map: DashMap::new(),
diagnostic_map: DashMap::new(),
hints_map: DashMap::new(),