Skip to content

Commit 1e01898

Browse files
committed
[CodeCompletion] Suggest 'in' after expression in closure
func test(value: [Int]) { value.map { value <HERE> } } In this case 'value' in the closure is ambiguous between an expression referring the outer function parameter, or a parameter declaration in the closure. Previously, code completion only considered the former and suggest the members of '[Int]', but not 'in' keyword. As a result, when the user actually want to type 'in' here, they needed to hit 'esc' to cancel the code completion. In this change, suggest 'in' keyword even without a newline, as long as the current decl context is a closure and it doesn't have 'in' in it. Also previously 'in' was suggested even outside the closure and even it already had the explict 'in'. This PR limit suggesting 'in' inside closures without explicit 'in'. rdar://80489548
1 parent 88b81de commit 1e01898

File tree

4 files changed

+70
-20
lines changed

4 files changed

+70
-20
lines changed

lib/IDE/CodeCompletion.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,12 @@ static void addStmtKeywords(CodeCompletionResultSink &Sink, DeclContext *DC,
816816
auto AddStmtKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind) {
817817
if (!MaybeFuncBody && Kind == CodeCompletionKeywordKind::kw_return)
818818
return;
819+
820+
// 'in' keyword is added in 'addClosureSignatureKeywordsIfApplicable' if
821+
// needed.
822+
if (Kind == CodeCompletionKeywordKind::kw_in)
823+
return;
824+
819825
addKeyword(Sink, Name, Kind, "", flair);
820826
};
821827
#define STMT_KEYWORD(kw) AddStmtKeyword(#kw, CodeCompletionKeywordKind::kw_##kw);
@@ -896,6 +902,18 @@ static void addAnyTypeKeyword(CodeCompletionResultSink &Sink, Type T) {
896902
Builder.addTypeAnnotation(T, PrintOptions());
897903
}
898904

905+
static void
906+
addClosureSignatureKeywordsIfApplicable(CodeCompletionResultSink &Sink,
907+
DeclContext *DC) {
908+
ClosureExpr *closure = dyn_cast<ClosureExpr>(DC);
909+
if (!closure)
910+
return;
911+
if (closure->getInLoc().isValid())
912+
return;
913+
914+
addKeyword(Sink, "in", CodeCompletionKeywordKind::kw_in);
915+
}
916+
899917
void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
900918
bool MaybeFuncBody) {
901919
switch (Kind) {
@@ -950,6 +968,8 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
950968
addDeclKeywords(Sink, CurDeclContext,
951969
Context.LangOpts.EnableExperimentalConcurrency);
952970
addStmtKeywords(Sink, CurDeclContext, MaybeFuncBody);
971+
addClosureSignatureKeywordsIfApplicable(Sink, CurDeclContext);
972+
953973
LLVM_FALLTHROUGH;
954974
case CompletionKind::ReturnStmtExpr:
955975
case CompletionKind::YieldStmtExpr:
@@ -974,6 +994,11 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
974994
break;
975995

976996
case CompletionKind::PostfixExpr:
997+
// Suggest 'in' for '{ value <HERE>'.
998+
if (HasSpace)
999+
addClosureSignatureKeywordsIfApplicable(Sink, CurDeclContext);
1000+
1001+
break;
9771002
case CompletionKind::CaseStmtBeginning:
9781003
case CompletionKind::TypeIdentifierWithDot:
9791004
case CompletionKind::TypeIdentifierWithoutDot:

test/IDE/complete_at_top_level_library.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ protocol MyProtocol {}
5656
// LIBRARY-DAG: Keyword[repeat]/None/Flair[ExprAtFileScope]: repeat; name=repeat
5757
// LIBRARY-DAG: Keyword[else]/None/Flair[ExprAtFileScope]: else; name=else
5858
// LIBRARY-DAG: Keyword[for]/None/Flair[ExprAtFileScope]: for; name=for
59-
// LIBRARY-DAG: Keyword[in]/None/Flair[ExprAtFileScope]: in; name=in
6059
// LIBRARY-DAG: Keyword[while]/None/Flair[ExprAtFileScope]: while; name=while
6160
// LIBRARY-DAG: Keyword[break]/None/Flair[ExprAtFileScope]: break; name=break
6261
// LIBRARY-DAG: Keyword[continue]/None/Flair[ExprAtFileScope]: continue; name=continue
@@ -133,7 +132,6 @@ protocol MyProtocol {}
133132
// SCRIPT-DAG: Keyword[repeat]/None: repeat; name=repeat
134133
// SCRIPT-DAG: Keyword[else]/None: else; name=else
135134
// SCRIPT-DAG: Keyword[for]/None: for; name=for
136-
// SCRIPT-DAG: Keyword[in]/None: in; name=in
137135
// SCRIPT-DAG: Keyword[while]/None: while; name=while
138136
// SCRIPT-DAG: Keyword[break]/None: break; name=break
139137
// SCRIPT-DAG: Keyword[continue]/None: continue; name=continue

test/IDE/complete_keywords.swift

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
// KW_RETURN: Keyword[return]/None: return{{; name=.+$}}
55
// KW_NO_RETURN-NOT: Keyword[return]
66

7+
// KW_IN: Keyword[in]/None: in{{; name=.+$}}
8+
// KW_NO_IN-NOT: Keyword[in]
9+
710
// KW_DECL: Begin completions
811
// KW_DECL-DAG: Keyword[class]/None: class{{; name=.+$}}
912
// KW_DECL-DAG: Keyword/None: actor{{; name=.+$}}
@@ -161,7 +164,6 @@
161164
// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[do]/None: do{{; name=.+$}}
162165
// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[else]/None: else{{; name=.+$}}
163166
// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[for]/None: for{{; name=.+$}}
164-
// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[in]/None: in{{; name=.+$}}
165167
// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[while]/None: while{{; name=.+$}}
166168
// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[break]/None: break{{; name=.+$}}
167169
// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[continue]/None: continue{{; name=.+$}}
@@ -234,7 +236,6 @@
234236
// KW_DECL_STMT-DAG: Keyword[do]/None: do{{; name=.+$}}
235237
// KW_DECL_STMT-DAG: Keyword[else]/None: else{{; name=.+$}}
236238
// KW_DECL_STMT-DAG: Keyword[for]/None: for{{; name=.+$}}
237-
// KW_DECL_STMT-DAG: Keyword[in]/None: in{{; name=.+$}}
238239
// KW_DECL_STMT-DAG: Keyword[while]/None: while{{; name=.+$}}
239240
// KW_DECL_STMT-DAG: Keyword[break]/None: break{{; name=.+$}}
240241
// KW_DECL_STMT-DAG: Keyword[continue]/None: continue{{; name=.+$}}
@@ -306,15 +307,15 @@
306307
// KW_EXPR_NEG-NOT: Keyword{{.*}}break
307308
// KW_EXPR_NEG: End completions
308309

309-
#^TOP_LEVEL_1?check=KW_DECL_STMT_TOPLEVEL;check=KW_NO_RETURN^#
310+
#^TOP_LEVEL_1?check=KW_DECL_STMT_TOPLEVEL;check=KW_NO_RETURN;check=KW_NO_IN^#
310311

311312
for _ in 1...10 {
312-
#^TOP_LEVEL_2?check=KW_DECL_STMT;check=KW_NO_RETURN^#
313+
#^TOP_LEVEL_2?check=KW_DECL_STMT;check=KW_NO_RETURN;check=KW_NO_IN^#
313314
}
314315

315-
if true {} #^TOP_LEVEL_AFTER_IF_1?check=KW_DECL_STMT_TOPLEVEL;check=KW_NO_RETURN^#
316+
if true {} #^TOP_LEVEL_AFTER_IF_1?check=KW_DECL_STMT_TOPLEVEL;check=KW_NO_RETURN;check=KW_NO_IN^#
316317
if true {}
317-
#^TOP_LEVEL_AFTER_IF_2?check=KW_DECL_STMT_TOPLEVEL;check=KW_NO_RETURN^#
318+
#^TOP_LEVEL_AFTER_IF_2?check=KW_DECL_STMT_TOPLEVEL;check=KW_NO_RETURN;check=KW_NO_IN^#
318319

319320

320321
if true {} else #^TOP_LEVEL_AFTER_IF_ELSE_1?check=AFTER_IF_ELSE^# {}
@@ -323,60 +324,60 @@ if true {} else #^TOP_LEVEL_AFTER_IF_ELSE_1?check=AFTER_IF_ELSE^# {}
323324
// AFTER_IF_ELSE: Keyword[if]/None: if;
324325

325326
func testAfterIf1() {
326-
if true {} #^AFTER_IF_1?check=KW_DECL_STMT;check=KW_RETURN^#
327+
if true {} #^AFTER_IF_1?check=KW_DECL_STMT;check=KW_RETURN;check=KW_NO_IN^#
327328
}
328329
func testAfterIfElse1() {
329330
if true {} else #^AFTER_IF_ELSE_1?check=AFTER_IF_ELSE^# {}
330331
}
331332

332333
func testInFuncBody1() {
333-
#^IN_FUNC_BODY_1?check=KW_DECL_STMT;check=KW_RETURN^#
334+
#^IN_FUNC_BODY_1?check=KW_DECL_STMT;check=KW_RETURN;check=KW_NO_IN^#
334335
}
335336

336337
struct InStructFunc {
337338
func testInFuncBody2() {
338-
#^IN_FUNC_BODY_2?check=KW_DECL_STMT;check=KW_RETURN^#
339+
#^IN_FUNC_BODY_2?check=KW_DECL_STMT;check=KW_RETURN;check=KW_NO_IN^#
339340
}
340341
}
341342

342343
enum InEnumFunc {
343344
func testInFuncBody3() {
344-
#^IN_FUNC_BODY_3?check=KW_DECL_STMT;check=KW_RETURN^#
345+
#^IN_FUNC_BODY_3?check=KW_DECL_STMT;check=KW_RETURN;check=KW_NO_IN^#
345346
}
346347
}
347348

348349
class InClassFunc {
349350
func testInFuncBody4() {
350-
#^IN_FUNC_BODY_4?check=KW_DECL_STMT;check=KW_RETURN^#
351+
#^IN_FUNC_BODY_4?check=KW_DECL_STMT;check=KW_RETURN;check=KW_NO_IN^#
351352
}
352353
}
353354

354355
class InClassFunc {
355356
class Nested {
356357
func testInFuncBody5() {
357-
#^IN_FUNC_BODY_5?check=KW_DECL_STMT;check=KW_RETURN^#
358+
#^IN_FUNC_BODY_5?check=KW_DECL_STMT;check=KW_RETURN;check=KW_NO_IN^#
358359
}
359360
}
360361
}
361362

362363
func testInClosure1() {
363-
{ #^IN_CLOSURE_1?check=KW_DECL_STMT;check=KW_RETURN^# }
364+
{ #^IN_CLOSURE_1?check=KW_DECL_STMT;check=KW_RETURN;check=KW_IN^# }
364365
}
365366
func testInClosure2() {
366-
{ #^IN_CLOSURE_2?check=KW_DECL_STMT;check=KW_RETURN^#
367+
{ #^IN_CLOSURE_2?check=KW_DECL_STMT;check=KW_RETURN;check=KW_IN^#
367368
}
368369
struct InVarClosureInit {
369-
let x = { #^IN_CLOSURE_3?check=KW_DECL_STMT;check=KW_RETURN^# }()
370+
let x = { #^IN_CLOSURE_3?check=KW_DECL_STMT;check=KW_RETURN;check=KW_IN^# }()
370371
}
371372

372-
{ #^IN_CLOSURE_4?check=KW_DECL_STMT;check=KW_RETURN^# }
373+
{ #^IN_CLOSURE_4?check=KW_DECL_STMT;check=KW_RETURN;check=KW_IN^# }
373374

374375
struct InSubscript {
375-
subscript(x: Int) -> Int { #^IN_SUBSCRIPT_1?check=KW_DECL_STMT;check=KW_RETURN^# }
376+
subscript(x: Int) -> Int { #^IN_SUBSCRIPT_1?check=KW_DECL_STMT;check=KW_RETURN;check=KW_NO_IN^# }
376377
}
377378

378379
struct InInit {
379-
init?() { #^IN_INIT_1?check=KW_DECL_STMT;check=KW_RETURN^# }
380+
init?() { #^IN_INIT_1?check=KW_DECL_STMT;check=KW_RETURN;check=KW_NO_IN^# }
380381
}
381382

382383
struct InStruct {

test/IDE/complete_rdar80489548.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: %empty-directory(%t.ccp)
2+
// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t
3+
4+
// KW_IN: Keyword[in]/None: in{{; name=.+$}}
5+
// KW_NO_IN-NOT: Keyword[in]
6+
7+
func test(value: [Int]) {
8+
value.map { #^NOIN_IMMEDIATE?check=KW_IN^# }
9+
10+
value.map { value#^NOIN_AFTER_EXPR_NOSPCACE?check=KW_NO_IN^# }
11+
value.map { value #^NOIN_AFTER_EXPR?check=KW_IN^# }
12+
value.map { value
13+
#^NOIN_NEWLINE?check=KW_IN^#
14+
}
15+
16+
value.map { value in #^IN_AFTER_IN?check=KW_NO_IN^# }
17+
value.map { value in
18+
#^IN_NEWLINE?check=KW_NO_IN^#
19+
}
20+
21+
#^FUNCBODY_STMT?check=KW_NO_IN^#
22+
value #^FUNCBODY_POSTFIX?check=KW_NO_IN^#
23+
}
24+
25+
#^GLOBAL_STMT?check=KW_NO_IN^#
26+
value #^GLOBAL_POSTFIX?check=KW_NO_IN^#

0 commit comments

Comments
 (0)