fix: don't panic on bad node range

This commit is contained in:
Myriad-Dreamin 2024-03-08 21:13:22 +08:00
parent 4abde9de2e
commit 964def25a9
13 changed files with 51 additions and 92 deletions

View file

@ -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())
}
}

View file

@ -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)),
}
}

View file

@ -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(),

View file

@ -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

View file

@ -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()?;

View file

@ -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),
})
}
}

View file

@ -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!(

View file

@ -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);

View file

@ -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> {

View file

@ -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))),

View file

@ -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)?;

View file

@ -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(),

View file

@ -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 => {