Skip to content

Commit 7d4ed34

Browse files
authored
fix(ls): code completion suggestions that don't make too much sense (VirusTotal#549)
Fix issues with code completion making suggestions that doesn't make sense, like keywords after a `.` character.
1 parent 6ab30fa commit 7d4ed34

9 files changed

Lines changed: 71 additions & 9 deletions

ls/src/features/completion.rs

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use async_lsp::lsp_types::{
2-
CompletionItem, CompletionItemKind, CompletionItemLabelDetails,
3-
InsertTextFormat, InsertTextMode, Position,
2+
CompletionContext, CompletionItem, CompletionItemKind,
3+
CompletionItemLabelDetails, CompletionTriggerKind, InsertTextFormat,
4+
InsertTextMode, Position,
45
};
56
use itertools::Itertools;
67

@@ -76,6 +77,7 @@ const CONDITION_SUGGESTIONS: [(&str, Option<&str>); 16] = [
7677
pub fn completion(
7778
document: &Document,
7879
pos: Position,
80+
context: Option<CompletionContext>,
7981
) -> Option<Vec<CompletionItem>> {
8082
let cst = &document.cst;
8183
// Get the token before cursor. There might be no token at cursor when the
@@ -84,13 +86,29 @@ pub fn completion(
8486
.and_then(|token| token.prev_token())
8587
.or_else(|| cst.root().last_token())?;
8688

89+
// Trigger characters are: `.`, `!`, `$`, `@`, `#`.
90+
let is_trigger_character = context.is_some_and(|ctx| {
91+
ctx.trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER
92+
});
93+
8794
// If the token is a direct child of `SOURCE_FILE`, return top-level suggestions.
88-
if non_error_parent(&token)?.kind() == SyntaxKind::SOURCE_FILE {
95+
if !is_trigger_character
96+
&& non_error_parent(&token)?.kind() == SyntaxKind::SOURCE_FILE
97+
{
8998
return Some(source_file_suggestions());
9099
}
91100

92101
let prev_token = prev_non_trivia_token(&token)?;
93102

103+
if prev_token.ancestors().any(|n| n.kind() == SyntaxKind::CONDITION_BLK) {
104+
return condition_suggestions(cst, token);
105+
}
106+
107+
// Trigger characters are recognized in the condition block only.
108+
if is_trigger_character {
109+
return Some(vec![]);
110+
}
111+
94112
if prev_token.kind() == SyntaxKind::IMPORT_KW {
95113
#[cfg(feature = "full-compiler")]
96114
return Some(import_suggestions());
@@ -104,10 +122,6 @@ pub fn completion(
104122
return Some(pattern_modifier_suggestions(pattern_def));
105123
}
106124

107-
if prev_token.ancestors().any(|n| n.kind() == SyntaxKind::CONDITION_BLK) {
108-
return condition_suggestions(cst, token);
109-
}
110-
111125
if prev_token.ancestors().any(|n| n.kind() == SyntaxKind::RULE_DECL) {
112126
return Some(rule_suggestions());
113127
}
@@ -192,6 +206,8 @@ fn condition_suggestions(
192206
}
193207
}
194208
}
209+
// Do not propose keywords for condition block after a dot.
210+
SyntaxKind::DOT => {}
195211
_ => {
196212
CONDITION_SUGGESTIONS.iter().for_each(|(kw, insert)| {
197213
result.push(CompletionItem {

ls/src/server.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,13 +290,14 @@ impl LanguageServer for YARALanguageServer {
290290
{
291291
let uri = params.text_document_position.text_document.uri;
292292
let position = params.text_document_position.position;
293+
let context = params.context;
293294
let document = match self.documents.get(&uri) {
294295
Some(entry) => entry,
295296
None => return Box::pin(async { Ok(None) }),
296297
};
297298

298-
let completions =
299-
completion(document, position).map(CompletionResponse::Array);
299+
let completions = completion(document, position, context)
300+
.map(CompletionResponse::Array);
300301

301302
Box::pin(async move { Ok(completions) })
302303
}

ls/src/tests/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,12 @@ async fn completion() {
267267

268268
#[cfg(all(feature = "full-compiler", not(feature = "magic-module")))]
269269
test_lsp_request::<_, Completion>("completion11.yar").await;
270+
271+
#[cfg(all(feature = "full-compiler", not(feature = "magic-module")))]
272+
test_lsp_request::<_, Completion>("completion12.yar").await;
273+
274+
#[cfg(all(feature = "full-compiler", not(feature = "magic-module")))]
275+
test_lsp_request::<_, Completion>("completion13.yar").await;
270276
}
271277

272278
#[tokio::test]
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"textDocument": {
3+
"uri": "file:///completion12.yar"
4+
},
5+
"position": {
6+
"line": 2,
7+
"character": 15
8+
},
9+
"context": {
10+
"triggerKind": 2,
11+
"triggerCharacter": "."
12+
}
13+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[]
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
rule test {
2+
strings:
3+
$a = "foo".
4+
condition:
5+
$a
6+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"textDocument": {
3+
"uri": "file:///completion13.yar"
4+
},
5+
"position": {
6+
"line": 3,
7+
"character": 24
8+
},
9+
"context": {
10+
"triggerKind": 2,
11+
"triggerCharacter": "."
12+
}
13+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import "pe"
2+
rule test {
3+
condition:
4+
pe.data_directories.
5+
}

0 commit comments

Comments
 (0)