@@ -16,17 +16,76 @@ import dotty.tools.dotc.core.StdNames
1616import dotty .tools .dotc .core .Symbols .Symbol
1717import dotty .tools .dotc .interactive .Interactive
1818import dotty .tools .dotc .interactive .InteractiveDriver
19+ import dotty .tools .dotc .util .SourceFile
1920import dotty .tools .dotc .util .SourcePosition
2021import dotty .tools .pc .utils .InteractiveEnrichments .*
2122import dotty .tools .pc .IndexedContext .Result
2223
2324import org .eclipse .lsp4j as l
2425
25- final class PcInlineValueProviderImpl (
26+ final class PcInlineValueProvider (
2627 driver : InteractiveDriver ,
2728 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+ }
3089
3190 val position : l.Position = pos.toLsp.getStart().nn
3291
@@ -41,7 +100,7 @@ final class PcInlineValueProviderImpl(
41100 Some (Occurence (tree, parent, adjustedPos))
42101 case _ => None
43102
44- override def defAndRefs (): Either [String , (Definition , List [Reference ])] =
103+ def defAndRefs (): Either [String , (Definition , List [Reference ])] =
45104 val newctx = driver.currentCtx.fresh.setCompilationUnit(unit)
46105 val allOccurences = result().flatten
47106 for
@@ -60,7 +119,6 @@ final class PcInlineValueProviderImpl(
60119 val defPos = definition.tree.sourcePos
61120 val defEdit = Definition (
62121 defPos.toLsp,
63- adjustRhs(definition.tree.rhs.sourcePos),
64122 RangeOffset (defPos.start, defPos.end),
65123 definitionRequiresBrackets(definition.tree.rhs)(using newctx),
66124 deleteDefinition
@@ -70,6 +128,18 @@ final class PcInlineValueProviderImpl(
70128 end for
71129 end defAndRefs
72130
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+
73143 private def definitionRequiresBrackets (tree : Tree )(using Context ): Boolean =
74144 NavigateAST
75145 .untypedPath(tree.span)
@@ -102,12 +172,12 @@ final class PcInlineValueProviderImpl(
102172
103173 end referenceRequiresBrackets
104174
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` */
106177 def extend (point : Int , acceptedChar : Char , step : Int ): Int =
107178 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
111181 then extend(newPoint, acceptedChar, step)
112182 else point
113183 val adjustedStart = extend(pos.start, '(' , - 1 )
@@ -139,7 +209,7 @@ final class PcInlineValueProviderImpl(
139209 .exists(e => e.isTerm)
140210 def allreferences = allOccurences.filterNot(_.isDefn)
141211 def inlineAll () =
142- makeRefsEdits(allreferences, symbols).map((true , _))
212+ makeRefsEdits(allreferences, symbols, definition ).map((true , _))
143213 if definition.tree.sourcePos.toLsp.encloses(position)
144214 then if defIsLocal then inlineAll() else Left (Errors .notLocal)
145215 else
@@ -150,14 +220,28 @@ final class PcInlineValueProviderImpl(
150220 ref <- list
151221 .find(_.pos.toLsp.encloses(position))
152222 .toRight(Errors .didNotFindReference)
153- refEdits <- makeRefsEdits(List (ref), symbols)
223+ refEdits <- makeRefsEdits(List (ref), symbols, definition )
154224 yield (false , refEdits)
155225 end if
156226 end getReferencesToInline
157227
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+
158241 private def makeRefsEdits (
159242 refs : List [Occurence ],
160- symbols : Set [Symbol ]
243+ symbols : Set [Symbol ],
244+ definition : DefinitionTree
161245 ): Either [String , List [Reference ]] =
162246 val newctx = driver.currentCtx.fresh.setCompilationUnit(unit)
163247 def buildRef (occurrence : Occurence ): Either [String , Reference ] =
@@ -178,6 +262,11 @@ final class PcInlineValueProviderImpl(
178262 Right (
179263 Reference (
180264 occurrence.pos.toLsp,
265+ stripIndentPrefix(
266+ extendWithSurroundingParens(definition.tree.rhs.sourcePos),
267+ occurrence.tree.startPos.startColumnIndentPadding,
268+ definition.tree.startPos.startColumnIndentPadding
269+ ),
181270 occurrence.parent.map(p =>
182271 RangeOffset (p.sourcePos.start, p.sourcePos.end)
183272 ),
@@ -196,7 +285,7 @@ final class PcInlineValueProviderImpl(
196285 )
197286 end makeRefsEdits
198287
199- end PcInlineValueProviderImpl
288+ end PcInlineValueProvider
200289
201290case class Occurence (tree : Tree , parent : Option [Tree ], pos : SourcePosition ):
202291 def isDefn =
@@ -205,3 +294,19 @@ case class Occurence(tree: Tree, parent: Option[Tree], pos: SourcePosition):
205294 case _ => false
206295
207296case 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