Skip to content

Commit eb3ba6c

Browse files
committed
refactor tools
1 parent a051a03 commit eb3ba6c

File tree

3 files changed

+133
-126
lines changed

3 files changed

+133
-126
lines changed

compiler/src/dotty/tools/dotc/semanticdb/Scala3.scala

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,4 +206,65 @@ object Scala3 with
206206
def hasLength = range.endLine > range.startLine || range.endCharacter > range.startCharacter
207207
end RangeOps
208208

209+
/** Sort symbol occurrences by their start position. */
210+
given OccurrenceOrdering: Ordering[SymbolOccurrence] = (x, y) =>
211+
x.range -> y.range match
212+
case None -> _ | _ -> None => 0
213+
case Some(a) -> Some(b) =>
214+
val byLine = Integer.compare(a.startLine, b.startLine)
215+
if (byLine != 0)
216+
byLine
217+
else // byCharacter
218+
Integer.compare(a.startCharacter, b.startCharacter)
219+
end OccurrenceOrdering
220+
221+
given Ordering[SymbolInformation] = Ordering.by[SymbolInformation, String](_.symbol)(IdentifierOrdering())
222+
223+
/**
224+
* A comparator for identifier like "Predef" or "Function10".
225+
*
226+
* Differences from the default string comparator:
227+
* - works with CharSequences like compiler `Name`
228+
* - orders numbers by their numerical value instead of lexicographical
229+
* - Good: `Function1`, `Function2`, `Function10`
230+
* - Bad: `Function1`, `Function10`, `Function2`
231+
*
232+
* taken from https://github.com/scalameta/scalameta/blob/master/semanticdb/metap/src/main/scala/scala/meta/internal/metap/IdentifierOrdering.scala
233+
*/
234+
private class IdentifierOrdering[T <: CharSequence] extends Ordering[T] with
235+
236+
override def compare(o1: T, o2: T): Int =
237+
val len = math.min(o1.length(), o2.length())
238+
var i = 0
239+
while i < len do
240+
val a = o1.charAt(i)
241+
val b = o2.charAt(i)
242+
if a.isDigit && b.isDigit
243+
val byDigit = Integer.compare(toDigit(o1, i), toDigit(o2, i))
244+
if (byDigit != 0) return byDigit
245+
else
246+
i = seekNonDigit(o1, i)
247+
else
248+
val result = Character.compare(a, b)
249+
if result != 0
250+
return result
251+
i += 1
252+
end while
253+
Integer.compare(o1.length(), o2.length())
254+
end compare
255+
256+
private def seekNonDigit(cs: T, i: Int): Int =
257+
var curr = i
258+
while curr < cs.length && cs.charAt(curr).isDigit do
259+
curr += 1
260+
curr
261+
end seekNonDigit
262+
263+
private def toDigit(cs: T, i: Int): Int =
264+
val digit = cs.subSequence(i, seekNonDigit(cs, i))
265+
Integer.parseUnsignedInt(digit.toString)
266+
end toDigit
267+
268+
end IdentifierOrdering
269+
209270
end Scala3

compiler/test/dotty/tools/dotc/semanticdb/Semanticdbs.scala renamed to compiler/src/dotty/tools/dotc/semanticdb/Tools.scala

Lines changed: 28 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import scala.collection.JavaConverters._
66
import dotty.tools.dotc.util.SourceFile
77
import dotty.tools.dotc.semanticdb.Scala3.{_, given}
88

9-
object Semanticdbs with
9+
object Tools with
1010

1111
/** Load SemanticDB TextDocument for a single Scala source file
1212
*
@@ -35,45 +35,33 @@ object Semanticdbs with
3535
end loadTextDocument
3636

3737
/** Parses SemanticDB text documents from an absolute path to a `*.semanticdb` file. */
38-
def parseTextDocuments(path: Path): TextDocuments =
38+
private def parseTextDocuments(path: Path): TextDocuments =
3939
val bytes = Files.readAllBytes(path) // NOTE: a semanticdb file is a TextDocuments message, not TextDocument
4040
TextDocuments.parseFrom(bytes)
4141

42-
/** Prettyprint a text document with symbol occurrences next to each resolved identifier.
43-
*
44-
* Useful for testing purposes to ensure that SymbolOccurrence values make sense and are correct.
45-
* Example output (NOTE, slightly modified to avoid "unclosed comment" errors):
46-
* {{{
47-
* class Example *example/Example#* {
48-
* val a *example/Example#a.* : String *scala/Predef.String#* = "1"
49-
* }
50-
* }}}
51-
**/
52-
def printTextDocument(doc: TextDocument): String =
53-
val symtab = doc.symbols.iterator.map(info => info.symbol -> info).toMap
54-
val sb = StringBuilder(1000)
55-
val sourceFile = SourceFile.virtual(doc.uri, doc.text)
56-
var offset = 0
57-
for occ <- doc.occurrences.sorted do
58-
val range = occ.range.get
59-
val end = math.max(
60-
offset,
61-
sourceFile.lineToOffset(range.endLine) + range.endCharacter
62-
)
63-
val isPrimaryConstructor =
64-
symtab.get(occ.symbol).exists(_.isPrimary)
65-
if !occ.symbol.isPackage && !isPrimaryConstructor
66-
sb.append(doc.text.substring(offset, end))
67-
sb.append("/*")
68-
.append(if (occ.role.isDefinition) "<-" else "->")
69-
.append(occ.symbol.replace("/", "::"))
70-
.append("*/")
71-
offset = end
72-
sb.append(doc.text.substring(offset))
73-
sb.toString
74-
end printTextDocument
42+
def metac(doc: TextDocument, realPath: Path)(given sb: StringBuilder): StringBuilder =
43+
val realURI = realPath.toString
44+
given SourceFile = SourceFile.virtual(doc.uri, doc.text)
45+
sb.append(realURI).nl
46+
sb.append("_" * realURI.length).nl
47+
sb.nl
48+
sb.append("Summary:").nl
49+
sb.append("Schema => ").append(schemaString(doc.schema)).nl
50+
sb.append("Uri => ").append(doc.uri).nl
51+
sb.append("Text => empty").nl
52+
sb.append("Language => ").append(languageString(doc.language)).nl
53+
sb.append("Symbols => ").append(doc.symbols.length).append(" entries").nl
54+
sb.append("Occurrences => ").append(doc.occurrences.length).append(" entries").nl
55+
sb.nl
56+
sb.append("Symbols:").nl
57+
doc.symbols.sorted.foreach(processSymbol)
58+
sb.nl
59+
sb.append("Occurrences:").nl
60+
doc.occurrences.sorted.foreach(processOccurrence)
61+
sb.nl
62+
end metac
7563

76-
def schemaString(schema: Schema) =
64+
private def schemaString(schema: Schema) =
7765
import Schema._
7866
schema match
7967
case SEMANTICDB3 => "SemanticDB v3"
@@ -82,15 +70,15 @@ object Semanticdbs with
8270
case Unrecognized(_) => "unknown"
8371
end schemaString
8472

85-
def languageString(language: Language) =
73+
private def languageString(language: Language) =
8674
import Language._
8775
language match
8876
case SCALA => "Scala"
8977
case JAVA => "Java"
9078
case UNKNOWN_LANGUAGE | Unrecognized(_) => "unknown"
9179
end languageString
9280

93-
def processSymbol(info: SymbolInformation)(given sb: StringBuilder): Unit =
81+
private def processSymbol(info: SymbolInformation)(given sb: StringBuilder): Unit =
9482
import SymbolInformation.Kind._
9583
sb.append(info.symbol).append(" => ")
9684
if info.isAbstract then sb.append("abstract ")
@@ -127,7 +115,7 @@ object Semanticdbs with
127115
sb.append(info.displayName).nl
128116
end processSymbol
129117

130-
def processOccurrence(occ: SymbolOccurrence)(given sb: StringBuilder, sourceFile: SourceFile): Unit =
118+
private def processOccurrence(occ: SymbolOccurrence)(given sb: StringBuilder, sourceFile: SourceFile): Unit =
131119
occ.range match
132120
case Some(range) =>
133121
sb.append('[')
@@ -146,87 +134,4 @@ object Semanticdbs with
146134
sb.append(if occ.role.isReference then " -> " else " <- ").append(occ.symbol).nl
147135
end processOccurrence
148136

149-
def metac(doc: TextDocument, realPath: Path)(given sb: StringBuilder): StringBuilder =
150-
val realURI = realPath.toString
151-
given SourceFile = SourceFile.virtual(doc.uri, doc.text)
152-
sb.append(realURI).nl
153-
sb.append("_" * realURI.length).nl
154-
sb.nl
155-
sb.append("Summary:").nl
156-
sb.append("Schema => ").append(schemaString(doc.schema)).nl
157-
sb.append("Uri => ").append(doc.uri).nl
158-
sb.append("Text => empty").nl
159-
sb.append("Language => ").append(languageString(doc.language)).nl
160-
sb.append("Symbols => ").append(doc.symbols.length).append(" entries").nl
161-
sb.append("Occurrences => ").append(doc.occurrences.length).append(" entries").nl
162-
sb.nl
163-
sb.append("Symbols:").nl
164-
doc.symbols.sorted.foreach(processSymbol)
165-
sb.nl
166-
sb.append("Occurrences:").nl
167-
doc.occurrences.sorted.foreach(processOccurrence)
168-
sb.nl
169-
end metac
170-
171-
/** Sort symbol occurrences by their start position. */
172-
given buildOccurrenceOrdering: Ordering[SymbolOccurrence] = (x, y) =>
173-
x.range -> y.range match
174-
case None -> _ | _ -> None => 0
175-
case Some(a) -> Some(b) =>
176-
val byLine = Integer.compare(a.startLine, b.startLine)
177-
if (byLine != 0)
178-
byLine
179-
else // byCharacter
180-
Integer.compare(a.startCharacter, b.startCharacter)
181-
end buildOccurrenceOrdering
182-
183-
given Ordering[SymbolInformation] = Ordering.by[SymbolInformation, String](_.symbol)(IdentifierOrdering())
184-
185-
/**
186-
* A comparator for identifier like "Predef" or "Function10".
187-
*
188-
* Differences from the default string comparator:
189-
* - works with CharSequences like compiler `Name`
190-
* - orders numbers by their numerical value instead of lexicographical
191-
* - Good: `Function1`, `Function2`, `Function10`
192-
* - Bad: `Function1`, `Function10`, `Function2`
193-
*
194-
* taken from https://github.com/scalameta/scalameta/blob/master/semanticdb/metap/src/main/scala/scala/meta/internal/metap/IdentifierOrdering.scala
195-
*/
196-
class IdentifierOrdering[T <: CharSequence] extends Ordering[T] with
197-
198-
override def compare(o1: T, o2: T): Int =
199-
val len = math.min(o1.length(), o2.length())
200-
var i = 0
201-
while i < len do
202-
val a = o1.charAt(i)
203-
val b = o2.charAt(i)
204-
if a.isDigit && b.isDigit
205-
val byDigit = Integer.compare(toDigit(o1, i), toDigit(o2, i))
206-
if (byDigit != 0) return byDigit
207-
else
208-
i = seekNonDigit(o1, i)
209-
else
210-
val result = Character.compare(a, b)
211-
if result != 0
212-
return result
213-
i += 1
214-
end while
215-
Integer.compare(o1.length(), o2.length())
216-
end compare
217-
218-
private def seekNonDigit(cs: T, i: Int): Int =
219-
var curr = i
220-
while curr < cs.length && cs.charAt(curr).isDigit do
221-
curr += 1
222-
curr
223-
end seekNonDigit
224-
225-
private def toDigit(cs: T, i: Int): Int =
226-
val digit = cs.subSequence(i, seekNonDigit(cs, i))
227-
Integer.parseUnsignedInt(digit.toString)
228-
end toDigit
229-
230-
end IdentifierOrdering
231-
232-
inline def (sb: StringBuilder) nl = sb.append(System.lineSeparator)
137+
private inline def (sb: StringBuilder) nl = sb.append(System.lineSeparator)

compiler/test/dotty/tools/dotc/semanticdb/SemanticdbTests.scala

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import org.junit.experimental.categories.Category
1818
import dotty.BootstrappedOnlyTests
1919
import dotty.tools.dotc.Main
2020
import dotty.tools.dotc.semanticdb.DiffAssertions._
21+
import dotty.tools.dotc.semanticdb.Scala3.given
22+
import dotty.tools.dotc.util.SourceFile
2123

2224
@main def updateExpect =
2325
SemanticdbTests().runExpectTest(updateExpectFiles = true)
@@ -47,9 +49,9 @@ class SemanticdbTests with
4749
.resolve(relpath)
4850
.resolveSibling(filename + ".semanticdb")
4951
val expectPath = source.resolveSibling(filename.replaceAllLiterally(".scala", ".expect.scala"))
50-
val doc = Semanticdbs.loadTextDocument(source, relpath, semanticdbPath)
51-
Semanticdbs.metac(doc, rootSrc.relativize(source))(given metacSb)
52-
val obtained = trimTrailingWhitespace(Semanticdbs.printTextDocument(doc))
52+
val doc = Tools.loadTextDocument(source, relpath, semanticdbPath)
53+
Tools.metac(doc, rootSrc.relativize(source))(given metacSb)
54+
val obtained = trimTrailingWhitespace(SemanticdbTests.printTextDocument(doc))
5355
if updateExpectFiles
5456
Files.write(expectPath, obtained.getBytes(StandardCharsets.UTF_8))
5557
println("updated: " + expectPath)
@@ -102,3 +104,42 @@ class SemanticdbTests with
102104
val exit = Main.process(args)
103105
assertFalse(s"dotc errors: ${exit.errorCount}", exit.hasErrors)
104106
target
107+
108+
end SemanticdbTests
109+
110+
object SemanticdbTests with
111+
/** Prettyprint a text document with symbol occurrences next to each resolved identifier.
112+
*
113+
* Useful for testing purposes to ensure that SymbolOccurrence values make sense and are correct.
114+
* Example output (NOTE, slightly modified to avoid "unclosed comment" errors):
115+
* {{{
116+
* class Example *example/Example#* {
117+
* val a *example/Example#a.* : String *scala/Predef.String#* = "1"
118+
* }
119+
* }}}
120+
**/
121+
def printTextDocument(doc: TextDocument): String =
122+
val symtab = doc.symbols.iterator.map(info => info.symbol -> info).toMap
123+
val sb = StringBuilder(1000)
124+
val sourceFile = SourceFile.virtual(doc.uri, doc.text)
125+
var offset = 0
126+
for occ <- doc.occurrences.sorted do
127+
val range = occ.range.get
128+
val end = math.max(
129+
offset,
130+
sourceFile.lineToOffset(range.endLine) + range.endCharacter
131+
)
132+
val isPrimaryConstructor =
133+
symtab.get(occ.symbol).exists(_.isPrimary)
134+
if !occ.symbol.isPackage && !isPrimaryConstructor
135+
sb.append(doc.text.substring(offset, end))
136+
sb.append("/*")
137+
.append(if (occ.role.isDefinition) "<-" else "->")
138+
.append(occ.symbol.replace("/", "::"))
139+
.append("*/")
140+
offset = end
141+
sb.append(doc.text.substring(offset))
142+
sb.toString
143+
end printTextDocument
144+
145+
end SemanticdbTests

0 commit comments

Comments
 (0)