6514: Fix extract_struct_from_enum_variant not updating record references r=Veykril a=Veykril
Related to #6510
Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
[[package]]
name = "cc"
-version = "1.0.61"
+version = "1.0.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d"
+checksum = "f1770ced377336a88a67c473594ccc14eca6f4559217c34f64aac8f83d641b40"
[[package]]
name = "cfg"
[[package]]
name = "filetime"
-version = "0.2.12"
+version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ed85775dcc68644b5c950ac06a2b23768d3bc9390464151aaf27136998dcf9e"
+checksum = "0c122a393ea57648015bf06fbd3d372378992e86b9ff5a7a497b076a28c79efe"
dependencies = [
- "cfg-if 0.1.10",
+ "cfg-if 1.0.0",
"libc",
"redox_syscall",
"winapi 0.3.9",
"toolchain",
]
+[[package]]
+name = "form_urlencoded"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00"
+dependencies = [
+ "matches",
+ "percent-encoding",
+]
+
[[package]]
name = "fsevent"
version = "2.0.2"
"hir_expand",
"itertools",
"log",
+ "once_cell",
"profile",
"rustc-hash",
"scoped-tls",
[[package]]
name = "inotify-sys"
-version = "0.1.3"
+version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0"
+checksum = "c4563555856585ab3180a5bf0b2f9f8d301a728462afffc8195b3f5394229c55"
dependencies = [
"libc",
]
[[package]]
name = "once_cell"
-version = "1.4.1"
+version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad"
+checksum = "f53cef67919d7d247eb9a2f128ca9e522789967ef1eb4ccd8c71a95a8aedf596"
[[package]]
name = "oorandom"
[[package]]
name = "rustc-ap-rustc_lexer"
-version = "686.0.0"
+version = "688.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5b04cd2159495584d976d501c5394498470c2e94e4f0cebb8186562d407a678"
+checksum = "ebbdcc99bd015349093fcbae4780fda21416fec5d8843acfb3d1733e130cd4db"
dependencies = [
"unicode-xid",
]
[[package]]
name = "url"
-version = "2.1.1"
+version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb"
+checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e"
dependencies = [
+ "form_urlencoded",
"idna",
"matches",
"percent-encoding",
let source_file = ctx.sema.parse(usage.file_range.file_id);
let call_expr: ast::CallExpr =
find_node_at_range(source_file.syntax(), usage.file_range.range)?;
- if call_expr.expr()?.syntax().text_range() != usage.file_range.range {
+ let call_expr_range = call_expr.expr()?.syntax().text_range();
+ if !call_expr_range.contains_range(usage.file_range.range) {
return None;
}
let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?;
);
}
+ #[test]
+ fn remove_unused_qualified_call() {
+ check_assist(
+ remove_unused_param,
+ r#"
+mod bar { pub fn foo(x: i32, <|>y: i32) { x; } }
+fn b() { bar::foo(9, 2) }
+"#,
+ r#"
+mod bar { pub fn foo(x: i32) { x; } }
+fn b() { bar::foo(9) }
+"#,
+ );
+ }
+
+ #[test]
+ fn remove_unused_turbofished_func() {
+ check_assist(
+ remove_unused_param,
+ r#"
+pub fn foo<T>(x: T, <|>y: i32) { x; }
+fn b() { foo::<i32>(9, 2) }
+"#,
+ r#"
+pub fn foo<T>(x: T) { x; }
+fn b() { foo::<i32>(9) }
+"#,
+ );
+ }
+
+ #[test]
+ fn remove_unused_generic_unused_param_func() {
+ check_assist(
+ remove_unused_param,
+ r#"
+pub fn foo<T>(x: i32, <|>y: T) { x; }
+fn b() { foo::<i32>(9, 2) }
+fn b2() { foo(9, 2) }
+"#,
+ r#"
+pub fn foo<T>(x: i32) { x; }
+fn b() { foo::<i32>(9) }
+fn b2() { foo(9) }
+"#,
+ );
+ }
+
#[test]
fn keep_used() {
mark::check!(keep_used);
"#,
);
}
+
+ #[test]
+ fn remove_across_files() {
+ check_assist(
+ remove_unused_param,
+ r#"
+//- /main.rs
+fn foo(x: i32, <|>y: i32) { x; }
+
+mod foo;
+
+//- /foo.rs
+use super::foo;
+
+fn bar() {
+ let _ = foo(1, 2);
+}
+"#,
+ r#"
+//- /main.rs
+fn foo(x: i32) { x; }
+
+mod foo;
+
+//- /foo.rs
+use super::foo;
+
+fn bar() {
+ let _ = foo(1);
+}
+"#,
+ )
+ }
}
edit::{AstNodeEdit, IndentLevel},
make, AstNode, PathSegmentKind, VisibilityOwner,
},
- InsertPosition, SyntaxElement, SyntaxNode,
+ AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken,
};
use test_utils::mark;
}
}
- fn insert_pos_after_inner_attribute(&self) -> (InsertPosition<SyntaxElement>, AddBlankLine) {
- // check if the scope has inner attributes, we dont want to insert in front of them
- match self
- .as_syntax_node()
- .children()
- // no flat_map here cause we want to short circuit the iterator
- .map(ast::Attr::cast)
- .take_while(|attr| {
- attr.as_ref().map(|attr| attr.kind() == ast::AttrKind::Inner).unwrap_or(false)
+ fn insert_pos_after_last_inner_element(&self) -> (InsertPosition<SyntaxElement>, AddBlankLine) {
+ self.as_syntax_node()
+ .children_with_tokens()
+ .filter(|child| match child {
+ NodeOrToken::Node(node) => is_inner_attribute(node.clone()),
+ NodeOrToken::Token(token) => is_inner_comment(token.clone()),
})
.last()
- .flatten()
- {
- Some(attr) => {
- (InsertPosition::After(attr.syntax().clone().into()), AddBlankLine::BeforeTwice)
- }
- None => self.first_insert_pos(),
- }
+ .map(|last_inner_element| {
+ (InsertPosition::After(last_inner_element.into()), AddBlankLine::BeforeTwice)
+ })
+ .unwrap_or_else(|| self.first_insert_pos())
}
}
+fn is_inner_attribute(node: SyntaxNode) -> bool {
+ ast::Attr::cast(node).map(|attr| attr.kind()) == Some(ast::AttrKind::Inner)
+}
+
+fn is_inner_comment(token: SyntaxToken) -> bool {
+ ast::Comment::cast(token).and_then(|comment| comment.kind().doc)
+ == Some(ast::CommentPlacement::Inner)
+}
+
/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur.
pub(crate) fn insert_use<'a>(
scope: &ImportScope,
(InsertPosition::After(node.into()), AddBlankLine::BeforeTwice)
}
// there are no imports in this file at all
- None => scope.insert_pos_after_inner_attribute(),
+ None => scope.insert_pos_after_last_inner_element(),
},
}
}
"foo::bar",
r"#![allow(unused_imports)]
+#![no_std]
fn main() {}",
r"#![allow(unused_imports)]
-use foo::bar;
+#![no_std]
+use foo::bar;
fn main() {}",
+ );
+ }
+
+ #[test]
+ fn inserts_after_single_line_inner_comments() {
+ check_none(
+ "foo::bar::Baz",
+ "//! Single line inner comments do not allow any code before them.",
+ r#"//! Single line inner comments do not allow any code before them.
+
+use foo::bar::Baz;"#,
+ );
+ }
+
+ #[test]
+ fn inserts_after_multiline_inner_comments() {
+ check_none(
+ "foo::bar::Baz",
+ r#"/*! Multiline inner comments do not allow any code before them. */
+
+/*! Still an inner comment, cannot place any code before. */
+fn main() {}"#,
+ r#"/*! Multiline inner comments do not allow any code before them. */
+
+/*! Still an inner comment, cannot place any code before. */
+
+use foo::bar::Baz;
+fn main() {}"#,
+ )
+ }
+
+ #[test]
+ fn inserts_after_all_inner_items() {
+ check_none(
+ "foo::bar::Baz",
+ r#"#![allow(unused_imports)]
+/*! Multiline line comment 2 */
+
+
+//! Single line comment 1
+#![no_std]
+//! Single line comment 2
+fn main() {}"#,
+ r#"#![allow(unused_imports)]
+/*! Multiline line comment 2 */
+
+
+//! Single line comment 1
+#![no_std]
+//! Single line comment 2
+
+use foo::bar::Baz;
+fn main() {}"#,
)
}
tracing = "0.1"
tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] }
tracing-tree = { version = "0.1.4" }
+once_cell = { version = "1.5.0", features = ["unstable"] }
AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId,
};
use hir_expand::{db::AstDatabase, InFile};
-use stdx::{format_to, RacyFlag};
+use once_cell::race::OnceBool;
+use stdx::format_to;
use syntax::{
algo,
ast::{self, AstNode},
// `env UPDATE_EXPECT=1 cargo test -p hir_ty` to update the snapshots.
fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> {
- static ENABLE: RacyFlag = RacyFlag::new();
- if !ENABLE.get(|| env::var("CHALK_DEBUG").is_ok()) {
+ static ENABLE: OnceBool = OnceBool::new();
+ if !ENABLE.get_or_init(|| env::var("CHALK_DEBUG").is_ok()) {
return None;
}
#[cfg(test)]
mod tests;
-use hir::{Local, Name, Semantics, VariantDef};
+use hir::{AsAssocItem, Local, Name, Semantics, VariantDef};
use ide_db::{
defs::{Definition, NameClass, NameRefClass},
RootDatabase,
if func.is_unsafe(db) {
h |= HighlightModifier::Unsafe;
}
+ if func.as_assoc_item(db).is_some() && func.self_param(db).is_none() {
+ h |= HighlightModifier::Static;
+ }
return h;
}
hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct,
Consuming,
Unsafe,
Callable,
+ /// Used for associated functions
+ Static,
}
impl HighlightTag {
HighlightModifier::Consuming,
HighlightModifier::Unsafe,
HighlightModifier::Callable,
+ HighlightModifier::Static,
];
fn as_str(self) -> &'static str {
HighlightModifier::Consuming => "consuming",
HighlightModifier::Unsafe => "unsafe",
HighlightModifier::Callable => "callable",
+ HighlightModifier::Static => "static",
}
}
--- /dev/null
+
+<style>
+body { margin: 0; }
+pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
+
+.lifetime { color: #DFAF8F; font-style: italic; }
+.comment { color: #7F9F7F; }
+.documentation { color: #629755; }
+.injected { opacity: 0.65 ; }
+.struct, .enum { color: #7CB8BB; }
+.enum_variant { color: #BDE0F3; }
+.string_literal { color: #CC9393; }
+.field { color: #94BFF3; }
+.function { color: #93E0E3; }
+.function.unsafe { color: #BC8383; }
+.operator.unsafe { color: #BC8383; }
+.parameter { color: #94BFF3; }
+.text { color: #DCDCCC; }
+.type { color: #7CB8BB; }
+.builtin_type { color: #8CD0D3; }
+.type_param { color: #DFAF8F; }
+.attribute { color: #94BFF3; }
+.numeric_literal { color: #BFEBBF; }
+.bool_literal { color: #BFE6EB; }
+.macro { color: #94BFF3; }
+.module { color: #AFD8AF; }
+.value_param { color: #DCDCCC; }
+.variable { color: #DCDCCC; }
+.format_specifier { color: #CC696B; }
+.mutable { text-decoration: underline; }
+.escape_sequence { color: #94BFF3; }
+.keyword { color: #F0DFAF; font-weight: bold; }
+.keyword.unsafe { color: #BC8383; font-weight: bold; }
+.control { font-style: italic; }
+
+.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
+</style>
+<pre><code><span class="keyword">fn</span> <span class="function declaration">not_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
+
+<span class="keyword">struct</span> <span class="struct declaration">foo</span> <span class="punctuation">{</span><span class="punctuation">}</span>
+
+<span class="keyword">impl</span> <span class="struct">foo</span> <span class="punctuation">{</span>
+ <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static">is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
+ <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
+<span class="punctuation">}</span>
+
+<span class="keyword">trait</span> <span class="trait declaration">t</span> <span class="punctuation">{</span>
+ <span class="keyword">fn</span> <span class="function declaration static">t_is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
+ <span class="keyword">fn</span> <span class="function declaration">t_is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
+<span class="punctuation">}</span>
+
+<span class="keyword">impl</span> <span class="trait">t</span> <span class="keyword">for</span> <span class="struct">foo</span> <span class="punctuation">{</span>
+ <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static">is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
+ <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
+<span class="punctuation">}</span>
+ </code></pre>
\ No newline at end of file
<span class="comment documentation">/// #</span><span class="generic injected"> </span><span class="attribute injected">#</span><span class="attribute injected">!</span><span class="attribute injected">[</span><span class="function attribute injected">allow</span><span class="punctuation injected">(</span><span class="attribute injected">unused_mut</span><span class="punctuation injected">)</span><span class="attribute injected">]</span>
<span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="keyword injected">mut</span><span class="generic injected"> </span><span class="variable declaration injected mutable">foo</span><span class="punctuation injected">:</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected">
</span> <span class="comment documentation">/// ```</span>
- <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration">new</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="struct">Foo</span> <span class="punctuation">{</span>
+ <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration static">new</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="struct">Foo</span> <span class="punctuation">{</span>
<span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">bar</span><span class="punctuation">:</span> <span class="bool_literal">true</span> <span class="punctuation">}</span>
<span class="punctuation">}</span>
<span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
<span class="function">fixture</span><span class="punctuation">(</span><span class="string_literal">r#"</span>
<span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="punctuation">{</span>
- <span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
+ <span class="keyword">fn</span> <span class="function declaration static">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
<span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"2 + 2 = {}"</span><span class="punctuation">,</span> <span class="numeric_literal">4</span><span class="punctuation">)</span><span class="punctuation">;</span>
<span class="punctuation">}</span>
<span class="punctuation">}</span><span class="string_literal">"#</span>
);
}
+#[test]
+fn test_associated_function() {
+ check_highlighting(
+ r#"
+fn not_static() {}
+
+struct foo {}
+
+impl foo {
+ pub fn is_static() {}
+ pub fn is_not_static(&self) {}
+}
+
+trait t {
+ fn t_is_static() {}
+ fn t_is_not_static(&self) {}
+}
+
+impl t for foo {
+ pub fn is_static() {}
+ pub fn is_not_static(&self) {}
+}
+ "#,
+ expect_file!["./test_data/highlight_assoc_functions.html"],
+ false,
+ )
+}
+
/// Highlights the code given by the `ra_fixture` argument, renders the
/// result as HTML, and compares it with the HTML file given as `snapshot`.
/// Note that the `snapshot` file is overwritten by the rendered HTML.
use crate::semantic_tokens;
pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabilities {
- let code_action_provider = code_action_capabilities(client_caps);
-
ServerCapabilities {
text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions {
open_close: Some(true),
document_highlight_provider: Some(OneOf::Left(true)),
document_symbol_provider: Some(OneOf::Left(true)),
workspace_symbol_provider: Some(OneOf::Left(true)),
- code_action_provider: Some(code_action_provider),
+ code_action_provider: Some(code_action_capabilities(client_caps)),
code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }),
document_formatting_provider: Some(OneOf::Left(true)),
document_range_formatting_provider: None,
CodeActionKind::REFACTOR_INLINE,
CodeActionKind::REFACTOR_REWRITE,
]),
- resolve_provider: None,
+ resolve_provider: Some(true),
work_done_progress_options: Default::default(),
})
})
pub code_action_literals: bool,
pub work_done_progress: bool,
pub code_action_group: bool,
- pub resolve_code_action: bool,
+ pub code_action_resolve: bool,
pub hover_actions: bool,
pub status_notification: bool,
pub signature_help_label_offsets: bool,
}
}
}
+
+ if let Some(code_action) = &doc_caps.code_action {
+ match (code_action.data_support, &code_action.resolve_support) {
+ (Some(true), Some(resolve_support)) => {
+ if resolve_support.properties.iter().any(|it| it == "edit") {
+ self.client_caps.code_action_resolve = true;
+ }
+ }
+ _ => (),
+ }
+ }
}
if let Some(window_caps) = caps.window.as_ref() {
self.assist.allow_snippets(snippet_text_edit);
self.client_caps.code_action_group = get_bool("codeActionGroup");
- self.client_caps.resolve_code_action = get_bool("resolveCodeAction");
self.client_caps.hover_actions = get_bool("hoverActions");
self.client_caps.status_notification = get_bool("statusNotification");
}
[
MappedRustDiagnostic {
- url: "file:///test/compiler/mir/tagset.rs",
+ url: Url {
+ scheme: "file",
+ host: None,
+ port: None,
+ path: "/test/compiler/mir/tagset.rs",
+ query: None,
+ fragment: None,
+ },
diagnostic: Diagnostic {
range: Range {
start: Position {
[
DiagnosticRelatedInformation {
location: Location {
- uri: "file:///test/compiler/lib.rs",
+ uri: Url {
+ scheme: "file",
+ host: None,
+ port: None,
+ path: "/test/compiler/lib.rs",
+ query: None,
+ fragment: None,
+ },
range: Range {
start: Position {
line: 0,
},
DiagnosticRelatedInformation {
location: Location {
- uri: "file:///test/compiler/mir/tagset.rs",
+ uri: Url {
+ scheme: "file",
+ host: None,
+ port: None,
+ path: "/test/compiler/mir/tagset.rs",
+ query: None,
+ fragment: None,
+ },
range: Range {
start: Position {
line: 41,
[
MappedRustDiagnostic {
- url: "file:///test/src/main.rs",
+ url: Url {
+ scheme: "file",
+ host: None,
+ port: None,
+ path: "/test/src/main.rs",
+ query: None,
+ fragment: None,
+ },
diagnostic: Diagnostic {
range: Range {
start: Position {
[
MappedRustDiagnostic {
- url: "file:///test/crates/hir_def/src/data.rs",
+ url: Url {
+ scheme: "file",
+ host: None,
+ port: None,
+ path: "/test/crates/hir_def/src/data.rs",
+ query: None,
+ fragment: None,
+ },
diagnostic: Diagnostic {
range: Range {
start: Position {
[
DiagnosticRelatedInformation {
location: Location {
- uri: "file:///test/crates/hir_def/src/path.rs",
+ uri: Url {
+ scheme: "file",
+ host: None,
+ port: None,
+ path: "/test/crates/hir_def/src/path.rs",
+ query: None,
+ fragment: None,
+ },
range: Range {
start: Position {
line: 264,
fixes: [],
},
MappedRustDiagnostic {
- url: "file:///test/crates/hir_def/src/path.rs",
+ url: Url {
+ scheme: "file",
+ host: None,
+ port: None,
+ path: "/test/crates/hir_def/src/path.rs",
+ query: None,
+ fragment: None,
+ },
diagnostic: Diagnostic {
range: Range {
start: Position {
[
DiagnosticRelatedInformation {
location: Location {
- uri: "file:///test/crates/hir_def/src/data.rs",
+ uri: Url {
+ scheme: "file",
+ host: None,
+ port: None,
+ path: "/test/crates/hir_def/src/data.rs",
+ query: None,
+ fragment: None,
+ },
range: Range {
start: Position {
line: 79,
[
MappedRustDiagnostic {
- url: "file:///test/compiler/ty/list_iter.rs",
+ url: Url {
+ scheme: "file",
+ host: None,
+ port: None,
+ path: "/test/compiler/ty/list_iter.rs",
+ query: None,
+ fragment: None,
+ },
diagnostic: Diagnostic {
range: Range {
start: Position {
[
MappedRustDiagnostic {
- url: "file:///test/runtime/compiler_support.rs",
+ url: Url {
+ scheme: "file",
+ host: None,
+ port: None,
+ path: "/test/runtime/compiler_support.rs",
+ query: None,
+ fragment: None,
+ },
diagnostic: Diagnostic {
range: Range {
start: Position {
[
MappedRustDiagnostic {
- url: "file:///test/driver/subcommand/repl.rs",
+ url: Url {
+ scheme: "file",
+ host: None,
+ port: None,
+ path: "/test/driver/subcommand/repl.rs",
+ query: None,
+ fragment: None,
+ },
diagnostic: Diagnostic {
range: Range {
start: Position {
fixes: [
CodeAction {
title: "consider prefixing with an underscore",
- id: None,
group: None,
kind: Some(
CodeActionKind(
SnippetWorkspaceEdit {
changes: Some(
{
- "file:///test/driver/subcommand/repl.rs": [
+ Url {
+ scheme: "file",
+ host: None,
+ port: None,
+ path: "/test/driver/subcommand/repl.rs",
+ query: None,
+ fragment: None,
+ }: [
TextEdit {
range: Range {
start: Position {
is_preferred: Some(
true,
),
+ data: None,
},
],
},
[
MappedRustDiagnostic {
- url: "file:///test/driver/subcommand/repl.rs",
+ url: Url {
+ scheme: "file",
+ host: None,
+ port: None,
+ path: "/test/driver/subcommand/repl.rs",
+ query: None,
+ fragment: None,
+ },
diagnostic: Diagnostic {
range: Range {
start: Position {
fixes: [
CodeAction {
title: "consider prefixing with an underscore",
- id: None,
group: None,
kind: Some(
CodeActionKind(
SnippetWorkspaceEdit {
changes: Some(
{
- "file:///test/driver/subcommand/repl.rs": [
+ Url {
+ scheme: "file",
+ host: None,
+ port: None,
+ path: "/test/driver/subcommand/repl.rs",
+ query: None,
+ fragment: None,
+ }: [
TextEdit {
range: Range {
start: Position {
is_preferred: Some(
true,
),
+ data: None,
},
],
},
[
MappedRustDiagnostic {
- url: "file:///test/driver/subcommand/repl.rs",
+ url: Url {
+ scheme: "file",
+ host: None,
+ port: None,
+ path: "/test/driver/subcommand/repl.rs",
+ query: None,
+ fragment: None,
+ },
diagnostic: Diagnostic {
range: Range {
start: Position {
fixes: [
CodeAction {
title: "consider prefixing with an underscore",
- id: None,
group: None,
kind: Some(
CodeActionKind(
SnippetWorkspaceEdit {
changes: Some(
{
- "file:///test/driver/subcommand/repl.rs": [
+ Url {
+ scheme: "file",
+ host: None,
+ port: None,
+ path: "/test/driver/subcommand/repl.rs",
+ query: None,
+ fragment: None,
+ }: [
TextEdit {
range: Range {
start: Position {
is_preferred: Some(
true,
),
+ data: None,
},
],
},
[
MappedRustDiagnostic {
- url: "file:///test/compiler/ty/select.rs",
+ url: Url {
+ scheme: "file",
+ host: None,
+ port: None,
+ path: "/test/compiler/ty/select.rs",
+ query: None,
+ fragment: None,
+ },
diagnostic: Diagnostic {
range: Range {
start: Position {
[
DiagnosticRelatedInformation {
location: Location {
- uri: "file:///test/compiler/ty/select.rs",
+ uri: Url {
+ scheme: "file",
+ host: None,
+ port: None,
+ path: "/test/compiler/ty/select.rs",
+ query: None,
+ fragment: None,
+ },
range: Range {
start: Position {
line: 218,
[
MappedRustDiagnostic {
- url: "file:///test/src/main.rs",
+ url: Url {
+ scheme: "file",
+ host: None,
+ port: None,
+ path: "/test/src/main.rs",
+ query: None,
+ fragment: None,
+ },
diagnostic: Diagnostic {
range: Range {
start: Position {
[
DiagnosticRelatedInformation {
location: Location {
- uri: "file:///test/src/main.rs",
+ uri: Url {
+ scheme: "file",
+ host: None,
+ port: None,
+ path: "/test/src/main.rs",
+ query: None,
+ fragment: None,
+ },
range: Range {
start: Position {
line: 2,
fixes: [
CodeAction {
title: "return the expression directly",
- id: None,
group: None,
kind: Some(
CodeActionKind(
SnippetWorkspaceEdit {
changes: Some(
{
- "file:///test/src/main.rs": [
+ Url {
+ scheme: "file",
+ host: None,
+ port: None,
+ path: "/test/src/main.rs",
+ query: None,
+ fragment: None,
+ }: [
TextEdit {
range: Range {
start: Position {
is_preferred: Some(
true,
),
+ data: None,
},
],
},
} else {
MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction {
title: rd.message.clone(),
- id: None,
group: None,
kind: Some(lsp_types::CodeActionKind::QUICKFIX),
edit: Some(lsp_ext::SnippetWorkspaceEdit {
document_changes: None,
}),
is_preferred: Some(true),
+ data: None,
})
}
}
let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?;
let action = lsp_ext::CodeAction {
title: fix.label.to_string(),
- id: None,
group: None,
kind: Some(CodeActionKind::QUICKFIX),
edit: Some(edit),
is_preferred: Some(false),
+ data: None,
};
res.push(action);
}
handle_fixes(&snap, ¶ms, &mut res)?;
- if snap.config.client_caps.resolve_code_action {
+ if snap.config.client_caps.code_action_resolve {
for (index, assist) in
snap.analysis.unresolved_assists(&snap.config.assist, frange)?.into_iter().enumerate()
{
- res.push(to_proto::unresolved_code_action(&snap, assist, index)?);
+ res.push(to_proto::unresolved_code_action(&snap, params.clone(), assist, index)?);
}
} else {
for assist in snap.analysis.resolved_assists(&snap.config.assist, frange)?.into_iter() {
Ok(Some(res))
}
-pub(crate) fn handle_resolve_code_action(
+pub(crate) fn handle_code_action_resolve(
mut snap: GlobalStateSnapshot,
- params: lsp_ext::ResolveCodeActionParams,
-) -> Result<Option<lsp_ext::SnippetWorkspaceEdit>> {
- let _p = profile::span("handle_resolve_code_action");
+ mut code_action: lsp_ext::CodeAction,
+) -> Result<lsp_ext::CodeAction> {
+ let _p = profile::span("handle_code_action_resolve");
+ let params = match code_action.data.take() {
+ Some(it) => it,
+ None => Err("can't resolve code action without data")?,
+ };
+
let file_id = from_proto::file_id(&snap, ¶ms.code_action_params.text_document.uri)?;
let line_index = snap.analysis.file_line_index(file_id)?;
let range = from_proto::text_range(&line_index, params.code_action_params.range);
let index = index.parse::<usize>().unwrap();
let assist = &assists[index];
assert!(assist.assist.id.0 == id);
- Ok(to_proto::resolved_code_action(&snap, assist.clone())?.edit)
+ let edit = to_proto::resolved_code_action(&snap, assist.clone())?.edit;
+ code_action.edit = edit;
+ Ok(code_action)
}
pub(crate) fn handle_code_lens(
pub ranges: Vec<Range>,
}
-pub enum ResolveCodeActionRequest {}
-
-impl Request for ResolveCodeActionRequest {
- type Params = ResolveCodeActionParams;
- type Result = Option<SnippetWorkspaceEdit>;
- const METHOD: &'static str = "experimental/resolveCodeAction";
-}
-
-/// Params for the ResolveCodeActionRequest
-#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct ResolveCodeActionParams {
- pub code_action_params: lsp_types::CodeActionParams,
- pub id: String,
-}
-
pub enum OnEnter {}
impl Request for OnEnter {
const METHOD: &'static str = "textDocument/codeAction";
}
+pub enum CodeActionResolveRequest {}
+impl Request for CodeActionResolveRequest {
+ type Params = CodeAction;
+ type Result = CodeAction;
+ const METHOD: &'static str = "codeAction/resolve";
+}
+
#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CodeAction {
pub title: String,
#[serde(skip_serializing_if = "Option::is_none")]
- pub id: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
pub group: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub kind: Option<CodeActionKind>,
pub edit: Option<SnippetWorkspaceEdit>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_preferred: Option<bool>,
+
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub data: Option<CodeActionData>,
+}
+
+#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CodeActionData {
+ pub code_action_params: lsp_types::CodeActionParams,
+ pub id: String,
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
.on::<lsp_ext::Runnables>(handlers::handle_runnables)
.on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)
.on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action)
- .on::<lsp_ext::ResolveCodeActionRequest>(handlers::handle_resolve_code_action)
+ .on::<lsp_ext::CodeActionResolveRequest>(handlers::handle_code_action_resolve)
.on::<lsp_ext::HoverRequest>(handlers::handle_hover)
.on::<lsp_ext::ExternalDocs>(handlers::handle_open_docs)
.on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)
HighlightModifier::Consuming => semantic_tokens::CONSUMING,
HighlightModifier::Unsafe => semantic_tokens::UNSAFE,
HighlightModifier::Callable => semantic_tokens::CALLABLE,
+ HighlightModifier::Static => lsp_types::SemanticTokenModifier::STATIC,
};
mods |= modifier;
}
pub(crate) fn unresolved_code_action(
snap: &GlobalStateSnapshot,
+ code_action_params: lsp_types::CodeActionParams,
assist: Assist,
index: usize,
) -> Result<lsp_ext::CodeAction> {
let res = lsp_ext::CodeAction {
title: assist.label.to_string(),
- id: Some(format!("{}:{}", assist.id.0, index.to_string())),
group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0),
kind: Some(code_action_kind(assist.id.1)),
edit: None,
is_preferred: None,
+ data: Some(lsp_ext::CodeActionData {
+ id: format!("{}:{}", assist.id.0, index.to_string()),
+ code_action_params,
+ }),
};
Ok(res)
}
assist: ResolvedAssist,
) -> Result<lsp_ext::CodeAction> {
let change = assist.source_change;
- unresolved_code_action(snap, assist.assist, 0).and_then(|it| {
- Ok(lsp_ext::CodeAction {
- id: None,
- edit: Some(snippet_workspace_edit(snap, change)?),
- ..it
- })
- })
+ let res = lsp_ext::CodeAction {
+ edit: Some(snippet_workspace_edit(snap, change)?),
+ title: assist.assist.label.to_string(),
+ group: assist
+ .assist
+ .group
+ .filter(|_| snap.config.client_caps.code_action_group)
+ .map(|gr| gr.0),
+ kind: Some(code_action_kind(assist.assist.id.1)),
+ is_preferred: None,
+ data: None,
+ };
+ Ok(res)
}
pub(crate) fn runnable(
//! Missing batteries for standard libraries.
-use std::{
- sync::atomic::{AtomicUsize, Ordering},
- time::Instant,
-};
+use std::time::Instant;
mod macros;
pub mod panic_context;
left
}
-pub struct RacyFlag(AtomicUsize);
-
-impl RacyFlag {
- pub const fn new() -> RacyFlag {
- RacyFlag(AtomicUsize::new(!0))
- }
-
- pub fn get(&self, init: impl FnMut() -> bool) -> bool {
- let mut init = Some(init);
- self.get_impl(&mut || init.take().map_or(false, |mut f| f()))
- }
-
- fn get_impl(&self, init: &mut dyn FnMut() -> bool) -> bool {
- match self.0.load(Ordering::Relaxed) {
- 0 => false,
- 1 => true,
- _ => {
- let res = init();
- self.0.store(if res { 1 } else { 0 }, Ordering::Relaxed);
- res
- }
- }
- }
-}
-
#[cfg(test)]
mod tests {
use super::*;
[dependencies]
itertools = "0.9.0"
rowan = "0.10.0"
-rustc_lexer = { version = "686.0.0", package = "rustc-ap-rustc_lexer" }
+rustc_lexer = { version = "688.0.0", package = "rustc-ap-rustc_lexer" }
rustc-hash = "1.1.0"
arrayvec = "0.5.1"
once_cell = "1.3.1"
type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<rustc_hash::FxHasher>>;
+#[derive(Debug, Hash, PartialEq, Eq)]
+enum TreeDiffInsertPos {
+ After(SyntaxElement),
+ AsFirstChild(SyntaxElement),
+}
+
#[derive(Debug)]
pub struct TreeDiff {
replacements: FxHashMap<SyntaxElement, SyntaxElement>,
deletions: Vec<SyntaxElement>,
// the vec as well as the indexmap are both here to preserve order
- insertions: FxIndexMap<SyntaxElement, Vec<SyntaxElement>>,
+ insertions: FxIndexMap<TreeDiffInsertPos, Vec<SyntaxElement>>,
}
impl TreeDiff {
pub fn into_text_edit(&self, builder: &mut TextEditBuilder) {
for (anchor, to) in self.insertions.iter() {
- to.iter().for_each(|to| builder.insert(anchor.text_range().end(), to.to_string()));
+ let offset = match anchor {
+ TreeDiffInsertPos::After(it) => it.text_range().end(),
+ TreeDiffInsertPos::AsFirstChild(it) => it.text_range().start(),
+ };
+ to.iter().for_each(|to| builder.insert(offset, to.to_string()));
}
for (from, to) in self.replacements.iter() {
builder.replace(from.text_range(), to.to_string())
let lhs_child = lhs_children.next();
match (lhs_child.clone(), rhs_children.next()) {
(None, None) => break,
- (None, Some(element)) => match last_lhs.clone() {
- Some(prev) => {
- mark::hit!(diff_insert);
- diff.insertions.entry(prev).or_insert_with(Vec::new).push(element);
- }
- // first iteration, this means we got no anchor element to insert after
- // therefor replace the parent node instead
- None => {
- mark::hit!(diff_replace_parent);
- diff.replacements.insert(lhs.clone().into(), rhs.clone().into());
- break;
- }
- },
+ (None, Some(element)) => {
+ let insert_pos = match last_lhs.clone() {
+ Some(prev) => {
+ mark::hit!(diff_insert);
+ TreeDiffInsertPos::After(prev)
+ }
+ // first iteration, insert into out parent as the first child
+ None => {
+ mark::hit!(diff_insert_as_first_child);
+ TreeDiffInsertPos::AsFirstChild(lhs.clone().into())
+ }
+ };
+ diff.insertions.entry(insert_pos).or_insert_with(Vec::new).push(element);
+ }
(Some(element), None) => {
mark::hit!(diff_delete);
diff.deletions.push(element);
}
}
let drain = look_ahead_scratch.drain(..);
- if let Some(prev) = last_lhs.clone().filter(|_| insert) {
- diff.insertions.entry(prev).or_insert_with(Vec::new).extend(drain);
+ if insert {
+ let insert_pos = if let Some(prev) = last_lhs.clone().filter(|_| insert) {
+ TreeDiffInsertPos::After(prev)
+ } else {
+ mark::hit!(insert_first_child);
+ TreeDiffInsertPos::AsFirstChild(lhs.clone().into())
+ };
+
+ diff.insertions.entry(insert_pos).or_insert_with(Vec::new).extend(drain);
rhs_children = rhs_children_clone;
} else {
go(diff, lhs_ele, rhs_ele)
#[test]
fn replace_parent() {
- mark::check!(diff_replace_parent);
+ mark::check!(diff_insert_as_first_child);
check_diff(
r#""#,
r#"use foo::bar;"#,
expect![[r#"
insertions:
-
+ Line 0: AsFirstChild(Node(SOURCE_FILE@0..0))
+ -> use foo::bar;
replacements:
- Line 0: Node(SOURCE_FILE@0..0) -> use foo::bar;
+
deletions:
expect![[r#"
insertions:
- Line 2: Node(USE@10..18)
+ Line 2: After(Node(USE@10..18))
-> "\n"
-> use baz;
expect![[r#"
insertions:
- Line 2: Token(WHITESPACE@9..10 "\n")
+ Line 2: After(Token(WHITESPACE@9..10 "\n"))
-> use bar;
-> "\n"
expect![[r#"
insertions:
- Line 0: Token(WHITESPACE@0..1 "\n")
+ Line 0: After(Token(WHITESPACE@0..1 "\n"))
-> use foo;
-> "\n"
)
}
+ #[test]
+ fn first_child_insertion() {
+ mark::check!(insert_first_child);
+ check_diff(
+ r#"fn main() {
+ stdi
+ }"#,
+ r#"use foo::bar;
+
+ fn main() {
+ stdi
+ }"#,
+ expect![[r#"
+ insertions:
+
+ Line 0: AsFirstChild(Node(SOURCE_FILE@0..30))
+ -> use foo::bar;
+ -> "\n\n "
+
+ replacements:
+
+
+
+ deletions:
+
+
+ "#]],
+ );
+ }
+
#[test]
fn delete_last() {
mark::check!(diff_delete);
expect![[r#"
insertions:
- Line 1: Node(USE@1..35)
+ Line 1: After(Node(USE@1..35))
-> "\n\n"
-> use crate::AstNode;
expect![[r#"
insertions:
- Line 2: Node(PATH_SEGMENT@5..8)
+ Line 2: After(Node(PATH_SEGMENT@5..8))
-> ::
-> fmt
- Line 6: Token(WHITESPACE@86..87 "\n")
+ Line 6: After(Token(WHITESPACE@86..87 "\n"))
-> use std::hash::BuildHasherDefault;
-> "\n"
-> use std::ops::{self, RangeInclusive};
expect![[r#"
insertions:
- Line 3: Node(BLOCK_EXPR@40..63)
+ Line 3: After(Node(BLOCK_EXPR@40..63))
-> " "
-> match Err(92) {
Ok(it) => it,
_ => return,
}
-> ;
- Line 3: Node(IF_EXPR@17..63)
+ Line 3: After(Node(IF_EXPR@17..63))
-> "\n "
-> foo(x);
_ => format!("{}", syn),
};
- let insertions = diff.insertions.iter().format_with("\n", |(k, v), f| {
- f(&format!(
- "Line {}: {:?}\n-> {}",
- line_number(k),
- k,
- v.iter().format_with("\n-> ", |v, f| f(&fmt_syntax(v)))
- ))
- });
+ let insertions =
+ diff.insertions.iter().format_with("\n", |(k, v), f| -> Result<(), std::fmt::Error> {
+ f(&format!(
+ "Line {}: {:?}\n-> {}",
+ line_number(match k {
+ super::TreeDiffInsertPos::After(syn) => syn,
+ super::TreeDiffInsertPos::AsFirstChild(syn) => syn,
+ }),
+ k,
+ v.iter().format_with("\n-> ", |v, f| f(&fmt_syntax(v)))
+ ))
+ });
let replacements = diff
.replacements
}
#[test]
-fn test_doc_comment_of_items() {
+fn test_outer_doc_comment_of_items() {
let file = SourceFile::parse(
r#"
- //! doc
+ /// doc
// non-doc
mod foo {}
"#,
assert_eq!("doc", module.doc_comment_text().unwrap());
}
+#[test]
+fn test_inner_doc_comment_of_items() {
+ let file = SourceFile::parse(
+ r#"
+ //! doc
+ // non-doc
+ mod foo {}
+ "#,
+ )
+ .ok()
+ .unwrap();
+ let module = file.syntax().descendants().find_map(Module::cast).unwrap();
+ assert!(module.doc_comment_text().is_none());
+}
+
#[test]
fn test_doc_comment_of_statics() {
let file = SourceFile::parse(
impl ast::Comment {
pub fn kind(&self) -> CommentKind {
- kind_by_prefix(self.text())
+ CommentKind::from_text(self.text())
}
pub fn prefix(&self) -> &'static str {
- for (prefix, k) in COMMENT_PREFIX_TO_KIND.iter() {
- if *k == self.kind() && self.text().starts_with(prefix) {
- return prefix;
- }
- }
- unreachable!()
+ let &(prefix, _kind) = CommentKind::BY_PREFIX
+ .iter()
+ .find(|&(prefix, kind)| self.kind() == *kind && self.text().starts_with(prefix))
+ .unwrap();
+ prefix
}
}
Outer,
}
-const COMMENT_PREFIX_TO_KIND: &[(&str, CommentKind)] = {
- use {CommentPlacement::*, CommentShape::*};
- &[
- ("////", CommentKind { shape: Line, doc: None }),
- ("///", CommentKind { shape: Line, doc: Some(Outer) }),
- ("//!", CommentKind { shape: Line, doc: Some(Inner) }),
- ("/**", CommentKind { shape: Block, doc: Some(Outer) }),
- ("/*!", CommentKind { shape: Block, doc: Some(Inner) }),
- ("//", CommentKind { shape: Line, doc: None }),
- ("/*", CommentKind { shape: Block, doc: None }),
- ]
-};
+impl CommentKind {
+ const BY_PREFIX: [(&'static str, CommentKind); 8] = [
+ ("/**/", CommentKind { shape: CommentShape::Block, doc: None }),
+ ("////", CommentKind { shape: CommentShape::Line, doc: None }),
+ ("///", CommentKind { shape: CommentShape::Line, doc: Some(CommentPlacement::Outer) }),
+ ("//!", CommentKind { shape: CommentShape::Line, doc: Some(CommentPlacement::Inner) }),
+ ("/**", CommentKind { shape: CommentShape::Block, doc: Some(CommentPlacement::Outer) }),
+ ("/*!", CommentKind { shape: CommentShape::Block, doc: Some(CommentPlacement::Inner) }),
+ ("//", CommentKind { shape: CommentShape::Line, doc: None }),
+ ("/*", CommentKind { shape: CommentShape::Block, doc: None }),
+ ];
-fn kind_by_prefix(text: &str) -> CommentKind {
- if text == "/**/" {
- return CommentKind { shape: CommentShape::Block, doc: None };
- }
- for (prefix, kind) in COMMENT_PREFIX_TO_KIND.iter() {
- if text.starts_with(prefix) {
- return *kind;
- }
+ pub(crate) fn from_text(text: &str) -> CommentKind {
+ let &(_prefix, kind) = CommentKind::BY_PREFIX
+ .iter()
+ .find(|&(prefix, _kind)| text.starts_with(prefix))
+ .unwrap();
+ kind
}
- panic!("bad comment text: {:?}", text)
}
impl ast::Whitespace {
use parser::{ParseError, TreeSink};
use crate::{
+ ast,
parsing::Token,
syntax_node::GreenNode,
SmolStr, SyntaxError,
while let Some((i, (kind, text))) = trivias.next() {
match kind {
- WHITESPACE => {
- if text.contains("\n\n") {
- // we check whether the next token is a doc-comment
- // and skip the whitespace in this case
- if let Some((peek_kind, peek_text)) =
- trivias.peek().map(|(_, pair)| pair)
- {
- if *peek_kind == COMMENT
- && peek_text.starts_with("///")
- && !peek_text.starts_with("////")
- {
- continue;
- }
+ WHITESPACE if text.contains("\n\n") => {
+ // we check whether the next token is a doc-comment
+ // and skip the whitespace in this case
+ if let Some((COMMENT, peek_text)) = trivias.peek().map(|(_, pair)| pair) {
+ let comment_kind = ast::CommentKind::from_text(peek_text);
+ if comment_kind.doc == Some(ast::CommentPlacement::Outer) {
+ continue;
}
- break;
}
+ break;
}
COMMENT => {
+ let comment_kind = ast::CommentKind::from_text(text);
+ if comment_kind.doc == Some(ast::CommentPlacement::Inner) {
+ break;
+ }
res = i + 1;
}
_ => (),
SOURCE_FILE@0..93
COMMENT@0..60 "// https://github.com ..."
WHITESPACE@60..62 "\n\n"
- MODULE@62..93
- COMMENT@62..70 "//! docs"
- WHITESPACE@70..71 "\n"
+ COMMENT@62..70 "//! docs"
+ WHITESPACE@70..71 "\n"
+ MODULE@71..93
COMMENT@71..82 "// non-docs"
WHITESPACE@82..83 "\n"
MOD_KW@83..86 "mod"
<!---
-lsp_ext.rs hash: 286f8bbac885531a
+lsp_ext.rs hash: 4f86fb54e4b2870e
If you need to change the above hash to make the test pass, please check if you
need to adjust this doc as well and ping this issue:
* Is a fixed two-level structure enough?
* Should we devise a general way to encode custom interaction protocols for GUI refactorings?
-## Lazy assists with `ResolveCodeAction`
-
-**Issue:** https://github.com/microsoft/language-server-protocol/issues/787
-
-**Client Capability** `{ "resolveCodeAction": boolean }`
-
-If this capability is set, the assists will be computed lazily. Thus `CodeAction` returned from the server will only contain `id` but not `edit` or `command` fields. The only exclusion from the rule is the diagnostic edits.
-
-After the client got the id, it should then call `experimental/resolveCodeAction` command on the server and provide the following payload:
-
-```typescript
-interface ResolveCodeActionParams {
- id: string;
- codeActionParams: lc.CodeActionParams;
-}
-```
-
-As a result of the command call the client will get the respective workspace edit (`lc.WorkspaceEdit`).
-
-### Unresolved Questions
-
-* Apply smarter filtering for ids?
-* Upon `resolveCodeAction` command only call the assits which should be resolved and not all of them?
-
## Parent Module
**Issue:** https://github.com/microsoft/language-server-protocol/issues/1002
"block-comments": {
"patterns": [
{
- "comment": "block comments",
+ "comment": "empty block comments",
"name": "comment.block.rust",
- "begin": "/\\*(?!\\*)",
+ "match": "/\\*\\*/"
+ },
+ {
+ "comment": "block documentation comments",
+ "name": "comment.block.documentation.rust",
+ "begin": "/\\*\\*",
"end": "\\*/",
"patterns": [
{
]
},
{
- "comment": "block documentation comments",
- "name": "comment.block.documentation.rust",
- "begin": "/\\*\\*",
+ "comment": "block comments",
+ "name": "comment.block.rust",
+ "begin": "/\\*(?!\\*)",
"end": "\\*/",
"patterns": [
{
import * as Is from 'vscode-languageclient/lib/common/utils/is';
import { DocumentSemanticsTokensSignature, DocumentSemanticsTokensEditsSignature, DocumentRangeSemanticTokensSignature } from 'vscode-languageclient/lib/common/semanticTokens';
import { assert } from './util';
+import { WorkspaceEdit } from 'vscode';
function renderCommand(cmd: ra.CommandLink) {
return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(JSON.stringify(cmd.arguments))} '${cmd.tooltip!}')`;
return Promise.resolve(null);
});
},
- // Using custom handling of CodeActions where each code action is resolved lazily
- // That's why we are not waiting for any command or edits
+ // Using custom handling of CodeActions to support action groups and snippet edits.
+ // Note that this means we have to re-implement lazy edit resolving ourselves as well.
async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) {
const params: lc.CodeActionParams = {
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
const kind = client.protocol2CodeConverter.asCodeActionKind((item as any).kind);
const action = new vscode.CodeAction(item.title, kind);
const group = (item as any).group;
- const id = (item as any).id;
- const resolveParams: ra.ResolveCodeActionParams = {
- id: id,
- codeActionParams: params
- };
action.command = {
command: "rust-analyzer.resolveCodeAction",
title: item.title,
- arguments: [resolveParams],
+ arguments: [item],
};
+
+ // Set a dummy edit, so that VS Code doesn't try to resolve this.
+ action.edit = new WorkspaceEdit();
+
if (group) {
let entry = groups.get(group);
if (!entry) {
return { label: item.title, arguments: item.command!!.arguments!![0] };
})],
};
+
+ // Set a dummy edit, so that VS Code doesn't try to resolve this.
+ action.edit = new WorkspaceEdit();
+
result[index] = action;
}
}
const caps: any = capabilities.experimental ?? {};
caps.snippetTextEdit = true;
caps.codeActionGroup = true;
- caps.resolveCodeAction = true;
caps.hoverActions = true;
caps.statusNotification = true;
capabilities.experimental = caps;
}
export function applyActionGroup(_ctx: Ctx): Cmd {
- return async (actions: { label: string; arguments: ra.ResolveCodeActionParams }[]) => {
+ return async (actions: { label: string; arguments: lc.CodeAction }[]) => {
const selectedAction = await vscode.window.showQuickPick(actions);
if (!selectedAction) return;
vscode.commands.executeCommand(
export function resolveCodeAction(ctx: Ctx): Cmd {
const client = ctx.client;
- return async (params: ra.ResolveCodeActionParams) => {
- const item: lc.WorkspaceEdit = await client.sendRequest(ra.resolveCodeAction, params);
- if (!item) {
+ return async (params: lc.CodeAction) => {
+ params.command = undefined;
+ const item = await client.sendRequest(lc.CodeActionResolveRequest.type, params);
+ if (!item.edit) {
return;
}
- const edit = client.protocol2CodeConverter.asWorkspaceEdit(item);
+ const edit = client.protocol2CodeConverter.asWorkspaceEdit(item.edit);
await applySnippetWorkspaceEdit(edit);
};
}
export const parentModule = new lc.RequestType<lc.TextDocumentPositionParams, lc.LocationLink[], void>("experimental/parentModule");
-export interface ResolveCodeActionParams {
- id: string;
- codeActionParams: lc.CodeActionParams;
-}
-export const resolveCodeAction = new lc.RequestType<ResolveCodeActionParams, lc.WorkspaceEdit, unknown>('experimental/resolveCodeAction');
-
export interface JoinLinesParams {
textDocument: lc.TextDocumentIdentifier;
ranges: lc.Range[];