Skip to content

Commit 2e89396

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 (cherry picked from commit 1e01898)
1 parent 6176d9d commit 2e89396

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
@@ -813,6 +813,12 @@ static void addStmtKeywords(CodeCompletionResultSink &Sink, DeclContext *DC,
813813
auto AddStmtKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind) {
814814
if (!MaybeFuncBody && Kind == CodeCompletionKeywordKind::kw_return)
815815
return;
816+
817+
// 'in' keyword is added in 'addClosureSignatureKeywordsIfApplicable' if
818+
// needed.
819+
if (Kind == CodeCompletionKeywordKind::kw_in)
820+
return;
821+
816822
addKeyword(Sink, Name, Kind, "", flair);
817823
};
818824
#define STMT_KEYWORD(kw) AddStmtKeyword(#kw, CodeCompletionKeywordKind::kw_##kw);
@@ -893,6 +899,18 @@ static void addAnyTypeKeyword(CodeCompletionResultSink &Sink, Type T) {
893899
Builder.addTypeAnnotation(T, PrintOptions());
894900
}
895901

902+
static void
903+
addClosureSignatureKeywordsIfApplicable(CodeCompletionResultSink &Sink,
904+
DeclContext *DC) {
905+
ClosureExpr *closure = dyn_cast<ClosureExpr>(DC);
906+
if (!closure)
907+
return;
908+
if (closure->getInLoc().isValid())
909+
return;
910+
911+
addKeyword(Sink, "in", CodeCompletionKeywordKind::kw_in);
912+
}
913+
896914
void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
897915
bool MaybeFuncBody) {
898916
switch (Kind) {
@@ -947,6 +965,8 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
947965
addDeclKeywords(Sink, CurDeclContext,
948966
Context.LangOpts.EnableExperimentalConcurrency);
949967
addStmtKeywords(Sink, CurDeclContext, MaybeFuncBody);
968+
addClosureSignatureKeywordsIfApplicable(Sink, CurDeclContext);
969+
950970
LLVM_FALLTHROUGH;
951971
case CompletionKind::ReturnStmtExpr:
952972
case CompletionKind::YieldStmtExpr:
@@ -971,6 +991,11 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
971991
break;
972992

973993
case CompletionKind::PostfixExpr:
994+
// Suggest 'in' for '{ value <HERE>'.
995+
if (HasSpace)
996+
addClosureSignatureKeywordsIfApplicable(Sink, CurDeclContext);
997+
998+
break;
974999
case CompletionKind::CaseStmtBeginning:
9751000
case CompletionKind::TypeIdentifierWithDot:
9761001
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: convenience{{; name=.+$}}
@@ -157,7 +160,6 @@
157160
// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[do]/None: do{{; name=.+$}}
158161
// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[else]/None: else{{; name=.+$}}
159162
// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[for]/None: for{{; name=.+$}}
160-
// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[in]/None: in{{; name=.+$}}
161163
// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[while]/None: while{{; name=.+$}}
162164
// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[break]/None: break{{; name=.+$}}
163165
// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[continue]/None: continue{{; name=.+$}}
@@ -230,7 +232,6 @@
230232
// KW_DECL_STMT-DAG: Keyword[do]/None: do{{; name=.+$}}
231233
// KW_DECL_STMT-DAG: Keyword[else]/None: else{{; name=.+$}}
232234
// KW_DECL_STMT-DAG: Keyword[for]/None: for{{; name=.+$}}
233-
// KW_DECL_STMT-DAG: Keyword[in]/None: in{{; name=.+$}}
234235
// KW_DECL_STMT-DAG: Keyword[while]/None: while{{; name=.+$}}
235236
// KW_DECL_STMT-DAG: Keyword[break]/None: break{{; name=.+$}}
236237
// KW_DECL_STMT-DAG: Keyword[continue]/None: continue{{; name=.+$}}
@@ -302,15 +303,15 @@
302303
// KW_EXPR_NEG-NOT: Keyword{{.*}}break
303304
// KW_EXPR_NEG: End completions
304305

305-
#^TOP_LEVEL_1?check=KW_DECL_STMT_TOPLEVEL;check=KW_NO_RETURN^#
306+
#^TOP_LEVEL_1?check=KW_DECL_STMT_TOPLEVEL;check=KW_NO_RETURN;check=KW_NO_IN^#
306307

307308
for _ in 1...10 {
308-
#^TOP_LEVEL_2?check=KW_DECL_STMT;check=KW_NO_RETURN^#
309+
#^TOP_LEVEL_2?check=KW_DECL_STMT;check=KW_NO_RETURN;check=KW_NO_IN^#
309310
}
310311

311-
if true {} #^TOP_LEVEL_AFTER_IF_1?check=KW_DECL_STMT_TOPLEVEL;check=KW_NO_RETURN^#
312+
if true {} #^TOP_LEVEL_AFTER_IF_1?check=KW_DECL_STMT_TOPLEVEL;check=KW_NO_RETURN;check=KW_NO_IN^#
312313
if true {}
313-
#^TOP_LEVEL_AFTER_IF_2?check=KW_DECL_STMT_TOPLEVEL;check=KW_NO_RETURN^#
314+
#^TOP_LEVEL_AFTER_IF_2?check=KW_DECL_STMT_TOPLEVEL;check=KW_NO_RETURN;check=KW_NO_IN^#
314315

315316

316317
if true {} else #^TOP_LEVEL_AFTER_IF_ELSE_1?check=AFTER_IF_ELSE^# {}
@@ -319,60 +320,60 @@ if true {} else #^TOP_LEVEL_AFTER_IF_ELSE_1?check=AFTER_IF_ELSE^# {}
319320
// AFTER_IF_ELSE: Keyword[if]/None: if;
320321

321322
func testAfterIf1() {
322-
if true {} #^AFTER_IF_1?check=KW_DECL_STMT;check=KW_RETURN^#
323+
if true {} #^AFTER_IF_1?check=KW_DECL_STMT;check=KW_RETURN;check=KW_NO_IN^#
323324
}
324325
func testAfterIfElse1() {
325326
if true {} else #^AFTER_IF_ELSE_1?check=AFTER_IF_ELSE^# {}
326327
}
327328

328329
func testInFuncBody1() {
329-
#^IN_FUNC_BODY_1?check=KW_DECL_STMT;check=KW_RETURN^#
330+
#^IN_FUNC_BODY_1?check=KW_DECL_STMT;check=KW_RETURN;check=KW_NO_IN^#
330331
}
331332

332333
struct InStructFunc {
333334
func testInFuncBody2() {
334-
#^IN_FUNC_BODY_2?check=KW_DECL_STMT;check=KW_RETURN^#
335+
#^IN_FUNC_BODY_2?check=KW_DECL_STMT;check=KW_RETURN;check=KW_NO_IN^#
335336
}
336337
}
337338

338339
enum InEnumFunc {
339340
func testInFuncBody3() {
340-
#^IN_FUNC_BODY_3?check=KW_DECL_STMT;check=KW_RETURN^#
341+
#^IN_FUNC_BODY_3?check=KW_DECL_STMT;check=KW_RETURN;check=KW_NO_IN^#
341342
}
342343
}
343344

344345
class InClassFunc {
345346
func testInFuncBody4() {
346-
#^IN_FUNC_BODY_4?check=KW_DECL_STMT;check=KW_RETURN^#
347+
#^IN_FUNC_BODY_4?check=KW_DECL_STMT;check=KW_RETURN;check=KW_NO_IN^#
347348
}
348349
}
349350

350351
class InClassFunc {
351352
class Nested {
352353
func testInFuncBody5() {
353-
#^IN_FUNC_BODY_5?check=KW_DECL_STMT;check=KW_RETURN^#
354+
#^IN_FUNC_BODY_5?check=KW_DECL_STMT;check=KW_RETURN;check=KW_NO_IN^#
354355
}
355356
}
356357
}
357358

358359
func testInClosure1() {
359-
{ #^IN_CLOSURE_1?check=KW_DECL_STMT;check=KW_RETURN^# }
360+
{ #^IN_CLOSURE_1?check=KW_DECL_STMT;check=KW_RETURN;check=KW_IN^# }
360361
}
361362
func testInClosure2() {
362-
{ #^IN_CLOSURE_2?check=KW_DECL_STMT;check=KW_RETURN^#
363+
{ #^IN_CLOSURE_2?check=KW_DECL_STMT;check=KW_RETURN;check=KW_IN^#
363364
}
364365
struct InVarClosureInit {
365-
let x = { #^IN_CLOSURE_3?check=KW_DECL_STMT;check=KW_RETURN^# }()
366+
let x = { #^IN_CLOSURE_3?check=KW_DECL_STMT;check=KW_RETURN;check=KW_IN^# }()
366367
}
367368

368-
{ #^IN_CLOSURE_4?check=KW_DECL_STMT;check=KW_RETURN^# }
369+
{ #^IN_CLOSURE_4?check=KW_DECL_STMT;check=KW_RETURN;check=KW_IN^# }
369370

370371
struct InSubscript {
371-
subscript(x: Int) -> Int { #^IN_SUBSCRIPT_1?check=KW_DECL_STMT;check=KW_RETURN^# }
372+
subscript(x: Int) -> Int { #^IN_SUBSCRIPT_1?check=KW_DECL_STMT;check=KW_RETURN;check=KW_NO_IN^# }
372373
}
373374

374375
struct InInit {
375-
init?() { #^IN_INIT_1?check=KW_DECL_STMT;check=KW_RETURN^# }
376+
init?() { #^IN_INIT_1?check=KW_DECL_STMT;check=KW_RETURN;check=KW_NO_IN^# }
376377
}
377378

378379
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)