mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-08-03 17:58:17 +00:00
fix: don't panic on bad node range
This commit is contained in:
parent
4abde9de2e
commit
964def25a9
13 changed files with 51 additions and 92 deletions
|
@ -15,15 +15,14 @@ impl CompletionRequest {
|
|||
position_encoding: PositionEncoding,
|
||||
) -> Option<CompletionResponse> {
|
||||
let source = get_suitable_source_in_workspace(world, &self.path).ok()?;
|
||||
let typst_offset =
|
||||
lsp_to_typst::position_to_offset(self.position, position_encoding, &source);
|
||||
let typst_offset = lsp_to_typst::position(self.position, position_encoding, &source)?;
|
||||
|
||||
let (typst_start_offset, completions) =
|
||||
typst_ide::autocomplete(world, doc.as_deref(), &source, typst_offset, self.explicit)?;
|
||||
|
||||
let lsp_start_position =
|
||||
typst_to_lsp::offset_to_position(typst_start_offset, position_encoding, &source);
|
||||
let replace_range = LspRawRange::new(lsp_start_position, self.position);
|
||||
let replace_range = LspRange::new(lsp_start_position, self.position);
|
||||
Some(typst_to_lsp::completions(&completions, replace_range).into())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,10 +30,10 @@ fn convert_diagnostic(
|
|||
if let Some((id, span)) = diagnostic_span_id(typst_diagnostic) {
|
||||
uri = Url::from_file_path(project.path_for_id(id)?).unwrap();
|
||||
let source = project.source(id)?;
|
||||
lsp_range = diagnostic_range(&source, span, position_encoding).raw_range;
|
||||
lsp_range = diagnostic_range(&source, span, position_encoding);
|
||||
} else {
|
||||
uri = Url::from_file_path(project.root.clone()).unwrap();
|
||||
lsp_range = LspRawRange::default();
|
||||
lsp_range = LspRange::default();
|
||||
};
|
||||
|
||||
let lsp_severity = diagnostic_severity(typst_diagnostic.severity);
|
||||
|
@ -71,7 +71,7 @@ fn tracepoint_to_relatedinformation(
|
|||
return Ok(Some(DiagnosticRelatedInformation {
|
||||
location: LspLocation {
|
||||
uri,
|
||||
range: lsp_range.raw_range,
|
||||
range: lsp_range,
|
||||
},
|
||||
message: tracepoint.v.to_string(),
|
||||
}));
|
||||
|
@ -121,10 +121,7 @@ fn diagnostic_range(
|
|||
let typst_range = node.range();
|
||||
typst_to_lsp::range(typst_range, source, position_encoding)
|
||||
}
|
||||
None => LspRange::new(
|
||||
LspRawRange::new(LspPosition::new(0, 0), LspPosition::new(0, 0)),
|
||||
position_encoding,
|
||||
),
|
||||
None => LspRange::new(LspPosition::new(0, 0), LspPosition::new(0, 0)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,8 +30,7 @@ fn filter_document_symbols(
|
|||
symbols
|
||||
.iter()
|
||||
.map(|e| {
|
||||
let rng =
|
||||
typst_to_lsp::range(e.info.range.clone(), source, position_encoding).raw_range;
|
||||
let rng = typst_to_lsp::range(e.info.range.clone(), source, position_encoding);
|
||||
|
||||
DocumentSymbol {
|
||||
name: e.info.name.clone(),
|
||||
|
|
|
@ -57,7 +57,7 @@ fn calc_folding_range(
|
|||
ranges: &mut Vec<FoldingRange>,
|
||||
) {
|
||||
for (i, e) in symbols.iter().enumerate() {
|
||||
let rng = typst_to_lsp::range(e.info.range.clone(), source, position_encoding).raw_range;
|
||||
let rng = typst_to_lsp::range(e.info.range.clone(), source, position_encoding);
|
||||
let is_not_last_range = i + 1 < symbols.len();
|
||||
let is_not_final_last_range = !is_last_range || is_not_last_range;
|
||||
|
||||
|
@ -72,8 +72,7 @@ fn calc_folding_range(
|
|||
|
||||
let next_start = if is_not_last_range {
|
||||
let next = &symbols[i + 1];
|
||||
let next_rng =
|
||||
typst_to_lsp::range(next.info.range.clone(), source, position_encoding).raw_range;
|
||||
let next_rng = typst_to_lsp::range(next.info.range.clone(), source, position_encoding);
|
||||
(next_rng.start.line, Some(next_rng.start.character))
|
||||
} else if is_not_final_last_range {
|
||||
parent_last_loc
|
||||
|
|
|
@ -16,8 +16,7 @@ impl GotoDefinitionRequest {
|
|||
position_encoding: PositionEncoding,
|
||||
) -> Option<GotoDefinitionResponse> {
|
||||
let source = get_suitable_source_in_workspace(world, &self.path).ok()?;
|
||||
let typst_offset =
|
||||
lsp_to_typst::position_to_offset(self.position, position_encoding, &source);
|
||||
let typst_offset = lsp_to_typst::position(self.position, position_encoding, &source)?;
|
||||
|
||||
let ast_node = LinkedNode::new(source.root()).leaf_at(typst_offset)?;
|
||||
|
||||
|
@ -86,13 +85,13 @@ impl GotoDefinitionRequest {
|
|||
};
|
||||
|
||||
let origin_selection_range =
|
||||
typst_to_lsp::range(callee_link.range(), &source, position_encoding).raw_range;
|
||||
typst_to_lsp::range(callee_link.range(), &source, position_encoding);
|
||||
|
||||
let span_path = world.path_for_id(id).ok()?;
|
||||
let span_source = world.source(id).ok()?;
|
||||
let offset = span_source.find(span)?;
|
||||
let typst_range = offset.range();
|
||||
let range = typst_to_lsp::range(typst_range, &span_source, position_encoding).raw_range;
|
||||
let range = typst_to_lsp::range(typst_range, &span_source, position_encoding);
|
||||
|
||||
let uri = Url::from_file_path(span_path).ok()?;
|
||||
|
||||
|
|
|
@ -14,8 +14,7 @@ impl HoverRequest {
|
|||
position_encoding: PositionEncoding,
|
||||
) -> Option<Hover> {
|
||||
let source = get_suitable_source_in_workspace(world, &self.path).ok()?;
|
||||
let typst_offset =
|
||||
lsp_to_typst::position_to_offset(self.position, position_encoding, &source);
|
||||
let typst_offset = lsp_to_typst::position(self.position, position_encoding, &source)?;
|
||||
|
||||
let typst_tooltip = typst_ide::tooltip(world, doc.as_deref(), &source, typst_offset)?;
|
||||
|
||||
|
@ -24,7 +23,7 @@ impl HoverRequest {
|
|||
|
||||
Some(Hover {
|
||||
contents: typst_to_lsp::tooltip(&typst_tooltip),
|
||||
range: Some(range.raw_range),
|
||||
range: Some(range),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use crate::prelude::*;
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct InlayHintRequest {
|
||||
pub path: PathBuf,
|
||||
pub range: LspRawRange,
|
||||
pub range: LspRange,
|
||||
}
|
||||
|
||||
impl InlayHintRequest {
|
||||
|
@ -24,13 +24,7 @@ impl InlayHintRequest {
|
|||
position_encoding: PositionEncoding,
|
||||
) -> Option<Vec<InlayHint>> {
|
||||
let source = get_suitable_source_in_workspace(world, &self.path).ok()?;
|
||||
let range = lsp_to_typst::range(
|
||||
&LspRange {
|
||||
raw_range: self.range,
|
||||
encoding: position_encoding,
|
||||
},
|
||||
&source,
|
||||
);
|
||||
let range = lsp_to_typst::range(self.range, position_encoding, &source)?;
|
||||
|
||||
let hints = inlay_hints(world, &source, range, position_encoding).ok()?;
|
||||
debug!(
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
//! Conversions between Typst and LSP types and representations
|
||||
|
||||
use tower_lsp::lsp_types;
|
||||
use typst::syntax::Source;
|
||||
|
||||
pub type LspPosition = lsp_types::Position;
|
||||
/// The interpretation of an `LspCharacterOffset` depends on the
|
||||
|
@ -15,7 +14,7 @@ pub type TypstSpan = typst::syntax::Span;
|
|||
|
||||
/// An LSP range. It needs its associated `LspPositionEncoding` to be used. The
|
||||
/// `LspRange` struct provides this range with that encoding.
|
||||
pub type LspRawRange = lsp_types::Range;
|
||||
pub type LspRange = lsp_types::Range;
|
||||
pub type TypstRange = std::ops::Range<usize>;
|
||||
|
||||
pub type TypstTooltip = typst_ide::Tooltip;
|
||||
|
@ -57,25 +56,6 @@ impl From<PositionEncoding> for lsp_types::PositionEncodingKind {
|
|||
}
|
||||
}
|
||||
|
||||
/// An LSP range with its associated encoding.
|
||||
pub struct LspRange {
|
||||
pub raw_range: LspRawRange,
|
||||
pub encoding: LspPositionEncoding,
|
||||
}
|
||||
|
||||
impl LspRange {
|
||||
pub fn new(raw_range: LspRawRange, encoding: LspPositionEncoding) -> Self {
|
||||
Self {
|
||||
raw_range,
|
||||
encoding,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_range_on(self, source: &Source) -> TypstRange {
|
||||
lsp_to_typst::range(&self, source)
|
||||
}
|
||||
}
|
||||
|
||||
pub type LspCompletion = lsp_types::CompletionItem;
|
||||
pub type LspCompletionKind = lsp_types::CompletionItemKind;
|
||||
pub type TypstCompletion = typst_ide::Completion;
|
||||
|
@ -86,18 +66,16 @@ pub mod lsp_to_typst {
|
|||
|
||||
use super::*;
|
||||
|
||||
pub fn position_to_offset(
|
||||
pub fn position(
|
||||
lsp_position: LspPosition,
|
||||
lsp_position_encoding: LspPositionEncoding,
|
||||
typst_source: &Source,
|
||||
) -> TypstOffset {
|
||||
) -> Option<TypstOffset> {
|
||||
match lsp_position_encoding {
|
||||
LspPositionEncoding::Utf8 => {
|
||||
let line_index = lsp_position.line as usize;
|
||||
let column_index = lsp_position.character as usize;
|
||||
typst_source
|
||||
.line_column_to_byte(line_index, column_index)
|
||||
.unwrap()
|
||||
typst_source.line_column_to_byte(line_index, column_index)
|
||||
}
|
||||
LspPositionEncoding::Utf16 => {
|
||||
// We have a line number and a UTF-16 offset into that line. We want a byte
|
||||
|
@ -121,26 +99,30 @@ pub mod lsp_to_typst {
|
|||
let line_index = lsp_position.line as usize;
|
||||
let utf16_offset_in_line = lsp_position.character as usize;
|
||||
|
||||
let byte_line_offset = typst_source.line_to_byte(line_index).unwrap();
|
||||
let utf16_line_offset = typst_source.byte_to_utf16(byte_line_offset).unwrap();
|
||||
let byte_line_offset = typst_source.line_to_byte(line_index)?;
|
||||
let utf16_line_offset = typst_source.byte_to_utf16(byte_line_offset)?;
|
||||
let utf16_offset = utf16_line_offset + utf16_offset_in_line;
|
||||
|
||||
typst_source.utf16_to_byte(utf16_offset).unwrap()
|
||||
typst_source.utf16_to_byte(utf16_offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn range(lsp_range: &LspRange, source: &Source) -> TypstRange {
|
||||
let lsp_start = lsp_range.raw_range.start;
|
||||
let typst_start = position_to_offset(lsp_start, lsp_range.encoding, source);
|
||||
pub fn range(
|
||||
lsp_range: LspRange,
|
||||
lsp_position_encoding: LspPositionEncoding,
|
||||
source: &Source,
|
||||
) -> Option<TypstRange> {
|
||||
let lsp_start = lsp_range.start;
|
||||
let typst_start = position(lsp_start, lsp_position_encoding, source)?;
|
||||
|
||||
let lsp_end = lsp_range.raw_range.end;
|
||||
let typst_end = position_to_offset(lsp_end, lsp_range.encoding, source);
|
||||
let lsp_end = lsp_range.end;
|
||||
let typst_end = position(lsp_end, lsp_position_encoding, source)?;
|
||||
|
||||
TypstRange {
|
||||
Some(TypstRange {
|
||||
start: typst_start,
|
||||
end: typst_end,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -202,8 +184,7 @@ pub mod typst_to_lsp {
|
|||
let typst_end = typst_range.end;
|
||||
let lsp_end = offset_to_position(typst_end, lsp_position_encoding, typst_source);
|
||||
|
||||
let raw_range = LspRawRange::new(lsp_start, lsp_end);
|
||||
LspRange::new(raw_range, lsp_position_encoding)
|
||||
LspRange::new(lsp_start, lsp_end)
|
||||
}
|
||||
|
||||
fn completion_kind(typst_completion_kind: TypstCompletionKind) -> LspCompletionKind {
|
||||
|
@ -234,10 +215,7 @@ pub mod typst_to_lsp {
|
|||
result.to_string()
|
||||
}
|
||||
|
||||
pub fn completion(
|
||||
typst_completion: &TypstCompletion,
|
||||
lsp_replace: LspRawRange,
|
||||
) -> LspCompletion {
|
||||
pub fn completion(typst_completion: &TypstCompletion, lsp_replace: LspRange) -> LspCompletion {
|
||||
let typst_snippet = typst_completion
|
||||
.apply
|
||||
.as_ref()
|
||||
|
@ -257,7 +235,7 @@ pub mod typst_to_lsp {
|
|||
|
||||
pub fn completions(
|
||||
typst_completions: &[TypstCompletion],
|
||||
lsp_replace: LspRawRange,
|
||||
lsp_replace: LspRange,
|
||||
) -> Vec<LspCompletion> {
|
||||
typst_completions
|
||||
.iter()
|
||||
|
@ -343,19 +321,17 @@ mod test {
|
|||
character: 12,
|
||||
};
|
||||
|
||||
let start_offset =
|
||||
lsp_to_typst::position_to_offset(start, PositionEncoding::Utf16, &source);
|
||||
let start_offset = lsp_to_typst::position(start, PositionEncoding::Utf16, &source).unwrap();
|
||||
let start_actual = 0;
|
||||
|
||||
let emoji_offset =
|
||||
lsp_to_typst::position_to_offset(emoji, PositionEncoding::Utf16, &source);
|
||||
let emoji_offset = lsp_to_typst::position(emoji, PositionEncoding::Utf16, &source).unwrap();
|
||||
let emoji_actual = 5;
|
||||
|
||||
let post_emoji_offset =
|
||||
lsp_to_typst::position_to_offset(post_emoji, PositionEncoding::Utf16, &source);
|
||||
lsp_to_typst::position(post_emoji, PositionEncoding::Utf16, &source).unwrap();
|
||||
let post_emoji_actual = 9;
|
||||
|
||||
let end_offset = lsp_to_typst::position_to_offset(end, PositionEncoding::Utf16, &source);
|
||||
let end_offset = lsp_to_typst::position(end, PositionEncoding::Utf16, &source).unwrap();
|
||||
let end_actual = 14;
|
||||
|
||||
assert_eq!(start_offset, start_actual);
|
||||
|
|
|
@ -27,8 +27,8 @@ pub use typst_ts_core::{TypstDocument, TypstFileId};
|
|||
|
||||
pub use crate::analysis::analyze_expr;
|
||||
pub use crate::lsp_typst_boundary::{
|
||||
lsp_to_typst, typst_to_lsp, LspDiagnostic, LspRange, LspRawRange, LspSeverity,
|
||||
PositionEncoding, TypstDiagnostic, TypstSeverity, TypstSpan,
|
||||
lsp_to_typst, typst_to_lsp, LspDiagnostic, LspRange, LspSeverity, PositionEncoding,
|
||||
TypstDiagnostic, TypstSeverity, TypstSpan,
|
||||
};
|
||||
|
||||
pub fn get_suitable_source_in_workspace(w: &TypstSystemWorld, p: &Path) -> FileResult<Source> {
|
||||
|
|
|
@ -14,8 +14,7 @@ impl SelectionRangeRequest {
|
|||
) -> Option<Vec<SelectionRange>> {
|
||||
let mut ranges = Vec::new();
|
||||
for position in self.positions {
|
||||
let typst_offset =
|
||||
lsp_to_typst::position_to_offset(position, position_encoding, &source);
|
||||
let typst_offset = lsp_to_typst::position(position, position_encoding, &source)?;
|
||||
let tree = LinkedNode::new(source.root());
|
||||
let leaf = tree.leaf_at(typst_offset)?;
|
||||
ranges.push(range_for_node(&source, position_encoding, &leaf));
|
||||
|
@ -32,7 +31,7 @@ fn range_for_node(
|
|||
) -> SelectionRange {
|
||||
let range = typst_to_lsp::range(node.range(), source, position_encoding);
|
||||
SelectionRange {
|
||||
range: range.raw_range,
|
||||
range,
|
||||
parent: node
|
||||
.parent()
|
||||
.map(|node| Box::new(range_for_node(source, position_encoding, node))),
|
||||
|
|
|
@ -13,8 +13,7 @@ impl SignatureHelpRequest {
|
|||
position_encoding: PositionEncoding,
|
||||
) -> Option<SignatureHelp> {
|
||||
let source = get_suitable_source_in_workspace(world, &self.path).ok()?;
|
||||
let typst_offset =
|
||||
lsp_to_typst::position_to_offset(self.position, position_encoding, &source);
|
||||
let typst_offset = lsp_to_typst::position(self.position, position_encoding, &source)?;
|
||||
|
||||
let ast_node = LinkedNode::new(source.root()).leaf_at(typst_offset)?;
|
||||
let (callee, callee_node, args) = surrounding_function_syntax(&ast_node)?;
|
||||
|
|
|
@ -58,8 +58,7 @@ fn filter_document_symbols(
|
|||
})
|
||||
.filter(|e| e.info.name.contains(query_string))
|
||||
.map(|e| {
|
||||
let rng =
|
||||
typst_to_lsp::range(e.info.range.clone(), source, position_encoding).raw_range;
|
||||
let rng = typst_to_lsp::range(e.info.range.clone(), source, position_encoding);
|
||||
|
||||
SymbolInformation {
|
||||
name: e.info.name.clone(),
|
||||
|
|
|
@ -8,8 +8,8 @@ use anyhow::anyhow;
|
|||
use futures::future::join_all;
|
||||
use log::{debug, error, trace, warn};
|
||||
use tinymist_query::{
|
||||
CompilerQueryRequest, CompilerQueryResponse, DiagnosticsMap, FoldRequestFeature, LspDiagnostic,
|
||||
LspRange, OnSaveExportRequest, PositionEncoding, SemanticTokenCache,
|
||||
lsp_to_typst, CompilerQueryRequest, CompilerQueryResponse, DiagnosticsMap, FoldRequestFeature,
|
||||
LspDiagnostic, OnSaveExportRequest, PositionEncoding, SemanticTokenCache,
|
||||
};
|
||||
use tokio::sync::{broadcast, mpsc, watch, Mutex, RwLock};
|
||||
use tower_lsp::lsp_types::{Diagnostic, TextDocumentContentChangeEvent, Url};
|
||||
|
@ -394,8 +394,8 @@ impl CompileCluster {
|
|||
let replacement = change.text;
|
||||
match change.range {
|
||||
Some(lsp_range) => {
|
||||
let range =
|
||||
LspRange::new(lsp_range, position_encoding).into_range_on(&meta.content);
|
||||
let range = lsp_to_typst::range(lsp_range, position_encoding, &meta.content)
|
||||
.expect("invalid range");
|
||||
meta.content.edit(range, &replacement);
|
||||
}
|
||||
None => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue