@@ -183,7 +183,7 @@ class DottyLanguageServer extends LanguageServer
183
183
val worksheetMode = isWorksheet(uri)
184
184
185
185
val (text, positionMapper) =
186
- if (worksheetMode) (wrapWorksheet(document.getText), Some (worksheetPositionMapper _))
186
+ if (worksheetMode) (wrapWorksheet(document.getText), Some (toUnwrappedPosition _))
187
187
else (document.getText, None )
188
188
189
189
val diags = driver.run(uri, text)
@@ -204,7 +204,7 @@ class DottyLanguageServer extends LanguageServer
204
204
assert(change.getRange == null , " TextDocumentSyncKind.Incremental support is not implemented" )
205
205
206
206
val (text, positionMapper) =
207
- if (worksheetMode) (wrapWorksheet(change.getText), Some (worksheetPositionMapper _))
207
+ if (worksheetMode) (wrapWorksheet(change.getText), Some (toUnwrappedPosition _))
208
208
else (change.getText, None )
209
209
210
210
val diags = driver.run(uri, text)
@@ -317,7 +317,7 @@ class DottyLanguageServer extends LanguageServer
317
317
(Nil , Include .overriding)
318
318
}
319
319
val defs = Interactive .namedTrees(trees, include, sym)
320
- defs.flatMap(d => location(d.namePos)).asJava
320
+ defs.flatMap(d => location(d.namePos, positionMapperFor(d.source) )).asJava
321
321
}
322
322
}
323
323
@@ -340,7 +340,7 @@ class DottyLanguageServer extends LanguageServer
340
340
Include .references | Include .overriding | (if (includeDeclaration) Include .definitions else 0 )
341
341
val refs = Interactive .findTreesMatching(trees, includes, sym)
342
342
343
- refs.flatMap(ref => location(ref.namePos)).asJava
343
+ refs.flatMap(ref => location(ref.namePos, positionMapperFor(ref.source) )).asJava
344
344
}
345
345
}
346
346
@@ -363,7 +363,7 @@ class DottyLanguageServer extends LanguageServer
363
363
val changes = refs.groupBy(ref => toUri(ref.source).toString)
364
364
.mapValues(refs =>
365
365
refs.flatMap(ref =>
366
- range(ref.namePos).map(nameRange => new TextEdit (nameRange, newName))).asJava)
366
+ range(ref.namePos, positionMapperFor(ref.source) ).map(nameRange => new TextEdit (nameRange, newName))).asJava)
367
367
368
368
new WorkspaceEdit (changes.asJava)
369
369
}
@@ -383,7 +383,7 @@ class DottyLanguageServer extends LanguageServer
383
383
val refs = Interactive .namedTrees(uriTrees, Include .references | Include .overriding, sym)
384
384
(for {
385
385
ref <- refs if ! ref.tree.symbol.isConstructor
386
- nameRange <- range(ref.namePos)
386
+ nameRange <- range(ref.namePos, positionMapperFor(ref.source) )
387
387
} yield new DocumentHighlight (nameRange, DocumentHighlightKind .Read )).asJava
388
388
}
389
389
}
@@ -416,8 +416,8 @@ class DottyLanguageServer extends LanguageServer
416
416
417
417
val defs = Interactive .namedTrees(uriTrees, includeReferences = false , _ => true )
418
418
(for {
419
- d <- defs
420
- info <- symbolInfo(d.tree.symbol, d.namePos)
419
+ d <- defs if ! isWorksheetWrapper(d)
420
+ info <- symbolInfo(d.tree.symbol, d.namePos, positionMapperFor(d.source) )
421
421
} yield JEither .forLeft(info)).asJava
422
422
}
423
423
@@ -429,7 +429,7 @@ class DottyLanguageServer extends LanguageServer
429
429
430
430
val trees = driver.allTrees
431
431
val defs = Interactive .namedTrees(trees, includeReferences = false , nameSubstring = query)
432
- defs.flatMap(d => symbolInfo(d.tree.symbol, d.namePos))
432
+ defs.flatMap(d => symbolInfo(d.tree.symbol, d.namePos, positionMapperFor(d.source) ))
433
433
}.asJava
434
434
}
435
435
@@ -473,9 +473,12 @@ object DottyLanguageServer {
473
473
474
474
/** Convert an lsp4j.Position to a SourcePosition */
475
475
def sourcePosition (driver : InteractiveDriver , uri : URI , pos : lsp4j.Position ): SourcePosition = {
476
+ val actualPosition =
477
+ if (isWorksheet(uri)) toWrappedPosition(pos)
478
+ else pos
476
479
val source = driver.openedFiles(uri)
477
480
if (source.exists) {
478
- val p = Positions .Position (source.lineToOffset(pos .getLine) + pos .getCharacter)
481
+ val p = Positions .Position (source.lineToOffset(actualPosition .getLine) + actualPosition .getCharacter)
479
482
new SourcePosition (source, p)
480
483
}
481
484
else NoSourcePosition
@@ -528,6 +531,10 @@ object DottyLanguageServer {
528
531
private def isWorksheet (uri : URI ): Boolean =
529
532
uri.toString.endsWith(" .sc" )
530
533
534
+ /** Does this sourcefile represent a worksheet? */
535
+ private def isWorksheet (sourcefile : SourceFile ): Boolean =
536
+ sourcefile.file.extension == " sc"
537
+
531
538
/** Wrap the source of a worksheet inside an `object`. */
532
539
private def wrapWorksheet (source : String ): String =
533
540
s """ object Worksheet {
@@ -544,13 +551,48 @@ object DottyLanguageServer {
544
551
* @param position The position as seen by the compiler (after wrapping)
545
552
* @return The position in the actual source file (before wrapping).
546
553
*/
547
- private def worksheetPositionMapper (position : SourcePosition ): SourcePosition = {
554
+ private def toUnwrappedPosition (position : SourcePosition ): SourcePosition = {
548
555
new SourcePosition (position.source, position.pos, position.outer) {
549
556
override def startLine : Int = position.startLine - 1
550
557
override def endLine : Int = position.endLine - 1
551
558
}
552
559
}
553
560
561
+ /**
562
+ * Map `position` in an unwrapped worksheet to the same position in the wrapped source.
563
+ *
564
+ * Because worksheet are wrapped in an `object`, the positions in the source are one line
565
+ * above from what the compiler sees.
566
+ *
567
+ * @see wrapWorksheet
568
+ * @param position The position as seen by VSCode (before wrapping)
569
+ * @return The position as seen by the compiler (after wrapping)
570
+ */
571
+ private def toWrappedPosition (position : lsp4j.Position ): lsp4j.Position = {
572
+ new lsp4j.Position (position.getLine + 1 , position.getCharacter)
573
+ }
574
+
575
+ /**
576
+ * Returns the position mapper necessary to unwrap positions for `sourcefile`. If `sourcefile` is
577
+ * not a worksheet, no mapper is necessary. Otherwise, return `toUnwrappedPosition`.
578
+ */
579
+ private def positionMapperFor (sourcefile : SourceFile ): Option [SourcePosition => SourcePosition ] = {
580
+ if (isWorksheet(sourcefile)) Some (toUnwrappedPosition _)
581
+ else None
582
+ }
583
+
584
+ /**
585
+ * Is `sourceTree` the wrapper object that we put around worksheet sources?
586
+ *
587
+ * @see wrapWorksheet
588
+ */
589
+ def isWorksheetWrapper (sourceTree : SourceTree )(implicit ctx : Context ): Boolean = {
590
+ val symbol = sourceTree.tree.symbol
591
+ isWorksheet(sourceTree.source) &&
592
+ symbol.name.toString == " Worksheet$" &&
593
+ symbol.owner == ctx.definitions.EmptyPackageClass
594
+ }
595
+
554
596
/** Create an lsp4j.CompletionItem from a Symbol */
555
597
def completionItem (sym : Symbol )(implicit ctx : Context ): lsp4j.CompletionItem = {
556
598
def completionItemKind (sym : Symbol )(implicit ctx : Context ): lsp4j.CompletionItemKind = {
@@ -596,7 +638,7 @@ object DottyLanguageServer {
596
638
}
597
639
598
640
/** Create an lsp4j.SymbolInfo from a Symbol and a SourcePosition */
599
- def symbolInfo (sym : Symbol , pos : SourcePosition )(implicit ctx : Context ): Option [lsp4j.SymbolInformation ] = {
641
+ def symbolInfo (sym : Symbol , pos : SourcePosition , positionMapper : Option [ SourcePosition => SourcePosition ] )(implicit ctx : Context ): Option [lsp4j.SymbolInformation ] = {
600
642
def symbolKind (sym : Symbol )(implicit ctx : Context ): lsp4j.SymbolKind = {
601
643
import lsp4j .{SymbolKind => SK }
602
644
@@ -621,6 +663,6 @@ object DottyLanguageServer {
621
663
else
622
664
null
623
665
624
- location(pos).map(l => new lsp4j.SymbolInformation (name, symbolKind(sym), l, containerName))
666
+ location(pos, positionMapper ).map(l => new lsp4j.SymbolInformation (name, symbolKind(sym), l, containerName))
625
667
}
626
668
}
0 commit comments