Skip to content

Commit c447a79

Browse files
committed
Prevent being able to rename items that are not part of the workspace
1 parent 75371eb commit c447a79

File tree

10 files changed

+92
-15
lines changed

10 files changed

+92
-15
lines changed

crates/ide/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,12 @@ impl Analysis {
244244
self.with_db(|db| db.parse(file_id).tree())
245245
}
246246

247+
/// Returns true if this file belongs to an immutable library.
248+
pub fn is_library_file(&self, file_id: FileId) -> Cancelable<bool> {
249+
use ide_db::base_db::SourceDatabaseExt;
250+
self.with_db(|db| db.source_root(db.file_source_root(file_id)).is_library)
251+
}
252+
247253
/// Gets the file's `LineIndex`: data structure to convert between absolute
248254
/// offsets and line/column representation.
249255
pub fn file_line_index(&self, file_id: FileId) -> Cancelable<Arc<LineIndex>> {

crates/rust-analyzer/src/config.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,17 @@ impl Config {
400400
pub fn will_rename(&self) -> bool {
401401
try_or!(self.caps.workspace.as_ref()?.file_operations.as_ref()?.will_rename?, false)
402402
}
403+
pub fn change_annotation_support(&self) -> bool {
404+
try_!(self
405+
.caps
406+
.workspace
407+
.as_ref()?
408+
.workspace_edit
409+
.as_ref()?
410+
.change_annotation_support
411+
.as_ref()?)
412+
.is_some()
413+
}
403414
pub fn code_action_resolve(&self) -> bool {
404415
try_or!(
405416
self.caps

crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@
326326
},
327327
),
328328
document_changes: None,
329+
change_annotations: None,
329330
},
330331
),
331332
is_preferred: Some(

crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@
179179
},
180180
),
181181
document_changes: None,
182+
change_annotations: None,
182183
},
183184
),
184185
is_preferred: Some(

crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@
179179
},
180180
),
181181
document_changes: None,
182+
change_annotations: None,
182183
},
183184
),
184185
is_preferred: Some(

crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@
179179
},
180180
),
181181
document_changes: None,
182+
change_annotations: None,
182183
},
183184
),
184185
is_preferred: Some(

crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@
339339
},
340340
),
341341
document_changes: None,
342+
change_annotations: None,
342343
},
343344
),
344345
is_preferred: Some(

crates/rust-analyzer/src/diagnostics/to_proto.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ fn map_rust_child_diagnostic(
136136
// FIXME: there's no good reason to use edit_map here....
137137
changes: Some(edit_map),
138138
document_changes: None,
139+
change_annotations: None,
139140
}),
140141
is_preferred: Some(true),
141142
data: None,

crates/rust-analyzer/src/lsp_ext.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,9 @@ pub struct SnippetWorkspaceEdit {
312312
pub changes: Option<HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>>>,
313313
#[serde(skip_serializing_if = "Option::is_none")]
314314
pub document_changes: Option<Vec<SnippetDocumentChangeOperation>>,
315+
#[serde(skip_serializing_if = "Option::is_none")]
316+
pub change_annotations:
317+
Option<HashMap<lsp_types::ChangeAnnotationIdentifier, lsp_types::ChangeAnnotation>>,
315318
}
316319

317320
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
@@ -335,6 +338,9 @@ pub struct SnippetTextEdit {
335338
pub new_text: String,
336339
#[serde(skip_serializing_if = "Option::is_none")]
337340
pub insert_text_format: Option<lsp_types::InsertTextFormat>,
341+
/// The annotation id if this is an annotated
342+
#[serde(skip_serializing_if = "Option::is_none")]
343+
pub annotation_id: Option<lsp_types::ChangeAnnotationIdentifier>,
338344
}
339345

340346
pub enum HoverRequest {}

crates/rust-analyzer/src/to_proto.rs

Lines changed: 63 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Conversion of rust-analyzer specific types to lsp_types equivalents.
22
use std::{
3+
collections::HashMap,
34
path::{self, Path},
45
sync::atomic::{AtomicU32, Ordering},
56
};
@@ -174,6 +175,7 @@ pub(crate) fn snippet_text_edit(
174175
range: text_edit.range,
175176
new_text: text_edit.new_text,
176177
insert_text_format,
178+
annotation_id: None,
177179
}
178180
}
179181

@@ -688,6 +690,10 @@ pub(crate) fn goto_definition_response(
688690
}
689691
}
690692

693+
fn outside_workspace_annotation(snap: &GlobalStateSnapshot) -> Option<String> {
694+
snap.config.change_annotation_support().then(|| String::from("OutsideWorkspace"))
695+
}
696+
691697
pub(crate) fn snippet_text_document_edit(
692698
snap: &GlobalStateSnapshot,
693699
is_snippet: bool,
@@ -696,7 +702,19 @@ pub(crate) fn snippet_text_document_edit(
696702
) -> Result<lsp_ext::SnippetTextDocumentEdit> {
697703
let text_document = optional_versioned_text_document_identifier(snap, file_id);
698704
let line_index = snap.file_line_index(file_id)?;
699-
let edits = edit.into_iter().map(|it| snippet_text_edit(&line_index, is_snippet, it)).collect();
705+
let outside_workspace_annotation = snap
706+
.analysis
707+
.is_library_file(file_id)?
708+
.then(|| outside_workspace_annotation(snap))
709+
.flatten();
710+
let edits = edit
711+
.into_iter()
712+
.map(|it| {
713+
let mut edit = snippet_text_edit(&line_index, is_snippet, it);
714+
edit.annotation_id = outside_workspace_annotation.clone();
715+
edit
716+
})
717+
.collect();
700718
Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits })
701719
}
702720

@@ -721,6 +739,7 @@ pub(crate) fn snippet_text_document_ops(
721739
range: lsp_types::Range::default(),
722740
new_text: initial_contents,
723741
insert_text_format: Some(lsp_types::InsertTextFormat::PlainText),
742+
annotation_id: None,
724743
};
725744
let edit_file =
726745
lsp_ext::SnippetTextDocumentEdit { text_document, edits: vec![text_edit] };
@@ -734,7 +753,12 @@ pub(crate) fn snippet_text_document_ops(
734753
old_uri,
735754
new_uri,
736755
options: None,
737-
annotation_id: None,
756+
annotation_id: snap
757+
.analysis
758+
.is_library_file(src)
759+
.unwrap()
760+
.then(|| outside_workspace_annotation(snap))
761+
.flatten(),
738762
});
739763
ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(rename_file))
740764
}
@@ -747,6 +771,7 @@ pub(crate) fn snippet_workspace_edit(
747771
source_change: SourceChange,
748772
) -> Result<lsp_ext::SnippetWorkspaceEdit> {
749773
let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new();
774+
750775
for op in source_change.file_system_edits {
751776
let ops = snippet_text_document_ops(snap, op);
752777
document_changes.extend_from_slice(&ops);
@@ -755,8 +780,24 @@ pub(crate) fn snippet_workspace_edit(
755780
let edit = snippet_text_document_edit(&snap, source_change.is_snippet, file_id, edit)?;
756781
document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit));
757782
}
758-
let workspace_edit =
759-
lsp_ext::SnippetWorkspaceEdit { changes: None, document_changes: Some(document_changes) };
783+
let change_annotations = outside_workspace_annotation(snap).map(|annotation| {
784+
use std::iter::FromIterator;
785+
HashMap::from_iter(Some((
786+
annotation,
787+
lsp_types::ChangeAnnotation {
788+
label: String::from("Edit outside of the workspace"),
789+
needs_confirmation: Some(true),
790+
description: Some(String::from(
791+
"This edit lies outside of the workspace and may affect dependencies",
792+
)),
793+
},
794+
)))
795+
});
796+
let workspace_edit = lsp_ext::SnippetWorkspaceEdit {
797+
changes: None,
798+
document_changes: Some(document_changes),
799+
change_annotations,
800+
};
760801
Ok(workspace_edit)
761802
}
762803

@@ -784,24 +825,31 @@ impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit {
784825
lsp_types::DocumentChangeOperation::Edit(
785826
lsp_types::TextDocumentEdit {
786827
text_document: edit.text_document,
787-
edits: edit
788-
.edits
789-
.into_iter()
790-
.map(|edit| {
791-
lsp_types::OneOf::Left(lsp_types::TextEdit {
792-
range: edit.range,
793-
new_text: edit.new_text,
794-
})
795-
})
796-
.collect(),
828+
edits: edit.edits.into_iter().map(From::from).collect(),
797829
},
798830
)
799831
}
800832
})
801833
.collect(),
802834
)
803835
}),
804-
change_annotations: None,
836+
change_annotations: snippet_workspace_edit.change_annotations,
837+
}
838+
}
839+
}
840+
841+
impl From<lsp_ext::SnippetTextEdit>
842+
for lsp_types::OneOf<lsp_types::TextEdit, lsp_types::AnnotatedTextEdit>
843+
{
844+
fn from(
845+
lsp_ext::SnippetTextEdit { annotation_id, insert_text_format:_, new_text, range }: lsp_ext::SnippetTextEdit,
846+
) -> Self {
847+
match annotation_id {
848+
Some(annotation_id) => lsp_types::OneOf::Right(lsp_types::AnnotatedTextEdit {
849+
text_edit: lsp_types::TextEdit { range, new_text },
850+
annotation_id,
851+
}),
852+
None => lsp_types::OneOf::Left(lsp_types::TextEdit { range, new_text }),
805853
}
806854
}
807855
}

0 commit comments

Comments
 (0)