Skip to content

Commit 8f9299c

Browse files
committed
[CodeCompletion] Add assignment to experimental operator completion
When the LHS is an lvalue/assignable tuple and there is no leading sequence of binary expressions. It's a bit hacky right now since we don't have a good way to differentiate general pattern completions from builtin operators. rdar://problem/23209683
1 parent d88d505 commit 8f9299c

File tree

11 files changed

+79
-8
lines changed

11 files changed

+79
-8
lines changed

include/swift/IDE/CodeCompletion.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ class CodeCompletionStringChunk {
104104
ExclamationMark,
105105
QuestionMark,
106106
Ampersand,
107+
Equal,
107108
Whitespace,
108109

109110
/// The first chunk of a substring that describes the parameter for a
@@ -193,6 +194,7 @@ class CodeCompletionStringChunk {
193194
Kind == ChunkKind::ExclamationMark ||
194195
Kind == ChunkKind::QuestionMark ||
195196
Kind == ChunkKind::Ampersand ||
197+
Kind == ChunkKind::Equal ||
196198
Kind == ChunkKind::Whitespace ||
197199
Kind == ChunkKind::CallParameterName ||
198200
Kind == ChunkKind::CallParameterInternalName ||

lib/IDE/CodeCompletion.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ void CodeCompletionString::print(raw_ostream &OS) const {
374374
case Chunk::ChunkKind::ExclamationMark:
375375
case Chunk::ChunkKind::QuestionMark:
376376
case Chunk::ChunkKind::Ampersand:
377+
case Chunk::ChunkKind::Equal:
377378
case Chunk::ChunkKind::Whitespace:
378379
AnnotatedTextChunk = C.isAnnotation();
379380
SWIFT_FALLTHROUGH;
@@ -951,6 +952,7 @@ Optional<unsigned> CodeCompletionString::getFirstTextChunkIndex(
951952
case CodeCompletionString::Chunk::ChunkKind::GenericParameterName:
952953
case CodeCompletionString::Chunk::ChunkKind::LeftParen:
953954
case CodeCompletionString::Chunk::ChunkKind::LeftBracket:
955+
case CodeCompletionString::Chunk::ChunkKind::Equal:
954956
case CodeCompletionString::Chunk::ChunkKind::DeclAttrParamKeyword:
955957
case CodeCompletionString::Chunk::ChunkKind::DeclAttrKeyword:
956958
return i;
@@ -2919,6 +2921,22 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
29192921
addPostfixOperatorCompletion(op, *T);
29202922
}
29212923

2924+
void addAssignmentOperator(Type RHSType, Type resultType) {
2925+
CodeCompletionResultBuilder builder(
2926+
Sink, CodeCompletionResult::ResultKind::Pattern,
2927+
SemanticContextKind::None, {});
2928+
2929+
if (HaveLeadingSpace)
2930+
builder.addAnnotatedWhitespace(" ");
2931+
else
2932+
builder.addWhitespace(" ");
2933+
builder.addEqual();
2934+
builder.addWhitespace(" ");
2935+
assert(RHSType && resultType);
2936+
builder.addCallParameter(Identifier(), Identifier(), RHSType, false, true);
2937+
addTypeAnnotation(builder, resultType);
2938+
}
2939+
29222940
void addInfixOperatorCompletion(OperatorDecl *op, Type resultType,
29232941
Type RHSType) {
29242942
// FIXME: we should get the semantic context of the function, not the
@@ -3067,6 +3085,12 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
30673085
}
30683086
}
30693087

3088+
if (leadingSequence.empty() && LHS->getType() &&
3089+
LHS->getType()->isAssignableType()) {
3090+
addAssignmentOperator(LHS->getType()->getRValueType(),
3091+
CurrDeclContext->getASTContext().TheEmptyTupleType);
3092+
}
3093+
30703094
// FIXME: unify this with the ?.member completions.
30713095
if (auto T = LHS->getType()->getRValueType()->getOptionalObjectType())
30723096
addPostfixBang(T);

lib/IDE/CodeCompletionResultBuilder.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ class CodeCompletionResultBuilder {
230230
CodeCompletionString::Chunk::ChunkKind::QuestionMark, "?");
231231
}
232232

233+
void addEqual() {
234+
addChunkWithTextNoCopy(CodeCompletionString::Chunk::ChunkKind::Equal, "=");
235+
}
236+
233237
void addDeclAttrParamKeyword(StringRef Name, StringRef Annotation,
234238
bool NeedSpecify) {
235239
addChunkWithText(CodeCompletionString::Chunk::ChunkKind::

lib/IDE/REPLCodeCompletion.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ static std::string toInsertableString(CodeCompletionResult *Result) {
5454
case CodeCompletionString::Chunk::ChunkKind::ExclamationMark:
5555
case CodeCompletionString::Chunk::ChunkKind::QuestionMark:
5656
case CodeCompletionString::Chunk::ChunkKind::Ampersand:
57+
case CodeCompletionString::Chunk::ChunkKind::Equal:
5758
case CodeCompletionString::Chunk::ChunkKind::Whitespace:
5859
case CodeCompletionString::Chunk::ChunkKind::DynamicLookupMethodCallTail:
5960
case CodeCompletionString::Chunk::ChunkKind::OptionalMethodCallTail:

test/IDE/complete_at_top_level.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ fooObject#^TYPE_CHECKED_EXPR_1^#
9292
// TYPE_CHECKED_EXPR_1: Begin completions
9393
// TYPE_CHECKED_EXPR_1-NEXT: Decl[InstanceVar]/CurrNominal: .instanceVar[#Int#]{{; name=.+$}}
9494
// TYPE_CHECKED_EXPR_1-NEXT: Decl[InstanceMethod]/CurrNominal: .instanceFunc({#(a): Int#})[#Void#]{{; name=.+$}}
95+
// TYPE_CHECKED_EXPR_1-NEXT: Pattern/None: = {#FooStruct#}[#Void#];
9596
// TYPE_CHECKED_EXPR_1-NEXT: End completions
9697

9798
func resyncParser2() {}
@@ -103,6 +104,7 @@ fooObject#^TYPE_CHECKED_EXPR_2^#
103104
// TYPE_CHECKED_EXPR_2: Begin completions
104105
// TYPE_CHECKED_EXPR_2-NEXT: Decl[InstanceVar]/CurrNominal: .instanceVar[#Int#]{{; name=.+$}}
105106
// TYPE_CHECKED_EXPR_2-NEXT: Decl[InstanceMethod]/CurrNominal: .instanceFunc({#(a): Int#})[#Void#]{{; name=.+$}}
107+
// TYPE_CHECKED_EXPR_2-NEXT: Pattern/None: = {#FooStruct#}[#Void#];
106108
// TYPE_CHECKED_EXPR_2-NEXT: End completions
107109

108110
func resyncParser3() {}
@@ -111,6 +113,7 @@ fooObject#^TYPE_CHECKED_EXPR_3^#.bar
111113
// TYPE_CHECKED_EXPR_3: Begin completions
112114
// TYPE_CHECKED_EXPR_3-NEXT: Decl[InstanceVar]/CurrNominal: .instanceVar[#Int#]{{; name=.+$}}
113115
// TYPE_CHECKED_EXPR_3-NEXT: Decl[InstanceMethod]/CurrNominal: .instanceFunc({#(a): Int#})[#Void#]{{; name=.+$}}
116+
// TYPE_CHECKED_EXPR_3-NEXT: Pattern/None: = {#FooStruct#}[#Void#];
114117
// TYPE_CHECKED_EXPR_3-NEXT: End completions
115118

116119
func resyncParser4() {}

test/IDE/complete_expr_tuple.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func testTupleNoDot1() {
2727
var t = (1, 2.0)
2828
t#^TUPLE_NO_DOT_1^#
2929
}
30-
// TUPLE_NO_DOT_1: Begin completions, 8 items
30+
// TUPLE_NO_DOT_1: Begin completions, 9 items
3131
// TUPLE_NO_DOT_1-DAG: Pattern/CurrNominal: .0[#Int#]{{; name=.+$}}
3232
// TUPLE_NO_DOT_1-DAG: Pattern/CurrNominal: .1[#Double#]{{; name=.+$}}
3333
// TUPLE_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: == {#(Int, Double)#}[#Bool#]{{; name=.+$}}
@@ -36,13 +36,14 @@ func testTupleNoDot1() {
3636
// TUPLE_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: < {#(Int, Double)#}[#Bool#]{{; name=.+$}}
3737
// TUPLE_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: != {#(Int, Double)#}[#Bool#]{{; name=.+$}}
3838
// TUPLE_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: > {#(Int, Double)#}[#Bool#]{{; name=.+$}}
39+
// TUPLE_NO_DOT_1-DAG: Pattern/None: = {#(Int, Double)#}[#Void#]{{; name=.+$}}
3940
// TUPLE_NO_DOT_1-NEXT: End completions
4041

4142
func testTupleNoDot2() {
4243
var t = (foo: 1, bar: 2.0)
4344
t#^TUPLE_NO_DOT_2^#
4445
}
45-
// TUPLE_NO_DOT_2: Begin completions, 8 items
46+
// TUPLE_NO_DOT_2: Begin completions, 9 items
4647
// TUPLE_NO_DOT_2-DAG: Pattern/CurrNominal: .foo[#Int#]{{; name=.+$}}
4748
// TUPLE_NO_DOT_2-DAG: Pattern/CurrNominal: .bar[#Double#]{{; name=.+$}}
4849
// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: == {#(Int, Double)#}[#Bool#]{{; name=.+$}}
@@ -51,13 +52,14 @@ func testTupleNoDot2() {
5152
// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: < {#(Int, Double)#}[#Bool#]{{; name=.+$}}
5253
// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: != {#(Int, Double)#}[#Bool#]{{; name=.+$}}
5354
// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: > {#(Int, Double)#}[#Bool#]{{; name=.+$}}
55+
// TUPLE_NO_DOT_2-DAG: Pattern/None: = {#(foo: Int, bar: Double)#}[#Void#]{{; name=.+$}}
5456
// TUPLE_NO_DOT_2-NEXT: End completions
5557

5658
func testTupleNoDot3() {
5759
var t = (foo: 1, 2.0)
5860
t#^TUPLE_NO_DOT_3^#
5961
}
60-
// TUPLE_NO_DOT_3: Begin completions, 8 items
62+
// TUPLE_NO_DOT_3: Begin completions, 9 items
6163
// TUPLE_NO_DOT_3-DAG: Pattern/CurrNominal: .foo[#Int#]{{; name=.+$}}
6264
// TUPLE_NO_DOT_3-DAG: Pattern/CurrNominal: .1[#Double#]{{; name=.+$}}
6365
// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: == {#(Int, Double)#}[#Bool#]{{; name=.+$}}
@@ -66,6 +68,7 @@ func testTupleNoDot3() {
6668
// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: < {#(Int, Double)#}[#Bool#]{{; name=.+$}}
6769
// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: != {#(Int, Double)#}[#Bool#]{{; name=.+$}}
6870
// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: > {#(Int, Double)#}[#Bool#]{{; name=.+$}}
71+
// TUPLE_NO_DOT_3-DAG: Pattern/None: = {#(foo: Int, Double)#}[#Void#]{{; name=.+$}}
6972
// TUPLE_NO_DOT_3-NEXT: End completions
7073

7174
func testTupleDot1() {

test/IDE/complete_operators.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
// RUN: FileCheck %s -check-prefix=S4_EXT_INFIX_NEG < %t.ext_infix_2
3939
// RUN: %target-swift-ide-test -code-completion -source-filename=%s -code-completion-token=EXT_INFIX_3 | FileCheck %s -check-prefix=S4_EXT_INFIX_SIMPLE
4040
// RUN: %target-swift-ide-test -code-completion -source-filename=%s -code-completion-token=EXT_INFIX_4 | FileCheck %s -check-prefix=S4_EXT_INFIX_SIMPLE
41+
// RUN: %target-swift-ide-test -code-completion -source-filename=%s -code-completion-token=ASSIGN_TUPLE_1| FileCheck %s -check-prefix=ASSIGN_TUPLE_1
42+
// RUN: %target-swift-ide-test -code-completion -source-filename=%s -code-completion-token=ASSIGN_TUPLE_2| FileCheck %s -check-prefix=ASSIGN_TUPLE_2
4143

4244
struct S {}
4345
postfix operator ++ {}
@@ -146,6 +148,7 @@ func testInfix1(x: S2) {
146148
// S2_INFIX-DAG-NOT: ??
147149
// S2_INFIX-DAG-NOT: ~=
148150
// S2_INFIX-DAG-NOT: ~>
151+
// S2_INFIX-DAG-NOT: = {#
149152
// S2_INFIX: End completions
150153

151154
func testInfix2(var x: S2) {
@@ -156,6 +159,7 @@ func testInfix2(var x: S2) {
156159
// S2_INFIX_LVALUE-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: + {#S2#}[#S2#]
157160
// S2_INFIX_LVALUE-DAG: Decl[InfixOperatorFunction]/CurrModule: ** {#Int#}[#S2#]
158161
// S2_INFIX_LVALUE-DAG: Decl[InfixOperatorFunction]/CurrModule: **= {#Int#}[#Void#]
162+
// S2_INFIX_LVALUE-DAG: Pattern/None: = {#S2#}[#Void#]
159163
// S2_INFIX_LVALUE-DAG-NOT: +=
160164
// S2_INFIX_LVALUE-DAG-NOT: *
161165
// S2_INFIX_LVALUE-DAG-NOT: ??
@@ -320,3 +324,15 @@ func testExtInfix3(x: S4) {
320324
func testExtInfix4(x: S4) {
321325
1 + 1.0 + x#^EXT_INFIX_4^#
322326
}
327+
328+
func testAssignTuple1() {
329+
()#^ASSIGN_TUPLE_1^#
330+
}
331+
// ASSIGN_TUPLE_1: Pattern/None: = {#()#}[#Void#];
332+
333+
func testAssignTuple2() {
334+
var x: S2
335+
var y: S2
336+
(x, y)#^ASSIGN_TUPLE_2^#
337+
}
338+
// ASSIGN_TUPLE_2: Pattern/None: = {#(S2, S2)#}[#Void#];

test/IDE/complete_value_expr.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ var fooObject: FooStruct
405405
// FOO_OBJECT_NO_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: .selectorStringFunc3({#(a): Int#}, {#b: (Float, Double)#})[#String#]{{; name=.+$}}
406406
// FOO_OBJECT_NO_DOT-NEXT: Decl[InstanceVar]/CurrNominal: .extProp[#Int#]{{; name=.+$}}
407407
// FOO_OBJECT_NO_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: .extFunc0()[#Void#]{{; name=.+$}}
408+
// FOO_OBJECT_NO_DOT-NEXT: Pattern/None: = {#Foo
408409
// FOO_OBJECT_NO_DOT-NEXT: End completions
409410

410411
// FOO_STRUCT_DOT: Begin completions
@@ -750,9 +751,9 @@ protocol BazProtocol {
750751

751752
typealias BarBazProtocolComposition = protocol<BarProtocol, BazProtocol>
752753

753-
var fooProtocolInstance: FooProtocol = FooProtocolImpl()
754-
var fooBarProtocolInstance: protocol<FooProtocol, BarProtocol>
755-
var fooExBarExProtocolInstance: protocol<FooExProtocol, BarExProtocol>
754+
let fooProtocolInstance: FooProtocol = FooProtocolImpl()
755+
let fooBarProtocolInstance: protocol<FooProtocol, BarProtocol>
756+
let fooExBarExProtocolInstance: protocol<FooExProtocol, BarExProtocol>
756757

757758
typealias FooTypealias = Int
758759

@@ -920,11 +921,13 @@ func testFuncTypeVars() {
920921
funcTypeVarsObject.funcVar1#^VF1^#
921922
// VF1: Begin completions
922923
// VF1-NEXT: Pattern/ExprSpecific: ()[#Double#]{{; name=.+$}}
924+
// VF1-NEXT: Pattern/None: = {#() -> Double##() -> Double#}[#Void#]
923925
// VF1-NEXT: End completions
924926

925927
funcTypeVarsObject.funcVar2#^VF2^#
926928
// VF2: Begin completions
927929
// VF2-NEXT: Pattern/ExprSpecific: ({#a: Int#})[#Double#]{{; name=.+$}}
930+
// VF2-NEXT: Pattern/None: = {#(a: Int) -> Double##(a: Int) -> Double#}[#Void#]
928931
// VF2-NEXT: End completions
929932
}
930933

test/SourceKit/CodeComplete/complete_operators.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ func test1(var x: MyInt) {
1818
// CHECK: !=
1919
// CHECK: +
2020
// CHECK: ++
21+
// CHECK: =
2122

2223
func test2(var x: MyInt) {
2324
#^INT_OPERATORS_INNER,x^#
@@ -26,6 +27,7 @@ func test2(var x: MyInt) {
2627
// INNER: x+
2728
// INNER: x++
2829
// INNER: xxxx
30+
// INNER: x=
2931
// INNER: x.bigPowers
3032

3133
// RAW: {

tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,9 +1026,14 @@ void CompletionBuilder::getFilterName(CodeCompletionString *str,
10261026
if (FirstTextChunk.hasValue()) {
10271027
for (auto C : str->getChunks().slice(*FirstTextChunk)) {
10281028

1029-
if (C.getKind() == ChunkKind::BraceStmtWithCursor)
1029+
if (C.is(ChunkKind::BraceStmtWithCursor))
10301030
break;
10311031

1032+
if (C.is(ChunkKind::Equal)) {
1033+
OS << C.getText();
1034+
break;
1035+
}
1036+
10321037
bool shouldPrint = !C.isAnnotation();
10331038
switch (C.getKind()) {
10341039
case ChunkKind::TypeAnnotation:
@@ -1064,8 +1069,13 @@ void CompletionBuilder::getDescription(SwiftResult *result, raw_ostream &OS,
10641069
if (FirstTextChunk.hasValue()) {
10651070
for (auto C : str->getChunks().slice(*FirstTextChunk)) {
10661071
using ChunkKind = CodeCompletionString::Chunk::ChunkKind;
1067-
if (C.getKind() == ChunkKind::BraceStmtWithCursor)
1072+
if (C.is(ChunkKind::BraceStmtWithCursor))
10681073
break;
1074+
1075+
// FIXME: we need a more uniform way to handle operator completions.
1076+
if (C.is(ChunkKind::Equal))
1077+
isOperator = true;
1078+
10691079
if (C.getKind() == ChunkKind::TypeAnnotation ||
10701080
C.getKind() == ChunkKind::CallParameterClosureType ||
10711081
C.getKind() == ChunkKind::Whitespace)

tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,9 @@ static void getResultStructure(
263263
C.is(ChunkKind::CallParameterBegin))
264264
break;
265265

266+
if (C.is(ChunkKind::Equal))
267+
isOperator = true;
268+
266269
if (C.hasText())
267270
textSize += C.getText().size();
268271
}

0 commit comments

Comments
 (0)