Skip to content

Commit a69923b

Browse files
committed
Unify completion pos usage, fix presentation compiler crash in interpolator
1 parent 95266f2 commit a69923b

File tree

11 files changed

+101
-86
lines changed

11 files changed

+101
-86
lines changed

presentation-compiler/src/main/dotty/tools/pc/AutoImportsProvider.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ final class AutoImportsProvider(
6767
val results = symbols.result.filter(isExactMatch(_, name))
6868

6969
if results.nonEmpty then
70-
val correctedPos = CompletionPos.infer(pos, params, path).sourcePos
70+
val correctedPos = CompletionPos.infer(pos, params, path).toSourcePosition
7171
val mkEdit =
7272
path match
7373
// if we are in import section just specify full name

presentation-compiler/src/main/dotty/tools/pc/completions/AmmoniteIvyCompletions.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ object AmmoniteIvyCompletions:
1313
completionPos: CompletionPos,
1414
text: String
1515
)(using Context): List[CompletionValue] =
16-
val pos = completionPos.sourcePos
16+
val pos = completionPos.originalCursorPosition
1717
val query = selector.collectFirst {
1818
case sel: ImportSelector
1919
if sel.sourcePos.encloses(pos) && sel.sourcePos.`end` > pos.`end` =>

presentation-compiler/src/main/dotty/tools/pc/completions/CompletionPos.scala

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,52 +6,54 @@ import java.net.URI
66
import scala.meta.pc.OffsetParams
77

88
import dotty.tools.dotc.ast.untpd.*
9-
import dotty.tools.dotc.ast.untpd.ImportSelector
109
import dotty.tools.dotc.core.Contexts.*
11-
import dotty.tools.dotc.core.StdNames.*
12-
import dotty.tools.dotc.util.Chars
1310
import dotty.tools.dotc.util.SourcePosition
14-
import dotty.tools.dotc.util.Spans
11+
import dotty.tools.dotc.util.Spans.*
1512
import dotty.tools.dotc.interactive.Completion
1613
import dotty.tools.pc.utils.MtagsEnrichments.*
1714

1815
import org.eclipse.lsp4j as l
19-
import scala.annotation.tailrec
16+
17+
case object Cursor:
18+
val value = "CURSOR"
2019

2120
case class CompletionPos(
22-
start: Int,
23-
end: Int,
24-
query: String,
25-
cursorPos: SourcePosition,
26-
sourceUri: URI
21+
queryStart: Int,
22+
identEnd: Int,
23+
query: String,
24+
originalCursorPosition: SourcePosition,
25+
sourceUri: URI
2726
):
28-
29-
def sourcePos: SourcePosition = cursorPos.withSpan(Spans.Span(start, end))
30-
def stripSuffixEditRange: l.Range = new l.Range(cursorPos.offsetToPos(start), cursorPos.offsetToPos(end))
31-
def toEditRange: l.Range = cursorPos.withStart(start).withEnd(cursorPos.point).toLsp
32-
33-
end CompletionPos
27+
def queryEnd: Int = originalCursorPosition.point
28+
def point: Int = originalCursorPosition.point
29+
def stripSuffixEditRange: l.Range = new l.Range(originalCursorPosition.offsetToPos(queryStart), originalCursorPosition.offsetToPos(identEnd))
30+
def toEditRange: l.Range = originalCursorPosition.withStart(queryStart).withEnd(originalCursorPosition.point).toLsp
31+
def toSourcePosition: SourcePosition = originalCursorPosition.withSpan(Span(queryStart, queryEnd, point))
3432

3533
object CompletionPos:
3634

3735
def infer(
38-
cursorPos: SourcePosition,
36+
sourcePosition: SourcePosition,
3937
offsetParams: OffsetParams,
4038
adjustedPath: List[Tree]
4139
)(using Context): CompletionPos =
42-
infer(cursorPos, offsetParams.uri().nn, offsetParams.text().nn, adjustedPath)
40+
infer(sourcePosition, offsetParams.uri().nn, String(sourcePosition.source.content()), adjustedPath)
4341

4442
def infer(
45-
cursorPos: SourcePosition,
43+
sourcePos: SourcePosition,
4644
uri: URI,
4745
text: String,
4846
adjustedPath: List[Tree]
4947
)(using Context): CompletionPos =
50-
val identEnd = inferIdentEnd(cursorPos, text)
51-
val query = Completion.completionPrefix(adjustedPath, cursorPos)
52-
val start = cursorPos.point - query.length()
48+
val identEnd = adjustedPath match
49+
case (ident: Ident) :: _ if ident.toString.contains(Cursor.value) =>
50+
ident.span.end - Cursor.value.length
51+
case _ => sourcePos.end
5352

54-
CompletionPos(start, identEnd, query.nn, cursorPos, uri)
53+
val query = Completion.completionPrefix(adjustedPath, sourcePos)
54+
val start = sourcePos.end - query.length()
55+
56+
CompletionPos(start, identEnd, query.nn, sourcePos, uri)
5557
end infer
5658

5759
/**
@@ -76,12 +78,4 @@ object CompletionPos:
7678
(i, tabIndented)
7779
end inferIndent
7880

79-
/**
80-
* Returns the end offset of the identifier starting as the given offset position.
81-
*/
82-
private def inferIdentEnd(pos: SourcePosition, text: String): Int =
83-
var i = pos.point
84-
while i < text.length() && Chars.isIdentifierPart(text.charAt(i)) do i += 1
85-
i
86-
8781
end CompletionPos

presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,10 @@ class CompletionProvider(
6161
val locatedCtx = Interactive.contextOfPath(tpdPath)(using newctx)
6262
val indexedCtx = IndexedContext(locatedCtx)
6363

64-
val completionPos =
65-
CompletionPos.infer(pos, params, adjustedPath)(using locatedCtx)
64+
val completionPos = CompletionPos.infer(pos, params, adjustedPath)(using locatedCtx)
6665

6766
val autoImportsGen = AutoImports.generator(
68-
completionPos.sourcePos,
67+
completionPos.toSourcePosition,
6968
text,
7069
unit.tpdTree,
7170
unit.comments,
@@ -75,7 +74,6 @@ class CompletionProvider(
7574

7675
val (completions, searchResult) =
7776
new Completions(
78-
pos,
7977
text,
8078
locatedCtx,
8179
search,
@@ -120,7 +118,7 @@ class CompletionProvider(
120118
* val a = 1
121119
* @@
122120
* }}}
123-
* it's required to modify actual code by addition Ident.
121+
* it's required to modify actual code by additional Ident.
124122
*
125123
* Otherwise, completion poisition doesn't point at any tree
126124
* because scala parser trim end position to the last statement pos.
@@ -168,7 +166,7 @@ class CompletionProvider(
168166
additionalEdits: List[TextEdit] = Nil,
169167
range: Option[LspRange] = None
170168
): CompletionItem =
171-
val oldText = params.text().nn.substring(completionPos.start, completionPos.end)
169+
val oldText = String(params.text().nn.substring(completionPos.queryStart, completionPos.identEnd))
172170
val editRange = if newText.startsWith(oldText) then completionPos.stripSuffixEditRange
173171
else completionPos.toEditRange
174172

@@ -231,6 +229,7 @@ class CompletionProvider(
231229
case Some(edits) =>
232230
edits match
233231
case AutoImportEdits(Some(nameEdit), other) =>
232+
println(nameEdit)
234233
mkItem(nameEdit.getNewText().nn, other.toList, range = Some(nameEdit.getRange().nn))
235234
case _ =>
236235
mkItem(
@@ -279,6 +278,3 @@ class CompletionProvider(
279278
end match
280279
end completionItems
281280
end CompletionProvider
282-
283-
case object Cursor:
284-
val value = "CURSOR"

presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ import dotty.tools.dotc.core.Denotations.SingleDenotation
3737
import dotty.tools.dotc.interactive.Interactive
3838

3939
class Completions(
40-
pos: SourcePosition,
4140
text: String,
4241
ctx: Context,
4342
search: SymbolSearch,
@@ -56,7 +55,7 @@ class Completions(
5655
given context: Context = ctx
5756

5857
private lazy val coursierComplete = new CoursierComplete(BuildInfo.scalaVersion)
59-
private lazy val completionMode = Completion.completionMode(adjustedPath, pos)
58+
private lazy val completionMode = Completion.completionMode(adjustedPath, completionPos.originalCursorPosition)
6059

6160
private lazy val shouldAddSnippet =
6261
path match
@@ -83,8 +82,8 @@ class Completions(
8382
if !sym.name.endsWith(Cursor.value) then false
8483
else
8584
val realNameLength = sym.decodedName.length() - Cursor.value.length()
86-
sym.source == pos.source &&
87-
sym.span.start + realNameLength == pos.span.end
85+
sym.source == completionPos.originalCursorPosition.source &&
86+
sym.span.start + realNameLength == completionPos.point
8887

8988
val generalExclude =
9089
isUninterestingSymbol(sym) ||
@@ -107,7 +106,7 @@ class Completions(
107106
end includeSymbol
108107

109108
def completions(): (List[CompletionValue], SymbolSearch.Result) =
110-
val (advanced, exclusive) = advancedCompletions(path, pos, completionPos)
109+
val (advanced, exclusive) = advancedCompletions(path, completionPos.originalCursorPosition, completionPos)
111110
val (all, result) =
112111
if exclusive then (advanced, SymbolSearch.Result.COMPLETE)
113112
else
@@ -116,19 +115,19 @@ class Completions(
116115
val allAdvanced = advanced ++ keywords
117116
path match
118117
// should not show completions for toplevel
119-
case Nil | (_: PackageDef) :: _ if pos.source.file.extension != "sc" =>
118+
case Nil | (_: PackageDef) :: _ if completionPos.originalCursorPosition.source.file.extension != "sc" =>
120119
(allAdvanced, SymbolSearch.Result.COMPLETE)
121120
case Select(qual, _) :: _ if qual.typeOpt.isErroneous =>
122121
(allAdvanced, SymbolSearch.Result.COMPLETE)
123122
case Select(qual, _) :: _ =>
124-
val compilerCompletions = Completion.rawCompletions(pos, completionMode, completionPos.query, path, adjustedPath)
123+
val compilerCompletions = Completion.rawCompletions(completionPos.originalCursorPosition, completionMode, completionPos.query, path, adjustedPath)
125124
val (compiler, result) = compilerCompletions
126125
.toList
127126
.flatMap(toCompletionValues)
128127
.filterInteresting(qual.typeOpt.widenDealias)
129128
(allAdvanced ++ compiler, result)
130129
case _ =>
131-
val compilerCompletions = Completion.rawCompletions(pos, completionMode, completionPos.query, path, adjustedPath)
130+
val compilerCompletions = Completion.rawCompletions(completionPos.originalCursorPosition, completionMode, completionPos.query, path, adjustedPath)
132131
val (compiler, result) = compilerCompletions
133132
.toList
134133
.flatMap(toCompletionValues)
@@ -393,10 +392,10 @@ class Completions(
393392
val values = FilenameCompletions.contribute(filename, td)
394393
(values, true)
395394
case (lit @ Literal(Constant(_: String))) :: _ =>
395+
println("COMpletion")
396396
val completions = InterpolatorCompletions
397397
.contribute(
398398
text,
399-
pos,
400399
completionPos,
401400
indexedContext,
402401
lit,
@@ -651,7 +650,7 @@ class Completions(
651650

652651
private def isNotLocalForwardReference(sym: Symbol)(using Context): Boolean =
653652
!sym.isLocalToBlock ||
654-
!sym.srcPos.isAfter(pos) ||
653+
!sym.srcPos.isAfter(completionPos.originalCursorPosition) ||
655654
sym.is(Param)
656655

657656
private def computeRelevancePenalty(
@@ -670,7 +669,7 @@ class Completions(
670669
def symbolRelevance(sym: Symbol): Int =
671670
var relevance = 0
672671
// symbols defined in this file are more relevant
673-
if pos.source != sym.source || sym.is(Package) then
672+
if completionPos.originalCursorPosition.source != sym.source || sym.is(Package) then
674673
relevance |= IsNotDefinedInFile
675674

676675
// fields are more relevant than non fields (such as method)

presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import dotty.tools.dotc.core.Contexts.Context
1212
import dotty.tools.dotc.core.Flags
1313
import dotty.tools.dotc.core.Flags.*
1414
import dotty.tools.dotc.core.Symbols.Symbol
15+
import dotty.tools.dotc.util.Spans
1516
import dotty.tools.dotc.core.Types.Type
1617
import dotty.tools.dotc.util.SourcePosition
1718
import dotty.tools.pc.CompilerSearchVisitor
@@ -24,7 +25,6 @@ object InterpolatorCompletions:
2425

2526
def contribute(
2627
text: String,
27-
pos: SourcePosition,
2828
completionPos: CompletionPos,
2929
indexedContext: IndexedContext,
3030
lit: Literal,
@@ -35,12 +35,12 @@ object InterpolatorCompletions:
3535
config: PresentationCompilerConfig,
3636
buildTargetIdentifier: String
3737
)(using Context, ReportContext) =
38-
InterpolationSplice(pos.span.point, text.toCharArray().nn, text) match
38+
InterpolationSplice(completionPos.queryEnd, text.toCharArray().nn, text) match
3939
case Some(interpolator) =>
4040
InterpolatorCompletions.contributeScope(
4141
text,
4242
lit,
43-
pos,
43+
completionPos,
4444
interpolator,
4545
indexedContext,
4646
completions,
@@ -55,7 +55,6 @@ object InterpolatorCompletions:
5555
lit,
5656
path,
5757
text,
58-
pos,
5958
completionPos,
6059
completions,
6160
snippetsEnabled,
@@ -106,7 +105,6 @@ object InterpolatorCompletions:
106105
lit: Literal,
107106
path: List[Tree],
108107
text: String,
109-
cursor: SourcePosition,
110108
completionPos: CompletionPos,
111109
completions: Completions,
112110
areSnippetsSupported: Boolean,
@@ -166,7 +164,7 @@ object InterpolatorCompletions:
166164
label,
167165
Some(newText(name, suffix.toEditOpt, identOrSelect)),
168166
Nil,
169-
Some(cursor.withStart(identOrSelect.span.start).toLsp),
167+
Some(completionPos.originalCursorPosition.withStart(identOrSelect.span.start).toLsp),
170168
// Needed for VS Code which will not show the completion otherwise
171169
Some(identOrSelect.name.toString() + "." + label),
172170
denot.symbol,
@@ -219,7 +217,7 @@ object InterpolatorCompletions:
219217
private def contributeScope(
220218
text: String,
221219
lit: Literal,
222-
position: SourcePosition,
220+
completionPos: CompletionPos,
223221
interpolator: InterpolationSplice,
224222
indexedContext: IndexedContext,
225223
completions: Completions,
@@ -230,14 +228,10 @@ object InterpolatorCompletions:
230228
)(using ctx: Context, reportsContext: ReportContext): List[CompletionValue] =
231229
val litStartPos = lit.span.start
232230
val litEndPos = lit.span.end - Cursor.value.length()
233-
val span = position.span
234-
val nameStart =
235-
span.withStart(span.start - interpolator.name.size)
236-
val nameRange = position.withSpan(nameStart).toLsp
237-
val hasClosingBrace: Boolean = text.charAt(span.point) == '}'
238-
val hasOpeningBrace: Boolean = text.charAt(
239-
span.start - interpolator.name.size - 1
240-
) == '{'
231+
val nameStart = Spans.Span(completionPos.point - interpolator.name.size, completionPos.queryEnd)
232+
val nameRange = completionPos.originalCursorPosition.withSpan(nameStart).toLsp
233+
val hasClosingBrace: Boolean = text.charAt(completionPos.point) == '}'
234+
val hasOpeningBrace: Boolean = text.charAt(nameStart.start - 1) == '{'
241235

242236
def additionalEdits(): List[l.TextEdit] =
243237
val interpolatorEdit =

presentation-compiler/src/main/dotty/tools/pc/completions/KeywordsCompletions.scala

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ object KeywordsCompletions:
2121
comments: List[Comment]
2222
)(using ctx: Context): List[CompletionValue] =
2323
lazy val notInComment =
24-
checkIfNotInComment(completionPos.cursorPos, comments)
24+
checkIfNotInComment(completionPos.originalCursorPosition, comments)
2525

2626
path match
2727
case Nil | (_: PackageDef) :: _ if completionPos.query.isEmpty() =>
@@ -33,8 +33,7 @@ object KeywordsCompletions:
3333
case _ =>
3434
val isExpression = this.isExpression(path)
3535
val isBlock = this.isBlock(path)
36-
val isDefinition =
37-
this.isDefinition(path, completionPos.query, completionPos.cursorPos)
36+
val isDefinition = this.isDefinition(path, completionPos.query, completionPos.originalCursorPosition)
3837
val isMethodBody = this.isMethodBody(path)
3938
val isTemplate = this.isTemplate(path)
4039
val isPackage = this.isPackage(path)
@@ -191,10 +190,10 @@ object KeywordsCompletions:
191190
case untpdTree: untpd.Tree =>
192191
collectTypeAndModuleDefs(untpdTree, {
193192
case typeDef: (untpd.TypeDef | untpd.ModuleDef) =>
194-
typeDef.span.exists && typeDef.span.end < pos.sourcePos.span.start
193+
typeDef.span.exists && typeDef.span.end < pos.queryStart
195194
case _ => false
196195
})
197-
.filter(tree => tree.span.exists && tree.span.end < pos.start)
196+
.filter(tree => tree.span.exists && tree.span.end < pos.queryStart)
198197
.maxByOption(_.span.end)
199198
case _ => None
200199
}
@@ -208,7 +207,7 @@ object KeywordsCompletions:
208207
template.derived.isEmpty
209208
)
210209

211-
val untpdPath = NavigateAST.untypedPath(pos.cursorPos.span)
210+
val untpdPath = NavigateAST.untypedPath(pos.originalCursorPosition.span)
212211

213212
findLastSatisfyingTree(untpdPath).orElse { enclosing match
214213
case (typeDef: TypeDef) :: _ if typeDef.symbol.isEnumClass => untpdPath.headOption

presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -519,8 +519,8 @@ class MatchCaseExtractor(
519519
name.toString().replace(Cursor.value, "")
520520
) && (text
521521
.charAt(
522-
completionPos.start - 1
523-
) == ' ' || text.charAt(completionPos.start - 1) == '.') =>
522+
completionPos.queryStart - 1
523+
) == ' ' || text.charAt(completionPos.queryStart - 1) == '.') =>
524524
Some(qualifier)
525525
case _ => None
526526
end MatchExtractor

presentation-compiler/src/main/dotty/tools/pc/completions/ScalaCliCompletions.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class ScalaCliCompletions(
2626

2727
def contribute(dependency: String) =
2828
val completions = coursierComplete.complete(dependency)
29-
val (editStart, editEnd) = CoursierComplete.inferEditRange(pos.point, text)
29+
val (editStart, editEnd) = CoursierComplete.inferEditRange(pos.end, text)
3030
val editRange = pos.withStart(editStart).withEnd(editEnd).toLsp
3131
completions
3232
.map(insertText =>

0 commit comments

Comments
 (0)