aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/diagnostics/unlinked_file.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src/diagnostics/unlinked_file.rs')
-rw-r--r--crates/ide/src/diagnostics/unlinked_file.rs54
1 files changed, 35 insertions, 19 deletions
diff --git a/crates/ide/src/diagnostics/unlinked_file.rs b/crates/ide/src/diagnostics/unlinked_file.rs
index 93fd25dea..51fe0f360 100644
--- a/crates/ide/src/diagnostics/unlinked_file.rs
+++ b/crates/ide/src/diagnostics/unlinked_file.rs
@@ -18,7 +18,7 @@ use syntax::{
18use text_edit::TextEdit; 18use text_edit::TextEdit;
19 19
20use crate::{ 20use crate::{
21 diagnostics::{fix, fixes::DiagnosticWithFix}, 21 diagnostics::{fix, fixes::DiagnosticWithFixes},
22 Assist, 22 Assist,
23}; 23};
24 24
@@ -50,13 +50,13 @@ impl Diagnostic for UnlinkedFile {
50 } 50 }
51} 51}
52 52
53impl DiagnosticWithFix for UnlinkedFile { 53impl DiagnosticWithFixes for UnlinkedFile {
54 fn fix( 54 fn fixes(
55 &self, 55 &self,
56 sema: &hir::Semantics<RootDatabase>, 56 sema: &hir::Semantics<RootDatabase>,
57 _resolve: &AssistResolveStrategy, 57 _resolve: &AssistResolveStrategy,
58 ) -> Option<Assist> { 58 ) -> Option<Vec<Assist>> {
59 // If there's an existing module that could add a `mod` item to include the unlinked file, 59 // If there's an existing module that could add `mod` or `pub mod` items to include the unlinked file,
60 // suggest that as a fix. 60 // suggest that as a fix.
61 61
62 let source_root = sema.db.source_root(sema.db.file_source_root(self.file_id)); 62 let source_root = sema.db.source_root(sema.db.file_source_root(self.file_id));
@@ -90,7 +90,7 @@ impl DiagnosticWithFix for UnlinkedFile {
90 } 90 }
91 91
92 if module.origin.file_id() == Some(*parent_id) { 92 if module.origin.file_id() == Some(*parent_id) {
93 return make_fix(sema.db, *parent_id, module_name, self.file_id); 93 return make_fixes(sema.db, *parent_id, module_name, self.file_id);
94 } 94 }
95 } 95 }
96 } 96 }
@@ -101,20 +101,23 @@ impl DiagnosticWithFix for UnlinkedFile {
101 } 101 }
102} 102}
103 103
104fn make_fix( 104fn make_fixes(
105 db: &RootDatabase, 105 db: &RootDatabase,
106 parent_file_id: FileId, 106 parent_file_id: FileId,
107 new_mod_name: &str, 107 new_mod_name: &str,
108 added_file_id: FileId, 108 added_file_id: FileId,
109) -> Option<Assist> { 109) -> Option<Vec<Assist>> {
110 fn is_outline_mod(item: &ast::Item) -> bool { 110 fn is_outline_mod(item: &ast::Item) -> bool {
111 matches!(item, ast::Item::Module(m) if m.item_list().is_none()) 111 matches!(item, ast::Item::Module(m) if m.item_list().is_none())
112 } 112 }
113 113
114 let mod_decl = format!("mod {};", new_mod_name); 114 let mod_decl = format!("mod {};", new_mod_name);
115 let pub_mod_decl = format!("pub mod {};", new_mod_name);
116
115 let ast: ast::SourceFile = db.parse(parent_file_id).tree(); 117 let ast: ast::SourceFile = db.parse(parent_file_id).tree();
116 118
117 let mut builder = TextEdit::builder(); 119 let mut mod_decl_builder = TextEdit::builder();
120 let mut pub_mod_decl_builder = TextEdit::builder();
118 121
119 // If there's an existing `mod m;` statement matching the new one, don't emit a fix (it's 122 // If there's an existing `mod m;` statement matching the new one, don't emit a fix (it's
120 // probably `#[cfg]`d out). 123 // probably `#[cfg]`d out).
@@ -138,30 +141,43 @@ fn make_fix(
138 { 141 {
139 Some(last) => { 142 Some(last) => {
140 cov_mark::hit!(unlinked_file_append_to_existing_mods); 143 cov_mark::hit!(unlinked_file_append_to_existing_mods);
141 builder.insert(last.syntax().text_range().end(), format!("\n{}", mod_decl)); 144 let offset = last.syntax().text_range().end();
145 mod_decl_builder.insert(offset, format!("\n{}", mod_decl));
146 pub_mod_decl_builder.insert(offset, format!("\n{}", pub_mod_decl));
142 } 147 }
143 None => { 148 None => {
144 // Prepend before the first item in the file. 149 // Prepend before the first item in the file.
145 match ast.items().next() { 150 match ast.items().next() {
146 Some(item) => { 151 Some(item) => {
147 cov_mark::hit!(unlinked_file_prepend_before_first_item); 152 cov_mark::hit!(unlinked_file_prepend_before_first_item);
148 builder.insert(item.syntax().text_range().start(), format!("{}\n\n", mod_decl)); 153 let offset = item.syntax().text_range().start();
154 mod_decl_builder.insert(offset, format!("{}\n\n", mod_decl));
155 pub_mod_decl_builder.insert(offset, format!("{}\n\n", pub_mod_decl));
149 } 156 }
150 None => { 157 None => {
151 // No items in the file, so just append at the end. 158 // No items in the file, so just append at the end.
152 cov_mark::hit!(unlinked_file_empty_file); 159 cov_mark::hit!(unlinked_file_empty_file);
153 builder.insert(ast.syntax().text_range().end(), format!("{}\n", mod_decl)); 160 let offset = ast.syntax().text_range().end();
161 mod_decl_builder.insert(offset, format!("{}\n", mod_decl));
162 pub_mod_decl_builder.insert(offset, format!("{}\n", pub_mod_decl));
154 } 163 }
155 } 164 }
156 } 165 }
157 } 166 }
158 167
159 let edit = builder.finish();
160 let trigger_range = db.parse(added_file_id).tree().syntax().text_range(); 168 let trigger_range = db.parse(added_file_id).tree().syntax().text_range();
161 Some(fix( 169 Some(vec![
162 "add_mod_declaration", 170 fix(
163 &format!("Insert `{}`", mod_decl), 171 "add_mod_declaration",
164 SourceChange::from_text_edit(parent_file_id, edit), 172 &format!("Insert `{}`", mod_decl),
165 trigger_range, 173 SourceChange::from_text_edit(parent_file_id, mod_decl_builder.finish()),
166 )) 174 trigger_range,
175 ),
176 fix(
177 "add_pub_mod_declaration",
178 &format!("Insert `{}`", pub_mod_decl),
179 SourceChange::from_text_edit(parent_file_id, pub_mod_decl_builder.finish()),
180 trigger_range,
181 ),
182 ])
167} 183}