diff options
Diffstat (limited to 'support')
18 files changed, 356 insertions, 232 deletions
diff --git a/support/texlab/CHANGELOG.md b/support/texlab/CHANGELOG.md index 1f0666306e..9009b272c7 100644 --- a/support/texlab/CHANGELOG.md +++ b/support/texlab/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [5.9.2] - 2023-08-14 + +### Fixed + +- Don't crash when using comments inside `\include`-like commands ([#919](https://github.com/latex-lsp/texlab/issues/919)) +- Folding ranges include only the contents instead of the entire range of the structure. + For example, the folding range of an environment will start after the `\begin` and stop before the `\end` + ([#915](https://github.com/latex-lsp/texlab/issues/915)) + ## [5.9.1] - 2023-08-11 ### Fixed diff --git a/support/texlab/Cargo.lock b/support/texlab/Cargo.lock index d0dfd6e75d..ad9296d29f 100644 --- a/support/texlab/Cargo.lock +++ b/support/texlab/Cargo.lock @@ -654,6 +654,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] +name = "folding" +version = "0.0.0" +dependencies = [ + "base-db", + "expect-test", + "rowan", + "syntax", + "test-utils", +] + +[[package]] name = "form_urlencoded" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1622,7 +1633,7 @@ dependencies = [ [[package]] name = "texlab" -version = "5.9.1" +version = "5.9.2" dependencies = [ "anyhow", "assert_unordered", @@ -1640,6 +1651,7 @@ dependencies = [ "encoding_rs", "encoding_rs_io", "fern", + "folding", "fuzzy-matcher", "hover", "insta", diff --git a/support/texlab/crates/folding/Cargo.toml b/support/texlab/crates/folding/Cargo.toml new file mode 100644 index 0000000000..2cf82699ea --- /dev/null +++ b/support/texlab/crates/folding/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "folding" +version = "0.0.0" +license.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true + +[dependencies] +base-db = { path = "../base-db" } +rowan = "0.15.11" +syntax = { path = "../syntax" } + +[dev-dependencies] +expect-test = "1.4.1" +test-utils = { path = "../test-utils" } + +[lib] +doctest = false diff --git a/support/texlab/crates/folding/src/lib.rs b/support/texlab/crates/folding/src/lib.rs new file mode 100644 index 0000000000..c87021015d --- /dev/null +++ b/support/texlab/crates/folding/src/lib.rs @@ -0,0 +1,107 @@ +use base_db::{Document, DocumentData}; +use rowan::{ast::AstNode, TextRange}; +use syntax::{ + bibtex::{self, HasDelims, HasName}, + latex, +}; + +#[derive(Debug)] +pub struct FoldingRange { + pub range: TextRange, + pub kind: FoldingRangeKind, +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] +pub enum FoldingRangeKind { + Section, + Environment, + Entry, +} + +pub fn find_all(document: &Document) -> Vec<FoldingRange> { + let mut builder = FoldingBuilder::default(); + + if let DocumentData::Tex(data) = &document.data { + for node in data.root_node().descendants() { + if let Some(section) = latex::Section::cast(node.clone()) { + builder.fold_section(§ion); + } else if let Some(item) = latex::EnumItem::cast(node.clone()) { + builder.fold_enum_item(&item); + } else if let Some(env) = latex::Environment::cast(node) { + builder.fold_environment(env); + } + } + } else if let DocumentData::Bib(data) = &document.data { + for node in data.root_node().descendants() { + if let Some(entry) = bibtex::Entry::cast(node.clone()) { + builder.fold_entry(&entry); + } else if let Some(string) = bibtex::StringDef::cast(node) { + builder.fold_entry(&string); + } + } + } + + builder.ranges +} + +#[derive(Debug, Default)] +struct FoldingBuilder { + ranges: Vec<FoldingRange>, +} + +impl FoldingBuilder { + fn fold_section(&mut self, section: &latex::Section) -> Option<()> { + let start = section + .name() + .map(|name| latex::small_range(&name).end()) + .or_else(|| section.command().map(|cmd| cmd.text_range().end()))?; + let end = section.syntax().text_range().end(); + + self.ranges.push(FoldingRange { + range: TextRange::new(start, end), + kind: FoldingRangeKind::Section, + }); + + Some(()) + } + + fn fold_enum_item(&mut self, item: &latex::EnumItem) -> Option<()> { + let start = item + .label() + .map(|label| latex::small_range(&label).end()) + .or_else(|| item.command().map(|cmd| cmd.text_range().end()))?; + + let end = item.syntax().text_range().end(); + self.ranges.push(FoldingRange { + range: TextRange::new(start, end), + kind: FoldingRangeKind::Section, + }); + + Some(()) + } + + fn fold_environment(&mut self, env: latex::Environment) -> Option<()> { + let start = latex::small_range(&env.begin()?).end(); + let end = latex::small_range(&env.end()?).start(); + self.ranges.push(FoldingRange { + range: TextRange::new(start, end), + kind: FoldingRangeKind::Environment, + }); + + Some(()) + } + + fn fold_entry(&mut self, entry: &(impl HasName + HasDelims)) -> Option<()> { + let start = entry.name_token()?.text_range().end(); + let end = entry.right_delim_token()?.text_range().start(); + self.ranges.push(FoldingRange { + range: TextRange::new(start, end), + kind: FoldingRangeKind::Entry, + }); + + Some(()) + } +} + +#[cfg(test)] +mod tests; diff --git a/support/texlab/crates/folding/src/tests.rs b/support/texlab/crates/folding/src/tests.rs new file mode 100644 index 0000000000..405b56bda1 --- /dev/null +++ b/support/texlab/crates/folding/src/tests.rs @@ -0,0 +1,88 @@ +use expect_test::{expect, Expect}; + +fn check(input: &str, expect: Expect) { + let fixture = test_utils::fixture::Fixture::parse(input); + let workspace = &fixture.workspace; + let document = workspace.lookup(&fixture.documents[0].uri).unwrap(); + let data = crate::find_all(document); + expect.assert_debug_eq(&data); +} + +#[test] +fn test_latex() { + check( + r#" +%! main.tex +\begin{document} + \section{Foo} + foo + \subsection{Bar} + bar + \section{Baz} + baz + \section{Qux} +\end{document} +|"#, + expect![[r#" + [ + FoldingRange { + range: 16..116, + kind: Environment, + }, + FoldingRange { + range: 34..76, + kind: Section, + }, + FoldingRange { + range: 63..76, + kind: Section, + }, + FoldingRange { + range: 89..102, + kind: Section, + }, + FoldingRange { + range: 115..116, + kind: Section, + }, + ] + "#]], + ); +} + +#[test] +fn test_bibtex() { + check( + r#" +%! main.bib +some junk +here + +@article{foo, + author = {bar}, + title = {baz} +} + +@string{foo = "bar"} + +@comment{foo, + author = {bar}, + title = {baz} +} + +@preamble{"foo"} +|"#, + expect![[r#" + [ + FoldingRange { + range: 28..68, + kind: Entry, + }, + FoldingRange { + range: 82..90, + kind: Entry, + }, + ] + "#]], + ); +} diff --git a/support/texlab/crates/parser/src/latex.rs b/support/texlab/crates/parser/src/latex.rs index 9d85d5c6b7..3c9cdf8665 100644 --- a/support/texlab/crates/parser/src/latex.rs +++ b/support/texlab/crates/parser/src/latex.rs @@ -732,15 +732,18 @@ impl<'a> Parser<'a> { while let Some(kind) = self.peek() { match kind { - Token::LineComment - | Token::Word + Token::Word | Token::Eq | Token::LBrack | Token::RBrack | Token::LParen | Token::RParen | Token::CommandName(CommandName::Generic) => self.path(), - Token::Whitespace | Token::LineBreak | Token::Comma | Token::Pipe => self.eat(), + Token::Whitespace + | Token::LineBreak + | Token::LineComment + | Token::Comma + | Token::Pipe => self.eat(), Token::LCurly => self.curly_group_path(), _ => break, }; diff --git a/support/texlab/crates/parser/src/snapshots/parser__latex__tests__parse@issue_919.txt.snap b/support/texlab/crates/parser/src/snapshots/parser__latex__tests__parse@issue_919.txt.snap new file mode 100644 index 0000000000..bf5d207fca --- /dev/null +++ b/support/texlab/crates/parser/src/snapshots/parser__latex__tests__parse@issue_919.txt.snap @@ -0,0 +1,61 @@ +--- +source: crates/parser/src/latex.rs +expression: root +input_file: crates/parser/src/test_data/latex/issue_919.txt +--- +ROOT@0..217 + PREAMBLE@0..217 + CLASS_INCLUDE@0..25 + COMMAND_NAME@0..14 "\\documentclass" + CURLY_GROUP_WORD_LIST@14..25 + L_CURLY@14..15 "{" + KEY@15..22 + WORD@15..22 "article" + R_CURLY@22..23 "}" + LINE_BREAK@23..25 "\n\n" + PACKAGE_INCLUDE@25..173 + COMMAND_NAME@25..36 "\\usepackage" + CURLY_GROUP_WORD_LIST@36..173 + L_CURLY@36..37 "{" + LINE_BREAK@37..38 "\n" + WHITESPACE@38..42 " " + KEY@42..48 + WORD@42..48 "lipsum" + COMMA@48..49 "," + WHITESPACE@49..50 " " + COMMENT@50..70 "% provides blindtext" + LINE_BREAK@70..71 "\n" + WHITESPACE@71..75 " " + KEY@75..83 + WORD@75..83 "booktabs" + COMMA@83..84 "," + WHITESPACE@84..85 " " + COMMENT@85..110 "% better rules for ta ..." + LINE_BREAK@110..111 "\n" + WHITESPACE@111..115 " " + COMMENT@115..169 "%xcolor % easily defi ..." + LINE_BREAK@169..170 "\n" + R_CURLY@170..171 "}" + LINE_BREAK@171..173 "\n\n" + ENVIRONMENT@173..217 + BEGIN@173..194 + COMMAND_NAME@173..179 "\\begin" + CURLY_GROUP_WORD@179..194 + L_CURLY@179..180 "{" + KEY@180..188 + WORD@180..188 "document" + R_CURLY@188..189 "}" + LINE_BREAK@189..190 "\n" + WHITESPACE@190..194 " " + GENERIC_COMMAND@194..202 + COMMAND_NAME@194..201 "\\lipsum" + LINE_BREAK@201..202 "\n" + END@202..217 + COMMAND_NAME@202..206 "\\end" + CURLY_GROUP_WORD@206..217 + L_CURLY@206..207 "{" + KEY@207..215 + WORD@207..215 "document" + R_CURLY@215..216 "}" + LINE_BREAK@216..217 "\n" + diff --git a/support/texlab/crates/parser/src/test_data/latex/issue_919.txt b/support/texlab/crates/parser/src/test_data/latex/issue_919.txt new file mode 100644 index 0000000000..40e1b16a03 --- /dev/null +++ b/support/texlab/crates/parser/src/test_data/latex/issue_919.txt @@ -0,0 +1,11 @@ +\documentclass{article} + +\usepackage{ + lipsum, % provides blindtext + booktabs, % better rules for tables + %xcolor % easily define colors with \definecolor{}{}{} +} + +\begin{document} + \lipsum +\end{document} diff --git a/support/texlab/crates/texlab/Cargo.toml b/support/texlab/crates/texlab/Cargo.toml index 98771a3900..a8d308b3a0 100644 --- a/support/texlab/crates/texlab/Cargo.toml +++ b/support/texlab/crates/texlab/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "texlab" description = "LaTeX Language Server" -version = "5.9.1" +version = "5.9.2" license.workspace = true readme = "README.md" authors.workspace = true @@ -36,6 +36,7 @@ distro = { path = "../distro" } encoding_rs = "0.8.32" encoding_rs_io = "0.1.7" fern = "0.6.2" +folding = { path = "../folding" } fuzzy-matcher = { version = "0.3.7", features = ["compact"] } hover = { path = "../hover" } itertools = "0.11.0" diff --git a/support/texlab/crates/texlab/benches/bench_main.rs b/support/texlab/crates/texlab/benches/bench_main.rs index 7fc6d8fc95..47b302e313 100644 --- a/support/texlab/crates/texlab/benches/bench_main.rs +++ b/support/texlab/crates/texlab/benches/bench_main.rs @@ -36,41 +36,6 @@ fn criterion_benchmark(c: &mut Criterion) { ) }); }); - - c.bench_function("BibTeX/Cite", |b| { - let uri1 = Url::parse("http://example.com/texlab.tex").unwrap(); - let uri2 = Url::parse("http://example.com/rht.bib").unwrap(); - let text1 = r#"\cite{a}\addbibresource{rht.bib}"#.to_string(); - let text2 = include_str!("/home/paddy/texlab-testing/rht.bib").to_string(); - let mut workspace = Workspace::default(); - workspace.open( - uri1.clone(), - text1, - Language::Tex, - Owner::Client, - LineCol { line: 0, col: 0 }, - ); - - workspace.open( - uri2.clone(), - text2, - Language::Bib, - Owner::Client, - LineCol { line: 0, col: 6 }, - ); - - let client_capabilities = ClientCapabilities::default(); - - b.iter(|| { - texlab::features::completion::complete( - &workspace, - &uri1, - Position::new(0, 7), - &client_capabilities, - None, - ) - }); - }); } criterion_group!(benches, criterion_benchmark); diff --git a/support/texlab/crates/texlab/src/features/folding.rs b/support/texlab/crates/texlab/src/features/folding.rs index 5d98736176..6de4ef0d22 100644 --- a/support/texlab/crates/texlab/src/features/folding.rs +++ b/support/texlab/crates/texlab/src/features/folding.rs @@ -1,62 +1,44 @@ -use base_db::{DocumentData, Workspace}; -use lsp_types::{FoldingRange, FoldingRangeKind, Range, Url}; -use rowan::ast::AstNode; -use syntax::{bibtex, latex}; +use base_db::Workspace; +use folding::FoldingRangeKind; +use lsp_types::{ClientCapabilities, Url}; use crate::util::line_index_ext::LineIndexExt; -pub fn find_all(workspace: &Workspace, uri: &Url) -> Option<Vec<FoldingRange>> { +pub fn find_all( + workspace: &Workspace, + uri: &Url, + capabilities: &ClientCapabilities, +) -> Option<Vec<serde_json::Value>> { + let custom_kinds = capabilities + .text_document + .as_ref() + .and_then(|cap| cap.folding_range.as_ref()) + .and_then(|cap| cap.folding_range_kind.as_ref()) + .and_then(|cap| cap.value_set.as_ref()) + .is_some(); + let document = workspace.lookup(uri)?; - let line_index = &document.line_index; - let foldings = match &document.data { - DocumentData::Tex(data) => { - let mut results = Vec::new(); - for node in data.root_node().descendants() { - if let Some(folding) = latex::Environment::cast(node.clone()) - .map(|node| latex::small_range(&node)) - .or_else(|| { - latex::Section::cast(node.clone()).map(|node| latex::small_range(&node)) - }) - .or_else(|| latex::EnumItem::cast(node).map(|node| latex::small_range(&node))) - .map(|node| line_index.line_col_lsp_range(node)) - .map(create_range) - { - results.push(folding); - } - } + let foldings = folding::find_all(document).into_iter().map(|folding| { + let range = document.line_index.line_col_lsp_range(folding.range); - results - } - DocumentData::Bib(data) => { - let root = data.root_node(); - root.descendants() - .filter(|node| { - matches!( - node.kind(), - bibtex::PREAMBLE | bibtex::STRING | bibtex::ENTRY - ) - }) - .map(|node| create_range(line_index.line_col_lsp_range(node.text_range()))) - .collect() - } - DocumentData::Aux(_) - | DocumentData::Log(_) - | DocumentData::Root - | DocumentData::Tectonic => { - return None; - } - }; + let kind = if custom_kinds { + Some(match folding.kind { + FoldingRangeKind::Section => "section", + FoldingRangeKind::Environment => "environment", + FoldingRangeKind::Entry => "entry", + }) + } else { + None + }; - Some(foldings) -} + serde_json::json!({ + "startLine": range.start.line, + "startCharacter": range.start.character, + "endLine": range.end.line, + "endCharacter": range.end.character, + "kind": kind, + }) + }); -fn create_range(range: Range) -> FoldingRange { - FoldingRange { - start_line: range.start.line, - start_character: Some(range.start.character), - end_line: range.end.line, - end_character: Some(range.end.character), - collapsed_text: None, - kind: Some(FoldingRangeKind::Region), - } + Some(foldings.collect()) } diff --git a/support/texlab/crates/texlab/src/server.rs b/support/texlab/crates/texlab/src/server.rs index 5d3fc4a2b4..8f58bb3826 100644 --- a/support/texlab/crates/texlab/src/server.rs +++ b/support/texlab/crates/texlab/src/server.rs @@ -605,8 +605,9 @@ impl Server { fn folding_range(&self, id: RequestId, params: FoldingRangeParams) -> Result<()> { let mut uri = params.text_document.uri; normalize_uri(&mut uri); + let client_capabilities = Arc::clone(&self.client_capabilities); self.run_query(id, move |db| { - folding::find_all(db, &uri).unwrap_or_default() + folding::find_all(db, &uri, &client_capabilities).unwrap_or_default() }); Ok(()) } diff --git a/support/texlab/crates/texlab/tests/lsp/text_document.rs b/support/texlab/crates/texlab/tests/lsp/text_document.rs index 88b6333877..729ab4283c 100644 --- a/support/texlab/crates/texlab/tests/lsp/text_document.rs +++ b/support/texlab/crates/texlab/tests/lsp/text_document.rs @@ -2,7 +2,6 @@ mod completion; mod document_highlight; mod document_link; mod document_symbol; -mod folding_range; mod formatting; mod inlay_hint; mod rename; diff --git a/support/texlab/crates/texlab/tests/lsp/text_document/folding_range.rs b/support/texlab/crates/texlab/tests/lsp/text_document/folding_range.rs deleted file mode 100644 index 063ca7533d..0000000000 --- a/support/texlab/crates/texlab/tests/lsp/text_document/folding_range.rs +++ /dev/null @@ -1,66 +0,0 @@ -use insta::assert_json_snapshot; -use lsp_types::{ - request::FoldingRangeRequest, ClientCapabilities, FoldingRange, FoldingRangeParams, -}; - -use crate::fixture::TestBed; - -fn find_foldings(fixture: &str) -> Vec<FoldingRange> { - let test_bed = TestBed::new(fixture).unwrap(); - - test_bed.initialize(ClientCapabilities::default()).unwrap(); - - let text_document = test_bed.cursor().unwrap().text_document; - test_bed - .client() - .send_request::<FoldingRangeRequest>(FoldingRangeParams { - text_document, - work_done_progress_params: Default::default(), - partial_result_params: Default::default(), - }) - .unwrap() - .unwrap_or_default() -} - -#[test] -fn latex() { - assert_json_snapshot!(find_foldings( - r#" -%! main.tex -\begin{document} - \section{Foo} - foo - \subsection{Bar} - bar - \section{Baz} - baz - \section{Qux} -\end{document} -|"# - )); -} - -#[test] -fn bibtex() { - assert_json_snapshot!(find_foldings( - r#" -%! main.bib -some junk -here - -@article{foo, - author = {bar}, - title = {baz} -} - -@string{foo = "bar"} - -@comment{foo, - author = {bar}, - title = {baz} -} - -@preamble{"foo"} -|"# - )); -} diff --git a/support/texlab/crates/texlab/tests/lsp/text_document/snapshots/lsp__text_document__folding_range__bibtex.snap b/support/texlab/crates/texlab/tests/lsp/text_document/snapshots/lsp__text_document__folding_range__bibtex.snap deleted file mode 100644 index 2063cc39e5..0000000000 --- a/support/texlab/crates/texlab/tests/lsp/text_document/snapshots/lsp__text_document__folding_range__bibtex.snap +++ /dev/null @@ -1,27 +0,0 @@ ---- -source: tests/lsp/text_document/folding_range.rs -expression: "find_foldings(r#\"\n%! main.bib\nsome junk\nhere\n\n@article{foo,\n author = {bar},\n title = {baz}\n}\n\n@string{foo = \"bar\"}\n\n@comment{foo,\n author = {bar},\n title = {baz}\n}\n\n@preamble{\"foo\"}\n|\"#)" ---- -[ - { - "startLine": 3, - "startCharacter": 0, - "endLine": 6, - "endCharacter": 1, - "kind": "region" - }, - { - "startLine": 8, - "startCharacter": 0, - "endLine": 8, - "endCharacter": 20, - "kind": "region" - }, - { - "startLine": 15, - "startCharacter": 0, - "endLine": 15, - "endCharacter": 16, - "kind": "region" - } -] diff --git a/support/texlab/crates/texlab/tests/lsp/text_document/snapshots/lsp__text_document__folding_range__latex.snap b/support/texlab/crates/texlab/tests/lsp/text_document/snapshots/lsp__text_document__folding_range__latex.snap deleted file mode 100644 index 74632eda56..0000000000 --- a/support/texlab/crates/texlab/tests/lsp/text_document/snapshots/lsp__text_document__folding_range__latex.snap +++ /dev/null @@ -1,41 +0,0 @@ ---- -source: tests/lsp/text_document/folding_range.rs -expression: "find_foldings(r#\"\n%! main.tex\n\\begin{document}\n \\section{Foo}\n foo\n \\subsection{Bar}\n bar\n \\section{Baz}\n baz\n \\section{Qux}\n\\end{document}\n|\"#)" ---- -[ - { - "startLine": 0, - "startCharacter": 0, - "endLine": 8, - "endCharacter": 14, - "kind": "region" - }, - { - "startLine": 1, - "startCharacter": 4, - "endLine": 4, - "endCharacter": 7, - "kind": "region" - }, - { - "startLine": 3, - "startCharacter": 4, - "endLine": 4, - "endCharacter": 7, - "kind": "region" - }, - { - "startLine": 5, - "startCharacter": 4, - "endLine": 6, - "endCharacter": 7, - "kind": "region" - }, - { - "startLine": 7, - "startCharacter": 4, - "endLine": 7, - "endCharacter": 17, - "kind": "region" - } -] diff --git a/support/texlab/texlab.1 b/support/texlab/texlab.1 index 9910cd0bb8..72df5cbdc8 100644 --- a/support/texlab/texlab.1 +++ b/support/texlab/texlab.1 @@ -1,7 +1,7 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. -.TH TEXLAB "1" "August 2023" "texlab 5.9.1" "User Commands" +.TH TEXLAB "1" "August 2023" "texlab 5.9.2" "User Commands" .SH NAME -texlab \- manual page for texlab 5.9.1 +texlab \- manual page for texlab 5.9.2 .SH SYNOPSIS .B texlab [\fI\,OPTIONS\/\fR] diff --git a/support/texlab/texlab.pdf b/support/texlab/texlab.pdf Binary files differindex 74092ac147..528ecd8434 100644 --- a/support/texlab/texlab.pdf +++ b/support/texlab/texlab.pdf |