]> git.lizzy.rs Git - rust.git/commitdiff
Create modules in correct directory for nested modules in move_module assist
authorLukas Wirth <lukastw97@gmail.com>
Thu, 17 Jun 2021 10:09:28 +0000 (12:09 +0200)
committerLukas Wirth <lukastw97@gmail.com>
Thu, 17 Jun 2021 10:09:28 +0000 (12:09 +0200)
crates/ide_assists/src/handlers/move_module_to_file.rs
crates/syntax/src/ast/node_ext.rs

index 93f702c556881af6d55197a2c519072194298765..cfc54be8d186a8d124d431299e63c49d836277cf 100644 (file)
@@ -1,5 +1,8 @@
+use std::iter;
+
 use ast::edit::IndentLevel;
 use ide_db::base_db::AnchoredPathBuf;
+use itertools::Itertools;
 use stdx::format_to;
 use syntax::{
     ast::{self, edit::AstNodeEdit, NameOwner},
@@ -34,7 +37,10 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext) -> Opt
 
     let module_name = module_ast.name()?;
 
-    let module_def = ctx.sema.to_def(&module_ast)?;
+    // get to the outermost module syntax so we can grab the module of file we are in
+    let outermost_mod_decl =
+        iter::successors(Some(module_ast.clone()), |module| module.parent()).last()?;
+    let module_def = ctx.sema.to_def(&outermost_mod_decl)?;
     let parent_module = module_def.parent(ctx.db())?;
 
     acc.add(
@@ -43,11 +49,19 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext) -> Opt
         target,
         |builder| {
             let path = {
-                let dir = match parent_module.name(ctx.db()) {
-                    Some(name) if !parent_module.is_mod_rs(ctx.db()) => format!("{}/", name),
-                    _ => String::new(),
-                };
-                format!("./{}{}.rs", dir, module_name)
+                let mut buf = String::from("./");
+                match parent_module.name(ctx.db()) {
+                    Some(name) if !parent_module.is_mod_rs(ctx.db()) => {
+                        format_to!(buf, "{}/", name)
+                    }
+                    _ => (),
+                }
+                let segments = iter::successors(Some(module_ast.clone()), |module| module.parent())
+                    .filter_map(|it| it.name())
+                    .collect::<Vec<_>>();
+                format_to!(buf, "{}", segments.into_iter().rev().format("/"));
+                format_to!(buf, ".rs");
+                buf
             };
             let contents = {
                 let items = module_items.dedent(IndentLevel(1)).to_string();
@@ -59,14 +73,13 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext) -> Opt
                 items
             };
 
-            let mut buf = String::new();
-            format_to!(buf, "mod {};", module_name);
+            let buf = format!("mod {};", module_name);
 
-            let replacement_start = if let Some(mod_token) = module_ast.mod_token() {
-                mod_token.text_range().start()
-            } else {
-                module_ast.syntax().text_range().start()
-            };
+            let replacement_start = match module_ast.mod_token() {
+                Some(mod_token) => mod_token.text_range(),
+                None => module_ast.syntax().text_range(),
+            }
+            .start();
 
             builder.replace(
                 TextRange::new(replacement_start, module_ast.syntax().text_range().end()),
@@ -209,6 +222,32 @@ mod $0tests {
 mod tests;
 //- /tests.rs
 #[test] fn t() {}
+"#,
+        );
+    }
+
+    #[test]
+    fn extract_nested() {
+        check_assist(
+            move_module_to_file,
+            r#"
+//- /lib.rs
+mod foo;
+//- /foo.rs
+mod bar {
+    mod baz {
+        mod qux$0 {}
+    }
+}
+"#,
+            r#"
+//- /foo.rs
+mod bar {
+    mod baz {
+        mod qux;
+    }
+}
+//- /foo/bar/baz/qux.rs
 "#,
         );
     }
index 2bd9ad867e1a0767bac5a77a1f8f5b2b08cc0dd7..b057e662407ace4158c897a2bc10285e51049d1e 100644 (file)
@@ -675,6 +675,14 @@ pub fn lifetime_bounds(&self) -> impl Iterator<Item = SyntaxToken> {
     }
 }
 
+impl ast::Module {
+    /// Returns the parent ast::Module, this is different than the semantic parent in that this only
+    /// considers parent declarations in the AST
+    pub fn parent(&self) -> Option<ast::Module> {
+        self.syntax().ancestors().nth(2).and_then(ast::Module::cast)
+    }
+}
+
 impl ast::RangePat {
     pub fn start(&self) -> Option<ast::Pat> {
         self.syntax()