Skip to content

Commit 604e3b0

Browse files
authored
Merge pull request #394 from olafurpg/lsif-typed
2 parents a96b7eb + 6c96c08 commit 604e3b0

File tree

12 files changed

+637
-55
lines changed

12 files changed

+637
-55
lines changed

lsif-java/src/main/scala/com/sourcegraph/lsif_java/commands/IndexSemanticdbCommand.scala

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,19 @@ final case class IndexSemanticdbCommand(
4444
@Inline() app: Application = Application.default
4545
) extends Command {
4646
def sourceroot: Path = AbsolutePath.of(app.env.workingDirectory)
47-
def isProtobufFormat: Boolean =
48-
IndexSemanticdbCommand.isProtobufFormat(output)
4947
def absoluteTargetroots: List[Path] =
5048
targetroot.map(AbsolutePath.of(_, app.env.workingDirectory))
5149
def run(): Int = {
5250
val reporter = new ConsoleLsifSemanticdbReporter(app)
53-
val format =
54-
if (isProtobufFormat)
55-
LsifOutputFormat.PROTOBUF
56-
else
57-
LsifOutputFormat.JSON
51+
val outputFilename = output.getFileName.toString
52+
val format = LsifOutputFormat.fromFilename(outputFilename)
53+
if (format == LsifOutputFormat.UNKNOWN) {
54+
app.error(
55+
s"unknown output format for filename '$outputFilename'. " +
56+
s"Supported file extension are `*.lsif`, `*.lsif-typed'"
57+
)
58+
return 1
59+
}
5860
val packages =
5961
absoluteTargetroots
6062
.iterator

lsif-java/src/main/scala/com/sourcegraph/lsif_java/commands/SnapshotLsifCommand.scala

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import moped.cli.Command
4242
import moped.cli.CommandParser
4343
import moped.reporters.Input
4444
import moped.reporters.Position
45+
import moped.reporters.Reporter
4546
import org.scalameta.ascii.layout.prefs.LayoutPrefsImpl
4647

4748
@CommandName("snapshot-lsif")
@@ -65,7 +66,11 @@ case class SnapshotLsifCommand(
6566
for {
6667
inputPath <- input
6768
in = AbsolutePath.of(inputPath, sourceroot)
68-
doc <- SnapshotLsifCommand.parseTextDocument(in, sourceroot)
69+
if Files.isRegularFile(in) || {
70+
app.error(s"no such file: $in")
71+
false
72+
}
73+
doc <- SnapshotLsifCommand.parseTextDocument(in, sourceroot, app.reporter)
6974
} {
7075
val docPath = AbsolutePath
7176
.of(Paths.get(doc.getUri), sourceroot)
@@ -86,16 +91,21 @@ case class SnapshotLsifCommand(
8691

8792
object SnapshotLsifCommand {
8893
private val jsonParser = JsonFormat.parser().ignoringUnknownFields()
89-
def parseTextDocument(input: Path, sourceroot: Path): List[TextDocument] = {
90-
parseSemanticdb(input, parseInput(input), sourceroot)
94+
def parseTextDocument(
95+
input: Path,
96+
sourceroot: Path,
97+
reporter: Reporter
98+
): List[TextDocument] = {
99+
parseSemanticdb(input, parseInput(input), sourceroot, reporter)
91100
}
92101

93102
def parseSemanticdb(
94103
input: Path,
95104
objects: mutable.Buffer[LsifObject],
96-
sourceroot: Path
105+
sourceroot: Path,
106+
reporter: Reporter
97107
): List[TextDocument] = {
98-
val lsif = new IndexedLsif(input, objects, sourceroot)
108+
val lsif = new IndexedLsif(input, objects, sourceroot, reporter)
99109
lsif
100110
.ranges
101111
.iterator
@@ -169,7 +179,8 @@ object SnapshotLsifCommand {
169179
class IndexedLsif(
170180
val path: Path,
171181
val objects: mutable.Buffer[LsifObject],
172-
val sourceroot: Path
182+
val sourceroot: Path,
183+
val reporter: Reporter
173184
) {
174185
val documents = mutable.Map.empty[Int, TextDocument.Builder]
175186
val next = mutable.Map.empty[Int, Int]
@@ -432,24 +443,28 @@ object SnapshotLsifCommand {
432443
case "document" =>
433444
val relativeFile = Paths.get(URI.create(o.getUri))
434445
val absoluteFile = sourceroot.resolve(relativeFile)
435-
val text =
436-
new String(
437-
Files.readAllBytes(absoluteFile),
438-
StandardCharsets.UTF_8
439-
)
440-
val relativeUri = sourceroot
441-
.relativize(absoluteFile)
442-
.iterator()
443-
.asScala
444-
.mkString("/")
445-
val language = Language
446-
.values()
447-
.find(_.name().compareToIgnoreCase(o.getLanguage) == 0)
448-
.getOrElse(Language.UNKNOWN_LANGUAGE)
449-
textDocument(o.getId)
450-
.setUri(relativeUri)
451-
.setLanguage(language)
452-
.setText(text)
446+
if (!Files.isRegularFile(absoluteFile)) {
447+
reporter.warning(s"no such file: $absoluteFile")
448+
} else {
449+
val text =
450+
new String(
451+
Files.readAllBytes(absoluteFile),
452+
StandardCharsets.UTF_8
453+
)
454+
val relativeUri = sourceroot
455+
.relativize(absoluteFile)
456+
.iterator()
457+
.asScala
458+
.mkString("/")
459+
val language = Language
460+
.values()
461+
.find(_.name().compareToIgnoreCase(o.getLanguage) == 0)
462+
.getOrElse(Language.UNKNOWN_LANGUAGE)
463+
textDocument(o.getId)
464+
.setUri(relativeUri)
465+
.setLanguage(language)
466+
.setText(text)
467+
}
453468
case "definitionResult" =>
454469
isDefinitionResult += o.getId()
455470
case "hoverResult" =>

lsif-semanticdb/src/main/java/com/sourcegraph/lsif_semanticdb/LsifOutputFormat.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,25 @@
66
* <p>The Protobuf format is experimental and currently only exists as a proof-of-concept.
77
*/
88
public enum LsifOutputFormat {
9-
JSON,
10-
PROTOBUF
9+
GRAPH_NDJSON,
10+
GRAPH_PROTOBUF,
11+
TYPED_PROTOBUF,
12+
TYPED_NDJSON,
13+
UNKNOWN;
14+
15+
public boolean isTyped() {
16+
return this == TYPED_NDJSON || this == TYPED_PROTOBUF;
17+
}
18+
19+
public boolean isNewlineDelimitedJSON() {
20+
return this == GRAPH_NDJSON || this == TYPED_NDJSON;
21+
}
22+
23+
public static LsifOutputFormat fromFilename(String name) {
24+
if (name.endsWith(".lsif")) return GRAPH_NDJSON;
25+
if (name.endsWith(".lsif-protobuf")) return GRAPH_PROTOBUF;
26+
if (name.endsWith(".lsif-typed")) return TYPED_PROTOBUF;
27+
if (name.endsWith(".lsif-typed.ndjson")) return TYPED_NDJSON;
28+
return UNKNOWN;
29+
}
1130
}

lsif-semanticdb/src/main/java/com/sourcegraph/lsif_semanticdb/LsifOutputStream.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ public void writeLsifObject(LsifObject.Builder object) {
4949
b.output.reset();
5050
try {
5151
switch (options.format) {
52-
case PROTOBUF:
52+
case GRAPH_PROTOBUF:
5353
object.buildPartial().writeTo(b.output);
5454
break;
55-
case JSON:
55+
case GRAPH_NDJSON:
5656
default:
5757
jsonPrinter.appendTo(object, b.writer);
5858
b.writer.flush();
@@ -69,7 +69,9 @@ public void flush() throws IOException {
6969
byte[] bytes = buffer.poll();
7070
while (bytes != null) {
7171
out.write(bytes);
72-
out.write(NEWLINE);
72+
if (options.format.isNewlineDelimitedJSON()) {
73+
out.write(NEWLINE);
74+
}
7375
bytes = buffer.poll();
7476
}
7577
out.flush();

lsif-semanticdb/src/main/java/com/sourcegraph/lsif_semanticdb/LsifSemanticdb.java

Lines changed: 129 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,18 @@
77
import com.sourcegraph.semanticdb_javac.Semanticdb.SymbolOccurrence;
88
import com.sourcegraph.semanticdb_javac.Semanticdb.SymbolOccurrence.Role;
99
import com.sourcegraph.semanticdb_javac.SemanticdbSymbols;
10+
import lib.codeintel.lsif_typed.LsifTyped;
1011

1112
import java.io.IOException;
13+
import java.net.URI;
1214
import java.nio.file.Files;
1315
import java.nio.file.Path;
16+
import java.nio.file.Paths;
1417
import java.util.*;
1518
import java.util.concurrent.ConcurrentHashMap;
1619
import java.util.stream.Collectors;
1720
import java.util.stream.Stream;
21+
import java.util.stream.StreamSupport;
1822

1923
/** The core logic that converts SemanticDB into LSIF. */
2024
public class LsifSemanticdb {
@@ -49,6 +53,114 @@ private void run() throws IOException {
4953
return;
5054
}
5155
options.reporter.startProcessing(files.size());
56+
if (options.format.isTyped()) {
57+
runTyped(files, packages);
58+
} else {
59+
runGraph(files, packages);
60+
}
61+
writer.build();
62+
options.reporter.endProcessing();
63+
}
64+
65+
private void runTyped(List<Path> files, PackageTable packages) {
66+
writer.emitTyped(typedMetadata());
67+
filesStream(files).forEach(document -> processTypedDocument(document, packages));
68+
}
69+
70+
private String typedSymbol(String symbol, Package pkg) {
71+
if (symbol.startsWith("local")) {
72+
return "local " + symbol.substring("local".length());
73+
}
74+
return "semanticdb maven " + pkg.repoName() + " " + pkg.version() + " " + symbol;
75+
}
76+
77+
private void processTypedDocument(Path path, PackageTable packages) {
78+
for (LsifTextDocument doc : parseTextDocument(path).collect(Collectors.toList())) {
79+
if (doc.semanticdb.getOccurrencesCount() == 0) {
80+
continue;
81+
}
82+
83+
Path absolutePath = Paths.get(URI.create(doc.semanticdb.getUri()));
84+
String relativePath =
85+
StreamSupport.stream(options.sourceroot.relativize(absolutePath).spliterator(), false)
86+
.map(p -> p.getFileName().toString())
87+
.collect(Collectors.joining("/"));
88+
LsifTyped.Document.Builder tdoc =
89+
LsifTyped.Document.newBuilder().setRelativePath(relativePath);
90+
for (SymbolOccurrence occ : doc.sortedSymbolOccurrences()) {
91+
int role = 0;
92+
if (occ.getRole() == Role.DEFINITION) {
93+
role |= LsifTyped.SymbolRole.Definition_VALUE;
94+
}
95+
boolean isSingleLineRange = occ.getRange().getStartLine() == occ.getRange().getEndLine();
96+
Iterable<Integer> range =
97+
isSingleLineRange
98+
? Arrays.asList(
99+
occ.getRange().getStartLine(),
100+
occ.getRange().getStartCharacter(),
101+
occ.getRange().getEndCharacter())
102+
: Arrays.asList(
103+
occ.getRange().getStartLine(),
104+
occ.getRange().getStartCharacter(),
105+
occ.getRange().getEndLine(),
106+
occ.getRange().getEndCharacter());
107+
Package pkg = packages.packageForSymbol(occ.getSymbol()).orElse(Package.EMPTY);
108+
tdoc.addOccurrences(
109+
LsifTyped.Occurrence.newBuilder()
110+
.addAllRange(range)
111+
.setSymbol(typedSymbol(occ.getSymbol(), pkg))
112+
.setSymbolRoles(role));
113+
}
114+
Symtab symtab = new Symtab(doc.semanticdb);
115+
for (SymbolInformation info : doc.semanticdb.getSymbolsList()) {
116+
Package pkg = packages.packageForSymbol(info.getSymbol()).orElse(Package.EMPTY);
117+
LsifTyped.SymbolInformation.Builder tinfo =
118+
LsifTyped.SymbolInformation.newBuilder().setSymbol(typedSymbol(info.getSymbol(), pkg));
119+
120+
for (String overriddenSymbol : info.getOverriddenSymbolsList()) {
121+
if (isIgnoredOverriddenSymbol(overriddenSymbol)) {
122+
continue;
123+
}
124+
Package overriddenSymbolPkg =
125+
packages.packageForSymbol(overriddenSymbol).orElse(Package.EMPTY);
126+
tinfo.addRelationships(
127+
LsifTyped.Relationship.newBuilder()
128+
.setSymbol(typedSymbol(overriddenSymbol, overriddenSymbolPkg))
129+
.setIsImplementation(true)
130+
.setIsReference(SemanticdbSymbols.isMethod(info.getSymbol())));
131+
}
132+
if (info.hasSignature()) {
133+
String language =
134+
doc.semanticdb.getLanguage().toString().toLowerCase(Locale.ROOT).intern();
135+
String signature = new SignatureFormatter(info, symtab).formatSymbol();
136+
tinfo.addDocumentation("```" + language + "\n" + signature + "\n```");
137+
}
138+
String documentation = info.getDocumentation().getMessage();
139+
if (!documentation.isEmpty()) {
140+
tinfo.addDocumentation(documentation);
141+
}
142+
tdoc.addSymbols(tinfo);
143+
}
144+
writer.emitTyped(LsifTyped.Index.newBuilder().addDocuments(tdoc).build());
145+
}
146+
}
147+
148+
private LsifTyped.Index typedMetadata() {
149+
return LsifTyped.Index.newBuilder()
150+
.setMetadata(
151+
LsifTyped.Metadata.newBuilder()
152+
.setVersion(LsifTyped.ProtocolVersion.UnspecifiedProtocolVersion)
153+
.setProjectRoot(options.sourceroot.toUri().toString())
154+
.setTextDocumentEncoding(LsifTyped.TextEncoding.UTF8)
155+
.setToolInfo(
156+
LsifTyped.ToolInfo.newBuilder()
157+
.setName(options.toolInfo.getName())
158+
.setVersion(options.toolInfo.getVersion())
159+
.addAllArguments(options.toolInfo.getArgsList())))
160+
.build();
161+
}
162+
163+
private void runGraph(List<Path> files, PackageTable packages) {
52164
writer.emitMetaData();
53165
int projectId = writer.emitProject(options.language);
54166

@@ -57,11 +169,7 @@ private void run() throws IOException {
57169
filesStream(files)
58170
.flatMap(d -> processPath(d, isExportedSymbol, packages))
59171
.collect(Collectors.toList());
60-
61172
writer.emitContains(projectId, documentIds);
62-
63-
writer.build();
64-
options.reporter.endProcessing();
65173
}
66174

67175
private Stream<Path> filesStream(List<Path> files) {
@@ -170,16 +278,22 @@ private Integer processDocumentUnsafe(
170278

171279
// Overrides
172280
if (symbolInformation.getOverriddenSymbolsCount() > 0) {
173-
int[] overriddenReferenceResultIds = new int[symbolInformation.getOverriddenSymbolsCount()];
281+
List<Integer> overriddenReferenceResultIds =
282+
new ArrayList<>(symbolInformation.getOverriddenSymbolsCount());
174283
for (int i = 0; i < symbolInformation.getOverriddenSymbolsCount(); i++) {
175284
String overriddenSymbol = symbolInformation.getOverriddenSymbols(i);
285+
if (isIgnoredOverriddenSymbol(overriddenSymbol)) {
286+
continue;
287+
}
176288
ResultIds overriddenIds = results.getOrInsertResultSet(overriddenSymbol);
177-
overriddenReferenceResultIds[i] = overriddenIds.referenceResult;
289+
overriddenReferenceResultIds.add(overriddenIds.referenceResult);
178290
writer.emitReferenceResultsItemEdge(
179-
overriddenIds.referenceResult, new int[] {rangeId}, doc.id);
291+
overriddenIds.referenceResult, Collections.singletonList(rangeId), doc.id);
292+
}
293+
if (overriddenReferenceResultIds.size() > 0) {
294+
writer.emitReferenceResultsItemEdge(
295+
ids.referenceResult, overriddenReferenceResultIds, doc.id);
180296
}
181-
writer.emitReferenceResultsItemEdge(
182-
ids.referenceResult, overriddenReferenceResultIds, doc.id);
183297
}
184298
}
185299
writer.emitContains(doc.id, new ArrayList<>(rangeIds));
@@ -214,4 +328,10 @@ private Semanticdb.TextDocuments textDocumentsParseFrom(Path semanticdbPath) thr
214328
return Semanticdb.TextDocuments.parseFrom(bytes);
215329
}
216330
}
331+
332+
private boolean isIgnoredOverriddenSymbol(String symbol) {
333+
// Skip java/lang/Object# since it's the parent of all classes
334+
// making it noisy for "find implementations" results.
335+
return symbol.equals("java/lang/Object#");
336+
}
217337
}

0 commit comments

Comments
 (0)