diff options
author | Norbert Preining <norbert@preining.info> | 2022-06-13 03:03:42 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2022-06-13 03:03:42 +0000 |
commit | ef6d63ded56fb892decaa8ab41d7deebbe670473 (patch) | |
tree | 9c8eccf2e633fcfe0ad512e5428222ce0707971e /support/texlab/src/features | |
parent | 86d0c4c6729cfb43e13fb29790810c3cc2521806 (diff) |
CTAN sync 202206130303
Diffstat (limited to 'support/texlab/src/features')
59 files changed, 640 insertions, 4560 deletions
diff --git a/support/texlab/src/features/build.rs b/support/texlab/src/features/build.rs index 7821fac43d..ae465590fc 100644 --- a/support/texlab/src/features/build.rs +++ b/support/texlab/src/features/build.rs @@ -84,7 +84,7 @@ impl<'a> ProgressReporter<'a> { impl<'a> Drop for ProgressReporter<'a> { fn drop(&mut self) { if self.supports_progress { - let _ = client::send_notification::<Progress>( + drop(client::send_notification::<Progress>( &self.lsp_sender, ProgressParams { token: NumberOrString::String(self.token.to_string()), @@ -92,7 +92,7 @@ impl<'a> Drop for ProgressReporter<'a> { WorkDoneProgressEnd { message: None }, )), }, - ); + )); } } } diff --git a/support/texlab/src/features/completion.rs b/support/texlab/src/features/completion.rs index 3b5187f8eb..3009b437e7 100644 --- a/support/texlab/src/features/completion.rs +++ b/support/texlab/src/features/completion.rs @@ -71,6 +71,7 @@ use super::{ pub const COMPLETION_LIMIT: usize = 50; +#[must_use] pub fn complete(request: FeatureRequest<CompletionParams>) -> Option<CompletionList> { let mut items = Vec::new(); let context = CursorContext::new(request); @@ -172,7 +173,6 @@ fn score(context: &CursorContext<CompletionParams>, items: &mut Vec<InternalComp token.text().into() } } - Cursor::Latex(_) => "".into(), Cursor::Bibtex(token) if matches!( token.kind(), @@ -185,8 +185,7 @@ fn score(context: &CursorContext<CompletionParams>, items: &mut Vec<InternalComp { token.text().into() } - Cursor::Bibtex(_) => "".into(), - Cursor::Nothing => "".into(), + Cursor::Latex(_) | Cursor::Bibtex(_) | Cursor::Nothing => "".into(), }; let file_pattern = pattern.split('/').last().unwrap(); diff --git a/support/texlab/src/features/completion/acronym_ref.rs b/support/texlab/src/features/completion/acronym_ref.rs index 2bb931fde4..84cbd8bc23 100644 --- a/support/texlab/src/features/completion/acronym_ref.rs +++ b/support/texlab/src/features/completion/acronym_ref.rs @@ -30,123 +30,3 @@ pub fn complete_acronyms<'a>( } Some(()) } - -#[cfg(test)] -mod tests { - use rowan::TextRange; - - use crate::features::testing::FeatureTester; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_acronyms(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_acronyms(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_simple() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}\n\\acrshort{f}")]) - .main("main.tex") - .line(1) - .character(11) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_acronyms(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(87.into(), 88.into())); - } - } - - #[test] - fn test_empty() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}\n\\acrshort{}")]) - .main("main.tex") - .line(1) - .character(10) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_acronyms(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(87.into(), 87.into())); - } - } - - #[test] - fn test_after_group() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}\n\\acrshort{}")]) - .main("main.tex") - .line(1) - .character(11) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_acronyms(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_open_brace() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}\n\\acrshort{f")]) - .main("main.tex") - .line(1) - .character(11) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_acronyms(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(87.into(), 88.into())); - } - } -} diff --git a/support/texlab/src/features/completion/argument.rs b/support/texlab/src/features/completion/argument.rs index ded16b359e..3ba28f9dea 100644 --- a/support/texlab/src/features/completion/argument.rs +++ b/support/texlab/src/features/completion/argument.rs @@ -65,123 +65,3 @@ pub fn complete_arguments<'a>( Some(()) } - -#[cfg(test)] -mod tests { - use rowan::TextRange; - - use crate::features::testing::FeatureTester; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_arguments(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_arguments(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_latex_simple() { - let request = FeatureTester::builder() - .files(vec![("main.tex", r#"\mathbb{}\usepackage{amsfonts}"#)]) - .main("main.tex") - .line(0) - .character(8) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_arguments(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(8.into(), 8.into())); - } - } - - #[test] - fn test_latex_word() { - let request = FeatureTester::builder() - .files(vec![("main.tex", r#"\mathbb{foo}\usepackage{amsfonts}"#)]) - .main("main.tex") - .line(0) - .character(8) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_arguments(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(8.into(), 11.into())); - } - } - - #[test] - fn test_latex_open_brace() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\mathbb{ \\usepackage{amsfonts}")]) - .main("main.tex") - .line(0) - .character(8) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_arguments(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(8.into(), 8.into())); - } - } - - #[test] - fn test_latex_open_brace_second() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\mathbb{}{\\usepackage{amsfonts}")]) - .main("main.tex") - .line(0) - .character(10) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_arguments(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } -} diff --git a/support/texlab/src/features/completion/begin_command.rs b/support/texlab/src/features/completion/begin_command.rs index b9b4f06117..699b06c067 100644 --- a/support/texlab/src/features/completion/begin_command.rs +++ b/support/texlab/src/features/completion/begin_command.rs @@ -16,66 +16,3 @@ pub fn complete_begin_command( )); Some(()) } - -#[cfg(test)] -mod tests { - use rowan::TextRange; - - use crate::features::testing::FeatureTester; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_begin_command(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_begin_command(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_latex_simple() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\")]) - .main("main.tex") - .line(0) - .character(1) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_begin_command(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(1.into(), 1.into())); - } - } -} diff --git a/support/texlab/src/features/completion/citation.rs b/support/texlab/src/features/completion/citation.rs index 92b21f0ffc..e6436689d7 100644 --- a/support/texlab/src/features/completion/citation.rs +++ b/support/texlab/src/features/completion/citation.rs @@ -42,7 +42,7 @@ pub fn complete_citations<'a>( .children() .filter_map(bibtex::Entry::cast) { - if let Some(item) = make_item(document, entry, range) { + if let Some(item) = make_item(document, &entry, range) { items.push(item); } } @@ -61,7 +61,9 @@ fn check_citation(context: &CursorContext<CompletionParams>) -> Option<()> { fn check_acronym(context: &CursorContext<CompletionParams>) -> Option<()> { let token = context.cursor.as_latex()?; - let pair = token.ancestors().find_map(latex::KeyValuePair::cast)?; + let pair = token + .parent_ancestors() + .find_map(latex::KeyValuePair::cast)?; if pair.key()?.to_string() != "cite" { return None; } @@ -70,16 +72,18 @@ fn check_acronym(context: &CursorContext<CompletionParams>) -> Option<()> { Some(()) } -fn make_item( - document: &Document, - entry: bibtex::Entry, +fn make_item<'a>( + document: &'a Document, + entry: &bibtex::Entry, range: TextRange, -) -> Option<InternalCompletionItem> { +) -> Option<InternalCompletionItem<'a>> { let key = entry.name_token()?.to_string(); let ty = LANGUAGE_DATA .find_entry_type(&entry.type_token()?.text()[1..]) - .map(|ty| Structure::Entry(ty.category)) - .unwrap_or_else(|| Structure::Entry(BibtexEntryTypeCategory::Misc)); + .map_or_else( + || Structure::Entry(BibtexEntryTypeCategory::Misc), + |ty| Structure::Entry(ty.category), + ); let entry_code = entry.syntax().text().to_string(); let text = format!( @@ -88,8 +92,8 @@ fn make_item( WHITESPACE_REGEX .replace_all( &entry_code - .replace('{', "") - .replace('}', "") + .replace('{', " ") + .replace('}', " ") .replace(',', " ") .replace('=', " "), " " @@ -109,188 +113,3 @@ fn make_item( } static WHITESPACE_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new("\\s+").unwrap()); - -#[cfg(test)] -mod tests { - use crate::features::testing::FeatureTester; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_citations(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_citations(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_latex_simple() { - let request = FeatureTester::builder() - .files(vec![ - ("main.tex", "\\addbibresource{main.bib}\n\\cite{}"), - ("main.bib", "@article{foo,}"), - ]) - .main("main.tex") - .line(1) - .character(6) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_citations(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(32.into(), 32.into())); - } - } - - #[test] - fn test_latex_two_words() { - let request = FeatureTester::builder() - .files(vec![ - ("main.tex", "\\addbibresource{main.bib}\n\\cite{foo}"), - ("main.bib", "@article{foo bar,}"), - ]) - .main("main.tex") - .line(1) - .character(6) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_citations(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(32.into(), 35.into())); - } - } - - #[test] - fn test_latex_open_brace() { - let request = FeatureTester::builder() - .files(vec![ - ("main.tex", "\\addbibresource{main.bib}\n\\cite{"), - ("main.bib", "@article{foo,}"), - ]) - .main("main.tex") - .line(1) - .character(6) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_citations(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(32.into(), 32.into())); - } - } - - #[test] - fn test_latex_open_brace_second() { - let request = FeatureTester::builder() - .files(vec![ - ("main.tex", "\\addbibresource{main.bib}\n\\cite{foo,a"), - ("main.bib", "@article{foo,}"), - ]) - .main("main.tex") - .line(1) - .character(10) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_citations(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(36.into(), 37.into())); - } - } - - #[test] - fn test_latex_acronym() { - let request = FeatureTester::builder() - .files(vec![ - ( - "main.tex", - "\\addbibresource{main.bib}\n\\DeclareAcronym{foo}{cite={\n}}", - ), - ("main.bib", "@article{foo,}"), - ]) - .main("main.tex") - .line(2) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_citations(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(54.into(), 54.into())); - } - } - - #[test] - fn test_latex_acronym_two_words() { - let request = FeatureTester::builder() - .files(vec![ - ( - "main.tex", - "\\addbibresource{main.bib}\n\\DeclareAcronym{foo}{cite={\na b}}", - ), - ("main.bib", "@article{foo,}"), - ]) - .main("main.tex") - .line(2) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_citations(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(54.into(), 57.into())); - } - } -} diff --git a/support/texlab/src/features/completion/color.rs b/support/texlab/src/features/completion/color.rs index 1d241d2c94..a9a385c3c9 100644 --- a/support/texlab/src/features/completion/color.rs +++ b/support/texlab/src/features/completion/color.rs @@ -21,86 +21,3 @@ pub fn complete_colors<'a>( Some(()) } - -#[cfg(test)] -mod tests { - use rowan::TextRange; - - use crate::features::testing::FeatureTester; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_colors(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_colors(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_latex_simple() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\color{}")]) - .main("main.tex") - .line(0) - .character(7) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_colors(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(7.into(), 7.into())); - } - } - - #[test] - fn test_latex_open_brace() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\color{")]) - .main("main.tex") - .line(0) - .character(7) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_colors(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(7.into(), 7.into())); - } - } -} diff --git a/support/texlab/src/features/completion/color_model.rs b/support/texlab/src/features/completion/color_model.rs index 2190b690ad..cd582d0031 100644 --- a/support/texlab/src/features/completion/color_model.rs +++ b/support/texlab/src/features/completion/color_model.rs @@ -41,126 +41,3 @@ fn check_color_definition_set(context: &CursorContext<CompletionParams>) -> Opti .filter(|model| model.syntax().text_range() == group.syntax().text_range())?; Some(range) } - -#[cfg(test)] -mod tests { - use rowan::TextRange; - - use crate::features::testing::FeatureTester; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_color_models(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_color_models(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_definition_simple() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\definecolor{foo}{}")]) - .main("main.tex") - .line(0) - .character(18) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_color_models(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(18.into(), 18.into())); - } - } - - #[test] - fn test_definition_open_brace() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\definecolor{foo}{")]) - .main("main.tex") - .line(0) - .character(18) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_color_models(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(18.into(), 18.into())); - } - } - - #[test] - fn test_definition_set_simple() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\definecolorset{}")]) - .main("main.tex") - .line(0) - .character(16) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_color_models(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(16.into(), 16.into())); - } - } - - #[test] - fn test_definition_set_open_brace() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\definecolorset{")]) - .main("main.tex") - .line(0) - .character(16) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_color_models(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(16.into(), 16.into())); - } - } -} diff --git a/support/texlab/src/features/completion/component_command.rs b/support/texlab/src/features/completion/component_command.rs index b93a782512..097b28633f 100644 --- a/support/texlab/src/features/completion/component_command.rs +++ b/support/texlab/src/features/completion/component_command.rs @@ -26,147 +26,3 @@ pub fn complete_component_commands<'a>( Some(()) } - -#[cfg(test)] -mod tests { - use rowan::TextRange; - - use crate::features::testing::FeatureTester; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_component_commands(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_component_commands(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_latex_simple() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\")]) - .main("main.tex") - .line(0) - .character(1) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_component_commands(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(1.into(), 1.into())); - } - } - - #[test] - fn test_latex_simple_before() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_component_commands(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_latex_simple_package() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\\n\\usepackage{lipsum}")]) - .main("main.tex") - .line(0) - .character(1) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_component_commands(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in &actual_items { - assert_eq!(item.range, TextRange::new(1.into(), 1.into())); - } - - assert!(actual_items - .iter() - .any(|item| item.data.label() == "lipsum")); - } - - #[test] - fn test_latex_simple_existing() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\foo")]) - .main("main.tex") - .line(0) - .character(2) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_component_commands(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(1.into(), 4.into())); - } - } - - #[test] - fn test_bibtex_simple() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "@a{b,c={\\ }}")]) - .main("main.bib") - .line(0) - .character(9) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_component_commands(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(9.into(), 10.into())); - } - } -} diff --git a/support/texlab/src/features/completion/component_environment.rs b/support/texlab/src/features/completion/component_environment.rs index 057e98e491..a9478694bb 100644 --- a/support/texlab/src/features/completion/component_environment.rs +++ b/support/texlab/src/features/completion/component_environment.rs @@ -24,150 +24,3 @@ pub fn complete_component_environments<'a>( Some(()) } - -#[cfg(test)] -mod tests { - use rowan::TextRange; - - use crate::features::testing::FeatureTester; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_component_environments(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_component_environments(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_simple() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\begin{")]) - .main("main.tex") - .line(0) - .character(7) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_component_environments(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(7.into(), 7.into())); - } - } - - #[test] - fn test_simple_end() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\begin{a}\n\\end{")]) - .main("main.tex") - .line(1) - .character(5) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_component_environments(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(15.into(), 15.into())); - } - } - - #[test] - fn test_simple_class() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\begin{}\n\\documentclass{article}")]) - .main("main.tex") - .line(0) - .character(7) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_component_environments(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in &actual_items { - assert_eq!(item.range, TextRange::new(7.into(), 7.into())); - } - - assert!(actual_items - .iter() - .any(|item| item.data.label() == "theindex")); - } - - #[test] - fn test_simple_existing() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\begin{d}")]) - .main("main.tex") - .line(0) - .character(7) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_component_environments(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(7.into(), 8.into())); - } - } - - #[test] - fn test_command_definition() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\newcommand{\\foo}{\\begin{\nd}")]) - .main("main.tex") - .line(1) - .character(1) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_component_environments(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(26.into(), 27.into())); - } - } -} diff --git a/support/texlab/src/features/completion/entry_type.rs b/support/texlab/src/features/completion/entry_type.rs index 06180dd8cc..519737ed08 100644 --- a/support/texlab/src/features/completion/entry_type.rs +++ b/support/texlab/src/features/completion/entry_type.rs @@ -13,7 +13,7 @@ pub fn complete_entry_types<'a>( .cursor .as_bibtex() .filter(|token| token.kind() == bibtex::TYPE) - .map(|token| token.text_range()) + .map(bibtex::SyntaxToken::text_range) .filter(|range| range.start() != context.offset) .map(|range| TextRange::new(range.start() + TextSize::from(1), range.end()))?; @@ -25,201 +25,3 @@ pub fn complete_entry_types<'a>( Some(()) } - -#[cfg(test)] -mod tests { - use crate::features::testing::FeatureTester; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_entry_types(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_entry_types(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_at_empty() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "@")]) - .main("main.bib") - .line(0) - .character(1) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_entry_types(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(1.into(), 1.into())); - } - } - - #[test] - fn test_before_preamble() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "@preamble")]) - .main("main.bib") - .line(0) - .character(1) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_entry_types(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(1.into(), 9.into())); - } - } - - #[test] - fn test_before_string() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "@string")]) - .main("main.bib") - .line(0) - .character(1) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_entry_types(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(1.into(), 7.into())); - } - } - - #[test] - fn test_before_article() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "@article")]) - .main("main.bib") - .line(0) - .character(1) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_entry_types(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(1.into(), 8.into())); - } - } - - #[test] - fn test_after_preamble() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "@preamble{")]) - .main("main.bib") - .line(0) - .character(9) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_entry_types(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(1.into(), 9.into())); - } - } - - #[test] - fn test_after_string() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "@string{")]) - .main("main.bib") - .line(0) - .character(7) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_entry_types(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(1.into(), 7.into())); - } - } - - #[test] - fn test_complete_entry() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "@article{foo, author = {foo}}")]) - .main("main.bib") - .line(0) - .character(3) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_entry_types(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(1.into(), 8.into())); - } - } - - #[test] - fn test_complete_entry_field() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "@article{foo, author = {foo}}")]) - .main("main.bib") - .line(0) - .character(17) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_entry_types(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } -} diff --git a/support/texlab/src/features/completion/field.rs b/support/texlab/src/features/completion/field.rs index 051b8d69b5..4f386ed1b1 100644 --- a/support/texlab/src/features/completion/field.rs +++ b/support/texlab/src/features/completion/field.rs @@ -37,161 +37,3 @@ pub fn complete_fields<'a>( } Some(()) } - -#[cfg(test)] -mod tests { - use crate::features::testing::FeatureTester; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_fields(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_fields(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_empty_entry_open() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "@article{foo,")]) - .main("main.bib") - .line(0) - .character(13) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_fields(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(13.into(), 13.into())); - } - } - - #[test] - fn test_empty_entry_closed() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "@article{foo,}")]) - .main("main.bib") - .line(0) - .character(13) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_fields(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(13.into(), 13.into())); - } - } - - #[test] - fn test_entry_field_name() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "@article{foo, a")]) - .main("main.bib") - .line(0) - .character(15) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_fields(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(14.into(), 15.into())); - } - } - - #[test] - fn test_entry_field_value() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "@article{foo, author = bar}")]) - .main("main.bib") - .line(0) - .character(24) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_fields(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_entry_two_fields_name_closed() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "@article{foo, author = bar, baz}")]) - .main("main.bib") - .line(0) - .character(29) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_fields(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(28.into(), 31.into())); - } - } - - #[test] - fn test_entry_two_fields_name_open() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "@article{foo, author = bar, baz")]) - .main("main.bib") - .line(0) - .character(29) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_fields(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(28.into(), 31.into())); - } - } -} diff --git a/support/texlab/src/features/completion/glossary_ref.rs b/support/texlab/src/features/completion/glossary_ref.rs index 6805108f16..6223079fdf 100644 --- a/support/texlab/src/features/completion/glossary_ref.rs +++ b/support/texlab/src/features/completion/glossary_ref.rs @@ -40,86 +40,3 @@ pub fn complete_glossary_entries<'a>( Some(()) } - -#[cfg(test)] -mod tests { - use rowan::TextRange; - - use crate::features::testing::FeatureTester; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_glossary_entries(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_glossary_entries(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_simple() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}\n\\gls{f}")]) - .main("main.tex") - .line(1) - .character(6) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_glossary_entries(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(82.into(), 83.into())); - } - } - - #[test] - fn test_open_brace() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}\n\\gls{f")]) - .main("main.tex") - .line(1) - .character(6) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_glossary_entries(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(82.into(), 83.into())); - } - } -} diff --git a/support/texlab/src/features/completion/import.rs b/support/texlab/src/features/completion/import.rs index 6e3a8deeaa..9cb35b68a7 100644 --- a/support/texlab/src/features/completion/import.rs +++ b/support/texlab/src/features/completion/import.rs @@ -38,8 +38,7 @@ pub fn complete_imports<'a>( file_names.insert(file_name); let stem = &file_name[0..file_name.len() - 4]; let data = factory(stem.into()); - let item = InternalCompletionItem::new(range, data); - items.push(item); + items.push(InternalCompletionItem::new(range, data)); } let resolver = &context.request.workspace.environment.resolver; @@ -50,132 +49,8 @@ pub fn complete_imports<'a>( { let stem = &file_name[0..file_name.len() - 4]; let data = factory(stem.into()); - let item = InternalCompletionItem::new(range, data); - items.push(item); + items.push(InternalCompletionItem::new(range, data)); } Some(()) } - -#[cfg(test)] -mod tests { - use rowan::TextRange; - - use crate::features::testing::FeatureTester; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_imports(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_imports(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_latex_simple_package() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\usepackage{}")]) - .main("main.tex") - .line(0) - .character(12) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_imports(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(12.into(), 12.into())); - } - } - - #[test] - fn test_latex_open_brace_package() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\usepackage{ \\foo")]) - .main("main.tex") - .line(0) - .character(12) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_imports(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(12.into(), 12.into())); - } - } - - #[test] - fn test_latex_simple_class() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\documentclass{}")]) - .main("main.tex") - .line(0) - .character(15) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_imports(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(15.into(), 15.into())); - } - } - - #[test] - fn test_latex_open_brace_class() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\documentclass{ \\foo")]) - .main("main.tex") - .line(0) - .character(15) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_imports(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(15.into(), 15.into())); - } - } -} diff --git a/support/texlab/src/features/completion/include.rs b/support/texlab/src/features/completion/include.rs index 7f6767b89f..d851aca820 100644 --- a/support/texlab/src/features/completion/include.rs +++ b/support/texlab/src/features/completion/include.rs @@ -101,19 +101,21 @@ fn current_dir( .options .root_directory .as_ref() - .map(|root_directory| { - context - .request - .workspace - .environment - .current_directory - .join(root_directory) - }) - .unwrap_or_else(|| { - let mut path = context.request.main_document().uri.to_file_path().unwrap(); - path.pop(); - path - }); + .map_or_else( + || { + let mut path = context.request.main_document().uri.to_file_path().unwrap(); + path.pop(); + path + }, + |root_directory| { + context + .request + .workspace + .environment + .current_directory + .join(root_directory) + }, + ); path = PathBuf::from(path.to_str()?.replace('\\', "/")); if !path_text.is_empty() { @@ -133,8 +135,8 @@ fn is_included(file: &Path, allowed_extensions: &[&str]) -> bool { allowed_extensions.is_empty() || file .extension() - .and_then(|ext| ext.to_str()) - .map(|ext| ext.to_lowercase()) + .and_then(std::ffi::OsStr::to_str) + .map(str::to_lowercase) .map(|ext| allowed_extensions.contains(&ext.as_str())) .unwrap_or_default() } @@ -142,51 +144,10 @@ fn is_included(file: &Path, allowed_extensions: &[&str]) -> bool { fn remove_extension(path: &mut PathBuf) { if let Some(stem) = path .file_stem() - .and_then(|stem| stem.to_str()) + .and_then(std::ffi::OsStr::to_str) .map(ToOwned::to_owned) { path.pop(); path.push(stem); } } - -#[cfg(test)] -mod tests { - use crate::features::testing::FeatureTester; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_includes(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_includes(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } -} diff --git a/support/texlab/src/features/completion/label.rs b/support/texlab/src/features/completion/label.rs index e6c02db565..b662f98c8a 100644 --- a/support/texlab/src/features/completion/label.rs +++ b/support/texlab/src/features/completion/label.rs @@ -99,106 +99,3 @@ fn find_reference_range(context: &CursorContext<CompletionParams>) -> Option<(Te latex::LabelReferenceRange::cast(group.syntax().parent()?)?; Some((range, false)) } - -#[cfg(test)] -mod tests { - use rowan::TextRange; - - use crate::features::testing::FeatureTester; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_labels(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_labels(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_simple() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\ref{}\\label{foo}")]) - .main("main.tex") - .line(0) - .character(5) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_labels(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(5.into(), 5.into())); - } - } - - #[test] - fn test_simple_range() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\crefrange{\n\\label{foo}")]) - .main("main.tex") - .line(0) - .character(11) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_labels(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(11.into(), 11.into())); - } - } - - #[test] - fn test_multi_word() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\ref{foo}\\label{foo bar}")]) - .main("main.tex") - .line(0) - .character(8) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_labels(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(5.into(), 8.into())); - } - } -} diff --git a/support/texlab/src/features/completion/theorem.rs b/support/texlab/src/features/completion/theorem.rs index 0945d1df51..65b511f7ad 100644 --- a/support/texlab/src/features/completion/theorem.rs +++ b/support/texlab/src/features/completion/theorem.rs @@ -25,109 +25,3 @@ pub fn complete_theorem_environments<'a>( Some(()) } - -#[cfg(test)] -mod tests { - use rowan::TextRange; - - use crate::features::testing::FeatureTester; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_theorem_environments(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_theorem_environments(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_simple() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\begin{ \\newtheorem{lemma}{Lemma}")]) - .main("main.tex") - .line(0) - .character(7) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_theorem_environments(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(7.into(), 7.into())); - } - } - - #[test] - fn test_simple_end() { - let request = FeatureTester::builder() - .files(vec![( - "main.tex", - "\\newtheorem{lemma}{Lemma}\\begin{a}\n\\end{", - )]) - .main("main.tex") - .line(1) - .character(5) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_theorem_environments(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(40.into(), 40.into())); - } - } - - #[test] - fn test_simple_existing() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\begin{d}\\newtheorem{lemma}{Lemma}")]) - .main("main.tex") - .line(0) - .character(7) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_theorem_environments(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(7.into(), 8.into())); - } - } -} diff --git a/support/texlab/src/features/completion/tikz_library.rs b/support/texlab/src/features/completion/tikz_library.rs index 62d66782a3..bf15695722 100644 --- a/support/texlab/src/features/completion/tikz_library.rs +++ b/support/texlab/src/features/completion/tikz_library.rs @@ -31,126 +31,3 @@ pub fn complete_tikz_libraries<'a>( Some(()) } - -#[cfg(test)] -mod tests { - use rowan::TextRange; - - use crate::features::testing::FeatureTester; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_tikz_libraries(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_tikz_libraries(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_latex_simple_pgf() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\usepgflibrary{}")]) - .main("main.tex") - .line(0) - .character(15) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_tikz_libraries(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(15.into(), 15.into())); - } - } - - #[test] - fn test_latex_open_brace_pgf() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\usepgflibrary{")]) - .main("main.tex") - .line(0) - .character(15) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_tikz_libraries(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(15.into(), 15.into())); - } - } - - #[test] - fn test_latex_simple_tikz() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\usetikzlibrary{}")]) - .main("main.tex") - .line(0) - .character(16) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_tikz_libraries(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(16.into(), 16.into())); - } - } - - #[test] - fn test_latex_open_brace_tikz() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\usetikzlibrary{")]) - .main("main.tex") - .line(0) - .character(16) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_tikz_libraries(&context, &mut actual_items); - - assert!(!actual_items.is_empty()); - for item in actual_items { - assert_eq!(item.range, TextRange::new(16.into(), 16.into())); - } - } -} diff --git a/support/texlab/src/features/completion/user_command.rs b/support/texlab/src/features/completion/user_command.rs index 8355025098..8c4d593bee 100644 --- a/support/texlab/src/features/completion/user_command.rs +++ b/support/texlab/src/features/completion/user_command.rs @@ -30,66 +30,3 @@ pub fn complete_user_commands<'a>( Some(()) } - -#[cfg(test)] -mod tests { - use rowan::TextRange; - - use crate::features::testing::FeatureTester; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_user_commands(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_user_commands(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_latex_simple() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "\\foo\\bar")]) - .main("main.tex") - .line(0) - .character(4) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_user_commands(&context, &mut actual_items); - - assert_eq!(actual_items.len(), 1); - for item in actual_items { - assert_eq!(item.range, TextRange::new(1.into(), 4.into())); - } - } -} diff --git a/support/texlab/src/features/completion/user_environment.rs b/support/texlab/src/features/completion/user_environment.rs index 3db135865e..91dd5a9ea2 100644 --- a/support/texlab/src/features/completion/user_environment.rs +++ b/support/texlab/src/features/completion/user_environment.rs @@ -29,69 +29,3 @@ pub fn complete_user_environments<'a>( Some(()) } - -#[cfg(test)] -mod tests { - use rowan::TextRange; - - use crate::features::testing::FeatureTester; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_user_environments(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_user_environments(&context, &mut actual_items); - - assert!(actual_items.is_empty()); - } - - #[test] - fn test_latex_simple() { - let request = FeatureTester::builder() - .files(vec![( - "main.tex", - "\\begin{foo}\\end{foo} \\begin{bar}\\end{bar}", - )]) - .main("main.tex") - .line(0) - .character(7) - .build() - .completion(); - - let context = CursorContext::new(request); - let mut actual_items = Vec::new(); - complete_user_environments(&context, &mut actual_items); - - assert_eq!(actual_items.len(), 1); - for item in actual_items { - assert_eq!(item.range, TextRange::new(7.into(), 10.into())); - } - } -} diff --git a/support/texlab/src/features/definition.rs b/support/texlab/src/features/definition.rs index b54a2be9f0..20da72a142 100644 --- a/support/texlab/src/features/definition.rs +++ b/support/texlab/src/features/definition.rs @@ -4,7 +4,12 @@ mod entry; mod label; mod string; -use lsp_types::{GotoDefinitionParams, GotoDefinitionResponse}; +use std::sync::Arc; + +use lsp_types::{GotoDefinitionParams, GotoDefinitionResponse, LocationLink, Url}; +use rowan::TextRange; + +use crate::LineIndexExt; use self::{ command::goto_command_definition, document::goto_document_definition, @@ -18,10 +23,46 @@ pub fn goto_definition( ) -> Option<GotoDefinitionResponse> { let context = CursorContext::new(request); log::debug!("[Definition] Cursor: {:?}", context.cursor); - let links = goto_command_definition(&context) + + let origin_document = context.request.main_document(); + let links: Vec<_> = goto_command_definition(&context) .or_else(|| goto_document_definition(&context)) .or_else(|| goto_entry_definition(&context)) .or_else(|| goto_label_definition(&context)) - .or_else(|| goto_string_definition(&context))?; + .or_else(|| goto_string_definition(&context))? + .into_iter() + .map(|result| { + let origin_selection_range = Some( + origin_document + .line_index + .line_col_lsp_range(result.origin_selection_range), + ); + + let target_document = &context.request.workspace.documents_by_uri[&result.target_uri]; + let target_uri = result.target_uri.as_ref().clone(); + let target_range = target_document + .line_index + .line_col_lsp_range(result.target_range); + let target_selection_range = target_document + .line_index + .line_col_lsp_range(result.target_selection_range); + + LocationLink { + origin_selection_range, + target_uri, + target_range, + target_selection_range, + } + }) + .collect(); + Some(GotoDefinitionResponse::Link(links)) } + +#[derive(Debug, Clone)] +struct DefinitionResult { + origin_selection_range: TextRange, + target_uri: Arc<Url>, + target_range: TextRange, + target_selection_range: TextRange, +} diff --git a/support/texlab/src/features/definition/command.rs b/support/texlab/src/features/definition/command.rs index 72cd4b5909..599c792dfd 100644 --- a/support/texlab/src/features/definition/command.rs +++ b/support/texlab/src/features/definition/command.rs @@ -1,123 +1,47 @@ -use lsp_types::{GotoDefinitionParams, LocationLink}; +use std::sync::Arc; + +use lsp_types::GotoDefinitionParams; use rowan::ast::AstNode; -use crate::{features::cursor::CursorContext, syntax::latex, LineIndexExt}; +use crate::{features::cursor::CursorContext, syntax::latex}; -pub fn goto_command_definition( - context: &CursorContext<GotoDefinitionParams>, -) -> Option<Vec<LocationLink>> { - let main_document = context.request.main_document(); +use super::DefinitionResult; +pub(super) fn goto_command_definition( + context: &CursorContext<GotoDefinitionParams>, +) -> Option<Vec<DefinitionResult>> { let name = context .cursor .as_latex() .filter(|token| token.kind().is_command_name())?; - let origin_selection_range = main_document - .line_index - .line_col_lsp_range(name.text_range()); + let origin_selection_range = name.text_range(); for document in context.request.workspace.documents_by_uri.values() { if let Some(data) = document.data.as_latex() { - for node in latex::SyntaxNode::new_root(data.green.clone()).descendants() { - if let Some(defintion) = latex::CommandDefinition::cast(node).filter(|def| { + let root = latex::SyntaxNode::new_root(data.green.clone()); + + if let Some(result) = root + .descendants() + .filter_map(latex::CommandDefinition::cast) + .filter(|def| { def.name() .and_then(|name| name.command()) .map_or(false, |node| node.text() == name.text()) - }) { - let target_selection_range = document - .line_index - .line_col_lsp_range(defintion.name()?.command()?.text_range()); - - let target_range = document - .line_index - .line_col_lsp_range(latex::small_range(&defintion)); - - return Some(vec![LocationLink { - origin_selection_range: Some(origin_selection_range), - target_uri: document.uri.as_ref().clone(), - target_range, - target_selection_range, - }]); - } + }) + .find_map(|def| { + Some(DefinitionResult { + origin_selection_range, + target_uri: Arc::clone(&document.uri), + target_range: latex::small_range(&def), + target_selection_range: def.name()?.command()?.text_range(), + }) + }) + { + return Some(vec![result]); } } } None } - -#[cfg(test)] -mod tests { - use indoc::indoc; - use lsp_types::Range; - - use crate::{features::testing::FeatureTester, RangeExt}; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .definition(); - - let context = CursorContext::new(request); - - let actual_links = goto_command_definition(&context); - - assert!(actual_links.is_none()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .definition(); - - let context = CursorContext::new(request); - let actual_links = goto_command_definition(&context); - - assert!(actual_links.is_none()); - } - - #[test] - fn test_command_definition() { - let tester = FeatureTester::builder() - .files(vec![( - "main.tex", - indoc! { - r#" - \DeclareMathOperator{\foo}{foo} - \foo - "# - }, - )]) - .main("main.tex") - .line(1) - .character(2) - .build(); - let target_uri = tester.uri("main.tex").as_ref().clone(); - - let request = tester.definition(); - let context = CursorContext::new(request); - let actual_links = goto_command_definition(&context).unwrap(); - - let expected_links = vec![LocationLink { - origin_selection_range: Some(Range::new_simple(1, 0, 1, 4)), - target_uri, - target_range: Range::new_simple(0, 0, 0, 31), - target_selection_range: Range::new_simple(0, 21, 0, 25), - }]; - - assert_eq!(actual_links, expected_links); - } -} diff --git a/support/texlab/src/features/definition/document.rs b/support/texlab/src/features/definition/document.rs index 2a93232cd7..1c09bbc88d 100644 --- a/support/texlab/src/features/definition/document.rs +++ b/support/texlab/src/features/definition/document.rs @@ -1,108 +1,40 @@ -use lsp_types::{GotoDefinitionParams, LocationLink, Range}; +use std::sync::Arc; -use crate::{features::cursor::CursorContext, LineIndexExt, RangeExt}; +use lsp_types::GotoDefinitionParams; +use rowan::TextRange; -pub fn goto_document_definition( +use crate::features::cursor::CursorContext; + +use super::DefinitionResult; + +pub(super) fn goto_document_definition( context: &CursorContext<GotoDefinitionParams>, -) -> Option<Vec<LocationLink>> { - let main_document = context.request.main_document(); - if let Some(data) = main_document.data.as_latex() { - for include in data - .extras - .explicit_links - .iter() - .filter(|link| link.stem_range.contains_inclusive(context.offset)) - { - for target in &include.targets { - if context - .request - .workspace - .documents_by_uri - .values() - .any(|document| document.uri.as_ref() == target.as_ref()) - { - return Some(vec![LocationLink { - origin_selection_range: Some( - main_document - .line_index - .line_col_lsp_range(include.stem_range), - ), - target_uri: target.as_ref().clone(), - target_range: Range::new_simple(0, 0, 0, 0), - target_selection_range: Range::new_simple(0, 0, 0, 0), - }]); - } +) -> Option<Vec<DefinitionResult>> { + let data = context.request.main_document().data.as_latex()?; + + for include in data + .extras + .explicit_links + .iter() + .filter(|link| link.stem_range.contains_inclusive(context.offset)) + { + for target in &include.targets { + if context + .request + .workspace + .documents_by_uri + .values() + .any(|document| document.uri.as_ref() == target.as_ref()) + { + return Some(vec![DefinitionResult { + origin_selection_range: include.stem_range, + target_uri: Arc::clone(target), + target_range: TextRange::default(), + target_selection_range: TextRange::default(), + }]); } } } - None -} - -#[cfg(test)] -mod tests { - use lsp_types::Range; - - use crate::{features::testing::FeatureTester, RangeExt}; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .definition(); - - let context = CursorContext::new(request); - let actual_links = goto_document_definition(&context); - - assert!(actual_links.is_none()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .definition(); - - let context = CursorContext::new(request); - let actual_links = goto_document_definition(&context); - - assert!(actual_links.is_none()); - } - #[test] - fn test_simple() { - let tester = FeatureTester::builder() - .files(vec![ - ("foo.tex", r#"\addbibresource{baz.bib}"#), - ("bar.bib", r#"@article{foo, bar = {baz}}"#), - ("baz.bib", r#"@article{foo, bar = {baz}}"#), - ]) - .main("foo.tex") - .line(0) - .character(18) - .build(); - let target_uri = tester.uri("baz.bib").as_ref().clone(); - - let request = tester.definition(); - let context = CursorContext::new(request); - let actual_links = goto_document_definition(&context).unwrap(); - - let expected_links = vec![LocationLink { - origin_selection_range: Some(Range::new_simple(0, 16, 0, 23)), - target_uri, - target_range: Range::new_simple(0, 0, 0, 0), - target_selection_range: Range::new_simple(0, 0, 0, 0), - }]; - - assert_eq!(actual_links, expected_links); - } + None } diff --git a/support/texlab/src/features/definition/entry.rs b/support/texlab/src/features/definition/entry.rs index a1ae28705d..1dcd983af1 100644 --- a/support/texlab/src/features/definition/entry.rs +++ b/support/texlab/src/features/definition/entry.rs @@ -1,4 +1,6 @@ -use lsp_types::{GotoDefinitionParams, LocationLink}; +use std::sync::Arc; + +use lsp_types::GotoDefinitionParams; use rowan::ast::AstNode; use crate::{ @@ -7,14 +9,13 @@ use crate::{ bibtex::{self, HasName}, latex, }, - LineIndexExt, }; -pub fn goto_entry_definition( - context: &CursorContext<GotoDefinitionParams>, -) -> Option<Vec<LocationLink>> { - let main_document = context.request.main_document(); +use super::DefinitionResult; +pub(super) fn goto_entry_definition( + context: &CursorContext<GotoDefinitionParams>, +) -> Option<Vec<DefinitionResult>> { let word = context .cursor .as_latex() @@ -24,9 +25,7 @@ pub fn goto_entry_definition( latex::Citation::cast(key.syntax().parent()?.parent()?)?; - let origin_selection_range = main_document - .line_index - .line_col_lsp_range(latex::small_range(&key)); + let origin_selection_range = latex::small_range(&key); for document in context.request.workspace.documents_by_uri.values() { if let Some(data) = document.data.as_bibtex() { @@ -35,15 +34,11 @@ pub fn goto_entry_definition( .filter_map(bibtex::Entry::cast) { if let Some(key) = entry.name_token().filter(|k| k.text() == word.text()) { - return Some(vec![LocationLink { - origin_selection_range: Some(origin_selection_range), - target_uri: document.uri.as_ref().clone(), - target_selection_range: document - .line_index - .line_col_lsp_range(key.text_range()), - target_range: document - .line_index - .line_col_lsp_range(entry.syntax().text_range()), + return Some(vec![DefinitionResult { + origin_selection_range, + target_uri: Arc::clone(&document.uri), + target_selection_range: key.text_range(), + target_range: entry.syntax().text_range(), }]); } } @@ -52,81 +47,3 @@ pub fn goto_entry_definition( None } - -#[cfg(test)] -mod tests { - use indoc::indoc; - use lsp_types::Range; - - use crate::{features::testing::FeatureTester, RangeExt}; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .definition(); - - let context = CursorContext::new(request); - let actual_links = goto_entry_definition(&context); - - assert!(actual_links.is_none()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .definition(); - - let context = CursorContext::new(request); - let actual_links = goto_entry_definition(&context); - - assert!(actual_links.is_none()); - } - - #[test] - fn test_simple() { - let tester = FeatureTester::builder() - .files(vec![ - ( - "foo.tex", - indoc!( - r#" - \addbibresource{baz.bib} - \cite{foo} - "# - ), - ), - ("bar.bib", r#"@article{foo, bar = {baz}}"#), - ("baz.bib", r#"@article{foo, bar = {baz}}"#), - ]) - .main("foo.tex") - .line(1) - .character(6) - .build(); - let target_uri = tester.uri("baz.bib").as_ref().clone(); - - let request = tester.definition(); - let context = CursorContext::new(request); - let actual_links = goto_entry_definition(&context).unwrap(); - - let expected_links = vec![LocationLink { - origin_selection_range: Some(Range::new_simple(1, 6, 1, 9)), - target_uri, - target_range: Range::new_simple(0, 0, 0, 26), - target_selection_range: Range::new_simple(0, 9, 0, 12), - }]; - - assert_eq!(actual_links, expected_links); - } -} diff --git a/support/texlab/src/features/definition/label.rs b/support/texlab/src/features/definition/label.rs index e5ccd49937..867e8a1076 100644 --- a/support/texlab/src/features/definition/label.rs +++ b/support/texlab/src/features/definition/label.rs @@ -1,39 +1,33 @@ -use lsp_types::{GotoDefinitionParams, LocationLink}; +use std::sync::Arc; -use crate::{ - features::cursor::CursorContext, find_label_definition, render_label, syntax::latex, - LineIndexExt, -}; +use lsp_types::GotoDefinitionParams; -pub fn goto_label_definition( - context: &CursorContext<GotoDefinitionParams>, -) -> Option<Vec<LocationLink>> { - let main_document = context.request.main_document(); +use crate::{features::cursor::CursorContext, find_label_definition, render_label, syntax::latex}; + +use super::DefinitionResult; - let (name_text, name_range) = context +pub(super) fn goto_label_definition( + context: &CursorContext<GotoDefinitionParams>, +) -> Option<Vec<DefinitionResult>> { + let (name_text, origin_selection_range) = context .find_label_name_key() .or_else(|| context.find_label_name_command())?; - let origin_selection_range = main_document.line_index.line_col_lsp_range(name_range); - for document in context.request.workspace.documents_by_uri.values() { if let Some(data) = document.data.as_latex() { - if let Some(definition) = - find_label_definition(&latex::SyntaxNode::new_root(data.green.clone()), &name_text) - { + let root = latex::SyntaxNode::new_root(data.green.clone()); + if let Some(definition) = find_label_definition(&root, &name_text) { let target_selection_range = latex::small_range(&definition.name()?.key()?); let target_range = render_label(&context.request.workspace, &name_text, Some(definition)) .map(|label| label.range) .unwrap_or(target_selection_range); - return Some(vec![LocationLink { - origin_selection_range: Some(origin_selection_range), - target_uri: document.uri.as_ref().clone(), - target_range: document.line_index.line_col_lsp_range(target_range), - target_selection_range: document - .line_index - .line_col_lsp_range(target_selection_range), + return Some(vec![DefinitionResult { + origin_selection_range, + target_uri: Arc::clone(&document.uri), + target_range, + target_selection_range, }]); } } @@ -41,42 +35,3 @@ pub fn goto_label_definition( None } - -#[cfg(test)] -mod tests { - use crate::features::testing::FeatureTester; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .definition(); - - let context = CursorContext::new(request); - let actual_links = goto_label_definition(&context); - - assert!(actual_links.is_none()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .definition(); - - let context = CursorContext::new(request); - let actual_links = goto_label_definition(&context); - - assert!(actual_links.is_none()); - } -} diff --git a/support/texlab/src/features/definition/string.rs b/support/texlab/src/features/definition/string.rs index 09e00fdf92..c774e24e1d 100644 --- a/support/texlab/src/features/definition/string.rs +++ b/support/texlab/src/features/definition/string.rs @@ -1,15 +1,18 @@ -use lsp_types::{GotoDefinitionParams, LocationLink}; +use std::sync::Arc; + +use lsp_types::GotoDefinitionParams; use rowan::ast::AstNode; use crate::{ features::cursor::CursorContext, syntax::bibtex::{self, HasName}, - LineIndexExt, }; -pub fn goto_string_definition( +use super::DefinitionResult; + +pub(super) fn goto_string_definition( context: &CursorContext<GotoDefinitionParams>, -) -> Option<Vec<LocationLink>> { +) -> Option<Vec<DefinitionResult>> { let main_document = context.request.main_document(); let data = main_document.data.as_bibtex()?; @@ -20,157 +23,21 @@ pub fn goto_string_definition( bibtex::Value::cast(key.parent()?)?; - let origin_selection_range = main_document - .line_index - .line_col_lsp_range(key.text_range()); + let origin_selection_range = key.text_range(); for string in bibtex::SyntaxNode::new_root(data.green.clone()) .children() .filter_map(bibtex::StringDef::cast) { if let Some(string_name) = string.name_token().filter(|k| k.text() == key.text()) { - return Some(vec![LocationLink { - origin_selection_range: Some(origin_selection_range), - target_uri: main_document.uri.as_ref().clone(), - target_selection_range: main_document - .line_index - .line_col_lsp_range(string_name.text_range()), - target_range: main_document - .line_index - .line_col_lsp_range(string.syntax().text_range()), + return Some(vec![DefinitionResult { + origin_selection_range, + target_uri: Arc::clone(&main_document.uri), + target_selection_range: string_name.text_range(), + target_range: string.syntax().text_range(), }]); } } None } - -#[cfg(test)] -mod tests { - use indoc::indoc; - use lsp_types::Range; - - use crate::{features::testing::FeatureTester, RangeExt}; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .definition(); - - let context = CursorContext::new(request); - let actual_links = goto_string_definition(&context); - - assert!(actual_links.is_none()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .definition(); - - let context = CursorContext::new(request); - let actual_links = goto_string_definition(&context); - - assert!(actual_links.is_none()); - } - - #[test] - fn test_simple() { - let tester = FeatureTester::builder() - .files(vec![( - "main.bib", - indoc! { - r#" - @string{foo = {bar}} - @article{bar, author = foo} - "# - }, - )]) - .main("main.bib") - .line(1) - .character(24) - .build(); - let target_uri = tester.uri("main.bib").as_ref().clone(); - - let request = tester.definition(); - let context = CursorContext::new(request); - let actual_links = goto_string_definition(&context).unwrap(); - - let expected_links = vec![LocationLink { - origin_selection_range: Some(Range::new_simple(1, 23, 1, 26)), - target_uri, - target_range: Range::new_simple(0, 0, 0, 20), - target_selection_range: Range::new_simple(0, 8, 0, 11), - }]; - - assert_eq!(actual_links, expected_links); - } - - #[test] - fn test_concat() { - let tester = FeatureTester::builder() - .files(vec![( - "main.bib", - indoc! { - r#" - @string{foo = {bar}} - @article{bar, author = foo # "bar"} - "# - }, - )]) - .main("main.bib") - .line(1) - .character(24) - .build(); - let target_uri = tester.uri("main.bib").as_ref().clone(); - - let request = tester.definition(); - let context = CursorContext::new(request); - let actual_links = goto_string_definition(&context).unwrap(); - - let expected_links = vec![LocationLink { - origin_selection_range: Some(Range::new_simple(1, 23, 1, 26)), - target_uri, - target_range: Range::new_simple(0, 0, 0, 20), - target_selection_range: Range::new_simple(0, 8, 0, 11), - }]; - - assert_eq!(actual_links, expected_links); - } - - #[test] - fn test_field() { - let tester = FeatureTester::builder() - .files(vec![( - "main.bib", - indoc! { - r#" - @string{foo = {bar}} - @article{bar, author = foo} - "# - }, - )]) - .main("main.bib") - .line(1) - .character(18) - .build(); - - let request = tester.definition(); - let context = CursorContext::new(request); - let actual_links = goto_string_definition(&context); - - assert!(actual_links.is_none()); - } -} diff --git a/support/texlab/src/features/execute_command.rs b/support/texlab/src/features/execute_command.rs new file mode 100644 index 0000000000..b1b89d4f50 --- /dev/null +++ b/support/texlab/src/features/execute_command.rs @@ -0,0 +1,103 @@ +use std::{path::PathBuf, process::Stdio, sync::Arc}; + +use anyhow::Result; +use lsp_types::{TextDocumentIdentifier, Url}; + +use crate::Workspace; + +pub fn execute_command( + workspace: &Workspace, + name: &str, + args: Vec<serde_json::Value>, +) -> Result<()> { + match name { + "texlab.cleanAuxiliary" => { + let params = args + .into_iter() + .next() + .ok_or_else(|| anyhow::anyhow!("texlab.cleanAuxiliary requires one argument"))?; + + clean_output_files(workspace, CleanOptions::Auxiliary, params)?; + } + "texlab.cleanArtifacts" => { + let params = args + .into_iter() + .next() + .ok_or_else(|| anyhow::anyhow!("texlab.cleanArtifacts requires one argument"))?; + + clean_output_files(workspace, CleanOptions::Artifacts, params)?; + } + _ => anyhow::bail!("Unknown command: {}", name), + } + + Ok(()) +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] +enum CleanOptions { + Auxiliary, + Artifacts, +} + +fn clean_output_files( + workspace: &Workspace, + options: CleanOptions, + params: serde_json::Value, +) -> Result<()> { + let params: TextDocumentIdentifier = serde_json::from_value(params)?; + + let uri = workspace + .find_parent(¶ms.uri) + .map(|document| document.uri) + .unwrap_or_else(|| Arc::new(params.uri)); + + if let Some(cx) = BuildContext::find(workspace, &uri) { + let flag = match options { + CleanOptions::Auxiliary => "-c", + CleanOptions::Artifacts => "-C", + }; + + std::process::Command::new("latexmk") + .arg(format!("-outdir={}", cx.output_dir.to_string_lossy())) + .arg(flag) + .arg(cx.input_file) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status()?; + } + + Ok(()) +} + +struct BuildContext { + input_file: PathBuf, + output_dir: PathBuf, +} + +impl BuildContext { + pub fn find(workspace: &Workspace, uri: &Url) -> Option<Self> { + if uri.scheme() != "file" { + return None; + } + + let input_file = uri.to_file_path().ok()?; + let options = &workspace.environment.options; + let current_dir = &workspace.environment.current_directory; + let output_dir = match ( + options.root_directory.as_ref(), + options.aux_directory.as_ref(), + ) { + (_, Some(aux_dir)) => current_dir.join(aux_dir), + (Some(root_dir), None) => current_dir.join(root_dir), + (None, None) => input_file.parent()?.to_path_buf(), + }; + + log::info!("Output = {:#?}", output_dir); + + Some(Self { + input_file, + output_dir, + }) + } +} diff --git a/support/texlab/src/features/folding.rs b/support/texlab/src/features/folding.rs index cd1a48f1a8..71fb0bee6f 100644 --- a/support/texlab/src/features/folding.rs +++ b/support/texlab/src/features/folding.rs @@ -55,175 +55,3 @@ fn create_range(range: Range) -> FoldingRange { kind: Some(FoldingRangeKind::Region), } } - -#[cfg(test)] -mod tests { - use indoc::indoc; - - use crate::features::testing::FeatureTester; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .build() - .folding(); - - let actual_foldings = find_foldings(request); - assert!(actual_foldings.is_empty()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .build() - .folding(); - - let actual_foldings = find_foldings(request); - assert!(actual_foldings.is_empty()); - } - - #[test] - fn test_latex() { - let req = FeatureTester::builder() - .files(vec![( - "main.tex", - indoc! {r#" - \begin{document} - \section{Foo} - foo - \subsection{Bar} - bar - \section{Baz} - baz - \section{Qux} - \end{document}"# }, - )]) - .main("main.tex") - .build() - .folding(); - - let mut actual_foldings = find_foldings(req); - actual_foldings.sort_by_key(|folding| (folding.start_line, folding.start_character)); - - assert_eq!(actual_foldings.len(), 5); - assert_eq!( - actual_foldings[0], - FoldingRange { - start_line: 0, - start_character: Some(0), - end_line: 8, - end_character: Some(14), - kind: Some(FoldingRangeKind::Region) - } - ); - assert_eq!( - actual_foldings[1], - FoldingRange { - start_line: 1, - start_character: Some(4), - end_line: 4, - end_character: Some(7), - kind: Some(FoldingRangeKind::Region) - } - ); - assert_eq!( - actual_foldings[2], - FoldingRange { - start_line: 3, - start_character: Some(4), - end_line: 4, - end_character: Some(7), - kind: Some(FoldingRangeKind::Region) - } - ); - assert_eq!( - actual_foldings[3], - FoldingRange { - start_line: 5, - start_character: Some(4), - end_line: 6, - end_character: Some(7), - kind: Some(FoldingRangeKind::Region) - } - ); - assert_eq!( - actual_foldings[4], - FoldingRange { - start_line: 7, - start_character: Some(4), - end_line: 7, - end_character: Some(17), - kind: Some(FoldingRangeKind::Region) - } - ); - } - - #[test] - fn test_bibtex() { - let request = FeatureTester::builder() - .files(vec![( - "main.bib", - indoc! {r#" - some junk - here - - @article{foo, - author = {bar}, - title = {baz} - } - - @string{foo = "bar"} - - @comment{foo, - author = {bar}, - title = {baz} - } - - @preamble{"foo"}"# }, - )]) - .main("main.bib") - .build() - .folding(); - - let mut actual_foldings = find_foldings(request); - actual_foldings.sort_by_key(|folding| (folding.start_line, folding.start_character)); - - assert_eq!(actual_foldings.len(), 3); - assert_eq!( - actual_foldings[0], - FoldingRange { - start_line: 3, - start_character: Some(0), - end_line: 6, - end_character: Some(1), - kind: Some(FoldingRangeKind::Region) - } - ); - assert_eq!( - actual_foldings[1], - FoldingRange { - start_line: 8, - start_character: Some(0), - end_line: 8, - end_character: Some(20), - kind: Some(FoldingRangeKind::Region) - } - ); - assert_eq!( - actual_foldings[2], - FoldingRange { - start_line: 15, - start_character: Some(0), - end_line: 15, - end_character: Some(16), - kind: Some(FoldingRangeKind::Region) - } - ); - } -} diff --git a/support/texlab/src/features/formatting/bibtex_internal.rs b/support/texlab/src/features/formatting/bibtex_internal.rs index 58aaf0635d..2dcc507be9 100644 --- a/support/texlab/src/features/formatting/bibtex_internal.rs +++ b/support/texlab/src/features/formatting/bibtex_internal.rs @@ -213,137 +213,3 @@ impl<'a> Formatter<'a> { } } } - -#[cfg(test)] -mod tests { - use insta::{assert_debug_snapshot, assert_display_snapshot}; - - use crate::features::testing::FeatureTester; - - use super::*; - - #[test] - fn test_wrap_long_lines() { - let request = FeatureTester::builder() - .files(vec![( - "main.bib", - "@article{foo, bar = {Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.},}", - )]) - .main("main.bib") - .build() - .formatting(); - - let edit = format_bibtex_internal(&request).unwrap().pop().unwrap(); - - assert_display_snapshot!(edit.new_text); - } - - #[test] - fn test_multiple_entries() { - let request = FeatureTester::builder() - .files(vec![( - "main.bib", - "@article{foo, bar = {Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.},}\n\n@article{foo, bar = {Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.},}", - )]) - .main("main.bib") - .build() - .formatting(); - - let mut edits = format_bibtex_internal(&request).unwrap(); - let edit2 = edits.pop().unwrap(); - let edit1 = edits.pop().unwrap(); - - assert_debug_snapshot!((edit1.new_text, edit2.new_text)); - } - - #[test] - fn test_trailing_comma() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "@article{foo, bar = baz}")]) - .main("main.bib") - .build() - .formatting(); - - let edit = format_bibtex_internal(&request).unwrap().pop().unwrap(); - - assert_display_snapshot!(edit.new_text); - } - - #[test] - fn test_insert_braces() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "@article{foo, bar = baz,")]) - .main("main.bib") - .build() - .formatting(); - - let edit = format_bibtex_internal(&request).unwrap().pop().unwrap(); - - assert_display_snapshot!(edit.new_text); - } - - #[test] - fn test_command() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "@article{foo, bar = \"\\baz\",}")]) - .main("main.bib") - .build() - .formatting(); - - let edit = format_bibtex_internal(&request).unwrap().pop().unwrap(); - - assert_display_snapshot!(edit.new_text); - } - - #[test] - fn test_concatenation() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "@article{foo, bar = \"baz\" # \"qux\"}")]) - .main("main.bib") - .build() - .formatting(); - - let edit = format_bibtex_internal(&request).unwrap().pop().unwrap(); - - assert_display_snapshot!(edit.new_text); - } - - #[test] - fn test_parens() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "@article(foo,)")]) - .main("main.bib") - .build() - .formatting(); - - let edit = format_bibtex_internal(&request).unwrap().pop().unwrap(); - - assert_display_snapshot!(edit.new_text); - } - - #[test] - fn test_string() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "@string{foo=\"bar\"}")]) - .main("main.bib") - .build() - .formatting(); - - let edit = format_bibtex_internal(&request).unwrap().pop().unwrap(); - - assert_display_snapshot!(edit.new_text); - } - - #[test] - fn test_preamble() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "@preamble{\n\"foo bar baz\"}")]) - .main("main.bib") - .build() - .formatting(); - - let edit = format_bibtex_internal(&request).unwrap().pop().unwrap(); - - assert_display_snapshot!(edit.new_text); - } -} diff --git a/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__command.snap b/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__command.snap deleted file mode 100644 index 792e7be277..0000000000 --- a/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__command.snap +++ /dev/null @@ -1,8 +0,0 @@ ---- -source: src/features/formatting/bibtex_internal.rs -expression: edit.new_text - ---- -@article{foo, - bar = "\baz", -} diff --git a/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__concatenation.snap b/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__concatenation.snap deleted file mode 100644 index 8cd51dfa92..0000000000 --- a/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__concatenation.snap +++ /dev/null @@ -1,8 +0,0 @@ ---- -source: src/features/formatting/bibtex_internal.rs -expression: edit.new_text - ---- -@article{foo, - bar = "baz" # "qux", -} diff --git a/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__insert_braces.snap b/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__insert_braces.snap deleted file mode 100644 index 3be9b15e9d..0000000000 --- a/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__insert_braces.snap +++ /dev/null @@ -1,8 +0,0 @@ ---- -source: src/features/formatting/bibtex_internal.rs -expression: edit.new_text - ---- -@article{foo, - bar = baz, -} diff --git a/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__multiple_entries.snap b/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__multiple_entries.snap deleted file mode 100644 index 636b4b665c..0000000000 --- a/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__multiple_entries.snap +++ /dev/null @@ -1,9 +0,0 @@ ---- -source: src/features/formatting/bibtex_internal.rs -expression: "(edit1.new_text, edit2.new_text)" - ---- -( - "@article{foo,\n\tbar = {Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum\n\t dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet,\n\t consectetur adipiscing elit.},\n}", - "@article{foo,\n\tbar = {Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum\n\t dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet,\n\t consectetur adipiscing elit.},\n}", -) diff --git a/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__parens.snap b/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__parens.snap deleted file mode 100644 index 5aff28cb5f..0000000000 --- a/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__parens.snap +++ /dev/null @@ -1,7 +0,0 @@ ---- -source: src/features/formatting/bibtex_internal.rs -expression: edit.new_text - ---- -@article{foo, -} diff --git a/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__preamble.snap b/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__preamble.snap deleted file mode 100644 index 663569eb5b..0000000000 --- a/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__preamble.snap +++ /dev/null @@ -1,6 +0,0 @@ ---- -source: src/features/formatting/bibtex_internal.rs -expression: edit.new_text - ---- -@preamble{"foo bar baz"} diff --git a/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__string.snap b/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__string.snap deleted file mode 100644 index 6d37a69e8e..0000000000 --- a/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__string.snap +++ /dev/null @@ -1,6 +0,0 @@ ---- -source: src/features/formatting/bibtex_internal.rs -expression: edit.new_text - ---- -@string{foo = "bar"} diff --git a/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__trailing_comma.snap b/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__trailing_comma.snap deleted file mode 100644 index 3be9b15e9d..0000000000 --- a/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__trailing_comma.snap +++ /dev/null @@ -1,8 +0,0 @@ ---- -source: src/features/formatting/bibtex_internal.rs -expression: edit.new_text - ---- -@article{foo, - bar = baz, -} diff --git a/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__wrap_long_lines.snap b/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__wrap_long_lines.snap deleted file mode 100644 index 6c0ec724b5..0000000000 --- a/support/texlab/src/features/formatting/snapshots/texlab__features__formatting__bibtex_internal__tests__wrap_long_lines.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: src/features/formatting/bibtex_internal.rs -expression: edit.new_text - ---- -@article{foo, - bar = {Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum - dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, - consectetur adipiscing elit.}, -} diff --git a/support/texlab/src/features/highlight/label.rs b/support/texlab/src/features/highlight/label.rs index 3e8b95eac8..cb51abdd68 100644 --- a/support/texlab/src/features/highlight/label.rs +++ b/support/texlab/src/features/highlight/label.rs @@ -77,71 +77,3 @@ pub fn find_label_highlights( Some(highlights) } - -#[cfg(test)] -mod tests { - use lsp_types::Range; - - use crate::{features::testing::FeatureTester, RangeExt}; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .highlight(); - let context = CursorContext::new(request); - - let actual_links = find_label_highlights(&context); - - assert!(actual_links.is_none()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .highlight(); - let context = CursorContext::new(request); - - let actual_links = find_label_highlights(&context); - - assert!(actual_links.is_none()); - } - - #[test] - fn test_label() { - let tester = FeatureTester::builder() - .files(vec![("main.tex", "\\label{foo}\n\\ref{foo}\\label{bar}")]) - .main("main.tex") - .line(0) - .character(7) - .build(); - let request = tester.highlight(); - let context = CursorContext::new(request); - - let actual_highlights = find_label_highlights(&context).unwrap(); - - let expected_highlights = vec![ - DocumentHighlight { - range: Range::new_simple(0, 7, 0, 10), - kind: Some(DocumentHighlightKind::WRITE), - }, - DocumentHighlight { - range: Range::new_simple(1, 5, 1, 8), - kind: Some(DocumentHighlightKind::READ), - }, - ]; - - assert_eq!(actual_highlights, expected_highlights); - } -} diff --git a/support/texlab/src/features/hover.rs b/support/texlab/src/features/hover.rs index 7d349de0a3..8663f6256d 100644 --- a/support/texlab/src/features/hover.rs +++ b/support/texlab/src/features/hover.rs @@ -5,9 +5,13 @@ mod field; mod label; mod string_ref; -use lsp_types::{Hover, HoverParams}; +use lsp_types::{Hover, HoverContents, HoverParams, MarkupContent, MarkupKind}; +use rowan::TextRange; -use crate::features::{cursor::CursorContext, hover::citation::find_citation_hover}; +use crate::{ + features::{cursor::CursorContext, hover::citation::find_citation_hover}, + LineIndexExt, +}; use self::{ component::find_component_hover, entry_type::find_entry_type_hover, field::find_field_hover, @@ -19,10 +23,31 @@ use super::FeatureRequest; pub fn find_hover(request: FeatureRequest<HoverParams>) -> Option<Hover> { let context = CursorContext::new(request); log::debug!("[Hover] Cursor: {:?}", context.cursor); - find_label_hover(&context) + let result = find_label_hover(&context) .or_else(|| find_citation_hover(&context)) .or_else(|| find_component_hover(&context)) .or_else(|| find_string_reference_hover(&context)) .or_else(|| find_field_hover(&context)) - .or_else(|| find_entry_type_hover(&context)) + .or_else(|| find_entry_type_hover(&context))?; + + Some(Hover { + contents: HoverContents::Markup(MarkupContent { + kind: result.value_kind, + value: result.value, + }), + range: Some( + context + .request + .main_document() + .line_index + .line_col_lsp_range(result.range), + ), + }) +} + +#[derive(Debug, Clone)] +struct HoverResult { + range: TextRange, + value: String, + value_kind: MarkupKind, } diff --git a/support/texlab/src/features/hover/citation.rs b/support/texlab/src/features/hover/citation.rs index 4a0e3d2313..5e86019eee 100644 --- a/support/texlab/src/features/hover/citation.rs +++ b/support/texlab/src/features/hover/citation.rs @@ -1,121 +1,32 @@ -use lsp_types::{Hover, HoverContents, HoverParams, MarkupContent, MarkupKind}; +use lsp_types::{HoverParams, MarkupKind}; use rowan::ast::AstNode; -use crate::{citation, features::cursor::CursorContext, syntax::bibtex, LineIndexExt}; +use crate::{citation, features::cursor::CursorContext, syntax::bibtex}; -pub fn find_citation_hover(context: &CursorContext<HoverParams>) -> Option<Hover> { - let main_document = context.request.main_document(); +use super::HoverResult; - let (key_text, key_range) = context +pub(super) fn find_citation_hover(context: &CursorContext<HoverParams>) -> Option<HoverResult> { + let (key, range) = context .find_citation_key_word() .or_else(|| context.find_citation_key_command()) .or_else(|| context.find_entry_key())?; - let contents = context + let value = context .request .workspace .documents_by_uri .values() .find_map(|document| { - document.data.as_bibtex().and_then(|data| { - let root = bibtex::SyntaxNode::new_root(data.green.clone()); - let root = bibtex::Root::cast(root)?; - let entry = root.find_entry(&key_text)?; - - citation::render(&entry).map(|value| MarkupContent { - kind: MarkupKind::Markdown, - value, - }) - }) + let data = document.data.as_bibtex()?; + let root = bibtex::SyntaxNode::new_root(data.green.clone()); + let root = bibtex::Root::cast(root)?; + let entry = root.find_entry(&key)?; + citation::render(&entry) })?; - Some(Hover { - range: Some(main_document.line_index.line_col_lsp_range(key_range)), - contents: HoverContents::Markup(contents), + Some(HoverResult { + range, + value, + value_kind: MarkupKind::Markdown, }) } - -#[cfg(test)] -mod tests { - use insta::assert_debug_snapshot; - - use crate::features::testing::FeatureTester; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_citation_hover(&context); - - assert_eq!(actual_hover, None); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_citation_hover(&context); - - assert_eq!(actual_hover, None); - } - - #[test] - fn test_inside_cite() { - let request = FeatureTester::builder() - .files(vec![ - ( - "main.bib", - "@article{foo, author = {Foo Bar}, title = {Baz Qux}, year = 1337}", - ), - ("main.tex", "\\addbibresource{main.bib}\n\\cite{foo}"), - ]) - .main("main.tex") - .line(1) - .character(7) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_citation_hover(&context).unwrap(); - - assert_debug_snapshot!(actual_hover); - } - - #[test] - fn test_inside_entry() { - let request = FeatureTester::builder() - .files(vec![ - ( - "main.bib", - "@article{foo, author = {Foo Bar}, title = {Baz Qux}, year = 1337}", - ), - ("main.tex", "\\addbibresource{main.bib}\n\\cite{foo}"), - ]) - .main("main.bib") - .line(0) - .character(11) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_citation_hover(&context).unwrap(); - - assert_debug_snapshot!(actual_hover); - } -} diff --git a/support/texlab/src/features/hover/component.rs b/support/texlab/src/features/hover/component.rs index 7b7e6c6e02..737a99d3ea 100644 --- a/support/texlab/src/features/hover/component.rs +++ b/support/texlab/src/features/hover/component.rs @@ -1,97 +1,25 @@ -use lsp_types::{Hover, HoverContents, HoverParams}; +use lsp_types::{HoverParams, MarkupKind}; -use crate::{ - component_db::COMPONENT_DATABASE, features::cursor::CursorContext, syntax::latex, LineIndexExt, -}; +use crate::{component_db::COMPONENT_DATABASE, features::cursor::CursorContext, syntax::latex}; -pub fn find_component_hover(context: &CursorContext<HoverParams>) -> Option<Hover> { - let main_document = context.request.main_document(); - let data = main_document.data.as_latex()?; +use super::HoverResult; + +pub(super) fn find_component_hover(context: &CursorContext<HoverParams>) -> Option<HoverResult> { + let data = context.request.main_document().data.as_latex()?; for link in &data.extras.explicit_links { if matches!( link.kind, latex::ExplicitLinkKind::Package | latex::ExplicitLinkKind::Class ) && link.stem_range.contains_inclusive(context.offset) { - let docs = COMPONENT_DATABASE.documentation(&link.stem)?; - return Some(Hover { - contents: HoverContents::Markup(docs), - range: Some(main_document.line_index.line_col_lsp_range(link.stem_range)), + let value = COMPONENT_DATABASE.documentation(&link.stem)?.value; + return Some(HoverResult { + value, + value_kind: MarkupKind::PlainText, + range: link.stem_range, }); } } - None -} - -#[cfg(test)] -mod tests { - use lsp_types::Range; - - use crate::{features::testing::FeatureTester, RangeExt}; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_component_hover(&context); - - assert_eq!(actual_hover, None); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_component_hover(&context); - - assert_eq!(actual_hover, None); - } - #[test] - fn test_known_package() { - let request = FeatureTester::builder() - .files(vec![("main.tex", r#"\usepackage{amsmath}"#)]) - .main("main.tex") - .line(0) - .character(15) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_component_hover(&context).unwrap(); - - assert_eq!(actual_hover.range.unwrap(), Range::new_simple(0, 12, 0, 19)); - } - - #[test] - fn test_unknown_class() { - let request = FeatureTester::builder() - .files(vec![("main.tex", r#"\documentclass{abcdefghijklmnop}"#)]) - .main("main.tex") - .line(0) - .character(20) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_component_hover(&context); - - assert_eq!(actual_hover, None); - } + None } diff --git a/support/texlab/src/features/hover/entry_type.rs b/support/texlab/src/features/hover/entry_type.rs index ab36177ae8..41a561395c 100644 --- a/support/texlab/src/features/hover/entry_type.rs +++ b/support/texlab/src/features/hover/entry_type.rs @@ -1,124 +1,19 @@ -use lsp_types::{Hover, HoverContents, HoverParams, MarkupContent}; +use lsp_types::{HoverParams, MarkupKind}; -use crate::{features::cursor::CursorContext, syntax::bibtex, LineIndexExt, LANGUAGE_DATA}; +use crate::{features::cursor::CursorContext, syntax::bibtex, LANGUAGE_DATA}; -pub fn find_entry_type_hover(context: &CursorContext<HoverParams>) -> Option<Hover> { +use super::HoverResult; + +pub(super) fn find_entry_type_hover(context: &CursorContext<HoverParams>) -> Option<HoverResult> { let name = context .cursor .as_bibtex() .filter(|token| token.kind() == bibtex::TYPE)?; let docs = LANGUAGE_DATA.entry_type_documentation(&name.text()[1..])?; - Some(Hover { - contents: HoverContents::Markup(MarkupContent { - kind: lsp_types::MarkupKind::Markdown, - value: docs.to_string(), - }), - range: Some( - context - .request - .main_document() - .line_index - .line_col_lsp_range(name.text_range()), - ), + Some(HoverResult { + range: name.text_range(), + value: docs.to_string(), + value_kind: MarkupKind::Markdown, }) } - -#[cfg(test)] -mod tests { - use lsp_types::{MarkupKind, Range}; - - use crate::{features::testing::FeatureTester, RangeExt}; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_entry_type_hover(&context); - - assert_eq!(actual_hover, None); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_entry_type_hover(&context); - - assert_eq!(actual_hover, None); - } - - #[test] - fn test_known_entry_type() { - let request = FeatureTester::builder() - .files(vec![("main.bib", r#"@article{foo,}"#)]) - .main("main.bib") - .line(0) - .character(3) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_entry_type_hover(&context).unwrap(); - - let expected_hover = Hover { - contents: HoverContents::Markup(MarkupContent { - kind: MarkupKind::Markdown, - value: LANGUAGE_DATA - .entry_type_documentation("article") - .unwrap() - .into(), - }), - range: Some(Range::new_simple(0, 0, 0, 8)), - }; - assert_eq!(actual_hover, expected_hover); - } - - #[test] - fn test_unknown_field() { - let request = FeatureTester::builder() - .files(vec![("main.bib", r#"@foo{bar,}"#)]) - .main("main.bib") - .line(0) - .character(3) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_entry_type_hover(&context); - - assert_eq!(actual_hover, None); - } - - #[test] - fn test_entry_key() { - let request = FeatureTester::builder() - .files(vec![("main.bib", r#"@article{foo,}"#)]) - .main("main.bib") - .line(0) - .character(11) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_entry_type_hover(&context); - - assert_eq!(actual_hover, None); - } -} diff --git a/support/texlab/src/features/hover/field.rs b/support/texlab/src/features/hover/field.rs index daa0c301cd..6102bd6b68 100644 --- a/support/texlab/src/features/hover/field.rs +++ b/support/texlab/src/features/hover/field.rs @@ -1,11 +1,11 @@ -use lsp_types::{Hover, HoverContents, HoverParams, MarkupContent}; +use lsp_types::{HoverParams, MarkupKind}; use rowan::ast::AstNode; -use crate::{features::cursor::CursorContext, syntax::bibtex, LineIndexExt, LANGUAGE_DATA}; +use crate::{features::cursor::CursorContext, syntax::bibtex, LANGUAGE_DATA}; -pub fn find_field_hover(context: &CursorContext<HoverParams>) -> Option<Hover> { - let main_document = context.request.main_document(); +use super::HoverResult; +pub(super) fn find_field_hover(context: &CursorContext<HoverParams>) -> Option<HoverResult> { let name = context .cursor .as_bibtex() @@ -14,108 +14,9 @@ pub fn find_field_hover(context: &CursorContext<HoverParams>) -> Option<Hover> { bibtex::Field::cast(name.parent()?)?; let docs = LANGUAGE_DATA.field_documentation(name.text())?; - Some(Hover { - contents: HoverContents::Markup(MarkupContent { - kind: lsp_types::MarkupKind::Markdown, - value: docs.to_string(), - }), - range: Some( - main_document - .line_index - .line_col_lsp_range(name.text_range()), - ), + Some(HoverResult { + range: name.text_range(), + value: docs.to_string(), + value_kind: MarkupKind::Markdown, }) } - -#[cfg(test)] -mod tests { - use lsp_types::{MarkupKind, Range}; - - use crate::{features::testing::FeatureTester, RangeExt}; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_field_hover(&context); - - assert_eq!(actual_hover, None); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_field_hover(&context); - - assert_eq!(actual_hover, None); - } - - #[test] - fn test_known_field() { - let request = FeatureTester::builder() - .files(vec![("main.bib", r#"@article{foo, author = bar}"#)]) - .main("main.bib") - .line(0) - .character(15) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_field_hover(&context).unwrap(); - let expected_hover = Hover { - contents: HoverContents::Markup(MarkupContent { - kind: MarkupKind::Markdown, - value: LANGUAGE_DATA.field_documentation("author").unwrap().into(), - }), - range: Some(Range::new_simple(0, 14, 0, 20)), - }; - assert_eq!(actual_hover, expected_hover); - } - - #[test] - fn test_unknown_field() { - let request = FeatureTester::builder() - .files(vec![("main.bib", r#"@article{foo, bar = baz}"#)]) - .main("main.bib") - .line(0) - .character(15) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_field_hover(&context); - assert_eq!(actual_hover, None); - } - - #[test] - fn test_entry_key() { - let request = FeatureTester::builder() - .files(vec![("main.bib", r#"@article{foo, author = bar}"#)]) - .main("main.bib") - .line(0) - .character(11) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_field_hover(&context); - assert_eq!(actual_hover, None); - } -} diff --git a/support/texlab/src/features/hover/label.rs b/support/texlab/src/features/hover/label.rs index 1018c187c5..2f8489d769 100644 --- a/support/texlab/src/features/hover/label.rs +++ b/support/texlab/src/features/hover/label.rs @@ -1,75 +1,19 @@ -use lsp_types::{Hover, HoverContents, HoverParams}; +use lsp_types::{HoverParams, MarkupKind}; -use crate::{features::cursor::CursorContext, render_label, LineIndexExt}; +use crate::{features::cursor::CursorContext, render_label}; -pub fn find_label_hover(context: &CursorContext<HoverParams>) -> Option<Hover> { - let main_document = context.request.main_document(); +use super::HoverResult; - let (name_text, name_range) = context +pub(super) fn find_label_hover(context: &CursorContext<HoverParams>) -> Option<HoverResult> { + let (name_text, range) = context .find_label_name_key() .or_else(|| context.find_label_name_command())?; let label = render_label(&context.request.workspace, &name_text, None)?; - Some(Hover { - range: Some(main_document.line_index.line_col_lsp_range(name_range)), - contents: HoverContents::Markup(label.documentation()), + Some(HoverResult { + range, + value: label.reference(), + value_kind: MarkupKind::PlainText, }) } - -#[cfg(test)] -mod tests { - use lsp_types::Range; - - use crate::{features::testing::FeatureTester, RangeExt}; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_label_hover(&context); - - assert_eq!(actual_hover, None); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_label_hover(&context); - - assert_eq!(actual_hover, None); - } - - #[test] - fn test_section() { - let request = FeatureTester::builder() - .files(vec![("main.tex", r#"\section{Foo}\label{sec:foo}"#)]) - .main("main.tex") - .line(0) - .character(23) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_label_hover(&context).unwrap(); - - assert_eq!(actual_hover.range.unwrap(), Range::new_simple(0, 20, 0, 27)); - } -} diff --git a/support/texlab/src/features/hover/snapshots/texlab__features__hover__citation__tests__inside_cite.snap b/support/texlab/src/features/hover/snapshots/texlab__features__hover__citation__tests__inside_cite.snap deleted file mode 100644 index 2bca88bbff..0000000000 --- a/support/texlab/src/features/hover/snapshots/texlab__features__hover__citation__tests__inside_cite.snap +++ /dev/null @@ -1,25 +0,0 @@ ---- -source: src/features/hover/citation.rs -assertion_line: 97 -expression: actual_hover ---- -Hover { - contents: Markup( - MarkupContent { - kind: Markdown, - value: "F. Bar: \"Baz Qux\". (1337).", - }, - ), - range: Some( - Range { - start: Position { - line: 1, - character: 6, - }, - end: Position { - line: 1, - character: 9, - }, - }, - ), -} diff --git a/support/texlab/src/features/hover/snapshots/texlab__features__hover__citation__tests__inside_entry.snap b/support/texlab/src/features/hover/snapshots/texlab__features__hover__citation__tests__inside_entry.snap deleted file mode 100644 index 73ac4b7b69..0000000000 --- a/support/texlab/src/features/hover/snapshots/texlab__features__hover__citation__tests__inside_entry.snap +++ /dev/null @@ -1,25 +0,0 @@ ---- -source: src/features/hover/citation.rs -assertion_line: 119 -expression: actual_hover ---- -Hover { - contents: Markup( - MarkupContent { - kind: Markdown, - value: "F. Bar: \"Baz Qux\". (1337).", - }, - ), - range: Some( - Range { - start: Position { - line: 0, - character: 9, - }, - end: Position { - line: 0, - character: 12, - }, - }, - ), -} diff --git a/support/texlab/src/features/hover/string_ref.rs b/support/texlab/src/features/hover/string_ref.rs index f1df08e8ab..adccdd556c 100644 --- a/support/texlab/src/features/hover/string_ref.rs +++ b/support/texlab/src/features/hover/string_ref.rs @@ -1,21 +1,27 @@ -use lsp_types::{Hover, HoverContents, HoverParams, MarkupContent, MarkupKind}; +use lsp_types::{HoverParams, MarkupKind}; use rowan::ast::AstNode; use crate::{ + citation::field::text::TextFieldData, features::cursor::CursorContext, syntax::bibtex::{self, HasName, HasValue}, - LineIndexExt, }; -pub fn find_string_reference_hover(context: &CursorContext<HoverParams>) -> Option<Hover> { - let main_document = context.request.main_document(); - let data = main_document.data.as_bibtex()?; +use super::HoverResult; - let key = context.cursor.as_bibtex().filter(|token| { - let parent = token.parent().unwrap(); - (token.kind() == bibtex::NAME && bibtex::Value::can_cast(parent.kind())) - || (token.kind() == bibtex::NAME && bibtex::StringDef::can_cast(parent.kind())) - })?; +pub(super) fn find_string_reference_hover( + context: &CursorContext<HoverParams>, +) -> Option<HoverResult> { + let data = context.request.main_document().data.as_bibtex()?; + + let name = context + .cursor + .as_bibtex() + .filter(|token| token.kind() == bibtex::NAME) + .filter(|token| { + let parent = token.parent().unwrap(); + bibtex::Value::can_cast(parent.kind()) || bibtex::StringDef::can_cast(parent.kind()) + })?; for string in bibtex::SyntaxNode::new_root(data.green.clone()) .children() @@ -23,118 +29,16 @@ pub fn find_string_reference_hover(context: &CursorContext<HoverParams>) -> Opti { if string .name_token() - .filter(|k| k.text() == key.text()) - .is_some() + .map_or(false, |token| token.text() == name.text()) { - let value = string.value()?.syntax().text().to_string(); - return Some(Hover { - range: Some( - main_document - .line_index - .line_col_lsp_range(key.text_range()), - ), - contents: HoverContents::Markup(MarkupContent { - kind: MarkupKind::PlainText, - value, - }), + let value = TextFieldData::parse(&string.value()?)?.text; + return Some(HoverResult { + range: name.text_range(), + value, + value_kind: MarkupKind::PlainText, }); } } None } - -#[cfg(test)] -mod tests { - use indoc::indoc; - use lsp_types::Range; - - use crate::{features::testing::FeatureTester, RangeExt}; - - use super::*; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_string_reference_hover(&context); - - assert_eq!(actual_hover, None); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_string_reference_hover(&context); - - assert_eq!(actual_hover, None); - } - - #[test] - fn test_inside_reference() { - let request = FeatureTester::builder() - .files(vec![( - "main.bib", - indoc! { r#" - @string{foo = "Foo"} - @string{bar = "Bar"} - @article{baz, author = bar} - "# }, - )]) - .main("main.bib") - .line(2) - .character(24) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_string_reference_hover(&context).unwrap(); - - let expected_hover = Hover { - contents: HoverContents::Markup(MarkupContent { - kind: MarkupKind::PlainText, - value: "\"Bar\"".into(), - }), - range: Some(Range::new_simple(2, 23, 2, 26)), - }; - - assert_eq!(actual_hover, expected_hover); - } - - #[test] - fn test_inside_field() { - let request = FeatureTester::builder() - .files(vec![( - "main.bib", - indoc! { r#" - @string{foo = "Foo"} - @string{bar = "Bar"} - @article{baz, author = bar} - "# }, - )]) - .main("main.bib") - .line(2) - .character(20) - .build() - .hover(); - - let context = CursorContext::new(request); - let actual_hover = find_string_reference_hover(&context); - assert_eq!(actual_hover, None); - } -} diff --git a/support/texlab/src/features/link.rs b/support/texlab/src/features/link.rs index 0a02f7819e..c4ce4413f5 100644 --- a/support/texlab/src/features/link.rs +++ b/support/texlab/src/features/link.rs @@ -1,107 +1,33 @@ -use lsp_types::{DocumentLink, DocumentLinkParams}; +mod include; -use crate::LineIndexExt; - -use super::FeatureRequest; - -pub fn find_document_links(request: FeatureRequest<DocumentLinkParams>) -> Vec<DocumentLink> { - let mut links = Vec::new(); - let main_document = request.main_document(); - if let Some(data) = main_document.data.as_latex() { - for include in &data.extras.explicit_links { - for target in &include.targets { - if request - .workspace - .documents_by_uri - .values() - .any(|document| document.uri.as_ref() == target.as_ref()) - { - links.push(DocumentLink { - range: main_document - .line_index - .line_col_lsp_range(include.stem_range), - target: Some(target.as_ref().clone()), - tooltip: None, - data: None, - }); - break; - } - } - } - } - links -} - -#[cfg(test)] -mod tests { - use super::*; - - use lsp_types::Range; - - use crate::{features::testing::FeatureTester, RangeExt}; - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .build() - .link(); +use std::sync::Arc; - let items = find_document_links(request); - assert!(items.is_empty()); - } +use lsp_types::{DocumentLink, DocumentLinkParams, Url}; +use rowan::TextRange; - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .build() - .link(); - - let items = find_document_links(request); - assert!(items.is_empty()); - } +use crate::LineIndexExt; - #[test] - fn test_includes() { - let tester = FeatureTester::builder() - .files(vec![("foo.tex", r#"\input{bar.tex}"#), ("bar.tex", r#""#)]) - .main("foo.tex") - .build(); - let target = tester.uri("bar.tex"); +use self::include::find_include_links; - let actual_items = find_document_links(tester.link()); +use super::FeatureRequest; - let expected_items = vec![DocumentLink { - range: Range::new_simple(0, 7, 0, 14), - target: Some(target.as_ref().clone()), +pub fn find_document_links(request: FeatureRequest<DocumentLinkParams>) -> Vec<DocumentLink> { + let document = request.main_document(); + let mut results = Vec::new(); + find_include_links(&request, &mut results); + results + .into_iter() + .map(|result| DocumentLink { + range: document.line_index.line_col_lsp_range(result.range), + target: Some(result.target.as_ref().clone()), tooltip: None, data: None, - }]; - assert_eq!(actual_items, expected_items); - } - - #[test] - fn test_imports() { - let tester = FeatureTester::builder() - .files(vec![ - ("foo.tex", r#"\import{bar/}{baz}"#), - ("bar/baz.tex", r#""#), - ]) - .main("foo.tex") - .build(); - let target = tester.uri("bar/baz.tex"); - - let actual_items = find_document_links(tester.link()); + }) + .collect() +} - let expected_items = vec![DocumentLink { - range: Range::new_simple(0, 14, 0, 17), - target: Some(target.as_ref().clone()), - tooltip: None, - data: None, - }]; - assert_eq!(actual_items, expected_items); - } +#[derive(Debug, Clone)] +struct LinkResult { + range: TextRange, + target: Arc<Url>, } diff --git a/support/texlab/src/features/link/include.rs b/support/texlab/src/features/link/include.rs new file mode 100644 index 0000000000..b4d1804cce --- /dev/null +++ b/support/texlab/src/features/link/include.rs @@ -0,0 +1,33 @@ +use std::sync::Arc; + +use lsp_types::DocumentLinkParams; + +use crate::features::FeatureRequest; + +use super::LinkResult; + +pub(super) fn find_include_links( + request: &FeatureRequest<DocumentLinkParams>, + results: &mut Vec<LinkResult>, +) -> Option<()> { + let data = request.main_document().data.as_latex()?; + + for include in &data.extras.explicit_links { + for target in &include.targets { + if request + .workspace + .documents_by_uri + .values() + .any(|document| document.uri.as_ref() == target.as_ref()) + { + results.push(LinkResult { + range: include.stem_range, + target: Arc::clone(target), + }); + break; + } + } + } + + Some(()) +} diff --git a/support/texlab/src/features/reference.rs b/support/texlab/src/features/reference.rs index db48453b4e..f84a3f67d5 100644 --- a/support/texlab/src/features/reference.rs +++ b/support/texlab/src/features/reference.rs @@ -2,7 +2,12 @@ mod entry; mod label; mod string; -use lsp_types::{Location, ReferenceParams}; +use std::sync::Arc; + +use lsp_types::{Location, ReferenceParams, Url}; +use rowan::TextRange; + +use crate::LineIndexExt; use self::{ entry::find_entry_references, label::find_label_references, string::find_string_references, @@ -10,12 +15,27 @@ use self::{ use super::{cursor::CursorContext, FeatureRequest}; -pub fn find_all_references(request: FeatureRequest<ReferenceParams>) -> Option<Vec<Location>> { - let mut references = Vec::new(); +pub fn find_all_references(request: FeatureRequest<ReferenceParams>) -> Vec<Location> { + let mut results = Vec::new(); let context = CursorContext::new(request); log::debug!("[References] Cursor: {:?}", context.cursor); - find_label_references(&context, &mut references); - find_entry_references(&context, &mut references); - find_string_references(&context, &mut references); - Some(references) + find_label_references(&context, &mut results); + find_entry_references(&context, &mut results); + find_string_references(&context, &mut results); + + results + .into_iter() + .map(|result| Location { + uri: result.uri.as_ref().clone(), + range: context.request.workspace.documents_by_uri[&result.uri] + .line_index + .line_col_lsp_range(result.range), + }) + .collect() +} + +#[derive(Debug, Clone)] +struct ReferenceResult { + uri: Arc<Url>, + range: TextRange, } diff --git a/support/texlab/src/features/reference/entry.rs b/support/texlab/src/features/reference/entry.rs index f245b6eb8d..0144d2d074 100644 --- a/support/texlab/src/features/reference/entry.rs +++ b/support/texlab/src/features/reference/entry.rs @@ -1,4 +1,6 @@ -use lsp_types::{Location, ReferenceParams}; +use std::sync::Arc; + +use lsp_types::ReferenceParams; use rowan::ast::AstNode; use crate::{ @@ -7,12 +9,14 @@ use crate::{ bibtex::{self, HasName}, latex, }, - DocumentData, LineIndexExt, + DocumentData, }; -pub fn find_entry_references( +use super::ReferenceResult; + +pub(super) fn find_entry_references( context: &CursorContext<ReferenceParams>, - references: &mut Vec<Location>, + results: &mut Vec<ReferenceResult>, ) -> Option<()> { let (key_text, _) = context .find_citation_key_word() @@ -28,13 +32,10 @@ pub fn find_entry_references( .filter_map(|citation| citation.key_list()) .flat_map(|keys| keys.keys()) .filter(|key| key.to_string() == key_text) - .map(|key| { - document - .line_index - .line_col_lsp_range(latex::small_range(&key)) - }) + .map(|key| latex::small_range(&key)) .for_each(|range| { - references.push(Location::new(document.uri.as_ref().clone(), range)); + let uri = Arc::clone(&document.uri); + results.push(ReferenceResult { uri, range }); }); } DocumentData::Bibtex(data) if context.request.params.context.include_declaration => { @@ -43,172 +44,15 @@ pub fn find_entry_references( .filter_map(bibtex::Entry::cast) .filter_map(|entry| entry.name_token()) .filter(|key| key.text() == key_text) - .map(|key| document.line_index.line_col_lsp_range(key.text_range())) + .map(|key| key.text_range()) .for_each(|range| { - references.push(Location::new(document.uri.as_ref().clone(), range)); + let uri = Arc::clone(&document.uri); + results.push(ReferenceResult { uri, range }); }); } DocumentData::Bibtex(_) | DocumentData::BuildLog(_) => {} } } - Some(()) -} - -#[cfg(test)] -mod tests { - use lsp_types::Range; - - use crate::{features::testing::FeatureTester, RangeExt}; - - use super::*; - - fn sort_references(actual_references: &mut [Location]) { - actual_references.sort_by(|a, b| { - a.uri - .cmp(&b.uri) - .then_with(|| a.range.start.cmp(&b.range.start)) - }); - } - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .reference(); - - let mut actual_references = Vec::new(); - let context = CursorContext::new(request); - find_entry_references(&context, &mut actual_references); - - assert!(actual_references.is_empty()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .reference(); - - let mut actual_references = Vec::new(); - let context = CursorContext::new(request); - find_entry_references(&context, &mut actual_references); - - assert!(actual_references.is_empty()); - } - #[test] - fn test_definition() { - let tester = FeatureTester::builder() - .files(vec![ - ("foo.bib", r#"@article{foo,}"#), - ("bar.tex", r#"\cite{foo}\addbibresource{foo.bib}"#), - ]) - .main("foo.bib") - .line(0) - .character(11) - .build(); - let uri = tester.uri("bar.tex"); - let mut actual_references = Vec::new(); - - let request = tester.reference(); - let context = CursorContext::new(request); - find_entry_references(&context, &mut actual_references); - - sort_references(&mut actual_references); - let expected_references = vec![Location::new( - uri.as_ref().clone(), - Range::new_simple(0, 6, 0, 9), - )]; - assert_eq!(actual_references, expected_references); - } - - #[test] - fn test_definition_include_declaration() { - let tester = FeatureTester::builder() - .files(vec![ - ("foo.bib", r#"@article{foo,}"#), - ("bar.tex", r#"\cite{foo}\addbibresource{foo.bib}"#), - ]) - .main("foo.bib") - .line(0) - .character(11) - .include_declaration(true) - .build(); - let uri1 = tester.uri("bar.tex"); - let uri2 = tester.uri("foo.bib"); - let mut actual_references = Vec::new(); - - let request = tester.reference(); - let context = CursorContext::new(request); - find_entry_references(&context, &mut actual_references); - - sort_references(&mut actual_references); - let expected_references = vec![ - Location::new(uri1.as_ref().clone(), Range::new_simple(0, 6, 0, 9)), - Location::new(uri2.as_ref().clone(), Range::new_simple(0, 9, 0, 12)), - ]; - assert_eq!(actual_references, expected_references); - } - - #[test] - fn test_reference() { - let tester = FeatureTester::builder() - .files(vec![ - ("foo.bib", r#"@article{foo,}"#), - ("bar.tex", r#"\cite{foo}\addbibresource{foo.bib}"#), - ]) - .main("bar.tex") - .line(0) - .character(8) - .build(); - let uri = tester.uri("bar.tex"); - let mut actual_references = Vec::new(); - - let request = tester.reference(); - let context = CursorContext::new(request); - find_entry_references(&context, &mut actual_references); - - sort_references(&mut actual_references); - let expected_references = vec![Location::new( - uri.as_ref().clone(), - Range::new_simple(0, 6, 0, 9), - )]; - assert_eq!(actual_references, expected_references); - } - - #[test] - fn test_reference_include_declaration() { - let tester = FeatureTester::builder() - .files(vec![ - ("foo.bib", r#"@article{foo,}"#), - ("bar.tex", r#"\cite{foo}\addbibresource{foo.bib}"#), - ]) - .main("bar.tex") - .line(0) - .character(6) - .include_declaration(true) - .build(); - let uri1 = tester.uri("foo.bib"); - let uri2 = tester.uri("bar.tex"); - let mut actual_references = Vec::new(); - - let request = tester.reference(); - let context = CursorContext::new(request); - find_entry_references(&context, &mut actual_references); - - sort_references(&mut actual_references); - let expected_references = vec![ - Location::new(uri2.as_ref().clone(), Range::new_simple(0, 6, 0, 9)), - Location::new(uri1.as_ref().clone(), Range::new_simple(0, 9, 0, 12)), - ]; - assert_eq!(actual_references, expected_references); - } + Some(()) } diff --git a/support/texlab/src/features/reference/label.rs b/support/texlab/src/features/reference/label.rs index 2fc3efc398..3dadada08b 100644 --- a/support/texlab/src/features/reference/label.rs +++ b/support/texlab/src/features/reference/label.rs @@ -1,10 +1,14 @@ -use lsp_types::{Location, ReferenceParams}; +use std::sync::Arc; -use crate::{features::cursor::CursorContext, LineIndexExt}; +use lsp_types::ReferenceParams; -pub fn find_label_references( +use crate::features::cursor::CursorContext; + +use super::ReferenceResult; + +pub(super) fn find_label_references( context: &CursorContext<ReferenceParams>, - references: &mut Vec<Location>, + results: &mut Vec<ReferenceResult>, ) -> Option<()> { let (name_text, _) = context .find_label_name_key() @@ -21,174 +25,13 @@ pub fn find_label_references( !name.is_definition || context.request.params.context.include_declaration }) { - references.push(Location::new( - document.uri.as_ref().clone(), - document.line_index.line_col_lsp_range(name.range), - )); + results.push(ReferenceResult { + uri: Arc::clone(&document.uri), + range: name.range, + }); } } } Some(()) } - -#[cfg(test)] -mod tests { - use lsp_types::Range; - - use crate::{features::testing::FeatureTester, RangeExt}; - - use super::*; - - fn sort_references(actual_references: &mut [Location]) { - actual_references.sort_by(|a, b| { - a.uri - .cmp(&b.uri) - .then_with(|| a.range.start.cmp(&b.range.start)) - }); - } - - #[test] - fn test_definition() { - let tester = FeatureTester::builder() - .files(vec![ - ("foo.tex", r#"\label{foo}"#), - ("bar.tex", r#"\ref{foo}\input{foo.tex}"#), - ]) - .main("foo.tex") - .line(0) - .character(8) - .build(); - let uri = tester.uri("bar.tex"); - let mut actual_references = Vec::new(); - - let request = tester.reference(); - let context = CursorContext::new(request); - find_label_references(&context, &mut actual_references); - - sort_references(&mut actual_references); - let expected_references = vec![Location::new( - uri.as_ref().clone(), - Range::new_simple(0, 5, 0, 8), - )]; - assert_eq!(actual_references, expected_references); - } - - #[test] - fn test_definition_include_declaration() { - let tester = FeatureTester::builder() - .files(vec![ - ("foo.tex", r#"\label{foo}\input{bar.tex}"#), - ("bar.tex", r#"\ref{foo}"#), - ]) - .main("foo.tex") - .line(0) - .character(9) - .include_declaration(true) - .build(); - let uri1 = tester.uri("bar.tex"); - let uri2 = tester.uri("foo.tex"); - let mut actual_references = Vec::new(); - - let request = tester.reference(); - let context = CursorContext::new(request); - find_label_references(&context, &mut actual_references); - - sort_references(&mut actual_references); - let expected_references = vec![ - Location::new(uri1.as_ref().clone(), Range::new_simple(0, 5, 0, 8)), - Location::new(uri2.as_ref().clone(), Range::new_simple(0, 7, 0, 10)), - ]; - assert_eq!(actual_references, expected_references); - } - - #[test] - fn test_reference() { - let tester = FeatureTester::builder() - .files(vec![ - ("foo.tex", r#"\label{foo}\input{bar.tex}"#), - ("bar.tex", r#"\ref{foo}"#), - ("baz.tex", r#"\ref{foo}\input{bar.tex}"#), - ]) - .main("bar.tex") - .line(0) - .character(7) - .build(); - let uri1 = tester.uri("bar.tex"); - let uri2 = tester.uri("baz.tex"); - let mut actual_references = Vec::new(); - - let request = tester.reference(); - let context = CursorContext::new(request); - find_label_references(&context, &mut actual_references); - - sort_references(&mut actual_references); - let expected_references = vec![ - Location::new(uri1.as_ref().clone(), Range::new_simple(0, 5, 0, 8)), - Location::new(uri2.as_ref().clone(), Range::new_simple(0, 5, 0, 8)), - ]; - assert_eq!(actual_references, expected_references); - } - - #[test] - fn test_reference_include_declaration() { - let tester = FeatureTester::builder() - .files(vec![ - ("foo.tex", r#"\label{foo}"#), - ("bar.tex", r#"\ref{foo}\input{foo.tex}"#), - ]) - .main("bar.tex") - .line(0) - .character(7) - .include_declaration(true) - .build(); - let uri1 = tester.uri("bar.tex"); - let uri2 = tester.uri("foo.tex"); - let mut actual_references = Vec::new(); - - let request = tester.reference(); - let context = CursorContext::new(request); - find_label_references(&context, &mut actual_references); - - sort_references(&mut actual_references); - let expected_references = vec![ - Location::new(uri1.as_ref().clone(), Range::new_simple(0, 5, 0, 8)), - Location::new(uri2.as_ref().clone(), Range::new_simple(0, 7, 0, 10)), - ]; - assert_eq!(actual_references, expected_references); - } - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .reference(); - let mut actual_references = Vec::new(); - - let context = CursorContext::new(request); - find_label_references(&context, &mut actual_references); - - assert!(actual_references.is_empty()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .reference(); - let mut actual_references = Vec::new(); - - let context = CursorContext::new(request); - find_label_references(&context, &mut actual_references); - - assert!(actual_references.is_empty()); - } -} diff --git a/support/texlab/src/features/reference/string.rs b/support/texlab/src/features/reference/string.rs index c957244a53..c00cf93546 100644 --- a/support/texlab/src/features/reference/string.rs +++ b/support/texlab/src/features/reference/string.rs @@ -1,23 +1,26 @@ -use lsp_types::{Location, ReferenceParams}; +use std::sync::Arc; + +use lsp_types::ReferenceParams; use rowan::ast::AstNode; use crate::{ features::cursor::CursorContext, syntax::bibtex::{self, HasName}, - LineIndexExt, }; -pub fn find_string_references( +use super::ReferenceResult; + +pub(super) fn find_string_references( context: &CursorContext<ReferenceParams>, - items: &mut Vec<Location>, + results: &mut Vec<ReferenceResult>, ) -> Option<()> { let name_text = context .cursor .as_bibtex() + .filter(|token| token.kind() == bibtex::NAME) .filter(|token| { let parent = token.parent().unwrap(); - (token.kind() == bibtex::NAME && bibtex::Value::can_cast(parent.kind())) - || (token.kind() == bibtex::NAME && bibtex::StringDef::can_cast(parent.kind())) + bibtex::Value::can_cast(parent.kind()) || bibtex::StringDef::can_cast(parent.kind()) })? .text(); @@ -35,173 +38,12 @@ pub fn find_string_references( .filter(|name| name.text() == name_text) }) { - items.push(Location::new( - document.uri.as_ref().clone(), - document.line_index.line_col_lsp_range(name.text_range()), - )); + results.push(ReferenceResult { + uri: Arc::clone(&document.uri), + range: name.text_range(), + }); } } - Some(()) -} - -#[cfg(test)] -mod tests { - use indoc::indoc; - use lsp_types::Range; - - use crate::{features::testing::FeatureTester, RangeExt}; - - use super::*; - - #[test] - fn test_definition() { - let tester = FeatureTester::builder() - .files(vec![( - "main.bib", - indoc! { r#" - @string{foo = {Foo}} - @string{bar = {Bar}} - @article{baz, author = foo} - "# }, - )]) - .main("main.bib") - .line(2) - .character(24) - .build(); - let uri = tester.uri("main.bib"); - - let mut actual_references = Vec::new(); - let request = tester.reference(); - let context = CursorContext::new(request); - find_string_references(&context, &mut actual_references); - - let expected_references = vec![Location::new( - uri.as_ref().clone(), - Range::new_simple(2, 23, 2, 26), - )]; - assert_eq!(actual_references, expected_references); - } - - #[test] - fn test_definition_include_declaration() { - let tester = FeatureTester::builder() - .files(vec![( - "main.bib", - indoc! { r#" - @string{foo = {Foo}} - @string{bar = {Bar}} - @article{baz, author = foo} - "# }, - )]) - .main("main.bib") - .line(2) - .character(24) - .include_declaration(true) - .build(); - let uri = tester.uri("main.bib"); - - let mut actual_references = Vec::new(); - let request = tester.reference(); - let context = CursorContext::new(request); - find_string_references(&context, &mut actual_references); - - let expected_references = vec![ - Location::new(uri.as_ref().clone(), Range::new_simple(0, 8, 0, 11)), - Location::new(uri.as_ref().clone(), Range::new_simple(2, 23, 2, 26)), - ]; - assert_eq!(actual_references, expected_references); - } - - #[test] - fn test_reference() { - let tester = FeatureTester::builder() - .files(vec![( - "main.bib", - indoc! { r#" - @string{foo = {Foo}} - @string{bar = {Bar}} - @article{baz, author = foo} - "# }, - )]) - .main("main.bib") - .line(0) - .character(10) - .build(); - let uri = tester.uri("main.bib"); - let mut actual_references = Vec::new(); - let request = tester.reference(); - let context = CursorContext::new(request); - find_string_references(&context, &mut actual_references); - - let expected_references = vec![Location::new( - uri.as_ref().clone(), - Range::new_simple(2, 23, 2, 26), - )]; - assert_eq!(actual_references, expected_references); - } - - #[test] - fn test_reference_include_declaration() { - let tester = FeatureTester::builder() - .files(vec![( - "main.bib", - indoc! { r#" - @string{foo = {Foo}} - @string{bar = {Bar}} - @article{baz, author = foo} - "# }, - )]) - .main("main.bib") - .line(0) - .character(10) - .include_declaration(true) - .build(); - let uri = tester.uri("main.bib"); - - let mut actual_references = Vec::new(); - let request = tester.reference(); - let context = CursorContext::new(request); - find_string_references(&context, &mut actual_references); - - let expected_references = vec![ - Location::new(uri.as_ref().clone(), Range::new_simple(0, 8, 0, 11)), - Location::new(uri.as_ref().clone(), Range::new_simple(2, 23, 2, 26)), - ]; - assert_eq!(actual_references, expected_references); - } - - #[test] - fn test_empty_latex_document() { - let request = FeatureTester::builder() - .files(vec![("main.tex", "")]) - .main("main.tex") - .line(0) - .character(0) - .build() - .reference(); - - let mut actual_references = Vec::new(); - let context = CursorContext::new(request); - find_string_references(&context, &mut actual_references); - - assert!(actual_references.is_empty()); - } - - #[test] - fn test_empty_bibtex_document() { - let request = FeatureTester::builder() - .files(vec![("main.bib", "")]) - .main("main.bib") - .line(0) - .character(0) - .build() - .reference(); - - let mut actual_references = Vec::new(); - let context = CursorContext::new(request); - find_string_references(&context, &mut actual_references); - - assert!(actual_references.is_empty()); - } + Some(()) } diff --git a/support/texlab/src/features/rename.rs b/support/texlab/src/features/rename.rs index a9a6732d04..c21faf262e 100644 --- a/support/texlab/src/features/rename.rs +++ b/support/texlab/src/features/rename.rs @@ -2,7 +2,13 @@ mod command; mod entry; mod label; -use lsp_types::{Range, RenameParams, TextDocumentPositionParams, WorkspaceEdit}; +use std::sync::Arc; + +use lsp_types::{Range, RenameParams, TextDocumentPositionParams, TextEdit, Url, WorkspaceEdit}; +use rowan::TextRange; +use rustc_hash::FxHashMap; + +use crate::LineIndexExt; use self::{ command::{prepare_command_rename, rename_command}, @@ -14,14 +20,46 @@ use super::{cursor::CursorContext, FeatureRequest}; pub fn prepare_rename_all(request: FeatureRequest<TextDocumentPositionParams>) -> Option<Range> { let context = CursorContext::new(request); - prepare_entry_rename(&context) + let range = prepare_entry_rename(&context) .or_else(|| prepare_label_rename(&context)) - .or_else(|| prepare_command_rename(&context)) + .or_else(|| prepare_command_rename(&context))?; + + let line_index = &context.request.main_document().line_index; + Some(line_index.line_col_lsp_range(range)) } pub fn rename_all(request: FeatureRequest<RenameParams>) -> Option<WorkspaceEdit> { let context = CursorContext::new(request); - rename_entry(&context) + let result = rename_entry(&context) .or_else(|| rename_label(&context)) - .or_else(|| rename_command(&context)) + .or_else(|| rename_command(&context))?; + + let changes = result + .changes + .into_iter() + .map(|(uri, old_edits)| { + let document = &context.request.workspace.documents_by_uri[&uri]; + let new_edits = old_edits + .into_iter() + .map(|Indel { delete, insert }| { + TextEdit::new(document.line_index.line_col_lsp_range(delete), insert) + }) + .collect(); + + (uri.as_ref().clone(), new_edits) + }) + .collect(); + + Some(WorkspaceEdit::new(changes)) +} + +#[derive(Debug, PartialEq, Eq, Clone)] +struct Indel { + delete: TextRange, + insert: String, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +struct RenameResult { + changes: FxHashMap<Arc<Url>, Vec<Indel>>, } diff --git a/support/texlab/src/features/rename/command.rs b/support/texlab/src/features/rename/command.rs index e0677f443e..aaa08f8cb4 100644 --- a/support/texlab/src/features/rename/command.rs +++ b/support/texlab/src/features/rename/command.rs @@ -1,88 +1,45 @@ -use std::collections::HashMap; +use std::sync::Arc; -use lsp_types::{Range, RenameParams, TextEdit, WorkspaceEdit}; +use lsp_types::RenameParams; use rowan::{TextRange, TextSize}; +use rustc_hash::FxHashMap; use crate::{ features::cursor::{CursorContext, HasPosition}, syntax::latex, - LineIndexExt, }; -pub fn prepare_command_rename<P: HasPosition>(context: &CursorContext<P>) -> Option<Range> { - Some( - context - .request - .main_document() - .line_index - .line_col_lsp_range(context.cursor.command_range(context.offset)?), - ) +use super::{Indel, RenameResult}; + +pub(super) fn prepare_command_rename<P: HasPosition>( + context: &CursorContext<P>, +) -> Option<TextRange> { + context.cursor.command_range(context.offset) } -pub fn rename_command(context: &CursorContext<RenameParams>) -> Option<WorkspaceEdit> { +pub(super) fn rename_command(context: &CursorContext<RenameParams>) -> Option<RenameResult> { prepare_command_rename(context)?; let name = context.cursor.as_latex()?.text(); - let mut changes = HashMap::new(); + let mut changes = FxHashMap::default(); for document in context.request.workspace.documents_by_uri.values() { if let Some(data) = document.data.as_latex() { - let edits = latex::SyntaxNode::new_root(data.green.clone()) + let root = latex::SyntaxNode::new_root(data.green.clone()); + let edits = root .descendants_with_tokens() .filter_map(|element| element.into_token()) .filter(|token| token.kind().is_command_name() && token.text() == name) .map(|token| { let range = token.text_range(); - let range = document.line_index.line_col_lsp_range(TextRange::new( - range.start() + TextSize::from(1), - range.end(), - )); - TextEdit::new(range, context.request.params.new_name.clone()) + Indel { + delete: TextRange::new(range.start() + TextSize::from(1), range.end()), + insert: context.request.params.new_name.clone(), + } }) .collect(); - changes.insert(document.uri.as_ref().clone(), edits); + changes.insert(Arc::clone(&document.uri), edits); } } - Some(WorkspaceEdit::new(changes)) -} - -#[cfg(test)] -mod tests { - use crate::{features::testing::FeatureTester, RangeExt}; - - use super::*; - - #[test] - fn test_command() { - let tester = FeatureTester::builder() - .files(vec![ - ("foo.tex", r#"\baz\include{bar.tex}"#), - ("bar.tex", r#"\baz"#), - ]) - .main("foo.tex") - .line(0) - .character(2) - .new_name("qux") - .build(); - - let uri1 = tester.uri("foo.tex"); - let uri2 = tester.uri("bar.tex"); - let req = tester.rename(); - - let context = CursorContext::new(req); - let actual_edit = rename_command(&context).unwrap(); - - let mut expected_changes = HashMap::new(); - expected_changes.insert( - uri1.as_ref().clone(), - vec![TextEdit::new(Range::new_simple(0, 1, 0, 4), "qux".into())], - ); - expected_changes.insert( - uri2.as_ref().clone(), - vec![TextEdit::new(Range::new_simple(0, 1, 0, 4), "qux".into())], - ); - let expected_edit = WorkspaceEdit::new(expected_changes); - - assert_eq!(actual_edit, expected_edit); - } + Some(RenameResult { changes }) } diff --git a/support/texlab/src/features/rename/entry.rs b/support/texlab/src/features/rename/entry.rs index 4861b63570..6eac79030b 100644 --- a/support/texlab/src/features/rename/entry.rs +++ b/support/texlab/src/features/rename/entry.rs @@ -1,7 +1,8 @@ -use std::collections::HashMap; +use std::sync::Arc; -use lsp_types::{Range, RenameParams, TextEdit, WorkspaceEdit}; -use rowan::ast::AstNode; +use lsp_types::RenameParams; +use rowan::{ast::AstNode, TextRange}; +use rustc_hash::FxHashMap; use crate::{ features::cursor::{CursorContext, HasPosition}, @@ -9,139 +10,63 @@ use crate::{ bibtex::{self, HasName}, latex, }, - DocumentData, LineIndexExt, + DocumentData, }; -pub fn prepare_entry_rename<P: HasPosition>(context: &CursorContext<P>) -> Option<Range> { +use super::{Indel, RenameResult}; + +pub(super) fn prepare_entry_rename<P: HasPosition>( + context: &CursorContext<P>, +) -> Option<TextRange> { let (_, range) = context .find_citation_key_word() .or_else(|| context.find_entry_key())?; - Some( - context - .request - .main_document() - .line_index - .line_col_lsp_range(range), - ) + Some(range) } -pub fn rename_entry(context: &CursorContext<RenameParams>) -> Option<WorkspaceEdit> { +pub(super) fn rename_entry(context: &CursorContext<RenameParams>) -> Option<RenameResult> { prepare_entry_rename(context)?; let (key_text, _) = context .find_citation_key_word() .or_else(|| context.find_entry_key())?; - let mut changes = HashMap::new(); + let mut changes = FxHashMap::default(); for document in context.request.workspace.documents_by_uri.values() { + let uri = Arc::clone(&document.uri); match &document.data { DocumentData::Latex(data) => { - let edits: Vec<_> = latex::SyntaxNode::new_root(data.green.clone()) + let root = latex::SyntaxNode::new_root(data.green.clone()); + let edits: Vec<_> = root .descendants() .filter_map(latex::Citation::cast) .filter_map(|citation| citation.key_list()) .flat_map(|keys| keys.keys()) .filter(|key| key.to_string() == key_text) - .map(|key| { - document - .line_index - .line_col_lsp_range(latex::small_range(&key)) + .map(|key| Indel { + delete: latex::small_range(&key), + insert: context.request.params.new_name.clone(), }) - .map(|range| TextEdit::new(range, context.request.params.new_name.clone())) .collect(); - changes.insert(document.uri.as_ref().clone(), edits); + changes.insert(uri, edits); } DocumentData::Bibtex(data) => { - let edits: Vec<_> = bibtex::SyntaxNode::new_root(data.green.clone()) + let root = bibtex::SyntaxNode::new_root(data.green.clone()); + let edits: Vec<_> = root .descendants() .filter_map(bibtex::Entry::cast) .filter_map(|entry| entry.name_token()) .filter(|key| key.text() == key_text) - .map(|key| document.line_index.line_col_lsp_range(key.text_range())) - .map(|range| TextEdit::new(range, context.request.params.new_name.clone())) + .map(|key| Indel { + delete: key.text_range(), + insert: context.request.params.new_name.clone(), + }) .collect(); - changes.insert(document.uri.as_ref().clone(), edits); + changes.insert(uri, edits); } DocumentData::BuildLog(_) => {} } } - Some(WorkspaceEdit::new(changes)) -} - -#[cfg(test)] -mod tests { - use lsp_types::TextEdit; - - use crate::{features::testing::FeatureTester, RangeExt}; - - use super::*; - - #[test] - fn test_entry() { - let tester = FeatureTester::builder() - .files(vec![ - ("main.bib", r#"@article{foo, bar = baz}"#), - ("main.tex", "\\addbibresource{main.bib}\n\\cite{foo}"), - ]) - .main("main.bib") - .line(0) - .character(9) - .new_name("qux") - .build(); - - let uri1 = tester.uri("main.bib"); - let uri2 = tester.uri("main.tex"); - let request = tester.rename(); - - let context = CursorContext::new(request); - let actual_edit = rename_entry(&context).unwrap(); - - let mut expected_changes = HashMap::new(); - expected_changes.insert( - uri1.as_ref().clone(), - vec![TextEdit::new(Range::new_simple(0, 9, 0, 12), "qux".into())], - ); - expected_changes.insert( - uri2.as_ref().clone(), - vec![TextEdit::new(Range::new_simple(1, 6, 1, 9), "qux".into())], - ); - let expected_edit = WorkspaceEdit::new(expected_changes); - - assert_eq!(actual_edit, expected_edit); - } - - #[test] - fn test_citation() { - let tester = FeatureTester::builder() - .files(vec![ - ("main.bib", r#"@article{foo, bar = baz}"#), - ("main.tex", "\\addbibresource{main.bib}\n\\cite{foo}"), - ]) - .main("main.tex") - .line(1) - .character(6) - .new_name("qux") - .build(); - - let uri1 = tester.uri("main.bib"); - let uri2 = tester.uri("main.tex"); - let request = tester.rename(); - - let context = CursorContext::new(request); - let actual_edit = rename_entry(&context).unwrap(); - - let mut expected_changes = HashMap::new(); - expected_changes.insert( - uri1.as_ref().clone(), - vec![TextEdit::new(Range::new_simple(0, 9, 0, 12), "qux".into())], - ); - expected_changes.insert( - uri2.as_ref().clone(), - vec![TextEdit::new(Range::new_simple(1, 6, 1, 9), "qux".into())], - ); - let expected_edit = WorkspaceEdit::new(expected_changes); - - assert_eq!(actual_edit, expected_edit); - } + Some(RenameResult { changes }) } diff --git a/support/texlab/src/features/rename/label.rs b/support/texlab/src/features/rename/label.rs index f24b86810b..30a99c3b36 100644 --- a/support/texlab/src/features/rename/label.rs +++ b/support/texlab/src/features/rename/label.rs @@ -1,31 +1,28 @@ -use std::collections::HashMap; +use std::sync::Arc; -use lsp_types::{Range, RenameParams, TextEdit, WorkspaceEdit}; -use rowan::ast::AstNode; +use lsp_types::RenameParams; +use rowan::{ast::AstNode, TextRange}; +use rustc_hash::FxHashMap; use crate::{ features::cursor::{CursorContext, HasPosition}, syntax::latex, - LineIndexExt, }; -pub fn prepare_label_rename<P: HasPosition>(context: &CursorContext<P>) -> Option<Range> { - let (_, range) = context.find_label_name_key()?; +use super::{Indel, RenameResult}; - Some( - context - .request - .main_document() - .line_index - .line_col_lsp_range(range), - ) +pub(super) fn prepare_label_rename<P: HasPosition>( + context: &CursorContext<P>, +) -> Option<TextRange> { + let (_, range) = context.find_label_name_key()?; + Some(range) } -pub fn rename_label(context: &CursorContext<RenameParams>) -> Option<WorkspaceEdit> { +pub(super) fn rename_label(context: &CursorContext<RenameParams>) -> Option<RenameResult> { prepare_label_rename(context)?; let (name_text, _) = context.find_label_name_key()?; - let mut changes = HashMap::new(); + let mut changes = FxHashMap::default(); for document in context.request.workspace.documents_by_uri.values() { if let Some(data) = document.data.as_latex() { let mut edits = Vec::new(); @@ -34,16 +31,12 @@ pub fn rename_label(context: &CursorContext<RenameParams>) -> Option<WorkspaceEd .and_then(|label| label.name()) .and_then(|name| name.key()) .filter(|name| name.to_string() == name_text) - .map(|name| { - document - .line_index - .line_col_lsp_range(latex::small_range(&name)) - }) + .map(|name| latex::small_range(&name)) { - edits.push(TextEdit::new( - range, - context.request.params.new_name.clone(), - )); + edits.push(Indel { + delete: range, + insert: context.request.params.new_name.clone(), + }); } latex::LabelReference::cast(node.clone()) @@ -51,16 +44,11 @@ pub fn rename_label(context: &CursorContext<RenameParams>) -> Option<WorkspaceEd .into_iter() .flat_map(|label| label.keys()) .filter(|name| name.to_string() == name_text) - .map(|name| { - document - .line_index - .line_col_lsp_range(latex::small_range(&name)) - }) - .for_each(|range| { - edits.push(TextEdit::new( - range, - context.request.params.new_name.clone(), - )); + .for_each(|name| { + edits.push(Indel { + delete: latex::small_range(&name), + insert: context.request.params.new_name.clone(), + }); }); if let Some(label) = latex::LabelReferenceRange::cast(node.clone()) { @@ -69,12 +57,10 @@ pub fn rename_label(context: &CursorContext<RenameParams>) -> Option<WorkspaceEd .and_then(|name| name.key()) .filter(|name| name.to_string() == name_text) { - edits.push(TextEdit::new( - document - .line_index - .line_col_lsp_range(latex::small_range(&name1)), - context.request.params.new_name.clone(), - )); + edits.push(Indel { + delete: latex::small_range(&name1), + insert: context.request.params.new_name.clone(), + }); } if let Some(name2) = label @@ -82,61 +68,17 @@ pub fn rename_label(context: &CursorContext<RenameParams>) -> Option<WorkspaceEd .and_then(|name| name.key()) .filter(|name| name.to_string() == name_text) { - edits.push(TextEdit::new( - document - .line_index - .line_col_lsp_range(latex::small_range(&name2)), - context.request.params.new_name.clone(), - )); + edits.push(Indel { + delete: latex::small_range(&name2), + insert: context.request.params.new_name.clone(), + }); } } } - changes.insert(document.uri.as_ref().clone(), edits); + changes.insert(Arc::clone(&document.uri), edits); } } - Some(WorkspaceEdit::new(changes)) -} - -#[cfg(test)] -mod tests { - use crate::{features::testing::FeatureTester, RangeExt}; - - use super::*; - - #[test] - fn test_label() { - let tester = FeatureTester::builder() - .files(vec![ - ("foo.tex", r#"\label{foo}\include{bar}"#), - ("bar.tex", r#"\ref{foo}"#), - ("baz.tex", r#"\ref{foo}"#), - ]) - .main("foo.tex") - .line(0) - .character(7) - .new_name("bar") - .build(); - - let uri1 = tester.uri("foo.tex"); - let uri2 = tester.uri("bar.tex"); - let request = tester.rename(); - - let context = CursorContext::new(request); - let actual_edit = rename_label(&context).unwrap(); - - let mut expected_changes = HashMap::new(); - expected_changes.insert( - uri1.as_ref().clone(), - vec![TextEdit::new(Range::new_simple(0, 7, 0, 10), "bar".into())], - ); - expected_changes.insert( - uri2.as_ref().clone(), - vec![TextEdit::new(Range::new_simple(0, 5, 0, 8), "bar".into())], - ); - let expected_edit = WorkspaceEdit::new(expected_changes); - - assert_eq!(actual_edit, expected_edit); - } + Some(RenameResult { changes }) } diff --git a/support/texlab/src/features/symbol.rs b/support/texlab/src/features/symbol.rs index 42b9d03d3f..24d599c3e1 100644 --- a/support/texlab/src/features/symbol.rs +++ b/support/texlab/src/features/symbol.rs @@ -14,10 +14,12 @@ use crate::{ClientCapabilitiesExt, Workspace}; use self::{ bibtex::find_bibtex_symbols, latex::find_latex_symbols, project_order::ProjectOrdering, + types::InternalSymbol, }; use super::FeatureRequest; +#[must_use] pub fn find_document_symbols(req: FeatureRequest<DocumentSymbolParams>) -> DocumentSymbolResponse { let mut buf = Vec::new(); find_latex_symbols(&req, &mut buf); @@ -30,7 +32,7 @@ pub fn find_document_symbols(req: FeatureRequest<DocumentSymbolParams>) -> Docum { DocumentSymbolResponse::Nested( buf.into_iter() - .map(|symbol| symbol.into_document_symbol()) + .map(InternalSymbol::into_document_symbol) .collect(), ) } else { @@ -53,6 +55,7 @@ struct WorkspaceSymbol { search_text: String, } +#[must_use] pub fn find_workspace_symbols( workspace: &Workspace, params: &WorkspaceSymbolParams, |