improve file observer mechanism to work with multiple files

This commit is contained in:
Martin Lippert
2020-01-31 09:42:41 +01:00
parent 6413bcb7cc
commit 4d6de9f41c
9 changed files with 200 additions and 98 deletions

View File

@@ -194,14 +194,14 @@ public class SpringSymbolIndex implements InitializingBean {
public void serverInitialized() {
List<String> globPattern = Arrays.asList(springIndexerJava.getFileWatchPatterns());
getWorkspaceService().getFileObserver().onFileDeleted(globPattern, (file) -> {
deleteDocument(new TextDocumentIdentifier(file).getUri());
getWorkspaceService().getFileObserver().onFilesDeleted(globPattern, (files) -> {
deleteDocuments(files);
});
getWorkspaceService().getFileObserver().onFileCreated(globPattern, (file) -> {
createDocument(new TextDocumentIdentifier(file).getUri());
getWorkspaceService().getFileObserver().onFilesCreated(globPattern, (files) -> {
createDocuments(files);
});
getWorkspaceService().getFileObserver().onFileChanged(globPattern, (file) -> {
updateDocument(new TextDocumentIdentifier(file).getUri(), null, "file changed");
getWorkspaceService().getFileObserver().onFilesChanged(globPattern, (files) -> {
updateDocuments(files, "file changed");
});
}
@@ -227,17 +227,17 @@ public class SpringSymbolIndex implements InitializingBean {
private void addXmlFileListeners(List<String> globPattern) {
removeXmlFileListeners();
watchXMLDeleteRegistration = getWorkspaceService().getFileObserver().onFileDeleted(globPattern,
(file) -> {
deleteDocument(new TextDocumentIdentifier(file).getUri());
watchXMLDeleteRegistration = getWorkspaceService().getFileObserver().onFilesDeleted(globPattern,
(files) -> {
deleteDocuments(files);
});
watchXMLCreatedRegistration = getWorkspaceService().getFileObserver().onFileCreated(globPattern,
(file) -> {
createDocument(new TextDocumentIdentifier(file).getUri());
watchXMLCreatedRegistration = getWorkspaceService().getFileObserver().onFilesCreated(globPattern,
(files) -> {
createDocuments(files);
});
watchXMLChangedRegistration = getWorkspaceService().getFileObserver().onFileChanged(globPattern,
(file) -> {
updateDocument(new TextDocumentIdentifier(file).getUri(), null, "xml changed");
watchXMLChangedRegistration = getWorkspaceService().getFileObserver().onFilesChanged(globPattern,
(files) -> {
updateDocuments(files, "xml changed");
});
}
@@ -346,6 +346,32 @@ public class SpringSymbolIndex implements InitializingBean {
}
}
public CompletableFuture<Void> createDocuments(String[] docURIs) {
synchronized(this) {
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (SpringIndexer indexer : this.indexers) {
String[] interestingDocs = getDocumentsInterestingForIndexer(indexer, docURIs);
Map<String, IJavaProject> projectsForDocs = getProjectsForDocs(interestingDocs);
Map<IJavaProject, List<String>> projectMapping = getProjectMapping(projectsForDocs);
for (IJavaProject project : projectMapping.keySet()) {
List<String> docs = projectMapping.get(project);
try {
UpdatedDoc[] updatedDocs = docs.stream().map(doc -> createUpdatedDoc(doc, null)).toArray(UpdatedDoc[]::new);
futures.add(updateItems(project, updatedDocs, indexer));
}
catch (Exception e) {
log.error("{}", e);
}
}
}
return CompletableFuture.allOf((CompletableFuture[]) futures.toArray(new CompletableFuture[futures.size()]));
}
}
private JavaProjectFinder projectFinder() {
return params.projectFinder;
}
@@ -454,7 +480,7 @@ public class SpringSymbolIndex implements InitializingBean {
try {
Optional<IJavaProject> maybeProject = projectFinder().find(new TextDocumentIdentifier(deletedDocURI));
if (maybeProject.isPresent()) {
DeleteItem deleteItem = new DeleteItem(maybeProject.get(), deletedDocURI, this.indexers);
DeleteItems deleteItem = new DeleteItems(maybeProject.get(), new String[] {deletedDocURI}, this.indexers);
return CompletableFuture.runAsync(deleteItem, this.updateQueue);
}
}
@@ -467,6 +493,30 @@ public class SpringSymbolIndex implements InitializingBean {
return null;
}
public CompletableFuture<Void> deleteDocuments(String[] deletedDocURIs) {
synchronized(this) {
try {
List<CompletableFuture<Void>> futures = new ArrayList<>();
Map<String, IJavaProject> projectsForDocs = getProjectsForDocs(deletedDocURIs);
Map<IJavaProject, List<String>> projectMapping = getProjectMapping(projectsForDocs);
for (IJavaProject project : projectMapping.keySet()) {
List<String> docURIs = projectMapping.get(project);
DeleteItems deleteItems = new DeleteItems(project, (String[]) docURIs.toArray(new String[docURIs.size()]), this.indexers);
futures.add(CompletableFuture.runAsync(deleteItems, this.updateQueue));
}
return CompletableFuture.allOf((CompletableFuture[]) futures.toArray(new CompletableFuture[futures.size()]));
}
catch (Exception e) {
log.error("", e);
return Futures.error(e);
}
}
}
public List<SymbolInformation> getAllSymbols(String query) {
if (query != null && query.length() > 0) {
synchronized(this.symbols) {
@@ -651,24 +701,27 @@ public class SpringSymbolIndex implements InitializingBean {
}, this.updateQueue);
}
private class DeleteItem implements Runnable {
private class DeleteItems implements Runnable {
private final String docURI;
private final String[] docURIs;
private final IJavaProject project;
private final SpringIndexer[] indexer;
public DeleteItem(IJavaProject project, String docURI, SpringIndexer[] indexer) {
public DeleteItems(IJavaProject project, String[] docURIs, SpringIndexer[] indexer) {
this.project = project;
this.docURI = docURI;
this.docURIs = docURIs;
this.indexer = indexer;
}
@Override
public void run() {
try {
removeSymbolsByDoc(project, docURI);
for (String doc : this.docURIs) {
removeSymbolsByDoc(project, doc);
}
for (SpringIndexer index : this.indexer) {
index.removeFiles(project, new String[] {docURI});
index.removeFiles(project, docURIs);
}
} catch (Exception e) {
log.error("{}", e);

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017, 2019 Pivotal, Inc.
* Copyright (c) 2017, 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

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2018 Pivotal, Inc.
* Copyright (c) 2018, 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
@@ -15,7 +15,9 @@ import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import org.eclipse.lsp4j.TextDocumentIdentifier;
@@ -63,11 +65,20 @@ public class AdHocSpringPropertyIndexProvider implements ProjectBasedPropertyInd
"**/application.properties",
"**/application.yml"
), changed -> {
log.debug("File changed: {}", changed);
projectFinder.find(new TextDocumentIdentifier(changed)).ifPresent(project -> {
log.debug("Files changed: {}", (Object[])changed);
Set<IJavaProject> affectedProjects = new HashSet<>();
for (String docURI : changed) {
projectFinder.find(new TextDocumentIdentifier(docURI)).ifPresent(project -> {
affectedProjects.add(project);
});
}
for (IJavaProject project : affectedProjects) {
log.debug("=> Project changed: {}", project.getElementName());
indexes.invalidate(project);
});
}
});
}
if (documents!=null) {

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2018, 2019 Pivotal, Inc.
* Copyright (c) 2018, 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
@@ -204,13 +204,16 @@ public class MockProjects {
}
private static class FileListener {
final PathMatcher matcher;
final Consumer<String> handler;
FileListener(List<String> globPatterns, Consumer<String> listener) {
final Consumer<String[]> handler;
FileListener(List<String> globPatterns, Consumer<String[]> listener) {
super();
this.matcher = buildPathMatcher(globPatterns);
this.handler = listener;
}
private PathMatcher buildPathMatcher(List<String> globPatterns) {
if (globPatterns.size()==0) {
return path -> true;
@@ -241,7 +244,7 @@ public class MockProjects {
final Map<String,FileListener> change_listeners = new HashMap<>();
final Map<String,FileListener> delete_listeners = new HashMap<>();
private String add(Map<String, FileListener> listeners, List<String> globPatterns, Consumer<String> handler) {
private String add(Map<String, FileListener> listeners, List<String> globPatterns, Consumer<String[]> handler) {
String id = ""+idGen.incrementAndGet();
synchronized (listeners) {
listeners.put(id, new FileListener(globPatterns, handler));
@@ -262,25 +265,25 @@ public class MockProjects {
synchronized (listeners) {
for (FileListener l : listeners.values()) {
if (l.matcher.matches(path)) {
l.handler.accept(target.toURI().toString());
l.handler.accept(new String[] {target.toURI().toString()});
}
}
}
}
@Override
public String onFileCreated(List<String> globPattern, Consumer<String> handler) {
public String onFilesCreated(List<String> globPattern, Consumer<String[]> handler) {
return add(create_listeners, globPattern, handler);
}
@Override
public String onFileChanged(List<String> globPattern, Consumer<String> handler) {
public String onFilesChanged(List<String> globPattern, Consumer<String[]> handler) {
return add(change_listeners, globPattern, handler);
}
@Override
public String onFileDeleted(List<String> globPattern, Consumer<String> handler) {
public String onFilesDeleted(List<String> globPattern, Consumer<String[]> handler) {
return add(delete_listeners, globPattern, handler);
}