From e2b7bc4b5687d5520b569b889a1742c006eacc22 Mon Sep 17 00:00:00 2001 From: Myriad-Dreamin <35292584+Myriad-Dreamin@users.noreply.github.com> Date: Thu, 9 May 2024 12:32:20 +0800 Subject: [PATCH] refactor: introduce uri_for_id and deref_syntax_at (#267) * dev: add documentation * refactor: introduce uri_for_id and deref_syntax_at --- crates/tinymist-query/src/analysis/global.rs | 46 ++++++- crates/tinymist-query/src/code_action.rs | 98 +++++++++++---- crates/tinymist-query/src/code_context.rs | 114 +++++++++--------- .../tinymist-query/src/color_presentation.rs | 19 ++- crates/tinymist-query/src/completion.rs | 17 +-- crates/tinymist-query/src/diagnostics.rs | 4 +- crates/tinymist-query/src/document_color.rs | 18 ++- crates/tinymist-query/src/goto_declaration.rs | 26 +--- crates/tinymist-query/src/goto_definition.rs | 18 +-- crates/tinymist-query/src/prelude.rs | 2 +- crates/tinymist-query/src/prepare_rename.rs | 13 +- crates/tinymist-query/src/references.rs | 15 +-- crates/tinymist-query/src/rename.rs | 20 +-- 13 files changed, 236 insertions(+), 174 deletions(-) diff --git a/crates/tinymist-query/src/analysis/global.rs b/crates/tinymist-query/src/analysis/global.rs index 8fe1fd39..f6eb3ed8 100644 --- a/crates/tinymist-query/src/analysis/global.rs +++ b/crates/tinymist-query/src/analysis/global.rs @@ -7,6 +7,7 @@ use std::{ }; use ecow::{EcoString, EcoVec}; +use lsp_types::Url; use once_cell::sync::OnceCell; use parking_lot::RwLock; use reflexo::hash::hash128; @@ -27,7 +28,8 @@ use super::{ analyze_bib, post_type_check, BibInfo, DefUseInfo, FlowType, ImportInfo, PathPreference, Signature, SignatureTarget, TypeCheckInfo, }; -use crate::syntax::resolve_id_by_path; +use crate::path_to_url; +use crate::syntax::{get_deref_target, resolve_id_by_path, DerefTarget}; use crate::{ lsp_to_typst, syntax::{ @@ -539,6 +541,14 @@ impl<'w> AnalysisContext<'w> { id.vpath().resolve(&root).ok_or(FileError::AccessDenied) } + /// Resolve the uri for a file id. + pub fn uri_for_id(&self, id: TypstFileId) -> Result { + self.path_for_id(id).and_then(|e| { + path_to_url(&e) + .map_err(|e| FileError::Other(Some(eco_format!("convert to url: {e:?}")))) + }) + } + /// Get the content of a file by file id. pub fn file_by_id(&mut self, id: TypstFileId) -> FileResult { self.get_mut(id); @@ -565,6 +575,31 @@ impl<'w> AnalysisContext<'w> { self.source_by_id(id) } + /// Get a syntax object at a position. + pub fn deref_syntax_at<'s>( + &mut self, + source: &'s Source, + position: LspPosition, + shift: usize, + ) -> Option> { + let (_, deref_target) = self.deref_syntax_at_(source, position, shift)?; + deref_target + } + + /// Get a syntax object at a position. + pub fn deref_syntax_at_<'s>( + &mut self, + source: &'s Source, + position: LspPosition, + shift: usize, + ) -> Option<(usize, Option>)> { + let offset = self.to_typst_pos(position, source)?; + let cursor = ceil_char_boundary(source.text(), offset + shift); + + let node = LinkedNode::new(source.root()).leaf_at(cursor)?; + Some((cursor, get_deref_target(node, cursor))) + } + /// Get the module-level analysis cache of a file. pub fn get(&self, file_id: TypstFileId) -> Option<&ModuleAnalysisCache> { self.caches.modules.get(&file_id) @@ -852,6 +887,15 @@ impl<'w> AnalysisContext<'w> { } } +fn ceil_char_boundary(text: &str, mut cursor: usize) -> usize { + // while is not char boundary, move cursor to right + while cursor < text.len() && !text.is_char_boundary(cursor) { + cursor += 1; + } + + cursor.min(text.len()) +} + #[comemo::memoize] fn get_loc_info(bytes: Bytes) -> Option> { let mut loc = EcoVec::new(); diff --git a/crates/tinymist-query/src/code_action.rs b/crates/tinymist-query/src/code_action.rs index 24e16b00..1bbea220 100644 --- a/crates/tinymist-query/src/code_action.rs +++ b/crates/tinymist-query/src/code_action.rs @@ -3,10 +3,64 @@ use once_cell::sync::OnceCell; use crate::{prelude::*, SemanticRequest}; -/// The [`textDocument/codeLens`] request is sent from the client to the server -/// to compute code lenses for a given text document. +/// The [`textDocument/codeAction`] request is sent from the client to the +/// server to compute commands for a given text document and range. These +/// commands are typically code fixes to either fix problems or to +/// beautify/refactor code. /// -/// [`textDocument/codeLens`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_codeLens +/// The result of a [`textDocument/codeAction`] request is an array of `Command` +/// literals which are typically presented in the user interface. +/// +/// [`textDocument/codeAction`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_codeAction +/// +/// To ensure that a server is useful in many clients, the commands specified in +/// a code actions should be handled by the server and not by the client (see +/// [`workspace/executeCommand`] and +/// `ServerCapabilities::execute_command_provider`). If the client supports +/// providing edits with a code action, then the mode should be used. +/// +/// When the command is selected the server should be contacted again (via the +/// [`workspace/executeCommand`] request) to execute the command. +/// +/// [`workspace/executeCommand`]: https://microsoft.github.io/language-server-protocol/specification#workspace_executeCommand +/// +/// # Compatibility +/// +/// ## Since version 3.16.0 +/// +/// A client can offer a server to delay the computation of code action +/// properties during a `textDocument/codeAction` request. This is useful for +/// cases where it is expensive to compute the value of a property (for example, +/// the `edit` property). +/// +/// Clients signal this through the `code_action.resolve_support` client +/// capability which lists all properties a client can resolve lazily. The +/// server capability `code_action_provider.resolve_provider` signals that a +/// server will offer a `codeAction/resolve` route. +/// +/// To help servers uniquely identify a code action in the resolve request, a +/// code action literal may optionally carry a `data` property. This is also +/// guarded by an additional client capability `code_action.data_support`. In +/// general, a client should offer data support if it offers resolve support. +/// +/// It should also be noted that servers shouldn’t alter existing attributes of +/// a code action in a `codeAction/resolve` request. +/// +/// ## Since version 3.8.0 +/// +/// Support for [`CodeAction`] literals to enable the following scenarios: +/// +/// * The ability to directly return a workspace edit from the code action +/// request. This avoids having another server roundtrip to execute an actual +/// code action. However server providers should be aware that if the code +/// action is expensive to compute or the edits are huge it might still be +/// beneficial if the result is simply a command and the actual edit is only +/// computed when needed. +/// +/// * The ability to group code actions using a kind. Clients are allowed to +/// ignore that information. However it allows them to better group code +/// action, for example, into corresponding menus (e.g. all refactor code +/// actions into a refactor menu). #[derive(Debug, Clone)] pub struct CodeActionRequest { /// The path of the document to request for. @@ -52,11 +106,7 @@ impl<'a, 'w> CodeActionWorker<'a, 'w> { fn local_url(&self) -> Option<&Url> { self.local_url - .get_or_init(|| { - let id = self.current.id(); - let path = self.ctx.path_for_id(id).ok()?; - path_to_url(path.as_path()).ok() - }) + .get_or_init(|| self.ctx.uri_for_id(self.current.id()).ok()) .as_ref() } @@ -71,18 +121,18 @@ impl<'a, 'w> CodeActionWorker<'a, 'w> { self.local_edits(vec![edit]) } - fn heading_actions(&mut self, leaf: &LinkedNode) -> Option<()> { - let h = leaf.cast::()?; + fn heading_actions(&mut self, node: &LinkedNode) -> Option<()> { + let h = node.cast::()?; let depth = h.depth().get(); // Only the marker is replaced, for minimal text change - let marker = leaf + let marker = node .children() .find(|e| e.kind() == SyntaxKind::HeadingMarker)?; let marker_range = marker.range(); if depth > 1 { - // decrease depth of heading + // Decrease depth of heading let action = CodeActionOrCommand::CodeAction(CodeAction { title: "Decrease depth of heading".to_string(), kind: Some(CodeActionKind::REFACTOR_REWRITE), @@ -95,7 +145,7 @@ impl<'a, 'w> CodeActionWorker<'a, 'w> { self.actions.push(action); } - // increase depth of heading + // Increase depth of heading let action = CodeActionOrCommand::CodeAction(CodeAction { title: "Increase depth of heading".to_string(), kind: Some(CodeActionKind::REFACTOR_REWRITE), @@ -110,20 +160,21 @@ impl<'a, 'w> CodeActionWorker<'a, 'w> { Some(()) } - fn equation_actions(&mut self, leaf: &LinkedNode) -> Option<()> { - let equation = leaf.cast::()?; + fn equation_actions(&mut self, node: &LinkedNode) -> Option<()> { + let equation = node.cast::()?; let body = equation.body(); let is_block = equation.block(); - let body = leaf.find(body.span())?; + let body = node.find(body.span())?; let body_range = body.range(); - let mut chs = leaf.children(); + let mut chs = node.children(); let chs = chs.by_ref(); let first_dollar = chs.take(1).find(|e| e.kind() == SyntaxKind::Dollar)?; let last_dollar = chs.rev().take(1).find(|e| e.kind() == SyntaxKind::Dollar)?; - // erroneous equation + // Erroneous equation is skipped. + // For example, some unclosed equation. if first_dollar.offset() == last_dollar.offset() { return None; } @@ -153,7 +204,7 @@ impl<'a, 'w> CodeActionWorker<'a, 'w> { })) }; - // prepare actions + // Prepare actions let a1 = if is_block { rewrite_action("Convert to inline equation", "")? } else { @@ -170,7 +221,8 @@ impl<'a, 'w> CodeActionWorker<'a, 'w> { } fn work(&mut self, root: LinkedNode, cursor: usize) -> Option<()> { - let mut node = root.leaf_at(cursor)?; + let node = root.leaf_at(cursor)?; + let mut node = &node; let mut heading_resolved = false; let mut equation_resolved = false; @@ -180,17 +232,17 @@ impl<'a, 'w> CodeActionWorker<'a, 'w> { // Only the deepest heading is considered SyntaxKind::Heading if !heading_resolved => { heading_resolved = true; - self.heading_actions(&node); + self.heading_actions(node); } // Only the deepest equation is considered SyntaxKind::Equation if !equation_resolved => { equation_resolved = true; - self.equation_actions(&node); + self.equation_actions(node); } _ => {} } - node = node.parent()?.clone(); + node = node.parent()?; } } } diff --git a/crates/tinymist-query/src/code_context.rs b/crates/tinymist-query/src/code_context.rs index 668698ac..465beb07 100644 --- a/crates/tinymist-query/src/code_context.rs +++ b/crates/tinymist-query/src/code_context.rs @@ -35,14 +35,14 @@ pub enum InteractCodeContextQuery { #[derive(Debug, Clone, Serialize)] #[serde(tag = "kind", rename_all = "camelCase")] pub enum InteractCodeContextResponse { - /// The mode at the requested position. + /// Get the mode at a specific position in a text document. ModeAt { /// The mode at the requested position. mode: InterpretMode, }, } -/// A request to get the mode at a specific position in a text document. +/// A request to get the code context of a text document. #[derive(Debug, Clone, Deserialize)] #[serde(tag = "kind")] pub struct InteractCodeContextRequest { @@ -65,59 +65,7 @@ impl SyntaxRequest for InteractCodeContextRequest { for query in self.query { match query { InteractCodeContextQuery::ModeAt { position } => { - let pos = lsp_to_typst::position(position, positing_encoding, source)?; - if pos == 0 || pos == source.text().len() { - // smart special case - responses.push(InteractCodeContextResponse::ModeAt { - mode: InterpretMode::Markup, - }); - continue; - } - - // get mode - let root = LinkedNode::new(source.root()); - let leaf = root.leaf_at(pos); - let mut leaf = leaf.as_ref(); - let mode = loop { - log::debug!("leaf for context: {leaf:?}"); - use SyntaxKind::*; - if let Some(t) = leaf { - match t.kind() { - LineComment | BlockComment => break InterpretMode::Comment, - Raw => break InterpretMode::Raw, - Str => break InterpretMode::String, - CodeBlock | Code => break InterpretMode::Code, - ContentBlock | Markup => break InterpretMode::Markup, - Equation | Math => break InterpretMode::Math, - Ident | FieldAccess | Bool | Int | Float | Numeric | Space - | Linebreak | Parbreak | Escape | Shorthand | SmartQuote - | RawLang | RawDelim | RawTrimmed | Hash | LeftBrace - | RightBrace | LeftBracket | RightBracket | LeftParen - | RightParen | Comma | Semicolon | Colon | Star | Underscore - | Dollar | Plus | Minus | Slash | Hat | Prime | Dot | Eq | EqEq - | ExclEq | Lt | LtEq | Gt | GtEq | PlusEq | HyphEq | StarEq - | SlashEq | Dots | Arrow | Root | Not | And | Or | None | Auto - | As | Named | Keyed | Error | Eof => {} - Text | Strong | Emph | Link | Label | Ref | RefMarker | Heading - | HeadingMarker | ListItem | ListMarker | EnumItem | EnumMarker - | TermItem | TermMarker => break InterpretMode::Markup, - MathIdent | MathAlignPoint | MathDelimited | MathAttach - | MathPrimes | MathFrac | MathRoot => break InterpretMode::Math, - Let | Set | Show | Context | If | Else | For | In | While - | Break | Continue | Return | Import | Include | Args | Spread - | Closure | Params | LetBinding | SetRule | ShowRule - | Contextual | Conditional | WhileLoop | ForLoop | ModuleImport - | ImportItems | RenamedImportItem | ModuleInclude | LoopBreak - | LoopContinue | FuncReturn | FuncCall | Unary | Binary - | Parenthesized | Dict | Array | Destructuring - | DestructAssignment => break InterpretMode::Code, - } - leaf = t.parent(); - } else { - break InterpretMode::Markup; - } - }; - + let mode = Self::mode_at(source, positing_encoding, position)?; responses.push(InteractCodeContextResponse::ModeAt { mode }); } } @@ -126,3 +74,59 @@ impl SyntaxRequest for InteractCodeContextRequest { Some(responses) } } + +impl InteractCodeContextRequest { + fn mode_at( + source: &Source, + positing_encoding: PositionEncoding, + position: LspPosition, + ) -> Option { + let pos = lsp_to_typst::position(position, positing_encoding, source)?; + // Smart special cases that is definitely at markup + if pos == 0 || pos >= source.text().len() { + return Some(InterpretMode::Markup); + } + + // Get mode + let root = LinkedNode::new(source.root()); + let leaf = root.leaf_at(pos); + let mut leaf = leaf.as_ref(); + Some(loop { + log::debug!("leaf for context: {leaf:?}"); + use SyntaxKind::*; + if let Some(t) = leaf { + match t.kind() { + LineComment | BlockComment => break InterpretMode::Comment, + Raw => break InterpretMode::Raw, + Str => break InterpretMode::String, + CodeBlock | Code => break InterpretMode::Code, + ContentBlock | Markup => break InterpretMode::Markup, + Equation | Math => break InterpretMode::Math, + Ident | FieldAccess | Bool | Int | Float | Numeric | Space | Linebreak + | Parbreak | Escape | Shorthand | SmartQuote | RawLang | RawDelim + | RawTrimmed | Hash | LeftBrace | RightBrace | LeftBracket | RightBracket + | LeftParen | RightParen | Comma | Semicolon | Colon | Star | Underscore + | Dollar | Plus | Minus | Slash | Hat | Prime | Dot | Eq | EqEq | ExclEq + | Lt | LtEq | Gt | GtEq | PlusEq | HyphEq | StarEq | SlashEq | Dots | Arrow + | Root | Not | And | Or | None | Auto | As | Named | Keyed | Error | Eof => {} + Text | Strong | Emph | Link | Label | Ref | RefMarker | Heading + | HeadingMarker | ListItem | ListMarker | EnumItem | EnumMarker | TermItem + | TermMarker => break InterpretMode::Markup, + MathIdent | MathAlignPoint | MathDelimited | MathAttach | MathPrimes + | MathFrac | MathRoot => break InterpretMode::Math, + Let | Set | Show | Context | If | Else | For | In | While | Break + | Continue | Return | Import | Include | Args | Spread | Closure | Params + | LetBinding | SetRule | ShowRule | Contextual | Conditional | WhileLoop + | ForLoop | ModuleImport | ImportItems | RenamedImportItem | ModuleInclude + | LoopBreak | LoopContinue | FuncReturn | FuncCall | Unary | Binary + | Parenthesized | Dict | Array | Destructuring | DestructAssignment => { + break InterpretMode::Code + } + } + leaf = t.parent(); + } else { + break InterpretMode::Markup; + } + }) + } +} diff --git a/crates/tinymist-query/src/color_presentation.rs b/crates/tinymist-query/src/color_presentation.rs index dc07f116..0ca5e999 100644 --- a/crates/tinymist-query/src/color_presentation.rs +++ b/crates/tinymist-query/src/color_presentation.rs @@ -2,7 +2,24 @@ use typst::foundations::Repr; use crate::prelude::*; -/// The +/// The [`textDocument/colorPresentation`] request is sent from the client to +/// the server to obtain a list of presentations for a color value at a given +/// location. +/// +/// [`textDocument/colorPresentation`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_colorPresentation +/// +/// Clients can use the result to: +/// +/// * Modify a color reference +/// * Show in a color picker and let users pick one of the presentations +/// +/// # Compatibility +/// +/// This request was introduced in specification version 3.6.0. +/// +/// This request has no special capabilities and registration options since it +/// is sent as a resolve request for the +/// [`textDocument/documentColor`](Self::document_color) request. #[derive(Debug, Clone)] pub struct ColorPresentationRequest { /// The path of the document to request color presentations for. diff --git a/crates/tinymist-query/src/completion.rs b/crates/tinymist-query/src/completion.rs index 379f7e46..92034cf0 100644 --- a/crates/tinymist-query/src/completion.rs +++ b/crates/tinymist-query/src/completion.rs @@ -8,7 +8,7 @@ use regex::{Captures, Regex}; use crate::{ analysis::{FlowBuiltinType, FlowType}, prelude::*, - syntax::{get_deref_target, DerefTarget}, + syntax::DerefTarget, upstream::{autocomplete, complete_path, CompletionContext}, StatefulRequest, }; @@ -57,7 +57,7 @@ impl StatefulRequest for CompletionRequest { ) -> Option { let doc = doc.as_ref().map(|doc| doc.document.as_ref()); let source = ctx.source_by_path(&self.path).ok()?; - let cursor = ceil_char_boundary(source.text(), ctx.to_typst_pos(self.position, &source)?); + let (cursor, deref_target) = ctx.deref_syntax_at_(&source, self.position, 0)?; // Please see // @@ -74,10 +74,6 @@ impl StatefulRequest for CompletionRequest { // assume that the completion is not explicit. let explicit = false; - let root = LinkedNode::new(source.root()); - let node = root.leaf_at(cursor); - let deref_target = node.and_then(|node| get_deref_target(node, cursor)); - // Skip if is the let binding item *directly* if let Some(DerefTarget::VarAccess(node)) = &deref_target { match node.parent_kind() { @@ -274,15 +270,6 @@ fn to_lsp_snippet(typst_snippet: &EcoString) -> String { result.to_string() } -fn ceil_char_boundary(text: &str, mut cursor: usize) -> usize { - // while is not char boundary, move cursor to right - while cursor < text.len() && !text.is_char_boundary(cursor) { - cursor += 1; - } - - cursor.min(text.len()) -} - fn is_arg_like_context(mut matching: &LinkedNode) -> bool { while let Some(parent) = matching.parent() { use SyntaxKind::*; diff --git a/crates/tinymist-query/src/diagnostics.rs b/crates/tinymist-query/src/diagnostics.rs index 31f03285..4ec053ba 100644 --- a/crates/tinymist-query/src/diagnostics.rs +++ b/crates/tinymist-query/src/diagnostics.rs @@ -28,7 +28,7 @@ fn convert_diagnostic( let uri; let lsp_range; if let Some((id, span)) = diagnostic_span_id(typst_diagnostic) { - uri = path_to_url(&ctx.path_for_id(id)?)?; + uri = ctx.uri_for_id(id)?; let source = ctx.world().source(id)?; lsp_range = diagnostic_range(&source, span, ctx.position_encoding()); } else { @@ -63,7 +63,7 @@ fn tracepoint_to_relatedinformation( position_encoding: PositionEncoding, ) -> anyhow::Result> { if let Some(id) = tracepoint.span.id() { - let uri = path_to_url(&project.path_for_id(id)?)?; + let uri = project.uri_for_id(id)?; let source = project.world().source(id)?; if let Some(typst_range) = source.range(tracepoint.span) { diff --git a/crates/tinymist-query/src/document_color.rs b/crates/tinymist-query/src/document_color.rs index a30bc93f..84f881ea 100644 --- a/crates/tinymist-query/src/document_color.rs +++ b/crates/tinymist-query/src/document_color.rs @@ -1,9 +1,23 @@ use crate::{analysis::get_color_exprs, prelude::*, SemanticRequest}; -/// The +/// The [`textDocument/documentColor`] request is sent from the client to the +/// server to list all color references found in a given text document. Along +/// with the range, a color value in RGB is returned. +/// +/// [`textDocument/documentColor`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_documentColor +/// +/// Clients can use the result to decorate color references in an editor. For +/// example: +/// +/// * Color boxes showing the actual color next to the reference +/// * Show a color picker when a color reference is edited +/// +/// # Compatibility +/// +/// This request was introduced in specification version 3.6.0. #[derive(Debug, Clone)] pub struct DocumentColorRequest { - /// The. + /// The path of the document to request color for. pub path: PathBuf, } diff --git a/crates/tinymist-query/src/goto_declaration.rs b/crates/tinymist-query/src/goto_declaration.rs index 8504cfd0..d4989d97 100644 --- a/crates/tinymist-query/src/goto_declaration.rs +++ b/crates/tinymist-query/src/goto_declaration.rs @@ -2,11 +2,7 @@ use std::ops::Range; use log::debug; -use crate::{ - prelude::*, - syntax::{get_deref_target, DerefTarget}, - SemanticRequest, -}; +use crate::{prelude::*, syntax::DerefTarget, SemanticRequest}; /// The [`textDocument/declaration`] request asks the server for the declaration /// location of a symbol at a given text document position. @@ -38,28 +34,16 @@ impl SemanticRequest for GotoDeclarationRequest { fn request(self, ctx: &mut AnalysisContext) -> Option { let source = ctx.source_by_path(&self.path).ok()?; - let offset = ctx.to_typst_pos(self.position, &source)?; - let cursor = offset + 1; - - let ast_node = LinkedNode::new(source.root()).leaf_at(cursor)?; - debug!("ast_node: {ast_node:?}", ast_node = ast_node); - let deref_target = get_deref_target(ast_node, cursor)?; - - let use_site = deref_target.node(); - let origin_selection_range = ctx.to_lsp_range(use_site.range(), &source); + let deref_target = ctx.deref_syntax_at(&source, self.position, 1)?; + let origin_selection_range = ctx.to_lsp_range(deref_target.node().range(), &source); let def_use = ctx.def_use(source.clone())?; let ref_spans = find_declarations(ctx, def_use, deref_target)?; let mut links = vec![]; for ref_range in ref_spans { - let ref_id = source.id(); - let ref_source = &source; - - let span_path = ctx.path_for_id(ref_id).ok()?; - let range = ctx.to_lsp_range(ref_range, ref_source); - - let uri = path_to_url(&span_path).ok()?; + let uri = ctx.uri_for_id(source.id()).ok()?; + let range = ctx.to_lsp_range(ref_range, &source); links.push(LocationLink { origin_selection_range: Some(origin_selection_range), diff --git a/crates/tinymist-query/src/goto_definition.rs b/crates/tinymist-query/src/goto_definition.rs index 9826b83c..5c2c6759 100644 --- a/crates/tinymist-query/src/goto_definition.rs +++ b/crates/tinymist-query/src/goto_definition.rs @@ -1,6 +1,4 @@ -use log::debug; - -use crate::{analysis::find_definition, prelude::*, syntax::get_deref_target}; +use crate::{analysis::find_definition, prelude::*}; /// The [`textDocument/definition`] request asks the server for the definition /// location of a symbol at a given text document position. @@ -34,22 +32,14 @@ impl StatefulRequest for GotoDefinitionRequest { doc: Option, ) -> Option { let source = ctx.source_by_path(&self.path).ok()?; - let offset = ctx.to_typst_pos(self.position, &source)?; - let cursor = offset + 1; - - let ast_node = LinkedNode::new(source.root()).leaf_at(cursor)?; - debug!("ast_node: {ast_node:?}", ast_node = ast_node); - - let deref_target = get_deref_target(ast_node, cursor)?; - let use_site = deref_target.node().clone(); - let origin_selection_range = ctx.to_lsp_range(use_site.range(), &source); + let deref_target = ctx.deref_syntax_at(&source, self.position, 1)?; + let origin_selection_range = ctx.to_lsp_range(deref_target.node().range(), &source); let def = find_definition(ctx, source.clone(), doc.as_ref(), deref_target)?; let (fid, def_range) = def.def_at?; - let span_path = ctx.path_for_id(fid).ok()?; - let uri = path_to_url(&span_path).ok()?; + let uri = ctx.uri_for_id(fid).ok()?; let range = ctx.to_lsp_range_(def_range, fid)?; diff --git a/crates/tinymist-query/src/prelude.rs b/crates/tinymist-query/src/prelude.rs index 959c16e2..60564a91 100644 --- a/crates/tinymist-query/src/prelude.rs +++ b/crates/tinymist-query/src/prelude.rs @@ -15,7 +15,7 @@ pub use lsp_types::{ Hover, InlayHint, LanguageString, Location as LspLocation, LocationLink, MarkedString, MarkupContent, MarkupKind, Position as LspPosition, PrepareRenameResponse, SelectionRange, SemanticTokens, SemanticTokensDelta, SemanticTokensFullDeltaResult, SemanticTokensResult, - SignatureHelp, SignatureInformation, SymbolInformation, Url, WorkspaceEdit, + SignatureHelp, SignatureInformation, SymbolInformation, TextEdit, Url, WorkspaceEdit, }; pub use reflexo::vector::ir::DefId; pub use serde_json::Value as JsonValue; diff --git a/crates/tinymist-query/src/prepare_rename.rs b/crates/tinymist-query/src/prepare_rename.rs index b58f452d..9f98406f 100644 --- a/crates/tinymist-query/src/prepare_rename.rs +++ b/crates/tinymist-query/src/prepare_rename.rs @@ -1,7 +1,6 @@ use crate::{ analysis::{find_definition, DefinitionLink}, prelude::*, - syntax::get_deref_target, }; use log::debug; @@ -39,16 +38,8 @@ impl StatefulRequest for PrepareRenameRequest { doc: Option, ) -> Option { let source = ctx.source_by_path(&self.path).ok()?; - - let offset = ctx.to_typst_pos(self.position, &source)?; - let cursor = offset + 1; - - let ast_node = LinkedNode::new(source.root()).leaf_at(cursor)?; - debug!("ast_node: {ast_node:?}", ast_node = ast_node); - - let deref_target = get_deref_target(ast_node, cursor)?; - let use_site = deref_target.node().clone(); - let origin_selection_range = ctx.to_lsp_range(use_site.range(), &source); + let deref_target = ctx.deref_syntax_at(&source, self.position, 1)?; + let origin_selection_range = ctx.to_lsp_range(deref_target.node().range(), &source); let lnk = find_definition(ctx, source.clone(), doc.as_ref(), deref_target)?; validate_renaming_definition(&lnk)?; diff --git a/crates/tinymist-query/src/references.rs b/crates/tinymist-query/src/references.rs index c244dcb8..4f3af72e 100644 --- a/crates/tinymist-query/src/references.rs +++ b/crates/tinymist-query/src/references.rs @@ -2,7 +2,7 @@ use log::debug; use crate::{ prelude::*, - syntax::{get_deref_target, DerefTarget, IdentRef}, + syntax::{DerefTarget, IdentRef}, SemanticRequest, }; @@ -24,12 +24,7 @@ impl SemanticRequest for ReferencesRequest { fn request(self, ctx: &mut AnalysisContext) -> Option { let source = ctx.source_by_path(&self.path).ok()?; - let offset = ctx.to_typst_pos(self.position, &source)?; - let cursor = offset + 1; - - let ast_node = LinkedNode::new(source.root()).leaf_at(cursor)?; - debug!("ast_node: {ast_node:?}", ast_node = ast_node); - let deref_target = get_deref_target(ast_node, cursor)?; + let deref_target = ctx.deref_syntax_at(&source, self.position, 1)?; let def_use = ctx.def_use(source.clone())?; let locations = find_references(ctx, def_use, deref_target, ctx.position_encoding())?; @@ -119,8 +114,7 @@ pub(crate) fn find_references_root( position_encoding: PositionEncoding, ) -> Option> { let def_source = ctx.source_by_id(def_fid).ok()?; - let def_path = ctx.path_for_id(def_fid).ok()?; - let uri = path_to_url(&def_path).ok()?; + let uri = ctx.uri_for_id(def_fid).ok()?; // todo: reuse uri, range to location let mut references = def_use @@ -143,8 +137,7 @@ pub(crate) fn find_references_root( let ref_source = ctx.ctx.source_by_id(ref_fid).ok()?; let def_use = ctx.ctx.def_use(ref_source.clone())?; - let uri = ctx.ctx.path_for_id(ref_fid).ok()?; - let uri = path_to_url(&uri).ok()?; + let uri = ctx.ctx.uri_for_id(ref_fid).ok()?; let mut redefines = vec![]; if let Some((id, _def)) = def_use.get_def(def_fid, &def_ident) { diff --git a/crates/tinymist-query/src/rename.rs b/crates/tinymist-query/src/rename.rs index 8e158df9..13e0d491 100644 --- a/crates/tinymist-query/src/rename.rs +++ b/crates/tinymist-query/src/rename.rs @@ -1,10 +1,4 @@ -use log::debug; -use lsp_types::TextEdit; - -use crate::{ - analysis::find_definition, find_references, prelude::*, syntax::get_deref_target, - validate_renaming_definition, -}; +use crate::{analysis::find_definition, find_references, prelude::*, validate_renaming_definition}; /// The [`textDocument/rename`] request is sent from the client to the server to /// ask the server to compute a workspace change so that the client can perform @@ -30,14 +24,7 @@ impl StatefulRequest for RenameRequest { doc: Option, ) -> Option { let source = ctx.source_by_path(&self.path).ok()?; - - let offset = ctx.to_typst_pos(self.position, &source)?; - let cursor = offset + 1; - - let ast_node = LinkedNode::new(source.root()).leaf_at(cursor)?; - debug!("ast_node: {ast_node:?}", ast_node = ast_node); - - let deref_target = get_deref_target(ast_node, cursor)?; + let deref_target = ctx.deref_syntax_at(&source, self.position, 1)?; let lnk = find_definition(ctx, source.clone(), doc.as_ref(), deref_target.clone())?; @@ -53,8 +40,7 @@ impl StatefulRequest for RenameRequest { let def_loc = { let def_source = ctx.source_by_id(fid).ok()?; - let span_path = ctx.path_for_id(fid).ok()?; - let uri = path_to_url(&span_path).ok()?; + let uri = ctx.uri_for_id(fid).ok()?; let Some(range) = lnk.name_range else { log::warn!("rename: no name range");