@@ -14,9 +14,12 @@ import dotty.tools.dotc.ast.tpd.*
14
14
import dotty .tools .dotc .core .Constants .Constant
15
15
import dotty .tools .dotc .core .Contexts .Context
16
16
import dotty .tools .dotc .core .Phases
17
- import dotty .tools .dotc .core .StdNames
17
+ import dotty .tools .dotc .core .StdNames .nme
18
+ import dotty .tools .dotc .core .Flags
18
19
import dotty .tools .dotc .interactive .Interactive
20
+ import dotty .tools .dotc .interactive .Completion
19
21
import dotty .tools .dotc .interactive .InteractiveDriver
22
+ import dotty .tools .dotc .parsing .Tokens
20
23
import dotty .tools .dotc .util .SourceFile
21
24
import dotty .tools .pc .AutoImports .AutoImportEdits
22
25
import dotty .tools .pc .AutoImports .AutoImportsGenerator
@@ -45,23 +48,31 @@ class CompletionProvider(
45
48
val uri = params.uri().nn
46
49
val text = params.text().nn
47
50
48
- val code = applyCompletionCursor(params)
51
+ val (wasCursorApplied, code) = applyCompletionCursor(params)
49
52
val sourceFile = SourceFile .virtual(uri, code)
50
53
driver.run(uri, sourceFile)
51
54
52
- val ctx = driver.currentCtx
55
+ given ctx : Context = driver.currentCtx
53
56
val pos = driver.sourcePosition(params)
54
57
val (items, isIncomplete) = driver.compilationUnits.get(uri) match
55
58
case Some (unit) =>
56
-
57
59
val newctx = ctx.fresh.setCompilationUnit(unit).withPhase(Phases .typerPhase(using ctx))
58
- val tpdPath = Interactive .pathTo(newctx.compilationUnit.tpdTree, pos.span)(using newctx)
59
- val adjustedPath = Interactive .resolveTypedOrUntypedPath(tpdPath, pos)(using newctx)
60
+ val tpdPath0 = Interactive .pathTo(unit.tpdTree, pos.span)(using newctx)
61
+ val adjustedPath = Interactive .resolveTypedOrUntypedPath(tpdPath0, pos)(using newctx)
62
+
63
+ val tpdPath = tpdPath0 match
64
+ // $1$ // FIXME add check for a $1$ name to make sure we only do the below in lifting case
65
+ case Select (qual, name) :: tail if qual.symbol.is(Flags .Synthetic ) =>
66
+ qual.symbol.defTree match
67
+ case valdef : ValDef => Select (valdef.rhs, name) :: tail
68
+ case _ => tpdPath0
69
+ case _ => tpdPath0
70
+
60
71
61
72
val locatedCtx = Interactive .contextOfPath(tpdPath)(using newctx)
62
73
val indexedCtx = IndexedContext (locatedCtx)
63
74
64
- val completionPos = CompletionPos .infer(pos, params, adjustedPath)(using locatedCtx)
75
+ val completionPos = CompletionPos .infer(pos, params, adjustedPath, wasCursorApplied )(using locatedCtx)
65
76
66
77
val autoImportsGen = AutoImports .generator(
67
78
completionPos.toSourcePosition,
@@ -111,6 +122,10 @@ class CompletionProvider(
111
122
)
112
123
end completions
113
124
125
+ val allKeywords =
126
+ val softKeywords = Tokens .softModifierNames + nme.as + nme.derives + nme.extension + nme.throws + nme.using
127
+ Tokens .keywords.toList.map(Tokens .tokenString) ++ softKeywords.map(_.toString)
128
+
114
129
/**
115
130
* In case if completion comes from empty line like:
116
131
* {{{
@@ -123,23 +138,30 @@ class CompletionProvider(
123
138
* Otherwise, completion poisition doesn't point at any tree
124
139
* because scala parser trim end position to the last statement pos.
125
140
*/
126
- private def applyCompletionCursor (params : OffsetParams ): String =
141
+ private def applyCompletionCursor (params : OffsetParams ): ( Boolean , String ) =
127
142
val text = params.text().nn
128
143
val offset = params.offset().nn
144
+ val query = Completion .naiveCompletionPrefix(text, offset)
129
145
130
- val isStartMultilineComment =
131
- val i = params.offset()
132
- i >= 3 && (text.charAt(i - 1 ) match
133
- case '*' =>
134
- text.charAt(i - 2 ) == '*' &&
135
- text.charAt(i - 3 ) == '/'
136
- case _ => false
137
- )
138
- if isStartMultilineComment then
139
- // Insert potentially missing `*/` to avoid comment out all codes after the "/**".
140
- text.substring(0 , offset).nn + Cursor .value + " */" + text.substring(offset)
146
+ if offset > 0 && text.charAt(offset - 1 ).isUnicodeIdentifierPart && ! allKeywords.contains(query) then
147
+ false -> text
141
148
else
142
- text.substring(0 , offset).nn + Cursor .value + text.substring(offset)
149
+ val isStartMultilineComment =
150
+
151
+ val i = params.offset()
152
+ i >= 3 && (text.charAt(i - 1 ) match
153
+ case '*' =>
154
+ text.charAt(i - 2 ) == '*' &&
155
+ text.charAt(i - 3 ) == '/'
156
+ case _ => false
157
+ )
158
+ true -> (
159
+ if isStartMultilineComment then
160
+ // Insert potentially missing `*/` to avoid comment out all codes after the "/**".
161
+ text.substring(0 , offset).nn + Cursor .value + " */" + text.substring(offset)
162
+ else
163
+ text.substring(0 , offset).nn + Cursor .value + text.substring(offset)
164
+ )
143
165
end applyCompletionCursor
144
166
145
167
private def completionItems (
@@ -172,7 +194,7 @@ class CompletionProvider(
172
194
Select (Apply (Select (Select (_, name), _), _), _),
173
195
_
174
196
) :: _ =>
175
- name == StdNames . nme.StringContext
197
+ name == nme.StringContext
176
198
// "My name is $name"
177
199
case Literal (Constant (_ : String )) :: _ =>
178
200
true
0 commit comments