diff --git a/crates/tinymist-query/Cargo.toml b/crates/tinymist-query/Cargo.toml index b21244e1..63df20ae 100644 --- a/crates/tinymist-query/Cargo.toml +++ b/crates/tinymist-query/Cargo.toml @@ -58,7 +58,7 @@ insta.workspace = true serde.workspace = true serde_json.workspace = true typst-assets = { workspace = true, features = ["fonts"] } -reflexo-typst.workspace = true +reflexo-typst = { workspace = true, features = ["no-content-hint"] } sha2 = { version = "0.10" } hex = { version = "0.4" } diff --git a/crates/tinymist-query/src/fixtures/folding_range/heading-in-multiple-content.typ b/crates/tinymist-query/src/fixtures/folding_range/heading-in-multiple-content.typ new file mode 100644 index 00000000..e0c912b3 --- /dev/null +++ b/crates/tinymist-query/src/fixtures/folding_range/heading-in-multiple-content.typ @@ -0,0 +1,6 @@ +#let slides(..args) = args +#slides()[ + = Heading +][ + +] \ No newline at end of file diff --git a/crates/tinymist-query/src/fixtures/folding_range/multiple-content-2.typ b/crates/tinymist-query/src/fixtures/folding_range/multiple-content-2.typ new file mode 100644 index 00000000..c5061f21 --- /dev/null +++ b/crates/tinymist-query/src/fixtures/folding_range/multiple-content-2.typ @@ -0,0 +1,6 @@ +#let slides(..args) = args +#slides[ + +][ + +] \ No newline at end of file diff --git a/crates/tinymist-query/src/fixtures/folding_range/multiple-content.typ b/crates/tinymist-query/src/fixtures/folding_range/multiple-content.typ new file mode 100644 index 00000000..c72ec8d1 --- /dev/null +++ b/crates/tinymist-query/src/fixtures/folding_range/multiple-content.typ @@ -0,0 +1,6 @@ +#let slides(..args) = args +#slides()[ + +][ + +] \ No newline at end of file diff --git a/crates/tinymist-query/src/fixtures/folding_range/snaps/test@array_folding.typ.snap b/crates/tinymist-query/src/fixtures/folding_range/snaps/test@array_folding.typ.snap index fc89e5a4..822f960e 100644 --- a/crates/tinymist-query/src/fixtures/folding_range/snaps/test@array_folding.typ.snap +++ b/crates/tinymist-query/src/fixtures/folding_range/snaps/test@array_folding.typ.snap @@ -1,14 +1,23 @@ --- source: crates/tinymist-query/src/folding_range.rs -expression: "JsonRepr::new_pure(result.unwrap())" +expression: "JsonRepr::new_pure(json!({ \"false\": result_false, \"true\": result_true, }))" input_file: crates/tinymist-query/src/fixtures/folding_range/array_folding.typ --- -[ - { - "collapsedText": "", - "endCharacter": 1, - "endLine": 4, - "startCharacter": 1, - "startLine": 0 - } -] +{ + "false": [ + { + "collapsedText": "", + "endCharacter": 1, + "endLine": 4, + "startCharacter": 1, + "startLine": 0 + } + ], + "true": [ + { + "collapsedText": "", + "endLine": 4, + "startLine": 0 + } + ] +} diff --git a/crates/tinymist-query/src/fixtures/folding_range/snaps/test@base.typ.snap b/crates/tinymist-query/src/fixtures/folding_range/snaps/test@base.typ.snap index 8d25e9b0..46d5dc83 100644 --- a/crates/tinymist-query/src/fixtures/folding_range/snaps/test@base.typ.snap +++ b/crates/tinymist-query/src/fixtures/folding_range/snaps/test@base.typ.snap @@ -1,35 +1,54 @@ --- source: crates/tinymist-query/src/folding_range.rs -expression: "JsonRepr::new_pure(result.unwrap())" +expression: "JsonRepr::new_pure(json!({ \"false\": result_false, \"true\": result_true, }))" input_file: crates/tinymist-query/src/fixtures/folding_range/base.typ --- -[ - { - "collapsedText": "Heading 2", - "endCharacter": 12, - "endLine": 2, - "startCharacter": 3, - "startLine": 2 - }, - { - "collapsedText": "Heading 1", - "endCharacter": 11, - "endLine": 3, - "startCharacter": 2, - "startLine": 0 - }, - { - "collapsedText": "", - "endCharacter": 1, - "endLine": 9, - "startCharacter": 9, - "startLine": 6 - }, - { - "collapsedText": "Heading 3", - "endCharacter": 11, - "endLine": 9, - "startCharacter": 2, - "startLine": 4 - } -] +{ + "false": [ + { + "collapsedText": "Heading 2", + "endCharacter": 12, + "endLine": 2, + "startCharacter": 3, + "startLine": 2 + }, + { + "collapsedText": "Heading 1", + "endCharacter": 11, + "endLine": 3, + "startCharacter": 2, + "startLine": 0 + }, + { + "collapsedText": "", + "endCharacter": 1, + "endLine": 9, + "startCharacter": 9, + "startLine": 6 + }, + { + "collapsedText": "Heading 3", + "endCharacter": 11, + "endLine": 9, + "startCharacter": 2, + "startLine": 4 + } + ], + "true": [ + { + "collapsedText": "Heading 1", + "endLine": 3, + "startLine": 0 + }, + { + "collapsedText": "", + "endLine": 9, + "startLine": 6 + }, + { + "collapsedText": "Heading 3", + "endLine": 9, + "startLine": 4 + } + ] +} diff --git a/crates/tinymist-query/src/fixtures/folding_range/snaps/test@heading-in-multiple-content.typ.snap b/crates/tinymist-query/src/fixtures/folding_range/snaps/test@heading-in-multiple-content.typ.snap new file mode 100644 index 00000000..8afb2fab --- /dev/null +++ b/crates/tinymist-query/src/fixtures/folding_range/snaps/test@heading-in-multiple-content.typ.snap @@ -0,0 +1,49 @@ +--- +source: crates/tinymist-query/src/folding_range.rs +expression: "JsonRepr::new_pure(json!({ \"false\": result_false, \"true\": result_true, }))" +input_file: crates/tinymist-query/src/fixtures/folding_range/heading-in-multiple-content.typ +--- +{ + "false": [ + { + "collapsedText": "Heading", + "endCharacter": 11, + "endLine": 3, + "startCharacter": 4, + "startLine": 2 + }, + { + "collapsedText": "", + "endCharacter": 1, + "endLine": 3, + "startCharacter": 9, + "startLine": 1 + }, + { + "collapsedText": "", + "endCharacter": 1, + "endLine": 5, + "startCharacter": 1, + "startLine": 3 + }, + { + "collapsedText": "", + "endCharacter": 1, + "endLine": 5, + "startCharacter": 7, + "startLine": 1 + } + ], + "true": [ + { + "collapsedText": "", + "endLine": 5, + "startLine": 3 + }, + { + "collapsedText": "", + "endLine": 5, + "startLine": 1 + } + ] +} diff --git a/crates/tinymist-query/src/fixtures/folding_range/snaps/test@headings-in-blocks.typ.snap b/crates/tinymist-query/src/fixtures/folding_range/snaps/test@headings-in-blocks.typ.snap index e0aa111c..becbf5ec 100644 --- a/crates/tinymist-query/src/fixtures/folding_range/snaps/test@headings-in-blocks.typ.snap +++ b/crates/tinymist-query/src/fixtures/folding_range/snaps/test@headings-in-blocks.typ.snap @@ -1,49 +1,73 @@ --- source: crates/tinymist-query/src/folding_range.rs -expression: "JsonRepr::new_pure(result.unwrap())" +expression: "JsonRepr::new_pure(json!({ \"false\": result_false, \"true\": result_true, }))" input_file: crates/tinymist-query/src/fixtures/folding_range/headings-in-blocks.typ --- -[ - { - "collapsedText": "", - "endCharacter": 11, - "endLine": 2, - "startCharacter": 8, - "startLine": 2 - }, - { - "collapsedText": "", - "endCharacter": 13, - "endLine": 6, - "startCharacter": 10, - "startLine": 6 - }, - { - "collapsedText": "Heading 2", - "endCharacter": 16, - "endLine": 8, - "startCharacter": 7, - "startLine": 5 - }, - { - "collapsedText": "", - "endCharacter": 3, - "endLine": 7, - "startCharacter": 11, - "startLine": 3 - }, - { - "collapsedText": "Heading 1", - "endCharacter": 14, - "endLine": 8, - "startCharacter": 5, - "startLine": 1 - }, - { - "collapsedText": "", - "endCharacter": 1, - "endLine": 8, - "startCharacter": 9, - "startLine": 0 - } -] +{ + "false": [ + { + "collapsedText": "", + "endCharacter": 11, + "endLine": 2, + "startCharacter": 8, + "startLine": 2 + }, + { + "collapsedText": "", + "endCharacter": 13, + "endLine": 6, + "startCharacter": 10, + "startLine": 6 + }, + { + "collapsedText": "Heading 2", + "endCharacter": 16, + "endLine": 8, + "startCharacter": 7, + "startLine": 5 + }, + { + "collapsedText": "", + "endCharacter": 3, + "endLine": 7, + "startCharacter": 11, + "startLine": 3 + }, + { + "collapsedText": "Heading 1", + "endCharacter": 14, + "endLine": 8, + "startCharacter": 5, + "startLine": 1 + }, + { + "collapsedText": "", + "endCharacter": 1, + "endLine": 8, + "startCharacter": 9, + "startLine": 0 + } + ], + "true": [ + { + "collapsedText": "Heading 2", + "endLine": 8, + "startLine": 5 + }, + { + "collapsedText": "", + "endLine": 7, + "startLine": 3 + }, + { + "collapsedText": "Heading 1", + "endLine": 8, + "startLine": 1 + }, + { + "collapsedText": "", + "endLine": 8, + "startLine": 0 + } + ] +} diff --git a/crates/tinymist-query/src/fixtures/folding_range/snaps/test@multiple-content-2.typ.snap b/crates/tinymist-query/src/fixtures/folding_range/snaps/test@multiple-content-2.typ.snap new file mode 100644 index 00000000..e13f6bea --- /dev/null +++ b/crates/tinymist-query/src/fixtures/folding_range/snaps/test@multiple-content-2.typ.snap @@ -0,0 +1,42 @@ +--- +source: crates/tinymist-query/src/folding_range.rs +expression: "JsonRepr::new_pure(json!({ \"false\": result_false, \"true\": result_true, }))" +input_file: crates/tinymist-query/src/fixtures/folding_range/multiple-content-2.typ +--- +{ + "false": [ + { + "collapsedText": "", + "endCharacter": 1, + "endLine": 3, + "startCharacter": 7, + "startLine": 1 + }, + { + "collapsedText": "", + "endCharacter": 1, + "endLine": 5, + "startCharacter": 1, + "startLine": 3 + }, + { + "collapsedText": "", + "endCharacter": 1, + "endLine": 5, + "startCharacter": 7, + "startLine": 1 + } + ], + "true": [ + { + "collapsedText": "", + "endLine": 5, + "startLine": 3 + }, + { + "collapsedText": "", + "endLine": 5, + "startLine": 1 + } + ] +} diff --git a/crates/tinymist-query/src/fixtures/folding_range/snaps/test@multiple-content.typ.snap b/crates/tinymist-query/src/fixtures/folding_range/snaps/test@multiple-content.typ.snap new file mode 100644 index 00000000..fc1e03ab --- /dev/null +++ b/crates/tinymist-query/src/fixtures/folding_range/snaps/test@multiple-content.typ.snap @@ -0,0 +1,42 @@ +--- +source: crates/tinymist-query/src/folding_range.rs +expression: "JsonRepr::new_pure(json!({ \"false\": result_false, \"true\": result_true, }))" +input_file: crates/tinymist-query/src/fixtures/folding_range/multiple-content.typ +--- +{ + "false": [ + { + "collapsedText": "", + "endCharacter": 1, + "endLine": 3, + "startCharacter": 9, + "startLine": 1 + }, + { + "collapsedText": "", + "endCharacter": 1, + "endLine": 5, + "startCharacter": 1, + "startLine": 3 + }, + { + "collapsedText": "", + "endCharacter": 1, + "endLine": 5, + "startCharacter": 7, + "startLine": 1 + } + ], + "true": [ + { + "collapsedText": "", + "endLine": 5, + "startLine": 3 + }, + { + "collapsedText": "", + "endLine": 5, + "startLine": 1 + } + ] +} diff --git a/crates/tinymist-query/src/fixtures/folding_range/snaps/test@nested-blocks.typ.snap b/crates/tinymist-query/src/fixtures/folding_range/snaps/test@nested-blocks.typ.snap index 8af199d3..58d40479 100644 --- a/crates/tinymist-query/src/fixtures/folding_range/snaps/test@nested-blocks.typ.snap +++ b/crates/tinymist-query/src/fixtures/folding_range/snaps/test@nested-blocks.typ.snap @@ -1,70 +1,114 @@ --- source: crates/tinymist-query/src/folding_range.rs -expression: "JsonRepr::new_pure(result.unwrap())" +expression: "JsonRepr::new_pure(json!({ \"false\": result_false, \"true\": result_true, }))" input_file: crates/tinymist-query/src/fixtures/folding_range/nested-blocks.typ --- -[ - { - "collapsedText": "", - "endCharacter": 5, - "endLine": 4, - "startCharacter": 12, - "startLine": 2 - }, - { - "collapsedText": "", - "endCharacter": 3, - "endLine": 5, - "startCharacter": 10, - "startLine": 1 - }, - { - "collapsedText": "", - "endCharacter": 3, - "endLine": 8, - "startCharacter": 11, - "startLine": 6 - }, - { - "collapsedText": "", - "endCharacter": 4, - "endLine": 8, - "startCharacter": 10, - "startLine": 6 - }, - { - "collapsedText": "", - "endCharacter": 5, - "endLine": 12, - "startCharacter": 12, - "startLine": 10 - }, - { - "collapsedText": "", - "endCharacter": 5, - "endLine": 14, - "startCharacter": 8, - "startLine": 12 - }, - { - "collapsedText": "", - "endCharacter": 3, - "endLine": 15, - "startCharacter": 10, - "startLine": 9 - }, - { - "collapsedText": "", - "endCharacter": 3, - "endLine": 17, - "startCharacter": 10, - "startLine": 16 - }, - { - "collapsedText": "", - "endCharacter": 1, - "endLine": 18, - "startCharacter": 9, - "startLine": 0 - } -] +{ + "false": [ + { + "collapsedText": "", + "endCharacter": 5, + "endLine": 4, + "startCharacter": 12, + "startLine": 2 + }, + { + "collapsedText": "", + "endCharacter": 3, + "endLine": 5, + "startCharacter": 10, + "startLine": 1 + }, + { + "collapsedText": "", + "endCharacter": 3, + "endLine": 8, + "startCharacter": 11, + "startLine": 6 + }, + { + "collapsedText": "", + "endCharacter": 4, + "endLine": 8, + "startCharacter": 10, + "startLine": 6 + }, + { + "collapsedText": "", + "endCharacter": 5, + "endLine": 12, + "startCharacter": 12, + "startLine": 10 + }, + { + "collapsedText": "", + "endCharacter": 5, + "endLine": 14, + "startCharacter": 8, + "startLine": 12 + }, + { + "collapsedText": "", + "endCharacter": 3, + "endLine": 15, + "startCharacter": 10, + "startLine": 9 + }, + { + "collapsedText": "", + "endCharacter": 3, + "endLine": 17, + "startCharacter": 10, + "startLine": 16 + }, + { + "collapsedText": "", + "endCharacter": 1, + "endLine": 18, + "startCharacter": 9, + "startLine": 0 + } + ], + "true": [ + { + "collapsedText": "", + "endLine": 4, + "startLine": 2 + }, + { + "collapsedText": "", + "endLine": 5, + "startLine": 1 + }, + { + "collapsedText": "", + "endLine": 8, + "startLine": 6 + }, + { + "collapsedText": "", + "endLine": 11, + "startLine": 10 + }, + { + "collapsedText": "", + "endLine": 14, + "startLine": 12 + }, + { + "collapsedText": "", + "endLine": 15, + "startLine": 9 + }, + { + "collapsedText": "", + "endLine": 17, + "startLine": 16 + }, + { + "collapsedText": "", + "endLine": 18, + "startLine": 0 + } + ] +} diff --git a/crates/tinymist-query/src/fixtures/folding_range/snaps/test@paren_folding.typ.snap b/crates/tinymist-query/src/fixtures/folding_range/snaps/test@paren_folding.typ.snap index 80d1bd06..5841c9c6 100644 --- a/crates/tinymist-query/src/fixtures/folding_range/snaps/test@paren_folding.typ.snap +++ b/crates/tinymist-query/src/fixtures/folding_range/snaps/test@paren_folding.typ.snap @@ -1,14 +1,23 @@ --- source: crates/tinymist-query/src/folding_range.rs -expression: "JsonRepr::new_pure(result.unwrap())" +expression: "JsonRepr::new_pure(json!({ \"false\": result_false, \"true\": result_true, }))" input_file: crates/tinymist-query/src/fixtures/folding_range/paren_folding.typ --- -[ - { - "collapsedText": "", - "endCharacter": 1, - "endLine": 2, - "startCharacter": 1, - "startLine": 0 - } -] +{ + "false": [ + { + "collapsedText": "", + "endCharacter": 1, + "endLine": 2, + "startCharacter": 1, + "startLine": 0 + } + ], + "true": [ + { + "collapsedText": "", + "endLine": 2, + "startLine": 0 + } + ] +} diff --git a/crates/tinymist-query/src/folding_range.rs b/crates/tinymist-query/src/folding_range.rs index 218eef0d..9dab6739 100644 --- a/crates/tinymist-query/src/folding_range.rs +++ b/crates/tinymist-query/src/folding_range.rs @@ -1,3 +1,5 @@ +use hashbrown::HashSet; + use crate::{ prelude::*, syntax::{get_lexical_hierarchy, LexicalHierarchy, LexicalKind, LexicalScopeKind}, @@ -42,12 +44,45 @@ impl SyntaxRequest for FoldingRangeRequest { &symbols, source, position_encoding, - line_folding_only, loc, loc, true, &mut results, ); + + // Generally process of folding ranges with line_folding_only + if line_folding_only { + let mut max_line = 0; + for r in &mut results { + r.start_character = None; + r.end_character = None; + max_line = max_line.max(r.end_line); + } + let mut line_coverage = vec![false; max_line as usize + 1]; + let mut pair_coverage = HashSet::new(); + results.reverse(); + results.retain_mut(|r| { + if pair_coverage.contains(&(r.start_line, r.end_line)) { + return false; + } + + if line_coverage[r.start_line as usize] { + r.start_line += 1; + } + if line_coverage[r.end_line as usize] { + r.end_line = r.end_line.saturating_sub(1); + } + if r.start_line >= r.end_line { + return false; + } + + line_coverage[r.start_line as usize] = true; + pair_coverage.insert((r.start_line, r.end_line)); + true + }); + results.reverse(); + } + if false { trace!("FoldingRangeRequest(line_folding_only={line_folding_only}) symbols: {symbols:#?} results: {results:#?}"); } @@ -58,13 +93,10 @@ impl SyntaxRequest for FoldingRangeRequest { type LoC = (u32, Option); -#[allow(clippy::too_many_arguments)] -#[allow(deprecated)] fn calc_folding_range( symbols: &[LexicalHierarchy], source: &Source, position_encoding: PositionEncoding, - line_folding_only: bool, parent_last_loc: LoC, last_loc: LoC, is_last_range: bool, @@ -79,7 +111,7 @@ fn calc_folding_range( start_line: rng.start.line, start_character: Some(rng.start.character), end_line: rng.end.line, - end_character: line_folding_only.then_some(rng.end.character), + end_character: Some(rng.end.character), kind: None, collapsed_text: Some(e.info.name.clone()), }; @@ -113,7 +145,6 @@ fn calc_folding_range( ch, source, position_encoding, - line_folding_only, parent_last_loc, last_loc, !is_not_final_last_range, @@ -133,15 +164,23 @@ mod tests { #[test] fn test() { snapshot_testing("folding_range", &|world, path| { - let request = FoldingRangeRequest { - path: path.clone(), - line_folding_only: true, + let mut r = |line_folding_only| { + let request = FoldingRangeRequest { + path: path.clone(), + line_folding_only, + }; + + let source = world.source_by_path(&path).unwrap(); + + request.request(&source, PositionEncoding::Utf16) }; - let source = world.source_by_path(&path).unwrap(); - - let result = request.request(&source, PositionEncoding::Utf16); - assert_snapshot!(JsonRepr::new_pure(result.unwrap())); + let result_false = r(false); + let result_true = r(true); + assert_snapshot!(JsonRepr::new_pure(json!({ + "false": result_false, + "true": result_true, + }))); }); } } diff --git a/crates/tinymist-query/src/tests.rs b/crates/tinymist-query/src/tests.rs index cf758de6..6414d73a 100644 --- a/crates/tinymist-query/src/tests.rs +++ b/crates/tinymist-query/src/tests.rs @@ -23,6 +23,7 @@ use typst::{diag::PackageError, foundations::Bytes}; pub use insta::assert_snapshot; pub use reflexo_typst::TypstSystemWorld; pub use serde::Serialize; +pub use serde_json::json; use crate::{ analysis::{Analysis, AnalysisResources}, diff --git a/crates/tinymist/Cargo.toml b/crates/tinymist/Cargo.toml index 6366d218..c868f828 100644 --- a/crates/tinymist/Cargo.toml +++ b/crates/tinymist/Cargo.toml @@ -100,7 +100,10 @@ embed-fonts = ["typst-assets/fonts"] # Disable the default content hint. # This requires modifying typst. -no-content-hint = ["reflexo-typst/no-content-hint"] +no-content-hint = [ + "reflexo-typst/no-content-hint", + "reflexo-vec2svg/no-content-hint", +] preview = [ "typst-preview",