]> git.lizzy.rs Git - rust.git/commitdiff
add apply ssr assist
authorJosh Mcguigan <joshmcg88@gmail.com>
Tue, 9 Mar 2021 05:11:28 +0000 (21:11 -0800)
committerJosh Mcguigan <joshmcg88@gmail.com>
Wed, 10 Mar 2021 14:02:15 +0000 (06:02 -0800)
crates/ide/src/lib.rs
crates/ide/src/ssr.rs [new file with mode: 0644]
crates/ide_ssr/src/from_comment.rs [new file with mode: 0644]
crates/ide_ssr/src/lib.rs

index f83ed65d5a6b58b9b593c6e8a18a3a68b589d152..d1a250d4874a81a173e520b0937f160442590527 100644 (file)
@@ -41,6 +41,7 @@ macro_rules! eprintln {
 mod references;
 mod fn_references;
 mod runnables;
+mod ssr;
 mod status;
 mod syntax_highlighting;
 mod syntax_tree;
@@ -51,6 +52,7 @@ macro_rules! eprintln {
 use std::sync::Arc;
 
 use cfg::CfgOptions;
+
 use ide_db::base_db::{
     salsa::{self, ParallelDatabase},
     CheckCanceled, Env, FileLoader, FileSet, SourceDatabase, VfsPath,
@@ -502,7 +504,11 @@ pub fn assists(
         resolve: bool,
         frange: FileRange,
     ) -> Cancelable<Vec<Assist>> {
-        self.with_db(|db| Assist::get(db, config, resolve, frange))
+        self.with_db(|db| {
+            let mut acc = Assist::get(db, config, resolve, frange);
+            ssr::add_ssr_assist(db, &mut acc, resolve, frange);
+            acc
+        })
     }
 
     /// Computes the set of diagnostics for the given file.
diff --git a/crates/ide/src/ssr.rs b/crates/ide/src/ssr.rs
new file mode 100644 (file)
index 0000000..f3638d9
--- /dev/null
@@ -0,0 +1,259 @@
+//! This module provides an SSR assist. It is not desirable to include this
+//! assist in ide_assists because that would require the ide_assists crate
+//! depend on the ide_ssr crate.
+
+use ide_assists::{Assist, AssistId, AssistKind, GroupLabel};
+use ide_db::{base_db::FileRange, label::Label, source_change::SourceChange, RootDatabase};
+
+pub(crate) fn add_ssr_assist(
+    db: &RootDatabase,
+    base: &mut Vec<Assist>,
+    resolve: bool,
+    frange: FileRange,
+) -> Option<()> {
+    let (match_finder, comment_range) = ide_ssr::ssr_from_comment(db, frange)?;
+
+    let (source_change_for_file, source_change_for_workspace) = if resolve {
+        let edits = match_finder.edits();
+
+        let source_change_for_file = {
+            let text_edit_for_file = edits.get(&frange.file_id).cloned().unwrap_or_default();
+            SourceChange::from_text_edit(frange.file_id, text_edit_for_file)
+        };
+
+        let source_change_for_workspace = SourceChange::from(match_finder.edits());
+
+        (Some(source_change_for_file), Some(source_change_for_workspace))
+    } else {
+        (None, None)
+    };
+
+    let assists = vec![
+        ("Apply SSR in file", source_change_for_file),
+        ("Apply SSR in workspace", source_change_for_workspace),
+    ];
+
+    for (label, source_change) in assists.into_iter() {
+        let assist = Assist {
+            id: AssistId("ssr", AssistKind::RefactorRewrite),
+            label: Label::new(label),
+            group: Some(GroupLabel("Apply SSR".into())),
+            target: comment_range,
+            source_change,
+        };
+
+        base.push(assist);
+    }
+    Some(())
+}
+
+#[cfg(test)]
+mod tests {
+    use std::sync::Arc;
+
+    use expect_test::expect;
+    use ide_assists::Assist;
+    use ide_db::{
+        base_db::{fixture::WithFixture, salsa::Durability, FileRange},
+        symbol_index::SymbolsDatabase,
+        RootDatabase,
+    };
+    use rustc_hash::FxHashSet;
+
+    use super::add_ssr_assist;
+
+    fn get_assists(ra_fixture: &str, resolve: bool) -> Vec<Assist> {
+        let (mut db, file_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture);
+        let mut local_roots = FxHashSet::default();
+        local_roots.insert(ide_db::base_db::fixture::WORKSPACE);
+        db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH);
+
+        let mut assists = vec![];
+
+        add_ssr_assist(
+            &db,
+            &mut assists,
+            resolve,
+            FileRange { file_id, range: range_or_offset.into() },
+        );
+
+        assists
+    }
+
+    #[test]
+    fn not_applicable_comment_not_ssr() {
+        let ra_fixture = r#"
+            //- /lib.rs
+
+            // This is foo $0
+            fn foo() {}
+            "#;
+        let resolve = true;
+
+        let assists = get_assists(ra_fixture, resolve);
+
+        assert_eq!(0, assists.len());
+    }
+
+    #[test]
+    fn resolve_edits_true() {
+        let resolve = true;
+        let assists = get_assists(
+            r#"
+            //- /lib.rs
+            mod bar;
+
+            // 2 ==>> 3$0
+            fn foo() { 2 }
+
+            //- /bar.rs
+            fn bar() { 2 }
+            "#,
+            resolve,
+        );
+
+        assert_eq!(2, assists.len());
+        let mut assists = assists.into_iter();
+
+        let apply_in_file_assist = assists.next().unwrap();
+        expect![[r#"
+            Assist {
+                id: AssistId(
+                    "ssr",
+                    RefactorRewrite,
+                ),
+                label: "Apply SSR in file",
+                group: Some(
+                    GroupLabel(
+                        "Apply SSR",
+                    ),
+                ),
+                target: 10..21,
+                source_change: Some(
+                    SourceChange {
+                        source_file_edits: {
+                            FileId(
+                                0,
+                            ): TextEdit {
+                                indels: [
+                                    Indel {
+                                        insert: "3",
+                                        delete: 33..34,
+                                    },
+                                ],
+                            },
+                        },
+                        file_system_edits: [],
+                        is_snippet: false,
+                    },
+                ),
+            }
+        "#]]
+        .assert_debug_eq(&apply_in_file_assist);
+
+        let apply_in_workspace_assist = assists.next().unwrap();
+        expect![[r#"
+            Assist {
+                id: AssistId(
+                    "ssr",
+                    RefactorRewrite,
+                ),
+                label: "Apply SSR in workspace",
+                group: Some(
+                    GroupLabel(
+                        "Apply SSR",
+                    ),
+                ),
+                target: 10..21,
+                source_change: Some(
+                    SourceChange {
+                        source_file_edits: {
+                            FileId(
+                                0,
+                            ): TextEdit {
+                                indels: [
+                                    Indel {
+                                        insert: "3",
+                                        delete: 33..34,
+                                    },
+                                ],
+                            },
+                            FileId(
+                                1,
+                            ): TextEdit {
+                                indels: [
+                                    Indel {
+                                        insert: "3",
+                                        delete: 11..12,
+                                    },
+                                ],
+                            },
+                        },
+                        file_system_edits: [],
+                        is_snippet: false,
+                    },
+                ),
+            }
+        "#]]
+        .assert_debug_eq(&apply_in_workspace_assist);
+    }
+
+    #[test]
+    fn resolve_edits_false() {
+        let resolve = false;
+        let assists = get_assists(
+            r#"
+            //- /lib.rs
+            mod bar;
+
+            // 2 ==>> 3$0
+            fn foo() { 2 }
+
+            //- /bar.rs
+            fn bar() { 2 }
+            "#,
+            resolve,
+        );
+
+        assert_eq!(2, assists.len());
+        let mut assists = assists.into_iter();
+
+        let apply_in_file_assist = assists.next().unwrap();
+        expect![[r#"
+            Assist {
+                id: AssistId(
+                    "ssr",
+                    RefactorRewrite,
+                ),
+                label: "Apply SSR in file",
+                group: Some(
+                    GroupLabel(
+                        "Apply SSR",
+                    ),
+                ),
+                target: 10..21,
+                source_change: None,
+            }
+        "#]]
+        .assert_debug_eq(&apply_in_file_assist);
+
+        let apply_in_workspace_assist = assists.next().unwrap();
+        expect![[r#"
+            Assist {
+                id: AssistId(
+                    "ssr",
+                    RefactorRewrite,
+                ),
+                label: "Apply SSR in workspace",
+                group: Some(
+                    GroupLabel(
+                        "Apply SSR",
+                    ),
+                ),
+                target: 10..21,
+                source_change: None,
+            }
+        "#]]
+        .assert_debug_eq(&apply_in_workspace_assist);
+    }
+}
diff --git a/crates/ide_ssr/src/from_comment.rs b/crates/ide_ssr/src/from_comment.rs
new file mode 100644 (file)
index 0000000..f1b3122
--- /dev/null
@@ -0,0 +1,32 @@
+//! This module allows building an SSR MatchFinder by parsing the SSR rule
+//! from a comment.
+
+use ide_db::{
+    base_db::{FilePosition, FileRange, SourceDatabase},
+    RootDatabase,
+};
+use syntax::{
+    ast::{self, AstNode, AstToken},
+    TextRange,
+};
+
+use crate::MatchFinder;
+
+/// Attempts to build an SSR MatchFinder from a comment at the given file
+/// range. If successful, returns the MatchFinder and a TextRange covering
+/// comment.
+pub fn ssr_from_comment(db: &RootDatabase, frange: FileRange) -> Option<(MatchFinder, TextRange)> {
+    let comment = {
+        let file = db.parse(frange.file_id);
+        file.tree().syntax().token_at_offset(frange.range.start()).find_map(ast::Comment::cast)
+    }?;
+    let comment_text_without_prefix = comment.text().strip_prefix(comment.prefix()).unwrap();
+    let ssr_rule = comment_text_without_prefix.parse().ok()?;
+
+    let lookup_context = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
+
+    let mut match_finder = MatchFinder::in_context(db, lookup_context, vec![]);
+    match_finder.add_rule(ssr_rule).ok()?;
+
+    Some((match_finder, comment.syntax().text_range()))
+}
index a97fc8bcacab263454fc23bfa74ce5bbc6c88c09..e72c611a3e20c7a819c6e8a6be3eae3bf2d05e7c 100644 (file)
@@ -58,6 +58,7 @@
 // | VS Code | **Rust Analyzer: Structural Search Replace**
 // |===
 
+mod from_comment;
 mod matching;
 mod nester;
 mod parsing;
@@ -71,6 +72,7 @@
 
 use crate::errors::bail;
 pub use crate::errors::SsrError;
+pub use crate::from_comment::ssr_from_comment;
 pub use crate::matching::Match;
 use crate::matching::MatchFailureReason;
 use hir::Semantics;