enhance symbol cache to update multiple files at once and use that new capability from java symbol indexer
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user