enhance symbol cache to update multiple files at once and use that new capability from java symbol indexer

This commit is contained in:
Martin Lippert
2020-02-03 14:55:37 +01:00
parent 4d6de9f41c
commit 74d8aef562
5 changed files with 164 additions and 18 deletions

View File

@@ -59,6 +59,7 @@ import org.springframework.ide.vscode.commons.util.UriUtil;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
/**
* @author Martin Lippert
@@ -183,46 +184,55 @@ public class SpringIndexerJava implements SpringIndexer {
Set<String> scannedFiles = new HashSet<>();
Set<String> scannedTypes = new HashSet<>();
Map<String, UpdatedDoc> updatedDocs = new HashMap<>();
Map<String, UpdatedDoc> updatedDocs = new HashMap<>(); // docURI -> UpdatedDoc
String[] javaFiles = new String[docs.length];
long[] lastModified = new long[docs.length];
for (int i = 0; i < docs.length; i++) {
updatedDocs.put(docs[i].getDocURI(), docs[i]);
String file = UriUtil.toFileString(docs[i].getDocURI());
javaFiles[i] = file;
lastModified[i] = docs[i].getLastModified();
scannedFiles.add(file);
}
List<CachedSymbol> generatedSymbols = new ArrayList<CachedSymbol>();
Multimap<String, String> dependencies = MultimapBuilder.hashKeys().hashSetValues().build();
FileASTRequestor requestor = new FileASTRequestor() {
@Override
public void acceptAST(String sourceFilePath, CompilationUnit cu) {
File file = new File(sourceFilePath);
String docURI = UriUtil.toUri(file).toString();
long lastModified = file.lastModified();
AtomicReference<TextDocument> docRef = new AtomicReference<>();
UpdatedDoc updatedDoc = updatedDocs.get(docURI);
List<CachedSymbol> generatedSymbols = new ArrayList<CachedSymbol>();
long lastModified = updatedDoc.getLastModified();
AtomicReference<TextDocument> docRef = new AtomicReference<>();
SpringIndexerJavaContext context = new SpringIndexerJavaContext(project, cu, docURI, sourceFilePath,
lastModified, docRef, updatedDoc.getContent().get(), generatedSymbols, SCAN_PASS.ONE, new ArrayList<>(), scannedTypes);
dependencies.putAll(sourceFilePath, context.getDependencies());
scanAST(context);
SymbolCacheKey cacheKey = getCacheKey(project);
SpringIndexerJava.this.cache.update(cacheKey, sourceFilePath, lastModified, generatedSymbols, context.getDependencies());
for (CachedSymbol symbol : generatedSymbols) {
symbolHandler.addSymbol(project, symbol.getDocURI(), symbol.getEnhancedSymbol());
}
fileScannedEvent(sourceFilePath);
}
};
parser.createASTs(javaFiles, null, new String[0], requestor, null);
for (CachedSymbol symbol : generatedSymbols) {
symbolHandler.addSymbol(project, symbol.getDocURI(), symbol.getEnhancedSymbol());
}
SymbolCacheKey cacheKey = getCacheKey(project);
SpringIndexerJava.this.cache.update(cacheKey, javaFiles, lastModified, generatedSymbols, dependencies);
scanAffectedFiles(project, scannedTypes, scannedFiles);
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019 Pivotal, Inc.
* Copyright (c) 2019, 2020 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -26,6 +26,7 @@ public interface SymbolCache {
Pair<CachedSymbol[], Multimap<String, String>> retrieve(SymbolCacheKey cacheKey, String[] files);
void update(SymbolCacheKey cacheKey, String file, long lastModified, List<CachedSymbol> generatedSymbols, Set<String> dependencies);
void update(SymbolCacheKey cacheKey, String[] files, long[] lastModified, List<CachedSymbol> generatedSymbols, Multimap<String, String> dependencies);
void remove(SymbolCacheKey cacheKey);
void removeFile(SymbolCacheKey symbolCacheKey, String file);

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019 Pivotal, Inc.
* Copyright (c) 2019 2020 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -19,6 +19,7 @@ import java.nio.file.Files;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -170,9 +171,10 @@ public class SymbolCacheOnDisc implements SymbolCache {
@Override
public void update(SymbolCacheKey cacheKey, String file, long lastModified, List<CachedSymbol> generatedSymbols, Set<String> dependencies) {
if (dependencies==null) {
if (dependencies == null) {
dependencies = ImmutableSet.of();
}
CacheStore cacheStore = this.stores.get(cacheKey);
if (cacheStore != null) {
@@ -197,6 +199,48 @@ public class SymbolCacheOnDisc implements SymbolCache {
}
}
@Override
public void update(SymbolCacheKey cacheKey, String[] files, long[] lastModified, List<CachedSymbol> generatedSymbols, Multimap<String, String> dependencies) {
if (dependencies == null) {
dependencies = ImmutableMultimap.of();
}
CacheStore cacheStore = this.stores.get(cacheKey);
if (cacheStore != null) {
SortedMap<String, Long> timestampedFiles = new TreeMap<>(cacheStore.getTimestampedFiles());
Set<String> allDocURIs = new HashSet<>();
Map<String, Collection<String>> changedDependencies = new HashMap<>(cacheStore.getDependencies());
for (int i = 0; i < files.length; i++) {
// update cache internal map of timestamps per file
String docURI = UriUtil.toUri(new File(files[i])).toString();
allDocURIs.add(docURI);
timestampedFiles.put(files[i], lastModified[i]);
// update cache internal map of dependencies per file
Collection<String> updatedDependencies = dependencies.get(files[i]);
if (updatedDependencies == null || updatedDependencies.isEmpty()) {
changedDependencies.remove(files[i]);
} else {
changedDependencies.put(files[i], ImmutableSet.copyOf(updatedDependencies));
}
}
// update cache internal list of cached symbols (by removing old ones and adding all new ones)
List<CachedSymbol> cachedSymbols = cacheStore.getSymbols().stream()
.filter(cachedSymbol -> !allDocURIs.contains(cachedSymbol.getDocURI()))
.collect(Collectors.toList());
cachedSymbols.addAll(generatedSymbols);
// store the complete cache content of this project to disc
save(cacheKey, cachedSymbols, timestampedFiles, changedDependencies);
}
}
private void save(SymbolCacheKey cacheKey, List<CachedSymbol> generatedSymbols,
SortedMap<String, Long> timestampedFiles, Map<String, Collection<String>> dependencies) {
CacheStore store = new CacheStore(timestampedFiles, generatedSymbols, dependencies);

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019 Pivotal, Inc.
* Copyright (c) 2019, 2020 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -35,6 +35,10 @@ public class SymbolCacheVoid implements SymbolCache {
public void update(SymbolCacheKey cacheKey, String file, long lastModified, List<CachedSymbol> generatedSymbols, Set<String> dependencies) {
}
@Override
public void update(SymbolCacheKey cacheKey, String[] file, long[] lastModified, List<CachedSymbol> generatedSymbols, Multimap<String, String> dependencies) {
}
@Override
public void remove(SymbolCacheKey cacheKey) {
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019 Pivotal, Inc.
* Copyright (c) 2019, 2020 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -32,6 +32,7 @@ import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.SymbolInformation;
import org.eclipse.lsp4j.SymbolKind;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformation;
@@ -46,7 +47,6 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
public class SymbolCacheOnDiscTest {
@@ -284,6 +284,93 @@ public class SymbolCacheOnDiscTest {
assertEquals(2, cachedSymbols.length);
}
@Test
public void testSymbolsAddedToMultipleFiles() throws Exception {
// create 3 files with one symbol each
Path file1 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile1");
Path file2 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile2");
Path file3 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile3");
Files.createFile(file1);
Files.createFile(file2);
Files.createFile(file3);
FileTime timeFile1 = Files.getLastModifiedTime(file1);
FileTime timeFile2 = Files.getLastModifiedTime(file2);
FileTime timeFile3 = Files.getLastModifiedTime(file3);
String[] files = {file1.toAbsolutePath().toString(), file2.toAbsolutePath().toString(), file3.toAbsolutePath().toString()};
String doc1URI = UriUtil.toUri(file1.toFile()).toString();
String doc2URI = UriUtil.toUri(file2.toFile()).toString();
String doc3URI = UriUtil.toUri(file3.toFile()).toString();
List<CachedSymbol> generatedSymbols = new ArrayList<>();
SymbolInformation symbol1 = new SymbolInformation("symbol1", SymbolKind.Field, new Location(doc1URI, new Range(new Position(3, 10), new Position(3, 20))));
EnhancedSymbolInformation enhancedSymbol1 = new EnhancedSymbolInformation(symbol1, null);
generatedSymbols.add(new CachedSymbol(doc1URI, timeFile1.toMillis(), enhancedSymbol1));
SymbolInformation symbol2 = new SymbolInformation("symbol2", SymbolKind.Field, new Location(doc2URI, new Range(new Position(3, 10), new Position(3, 20))));
EnhancedSymbolInformation enhancedSymbol2 = new EnhancedSymbolInformation(symbol2, null);
generatedSymbols.add(new CachedSymbol(doc2URI, timeFile2.toMillis(), enhancedSymbol2));
SymbolInformation symbol3 = new SymbolInformation("symbol3", SymbolKind.Field, new Location(doc3URI, new Range(new Position(3, 10), new Position(3, 20))));
EnhancedSymbolInformation enhancedSymbol3 = new EnhancedSymbolInformation(symbol3, null);
generatedSymbols.add(new CachedSymbol(doc3URI, timeFile3.toMillis(), enhancedSymbol3));
// store original version of the symbols to the cache
cache.store(new SymbolCacheKey("somekey", "1"), files, generatedSymbols, null);
// create updated and new symbols
List<CachedSymbol> updatedSymbols = new ArrayList<>();
SymbolInformation updatedSymbol1 = new SymbolInformation("symbol1", SymbolKind.Field, new Location(doc1URI, new Range(new Position(3, 10), new Position(3, 20))));
EnhancedSymbolInformation updatedEnhancedSymbol1 = new EnhancedSymbolInformation(updatedSymbol1, null);
SymbolInformation newSymbol1 = new SymbolInformation("symbol1-new", SymbolKind.Interface, new Location(doc1URI, new Range(new Position(5, 5), new Position(5, 10))));
EnhancedSymbolInformation newEnhancedSymbol1 = new EnhancedSymbolInformation(newSymbol1, null);
updatedSymbols.add(new CachedSymbol(doc1URI, timeFile1.toMillis() + 2000, updatedEnhancedSymbol1));
updatedSymbols.add(new CachedSymbol(doc1URI, timeFile1.toMillis() + 2000, newEnhancedSymbol1));
assertTrue(file1.toFile().setLastModified(timeFile1.toMillis() + 2000));
SymbolInformation updatedSymbol2 = new SymbolInformation("symbol2-updated", SymbolKind.Field, new Location(doc2URI, new Range(new Position(3, 10), new Position(3, 20))));
EnhancedSymbolInformation updatedEnhancedSymbol2 = new EnhancedSymbolInformation(updatedSymbol2, null);
updatedSymbols.add(new CachedSymbol(doc2URI, timeFile2.toMillis() + 3000, updatedEnhancedSymbol2));
assertTrue(file2.toFile().setLastModified(timeFile2.toMillis() + 3000));
String[] updatedFiles = new String[] {file1.toAbsolutePath().toString(), file2.toAbsolutePath().toString()};
long[] updatedModificationTimestamps = new long[] {timeFile1.toMillis() + 2000, timeFile2.toMillis() + 3000};
// update multiple files in the cache
cache.update(new SymbolCacheKey("somekey", "1"), updatedFiles, updatedModificationTimestamps, updatedSymbols, null);
// double check whether all changes got stored and retrieved correctly
CachedSymbol[] cachedSymbols = cache.retrieveSymbols(new SymbolCacheKey("somekey", "1"), files);
assertNotNull(cachedSymbols);
assertEquals(4, cachedSymbols.length);
assertSymbol(updatedEnhancedSymbol1, cachedSymbols);
assertSymbol(newEnhancedSymbol1, cachedSymbols);
assertSymbol(updatedEnhancedSymbol2, cachedSymbols);
assertSymbol(enhancedSymbol3, cachedSymbols);
}
private void assertSymbol(EnhancedSymbolInformation enhancedSymbol, CachedSymbol[] cachedSymbols) {
for (CachedSymbol cachedSymbol : cachedSymbols) {
SymbolInformation symbol = cachedSymbol.getEnhancedSymbol().getSymbol();
if (symbol.toString().equals(enhancedSymbol.getSymbol().toString())) {
return;
}
}
Assert.fail("symbol not found: " + enhancedSymbol.getSymbol().toString());
}
@Test
public void testDependencyAddedToExistingFile() throws Exception {
Path file1 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile1");