@@ -16,17 +16,76 @@ import dotty.tools.dotc.core.StdNames
16
16
import dotty .tools .dotc .core .Symbols .Symbol
17
17
import dotty .tools .dotc .interactive .Interactive
18
18
import dotty .tools .dotc .interactive .InteractiveDriver
19
+ import dotty .tools .dotc .util .SourceFile
19
20
import dotty .tools .dotc .util .SourcePosition
20
21
import dotty .tools .pc .utils .InteractiveEnrichments .*
21
22
import dotty .tools .pc .IndexedContext .Result
22
23
23
24
import org .eclipse .lsp4j as l
24
25
25
- final class PcInlineValueProviderImpl (
26
+ final class PcInlineValueProvider (
26
27
driver : InteractiveDriver ,
27
28
val params : OffsetParams
28
- ) extends WithSymbolSearchCollector [Option [Occurence ]](driver, params)
29
- with InlineValueProvider :
29
+ ) extends WithSymbolSearchCollector [Option [Occurence ]](driver, params):
30
+
31
+ // We return a result or an error
32
+ def getInlineTextEdits (): Either [String , List [l.TextEdit ]] =
33
+ defAndRefs() match {
34
+ case Right ((defn, refs)) =>
35
+ val edits =
36
+ if (defn.shouldBeRemoved) {
37
+ val defEdit = definitionTextEdit(defn)
38
+ val refsEdits = refs.map(referenceTextEdit(defn))
39
+ defEdit :: refsEdits
40
+ } else refs.map(referenceTextEdit(defn))
41
+ Right (edits)
42
+ case Left (error) => Left (error)
43
+ }
44
+
45
+ private def referenceTextEdit (
46
+ definition : Definition
47
+ )(ref : Reference ): l.TextEdit =
48
+ if (definition.requiresBrackets && ref.requiresBrackets)
49
+ new l.TextEdit (
50
+ ref.range,
51
+ s """ ( ${ref.rhs}) """
52
+ )
53
+ else new l.TextEdit (ref.range, ref.rhs)
54
+
55
+ private def definitionTextEdit (definition : Definition ): l.TextEdit =
56
+ new l.TextEdit (
57
+ extend(
58
+ definition.rangeOffsets.start,
59
+ definition.rangeOffsets.end,
60
+ definition.range
61
+ ),
62
+ " "
63
+ )
64
+
65
+ private def extend (
66
+ startOffset : Int ,
67
+ endOffset : Int ,
68
+ range : l.Range
69
+ ): l.Range = {
70
+ val (startWithSpace, endWithSpace): (Int , Int ) =
71
+ extendRangeToIncludeWhiteCharsAndTheFollowingNewLine(
72
+ text
73
+ )(startOffset, endOffset)
74
+ val startPos = new l.Position (
75
+ range.getStart.getLine,
76
+ range.getStart.getCharacter - (startOffset - startWithSpace)
77
+ )
78
+ val endPos =
79
+ if (endWithSpace - 1 >= 0 && text(endWithSpace - 1 ) == '\n ' )
80
+ new l.Position (range.getEnd.getLine + 1 , 0 )
81
+ else
82
+ new l.Position (
83
+ range.getEnd.getLine,
84
+ range.getEnd.getCharacter + endWithSpace - endOffset
85
+ )
86
+
87
+ new l.Range (startPos, endPos)
88
+ }
30
89
31
90
val position : l.Position = pos.toLsp.getStart().nn
32
91
@@ -41,7 +100,7 @@ final class PcInlineValueProviderImpl(
41
100
Some (Occurence (tree, parent, adjustedPos))
42
101
case _ => None
43
102
44
- override def defAndRefs (): Either [String , (Definition , List [Reference ])] =
103
+ def defAndRefs (): Either [String , (Definition , List [Reference ])] =
45
104
val newctx = driver.currentCtx.fresh.setCompilationUnit(unit)
46
105
val allOccurences = result().flatten
47
106
for
@@ -60,7 +119,6 @@ final class PcInlineValueProviderImpl(
60
119
val defPos = definition.tree.sourcePos
61
120
val defEdit = Definition (
62
121
defPos.toLsp,
63
- adjustRhs(definition.tree.rhs.sourcePos),
64
122
RangeOffset (defPos.start, defPos.end),
65
123
definitionRequiresBrackets(definition.tree.rhs)(using newctx),
66
124
deleteDefinition
@@ -70,6 +128,18 @@ final class PcInlineValueProviderImpl(
70
128
end for
71
129
end defAndRefs
72
130
131
+ private def stripIndentPrefix (rhs : String , refIndent : String , defIndent : String ): String =
132
+ val rhsLines = rhs.split(" \n " ).toList
133
+ rhsLines match
134
+ case h :: Nil => rhs
135
+ case h :: t =>
136
+ val noPrefixH = h.stripPrefix(refIndent)
137
+ if noPrefixH.startsWith(" {" ) then
138
+ noPrefixH ++ t.map(refIndent ++ _.stripPrefix(defIndent)).mkString(" \n " ," \n " , " " )
139
+ else
140
+ ((" " ++ h) :: t).map(refIndent ++ _.stripPrefix(defIndent)).mkString(" \n " , " \n " , " " )
141
+ case Nil => rhs
142
+
73
143
private def definitionRequiresBrackets (tree : Tree )(using Context ): Boolean =
74
144
NavigateAST
75
145
.untypedPath(tree.span)
@@ -102,12 +172,12 @@ final class PcInlineValueProviderImpl(
102
172
103
173
end referenceRequiresBrackets
104
174
105
- private def adjustRhs (pos : SourcePosition ) =
175
+ private def extendWithSurroundingParens (pos : SourcePosition ) =
176
+ /** Move `point` by `step` as long as the character at `point` is `acceptedChar` */
106
177
def extend (point : Int , acceptedChar : Char , step : Int ): Int =
107
178
val newPoint = point + step
108
- if newPoint > 0 && newPoint < text.length && text(
109
- newPoint
110
- ) == acceptedChar
179
+ if newPoint > 0 && newPoint < text.length &&
180
+ text(newPoint) == acceptedChar
111
181
then extend(newPoint, acceptedChar, step)
112
182
else point
113
183
val adjustedStart = extend(pos.start, '(' , - 1 )
@@ -139,7 +209,7 @@ final class PcInlineValueProviderImpl(
139
209
.exists(e => e.isTerm)
140
210
def allreferences = allOccurences.filterNot(_.isDefn)
141
211
def inlineAll () =
142
- makeRefsEdits(allreferences, symbols).map((true , _))
212
+ makeRefsEdits(allreferences, symbols, definition ).map((true , _))
143
213
if definition.tree.sourcePos.toLsp.encloses(position)
144
214
then if defIsLocal then inlineAll() else Left (Errors .notLocal)
145
215
else
@@ -150,14 +220,28 @@ final class PcInlineValueProviderImpl(
150
220
ref <- list
151
221
.find(_.pos.toLsp.encloses(position))
152
222
.toRight(Errors .didNotFindReference)
153
- refEdits <- makeRefsEdits(List (ref), symbols)
223
+ refEdits <- makeRefsEdits(List (ref), symbols, definition )
154
224
yield (false , refEdits)
155
225
end if
156
226
end getReferencesToInline
157
227
228
+ extension (pos : SourcePosition )
229
+ def startColumnIndentPadding : String = {
230
+ val source = pos.source
231
+ val offset = pos.start
232
+ var idx = source.startOfLine(offset)
233
+ val pad = new StringBuilder
234
+ while (idx != offset && idx < source.content().length && source.content()(idx).isWhitespace) {
235
+ pad.append(if (idx < source.content().length && source.content()(idx) == '\t ' ) '\t ' else ' ' )
236
+ idx += 1
237
+ }
238
+ pad.result()
239
+ }
240
+
158
241
private def makeRefsEdits (
159
242
refs : List [Occurence ],
160
- symbols : Set [Symbol ]
243
+ symbols : Set [Symbol ],
244
+ definition : DefinitionTree
161
245
): Either [String , List [Reference ]] =
162
246
val newctx = driver.currentCtx.fresh.setCompilationUnit(unit)
163
247
def buildRef (occurrence : Occurence ): Either [String , Reference ] =
@@ -178,6 +262,11 @@ final class PcInlineValueProviderImpl(
178
262
Right (
179
263
Reference (
180
264
occurrence.pos.toLsp,
265
+ stripIndentPrefix(
266
+ extendWithSurroundingParens(definition.tree.rhs.sourcePos),
267
+ occurrence.tree.startPos.startColumnIndentPadding,
268
+ definition.tree.startPos.startColumnIndentPadding
269
+ ),
181
270
occurrence.parent.map(p =>
182
271
RangeOffset (p.sourcePos.start, p.sourcePos.end)
183
272
),
@@ -196,7 +285,7 @@ final class PcInlineValueProviderImpl(
196
285
)
197
286
end makeRefsEdits
198
287
199
- end PcInlineValueProviderImpl
288
+ end PcInlineValueProvider
200
289
201
290
case class Occurence (tree : Tree , parent : Option [Tree ], pos : SourcePosition ):
202
291
def isDefn =
@@ -205,3 +294,19 @@ case class Occurence(tree: Tree, parent: Option[Tree], pos: SourcePosition):
205
294
case _ => false
206
295
207
296
case class DefinitionTree (tree : ValDef , pos : SourcePosition )
297
+
298
+ case class RangeOffset (start : Int , end : Int )
299
+
300
+ case class Definition (
301
+ range : l.Range ,
302
+ rangeOffsets : RangeOffset ,
303
+ requiresBrackets : Boolean ,
304
+ shouldBeRemoved : Boolean
305
+ )
306
+
307
+ case class Reference (
308
+ range : l.Range ,
309
+ rhs : String ,
310
+ parentOffsets : Option [RangeOffset ],
311
+ requiresBrackets : Boolean
312
+ )
0 commit comments