summaryrefslogtreecommitdiff
path: root/support/texlab/src/rename/bibtex_entry.rs
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/src/rename/bibtex_entry.rs')
-rw-r--r--support/texlab/src/rename/bibtex_entry.rs272
1 files changed, 146 insertions, 126 deletions
diff --git a/support/texlab/src/rename/bibtex_entry.rs b/support/texlab/src/rename/bibtex_entry.rs
index 46484c7f23..97eaa8fd9d 100644
--- a/support/texlab/src/rename/bibtex_entry.rs
+++ b/support/texlab/src/rename/bibtex_entry.rs
@@ -1,172 +1,192 @@
-use crate::range::RangeExt;
-use crate::syntax::*;
-use crate::workspace::*;
-use futures_boxed::boxed;
-use lsp_types::*;
+use crate::{
+ feature::{FeatureProvider, FeatureRequest},
+ protocol::{
+ Position, Range, RangeExt, RenameParams, TextDocumentPositionParams, TextEdit,
+ WorkspaceEdit,
+ },
+ syntax::{Span, SyntaxNode},
+ workspace::DocumentContent,
+};
+use async_trait::async_trait;
use std::collections::HashMap;
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
pub struct BibtexEntryPrepareRenameProvider;
+#[async_trait]
impl FeatureProvider for BibtexEntryPrepareRenameProvider {
type Params = TextDocumentPositionParams;
type Output = Option<Range>;
- #[boxed]
- async fn execute<'a>(
- &'a self,
- request: &'a FeatureRequest<TextDocumentPositionParams>,
- ) -> Option<Range> {
- find_key(&request.document().tree, request.params.position).map(Span::range)
+ async fn execute<'a>(&'a self, req: &'a FeatureRequest<Self::Params>) -> Self::Output {
+ find_key(&req.current().content, req.params.position).map(Span::range)
}
}
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
pub struct BibtexEntryRenameProvider;
+#[async_trait]
impl FeatureProvider for BibtexEntryRenameProvider {
type Params = RenameParams;
type Output = Option<WorkspaceEdit>;
- #[boxed]
- async fn execute<'a>(
- &'a self,
- request: &'a FeatureRequest<RenameParams>,
- ) -> Option<WorkspaceEdit> {
+ async fn execute<'a>(&'a self, req: &'a FeatureRequest<Self::Params>) -> Self::Output {
let key_name = find_key(
- &request.document().tree,
- request.params.text_document_position.position,
+ &req.current().content,
+ req.params.text_document_position.position,
)?;
let mut changes = HashMap::new();
- for document in request.related_documents() {
- let mut edits = Vec::new();
- match &document.tree {
- SyntaxTree::Latex(tree) => {
- tree.citations
- .iter()
- .flat_map(LatexCitation::keys)
- .filter(|citation| citation.text() == key_name.text)
- .map(|citation| {
- TextEdit::new(citation.range(), request.params.new_name.clone())
- })
- .for_each(|edit| edits.push(edit));
- }
- SyntaxTree::Bibtex(tree) => {
- for entry in tree.entries() {
- if let Some(key) = &entry.key {
- if key.text() == key_name.text {
- edits.push(TextEdit::new(
- key.range(),
- request.params.new_name.clone(),
- ));
- }
- }
- }
- }
+ for doc in req.related() {
+ let edits = match &doc.content {
+ DocumentContent::Latex(table) => table
+ .citations
+ .iter()
+ .flat_map(|citation| citation.keys(&table))
+ .filter(|citation| citation.text() == key_name.text)
+ .map(|citation| TextEdit::new(citation.range(), req.params.new_name.clone()))
+ .collect(),
+ DocumentContent::Bibtex(tree) => tree
+ .children(tree.root)
+ .filter_map(|node| tree.as_entry(node))
+ .filter_map(|entry| entry.key.as_ref())
+ .filter(|entry_key| entry_key.text() == key_name.text)
+ .map(|entry_key| TextEdit::new(entry_key.range(), req.params.new_name.clone()))
+ .collect(),
};
- changes.insert(document.uri.clone().into(), edits);
+ changes.insert(doc.uri.clone().into(), edits);
}
Some(WorkspaceEdit::new(changes))
}
}
-fn find_key(tree: &SyntaxTree, position: Position) -> Option<&Span> {
- match tree {
- SyntaxTree::Latex(tree) => {
- for citation in &tree.citations {
- let keys = citation.keys();
- for key in keys {
- if key.range().contains(position) {
- return Some(&key.span);
- }
- }
- }
- None
- }
- SyntaxTree::Bibtex(tree) => {
- for entry in tree.entries() {
- if let Some(key) = &entry.key {
- if key.range().contains(position) {
- return Some(&key.span);
- }
- }
- }
- None
- }
+fn find_key(content: &DocumentContent, pos: Position) -> Option<&Span> {
+ match content {
+ DocumentContent::Latex(table) => table
+ .citations
+ .iter()
+ .flat_map(|citation| citation.keys(&table))
+ .find(|key| key.range().contains(pos))
+ .map(|key| &key.span),
+ DocumentContent::Bibtex(tree) => tree
+ .children(tree.root)
+ .filter_map(|node| tree.as_entry(node))
+ .filter_map(|entry| entry.key.as_ref())
+ .find(|key| key.range().contains(pos))
+ .map(|key| &key.span),
}
}
#[cfg(test)]
mod tests {
use super::*;
- use lsp_types::Position;
-
- #[test]
- fn test_entry() {
- let edit = test_feature(
- BibtexEntryRenameProvider,
- FeatureSpec {
- files: vec![
- FeatureSpec::file("foo.bib", "@article{foo, bar = baz}"),
- FeatureSpec::file("bar.tex", "\\addbibresource{foo.bib}\n\\cite{foo}"),
- ],
- main_file: "foo.bib",
- position: Position::new(0, 9),
- new_name: "qux",
- ..FeatureSpec::default()
- },
- );
- let mut changes = HashMap::new();
- changes.insert(
- FeatureSpec::uri("foo.bib"),
+ use crate::{feature::FeatureTester, protocol::RangeExt};
+ use indoc::indoc;
+
+ #[tokio::test]
+ async fn entry() {
+ let actual_edit = FeatureTester::new()
+ .file("main.bib", r#"@article{foo, bar = baz}"#)
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \addbibresource{main.bib}
+ \cite{foo}
+ "#
+ ),
+ )
+ .main("main.bib")
+ .position(0, 9)
+ .new_name("qux")
+ .test_rename(BibtexEntryRenameProvider)
+ .await
+ .unwrap();
+
+ let mut expected_changes = HashMap::new();
+ expected_changes.insert(
+ FeatureTester::uri("main.bib").into(),
vec![TextEdit::new(Range::new_simple(0, 9, 0, 12), "qux".into())],
);
- changes.insert(
- FeatureSpec::uri("bar.tex"),
+ expected_changes.insert(
+ FeatureTester::uri("main.tex").into(),
vec![TextEdit::new(Range::new_simple(1, 6, 1, 9), "qux".into())],
);
- assert_eq!(edit, Some(WorkspaceEdit::new(changes)));
+ let expected_edit = WorkspaceEdit::new(expected_changes);
+
+ assert_eq!(actual_edit, expected_edit);
}
- #[test]
- fn test_citation() {
- let edit = test_feature(
- BibtexEntryRenameProvider,
- FeatureSpec {
- files: vec![
- FeatureSpec::file("foo.bib", "@article{foo, bar = baz}"),
- FeatureSpec::file("bar.tex", "\\addbibresource{foo.bib}\n\\cite{foo}"),
- ],
- main_file: "bar.tex",
- position: Position::new(1, 6),
- new_name: "qux",
- ..FeatureSpec::default()
- },
- );
- let mut changes = HashMap::new();
- changes.insert(
- FeatureSpec::uri("foo.bib"),
+ #[tokio::test]
+ async fn citation() {
+ let actual_edit = FeatureTester::new()
+ .file("main.bib", r#"@article{foo, bar = baz}"#)
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \addbibresource{main.bib}
+ \cite{foo}
+ "#
+ ),
+ )
+ .main("main.tex")
+ .position(1, 6)
+ .new_name("qux")
+ .test_rename(BibtexEntryRenameProvider)
+ .await
+ .unwrap();
+
+ let mut expected_changes = HashMap::new();
+ expected_changes.insert(
+ FeatureTester::uri("main.bib").into(),
vec![TextEdit::new(Range::new_simple(0, 9, 0, 12), "qux".into())],
);
- changes.insert(
- FeatureSpec::uri("bar.tex"),
+ expected_changes.insert(
+ FeatureTester::uri("main.tex").into(),
vec![TextEdit::new(Range::new_simple(1, 6, 1, 9), "qux".into())],
);
- assert_eq!(edit, Some(WorkspaceEdit::new(changes)));
+ let expected_edit = WorkspaceEdit::new(expected_changes);
+
+ assert_eq!(actual_edit, expected_edit);
}
- #[test]
- fn test_field_name() {
- let edit = test_feature(
- BibtexEntryRenameProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file("foo.bib", "@article{foo, bar = baz}")],
- main_file: "foo.bib",
- position: Position::new(0, 14),
- new_name: "qux",
- ..FeatureSpec::default()
- },
- );
- assert_eq!(edit, None);
+ #[tokio::test]
+ async fn field_name() {
+ let actual_edit = FeatureTester::new()
+ .file("main.bib", r#"@article{foo, bar = baz}"#)
+ .main("main.bib")
+ .position(0, 14)
+ .new_name("qux")
+ .test_rename(BibtexEntryRenameProvider)
+ .await;
+
+ assert_eq!(actual_edit, None);
+ }
+
+ #[tokio::test]
+ async fn empty_latex_document() {
+ let actual_edit = FeatureTester::new()
+ .file("main.tex", "")
+ .main("main.tex")
+ .position(0, 0)
+ .new_name("")
+ .test_rename(BibtexEntryRenameProvider)
+ .await;
+
+ assert_eq!(actual_edit, None);
+ }
+
+ #[tokio::test]
+ async fn empty_bibtex_document() {
+ let actual_edit = FeatureTester::new()
+ .file("main.bib", "")
+ .main("main.bib")
+ .position(0, 0)
+ .new_name("")
+ .test_rename(BibtexEntryRenameProvider)
+ .await;
+
+ assert_eq!(actual_edit, None);
}
}