Skip to content

Commit 57ed622

Browse files
bors[bot]BGluthdavidlattimoreVeetahafannheyward
authored
5089: Disable auto-complete on comments r=matklad a=BGluth Resolves #4907 by disabling any auto-completion on comments. As flodiebold [pointed out](#4907 (comment)), in the future we may want to support some form of auto-completion within doc comments, but for now it was suggested to just disable auto-completion on them entirely. The implementation involves adding a new field `is_comment` to `CompletionContext` and checking if the immediate token we auto-completed on is a comment. I couldn't see a case where we need to check any of the ancestors, but let me know if this is not sufficient. I also wasn't sure if it was necessary to add a new field to this struct, but I decided it's probably the best option if we want to potentially do auto-completion on doc comments in the future. Finally, the three tests I added should I think ideally not filter results by `CompletionKind::Keyword`, but if I want to get unfiltered results, I need access to a non-public function [get_all_completion_items](https://github.com/rust-analyzer/rust-analyzer/blob/9a4d02faf9c47f401b8756c3f7fcab2198f5f9cd/crates/ra_ide/src/completion/test_utils.rs#L32-L39) which I don't know if I should make public just for this. 5161: SSR: Add initial support for placeholder constraints r=matklad a=davidlattimore 5184: Always install required nightly extension if current one is not nightly r=matklad a=Veetaha This is weird, but having switched back to stable by uninstalling the extension appears that vscode doesn't destroy the `PersistentState` and thus changing to `nightly` channel doesn't work because the last check for nightly extension was less than 1 hour ago. The simple solution is to skip this check if we know that the current extension version is not nightly. 5185: Force showing extension activation error pop-up notification r=matklad a=Veetaha Fixes #5091 5186: fix: correct pd/ppd/tfn/tmod completion doc r=matklad a=fannheyward https://github.com/rust-analyzer/rust-analyzer/blob/a33eefa3b26000b3018e6bb873f18dbe15ab4ab7/crates/ra_ide/src/completion/complete_snippet.rs#L23-L24 Co-authored-by: BGluth <[email protected]> Co-authored-by: David Lattimore <[email protected]> Co-authored-by: Veetaha <[email protected]> Co-authored-by: Heyward Fann <[email protected]>
6 parents e5f8fb3 + cc77bdf + 83588a1 + 69b6f6d + 6a6ce61 + f1986be commit 57ed622

File tree

10 files changed

+242
-26
lines changed

10 files changed

+242
-26
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ra_ide/src/completion.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,19 +63,19 @@ pub use crate::completion::{
6363
// There also snippet completions:
6464
//
6565
// .Expressions
66-
// - `pd` -> `println!("{:?}")`
67-
// - `ppd` -> `println!("{:#?}")`
66+
// - `pd` -> `eprintln!(" = {:?}", );")`
67+
// - `ppd` -> `eprintln!(" = {:#?}", );`
6868
//
6969
// .Items
70-
// - `tfn` -> `#[test] fn f(){}`
70+
// - `tfn` -> `#[test] fn feature(){}`
7171
// - `tmod` ->
7272
// ```rust
7373
// #[cfg(test)]
7474
// mod tests {
7575
// use super::*;
7676
//
7777
// #[test]
78-
// fn test_fn() {}
78+
// fn test_name() {}
7979
// }
8080
// ```
8181

crates/ra_ide/src/completion/complete_keyword.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! FIXME: write short doc here
22
3-
use ra_syntax::ast;
3+
use ra_syntax::{ast, SyntaxKind};
44

55
use crate::completion::{
66
CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
@@ -37,6 +37,10 @@ pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
3737
}
3838

3939
pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
40+
if ctx.token.kind() == SyntaxKind::COMMENT {
41+
return;
42+
}
43+
4044
let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent;
4145
if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling {
4246
add_keyword(ctx, acc, "where", "where ");

crates/ra_ide/src/completion/presentation.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,4 +1516,54 @@ mod tests {
15161516
"###
15171517
);
15181518
}
1519+
1520+
#[test]
1521+
fn no_keyword_autocompletion_on_line_comments() {
1522+
assert_debug_snapshot!(
1523+
do_completion(
1524+
r"
1525+
fn test() {
1526+
let x = 2; // A comment<|>
1527+
}
1528+
",
1529+
CompletionKind::Keyword
1530+
),
1531+
@r###"
1532+
[]
1533+
"###
1534+
);
1535+
}
1536+
1537+
#[test]
1538+
fn no_keyword_autocompletion_on_multi_line_comments() {
1539+
assert_debug_snapshot!(
1540+
do_completion(
1541+
r"
1542+
/*
1543+
Some multi-line comment<|>
1544+
*/
1545+
",
1546+
CompletionKind::Keyword
1547+
),
1548+
@r###"
1549+
[]
1550+
"###
1551+
);
1552+
}
1553+
1554+
#[test]
1555+
fn no_keyword_autocompletion_on_doc_comments() {
1556+
assert_debug_snapshot!(
1557+
do_completion(
1558+
r"
1559+
/// Some doc comment
1560+
/// let test<|> = 1
1561+
",
1562+
CompletionKind::Keyword
1563+
),
1564+
@r###"
1565+
[]
1566+
"###
1567+
);
1568+
}
15191569
}

crates/ra_ide/src/ssr.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,18 @@ use ra_ssr::{MatchFinder, SsrError, SsrRule};
1010
// The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`.
1111
// A `$<name>` placeholder in the search pattern will match any AST node and `$<name>` will reference it in the replacement.
1212
// Within a macro call, a placeholder will match up until whatever token follows the placeholder.
13+
//
14+
// Placeholders may be given constraints by writing them as `${<name>:<constraint1>:<constraint2>...}`.
15+
//
16+
// Supported constraints:
17+
//
18+
// |===
19+
// | Constraint | Restricts placeholder
20+
//
21+
// | kind(literal) | Is a literal (e.g. `42` or `"forty two"`)
22+
// | not(a) | Negates the constraint `a`
23+
// |===
24+
//
1325
// Available via the command `rust-analyzer.ssr`.
1426
//
1527
// ```rust

crates/ra_ssr/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ ra_db = { path = "../ra_db" }
1717
ra_ide_db = { path = "../ra_ide_db" }
1818
hir = { path = "../ra_hir", package = "ra_hir" }
1919
rustc-hash = "1.1.0"
20+
test_utils = { path = "../test_utils" }

crates/ra_ssr/src/matching.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//! process of matching, placeholder values are recorded.
33
44
use crate::{
5-
parsing::{Placeholder, SsrTemplate},
5+
parsing::{Constraint, NodeKind, Placeholder, SsrTemplate},
66
SsrMatches, SsrPattern, SsrRule,
77
};
88
use hir::Semantics;
@@ -11,6 +11,7 @@ use ra_syntax::ast::{AstNode, AstToken};
1111
use ra_syntax::{ast, SyntaxElement, SyntaxElementChildren, SyntaxKind, SyntaxNode, SyntaxToken};
1212
use rustc_hash::FxHashMap;
1313
use std::{cell::Cell, iter::Peekable};
14+
use test_utils::mark;
1415

1516
// Creates a match error. If we're currently attempting to match some code that we thought we were
1617
// going to match, as indicated by the --debug-snippet flag, then populate the reason field.
@@ -169,6 +170,9 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
169170
if let Some(placeholder) =
170171
match_inputs.get_placeholder(&SyntaxElement::Node(pattern.clone()))
171172
{
173+
for constraint in &placeholder.constraints {
174+
self.check_constraint(constraint, code)?;
175+
}
172176
if self.match_out.is_none() {
173177
return Ok(());
174178
}
@@ -292,6 +296,24 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
292296
Ok(())
293297
}
294298

299+
fn check_constraint(
300+
&self,
301+
constraint: &Constraint,
302+
code: &SyntaxNode,
303+
) -> Result<(), MatchFailed> {
304+
match constraint {
305+
Constraint::Kind(kind) => {
306+
kind.matches(code)?;
307+
}
308+
Constraint::Not(sub) => {
309+
if self.check_constraint(&*sub, code).is_ok() {
310+
fail_match!("Constraint {:?} failed for '{}'", constraint, code.text());
311+
}
312+
}
313+
}
314+
Ok(())
315+
}
316+
295317
/// We want to allow the records to match in any order, so we have special matching logic for
296318
/// them.
297319
fn attempt_match_record_field_list(
@@ -515,6 +537,21 @@ impl SsrPattern {
515537
}
516538
}
517539

540+
impl NodeKind {
541+
fn matches(&self, node: &SyntaxNode) -> Result<(), MatchFailed> {
542+
let ok = match self {
543+
Self::Literal => {
544+
mark::hit!(literal_constraint);
545+
ast::Literal::can_cast(node.kind())
546+
}
547+
};
548+
if !ok {
549+
fail_match!("Code '{}' isn't of kind {:?}", node.text(), self);
550+
}
551+
Ok(())
552+
}
553+
}
554+
518555
// If `node` contains nothing but an ident then return it, otherwise return None.
519556
fn only_ident(element: SyntaxElement) -> Option<SyntaxToken> {
520557
match element {

crates/ra_ssr/src/parsing.rs

Lines changed: 95 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
//! e.g. expressions, type references etc.
77
88
use crate::{SsrError, SsrPattern, SsrRule};
9-
use ra_syntax::{ast, AstNode, SmolStr, SyntaxKind};
9+
use ra_syntax::{ast, AstNode, SmolStr, SyntaxKind, T};
1010
use rustc_hash::{FxHashMap, FxHashSet};
1111
use std::str::FromStr;
1212

@@ -39,6 +39,18 @@ pub(crate) struct Placeholder {
3939
pub(crate) ident: SmolStr,
4040
/// A unique name used in place of this placeholder when we parse the pattern as Rust code.
4141
stand_in_name: String,
42+
pub(crate) constraints: Vec<Constraint>,
43+
}
44+
45+
#[derive(Clone, Debug, PartialEq, Eq)]
46+
pub(crate) enum Constraint {
47+
Kind(NodeKind),
48+
Not(Box<Constraint>),
49+
}
50+
51+
#[derive(Clone, Debug, PartialEq, Eq)]
52+
pub(crate) enum NodeKind {
53+
Literal,
4254
}
4355

4456
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -149,7 +161,7 @@ fn parse_pattern(pattern_str: &str) -> Result<Vec<PatternElement>, SsrError> {
149161
let mut placeholder_names = FxHashSet::default();
150162
let mut tokens = tokenize(pattern_str)?.into_iter();
151163
while let Some(token) = tokens.next() {
152-
if token.kind == SyntaxKind::DOLLAR {
164+
if token.kind == T![$] {
153165
let placeholder = parse_placeholder(&mut tokens)?;
154166
if !placeholder_names.insert(placeholder.ident.clone()) {
155167
bail!("Name `{}` repeats more than once", placeholder.ident);
@@ -177,6 +189,9 @@ fn validate_rule(rule: &SsrRule) -> Result<(), SsrError> {
177189
if !defined_placeholders.contains(&placeholder.ident) {
178190
undefined.push(format!("${}", placeholder.ident));
179191
}
192+
if !placeholder.constraints.is_empty() {
193+
bail!("Replacement placeholders cannot have constraints");
194+
}
180195
}
181196
}
182197
if !undefined.is_empty() {
@@ -205,23 +220,90 @@ fn tokenize(source: &str) -> Result<Vec<Token>, SsrError> {
205220

206221
fn parse_placeholder(tokens: &mut std::vec::IntoIter<Token>) -> Result<Placeholder, SsrError> {
207222
let mut name = None;
223+
let mut constraints = Vec::new();
208224
if let Some(token) = tokens.next() {
209225
match token.kind {
210226
SyntaxKind::IDENT => {
211227
name = Some(token.text);
212228
}
229+
T!['{'] => {
230+
let token =
231+
tokens.next().ok_or_else(|| SsrError::new("Unexpected end of placeholder"))?;
232+
if token.kind == SyntaxKind::IDENT {
233+
name = Some(token.text);
234+
}
235+
loop {
236+
let token = tokens
237+
.next()
238+
.ok_or_else(|| SsrError::new("Placeholder is missing closing brace '}'"))?;
239+
match token.kind {
240+
T![:] => {
241+
constraints.push(parse_constraint(tokens)?);
242+
}
243+
T!['}'] => break,
244+
_ => bail!("Unexpected token while parsing placeholder: '{}'", token.text),
245+
}
246+
}
247+
}
213248
_ => {
214-
bail!("Placeholders should be $name");
249+
bail!("Placeholders should either be $name or ${name:constraints}");
215250
}
216251
}
217252
}
218253
let name = name.ok_or_else(|| SsrError::new("Placeholder ($) with no name"))?;
219-
Ok(Placeholder::new(name))
254+
Ok(Placeholder::new(name, constraints))
255+
}
256+
257+
fn parse_constraint(tokens: &mut std::vec::IntoIter<Token>) -> Result<Constraint, SsrError> {
258+
let constraint_type = tokens
259+
.next()
260+
.ok_or_else(|| SsrError::new("Found end of placeholder while looking for a constraint"))?
261+
.text
262+
.to_string();
263+
match constraint_type.as_str() {
264+
"kind" => {
265+
expect_token(tokens, "(")?;
266+
let t = tokens.next().ok_or_else(|| {
267+
SsrError::new("Unexpected end of constraint while looking for kind")
268+
})?;
269+
if t.kind != SyntaxKind::IDENT {
270+
bail!("Expected ident, found {:?} while parsing kind constraint", t.kind);
271+
}
272+
expect_token(tokens, ")")?;
273+
Ok(Constraint::Kind(NodeKind::from(&t.text)?))
274+
}
275+
"not" => {
276+
expect_token(tokens, "(")?;
277+
let sub = parse_constraint(tokens)?;
278+
expect_token(tokens, ")")?;
279+
Ok(Constraint::Not(Box::new(sub)))
280+
}
281+
x => bail!("Unsupported constraint type '{}'", x),
282+
}
283+
}
284+
285+
fn expect_token(tokens: &mut std::vec::IntoIter<Token>, expected: &str) -> Result<(), SsrError> {
286+
if let Some(t) = tokens.next() {
287+
if t.text == expected {
288+
return Ok(());
289+
}
290+
bail!("Expected {} found {}", expected, t.text);
291+
}
292+
bail!("Expected {} found end of stream");
293+
}
294+
295+
impl NodeKind {
296+
fn from(name: &SmolStr) -> Result<NodeKind, SsrError> {
297+
Ok(match name.as_str() {
298+
"literal" => NodeKind::Literal,
299+
_ => bail!("Unknown node kind '{}'", name),
300+
})
301+
}
220302
}
221303

222304
impl Placeholder {
223-
fn new(name: SmolStr) -> Self {
224-
Self { stand_in_name: format!("__placeholder_{}", name), ident: name }
305+
fn new(name: SmolStr, constraints: Vec<Constraint>) -> Self {
306+
Self { stand_in_name: format!("__placeholder_{}", name), constraints, ident: name }
225307
}
226308
}
227309

@@ -241,31 +323,31 @@ mod tests {
241323
PatternElement::Token(Token { kind, text: SmolStr::new(text) })
242324
}
243325
fn placeholder(name: &str) -> PatternElement {
244-
PatternElement::Placeholder(Placeholder::new(SmolStr::new(name)))
326+
PatternElement::Placeholder(Placeholder::new(SmolStr::new(name), Vec::new()))
245327
}
246328
let result: SsrRule = "foo($a, $b) ==>> bar($b, $a)".parse().unwrap();
247329
assert_eq!(
248330
result.pattern.raw.tokens,
249331
vec![
250332
token(SyntaxKind::IDENT, "foo"),
251-
token(SyntaxKind::L_PAREN, "("),
333+
token(T!['('], "("),
252334
placeholder("a"),
253-
token(SyntaxKind::COMMA, ","),
335+
token(T![,], ","),
254336
token(SyntaxKind::WHITESPACE, " "),
255337
placeholder("b"),
256-
token(SyntaxKind::R_PAREN, ")"),
338+
token(T![')'], ")"),
257339
]
258340
);
259341
assert_eq!(
260342
result.template.tokens,
261343
vec![
262344
token(SyntaxKind::IDENT, "bar"),
263-
token(SyntaxKind::L_PAREN, "("),
345+
token(T!['('], "("),
264346
placeholder("b"),
265-
token(SyntaxKind::COMMA, ","),
347+
token(T![,], ","),
266348
token(SyntaxKind::WHITESPACE, " "),
267349
placeholder("a"),
268-
token(SyntaxKind::R_PAREN, ")"),
350+
token(T![')'], ")"),
269351
]
270352
);
271353
}

0 commit comments

Comments
 (0)