Skip to content

Commit

Permalink
Use CompletableFuture to load DOMDocument
Browse files Browse the repository at this point in the history
Fix #439
  • Loading branch information
angelozerr committed Jun 19, 2019
1 parent a0254be commit 231bf5d
Show file tree
Hide file tree
Showing 11 changed files with 275 additions and 356 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
import org.eclipse.lsp4j.services.LanguageServer;
import org.eclipse.lsp4j.services.TextDocumentService;
import org.eclipse.lsp4j.services.WorkspaceService;
import org.eclipse.lsp4xml.commons.ModelTextDocument;
import org.eclipse.lsp4xml.commons.ParentProcessWatcher.ProcessLanguageServer;
import org.eclipse.lsp4xml.commons.TextDocument;
import org.eclipse.lsp4xml.customservice.AutoCloseTagResponse;
import org.eclipse.lsp4xml.customservice.XMLCustomService;
import org.eclipse.lsp4xml.dom.DOMDocument;
Expand Down Expand Up @@ -82,7 +82,7 @@ public XMLLanguageServer() {

@Override
public CompletableFuture<InitializeResult> initialize(InitializeParams params) {
LOGGER.info("Initializing LSP4XML server " + getVersion()+" with " + System.getProperty("java.home"));
LOGGER.info("Initializing LSP4XML server " + getVersion() + " with " + System.getProperty("java.home"));
this.parentProcessId = params.getProcessId();

// Update XML language service extensions with InitializeParams
Expand Down Expand Up @@ -143,7 +143,7 @@ public synchronized void updateSettings(Object initializationOptionsSettings) {
}

XMLSymbolSettings newSymbols = xmlClientSettings.getSymbols();
if(newSymbols != null) {
if (newSymbols != null) {
xmlTextDocumentService.updateSymbolSettings(newSymbols);
}

Expand All @@ -154,9 +154,9 @@ public synchronized void updateSettings(Object initializationOptionsSettings) {
}

XMLExperimentalSettings experimentalSettings = xmlClientSettings.getExperimental();
if(experimentalSettings != null) {
if (experimentalSettings != null) {
XMLIncrementalSupportSettings incrementalSettings = experimentalSettings.getIncrementalSupport();
if(incrementalSettings != null) {
if (incrementalSettings != null) {
xmlTextDocumentService.updateIncrementalSettings(incrementalSettings);
}
}
Expand Down Expand Up @@ -226,17 +226,14 @@ public long getParentProcessId() {

@Override
public CompletableFuture<AutoCloseTagResponse> closeTag(TextDocumentPositionParams params) {
return computeAsync((cancelChecker) -> {
DOMDocument xmlDocument = xmlTextDocumentService.getXMLDocument(params.getTextDocument().getUri(),
cancelChecker);
return xmlTextDocumentService.computeDOMAsync(params.getTextDocument(), (cancelChecker, xmlDocument) -> {
return getXMLLanguageService().doAutoClose(xmlDocument, params.getPosition(), cancelChecker);
});
}

@Override
public DOMDocument getDocument(String uri) {
TextDocument document = xmlTextDocumentService.getDocument(uri);
return document != null ? xmlTextDocumentService.getXMLDocument(document) : null;
ModelTextDocument<DOMDocument> document = xmlTextDocumentService.getDocument(uri);
return document != null ? document.getModel().getNow(null) : null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -55,16 +56,15 @@
import org.eclipse.lsp4j.SymbolInformation;
import org.eclipse.lsp4j.TextDocumentClientCapabilities;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.TextDocumentItem;
import org.eclipse.lsp4j.TextDocumentPositionParams;
import org.eclipse.lsp4j.TextEdit;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.eclipse.lsp4j.services.TextDocumentService;
import org.eclipse.lsp4xml.commons.LanguageModelCache;
import org.eclipse.lsp4xml.commons.ModelTextDocument;
import org.eclipse.lsp4xml.commons.ModelTextDocuments;
import org.eclipse.lsp4xml.commons.TextDocument;
import org.eclipse.lsp4xml.commons.TextDocumentVersionChecker;
import org.eclipse.lsp4xml.commons.TextDocuments;
import org.eclipse.lsp4xml.dom.DOMDocument;
import org.eclipse.lsp4xml.dom.DOMParser;
Expand All @@ -84,16 +84,15 @@
public class XMLTextDocumentService implements TextDocumentService {

private final XMLLanguageServer xmlLanguageServer;
private final TextDocuments documents;
private final LanguageModelCache<DOMDocument> xmlDocuments;
private final TextDocuments<ModelTextDocument<DOMDocument>> documents;
private SharedSettings sharedSettings;

/**
* Save context.
*/
class SaveContext extends AbstractSaveContext {

private final Collection<TextDocument> documentsToValidate;
private final Collection<ModelTextDocument<DOMDocument>> documentsToValidate;

public SaveContext(Object settings) {
super(settings);
Expand All @@ -108,8 +107,9 @@ public SaveContext(String uri) {
@Override
public void collectDocumentToValidate(Predicate<DOMDocument> validateDocumentPredicate) {
documents.all().stream().forEach(document -> {
DOMDocument xmlDocument = getXMLDocument(document);
if (!documentsToValidate.contains(document) && validateDocumentPredicate.test(xmlDocument)) {
DOMDocument xmlDocument = document.getModel().getNow(null);
if (xmlDocument != null && !documentsToValidate.contains(document)
&& validateDocumentPredicate.test(xmlDocument)) {
documentsToValidate.add(document);
}
});
Expand All @@ -131,12 +131,10 @@ public void triggerValidationIfNeeded() {

public XMLTextDocumentService(XMLLanguageServer xmlLanguageServer) {
this.xmlLanguageServer = xmlLanguageServer;
this.documents = new TextDocuments();
DOMParser parser = DOMParser.getInstance();
this.xmlDocuments = new LanguageModelCache<DOMDocument>(10, 60, documents, (document, cancelChecker) -> {
this.documents = new ModelTextDocuments<DOMDocument>((document, cancelChecker) -> {
return parser.parse(document, getXMLLanguageService().getResolverExtensionManager(), true, cancelChecker);
});

this.sharedSettings = new SharedSettings();
}

Expand All @@ -153,29 +151,9 @@ public void updateClientCapabilities(ClientCapabilities capabilities) {
}
}

public TextDocument getDocument(String uri) {
return documents.get(uri);
}

public DOMDocument getXMLDocument(TextDocumentItem document) {
return xmlDocuments.get(document);
}

public DOMDocument getXMLDocument(String uri, CancelChecker cancelChecker) {
TextDocument document = getDocument(uri, cancelChecker);
cancelChecker.checkCanceled();
return getXMLDocument(document);
}

TextDocument getDocument(String uri, CancelChecker cancelChecker) {
cancelChecker.checkCanceled();
return getDocument(uri);
}

@Override
public CompletableFuture<Either<List<CompletionItem>, CompletionList>> completion(CompletionParams params) {
return computeAsync((cancelChecker) -> {
DOMDocument xmlDocument = getXMLDocument(params.getTextDocument().getUri(), cancelChecker);
return computeDOMAsync(params.getTextDocument(), (cancelChecker, xmlDocument) -> {
CompletionList list = getXMLLanguageService().doComplete(xmlDocument, params.getPosition(), sharedSettings,
cancelChecker);
return Either.forRight(list);
Expand All @@ -184,8 +162,7 @@ public CompletableFuture<Either<List<CompletionItem>, CompletionList>> completio

@Override
public CompletableFuture<Hover> hover(TextDocumentPositionParams params) {
return computeAsync((cancelChecker) -> {
DOMDocument xmlDocument = getXMLDocument(params.getTextDocument().getUri(), cancelChecker);
return computeDOMAsync(params.getTextDocument(), (cancelChecker, xmlDocument) -> {
return getXMLLanguageService().doHover(xmlDocument, params.getPosition(), cancelChecker);
});
}
Expand All @@ -198,24 +175,22 @@ private XMLFormattingOptions getFormattingSettings(String uri) {

@Override
public CompletableFuture<List<? extends DocumentHighlight>> documentHighlight(TextDocumentPositionParams params) {
return computeAsync((cancelChecker) -> {
DOMDocument xmlDocument = getXMLDocument(params.getTextDocument().getUri(), cancelChecker);
return computeDOMAsync(params.getTextDocument(), (cancelChecker, xmlDocument) -> {
return getXMLLanguageService().findDocumentHighlights(xmlDocument, params.getPosition(), cancelChecker);
});
}

@Override
public CompletableFuture<List<Either<SymbolInformation, DocumentSymbol>>> documentSymbol(
DocumentSymbolParams params) {

TextDocument document = getDocument(params.getTextDocument().getUri());
if(!sharedSettings.symbolSettings.isEnabled() || sharedSettings.symbolSettings.isExcluded(document.getUri())) {

if (!sharedSettings.symbolSettings.isEnabled() || sharedSettings.symbolSettings.isExcluded(document.getUri())) {
return CompletableFuture.completedFuture(Collections.emptyList());
}

return computeAsync((cancelChecker) -> {
DOMDocument xmlDocument = getXMLDocument(document);
return computeDOMAsync(params.getTextDocument(), (cancelChecker, xmlDocument) -> {
if (hierarchicalDocumentSymbolSupport) {
return getXMLLanguageService().findDocumentSymbols(xmlDocument, cancelChecker) //
.stream() //
Expand All @@ -239,7 +214,7 @@ public CompletableFuture<List<Either<SymbolInformation, DocumentSymbol>>> docume
public CompletableFuture<List<? extends TextEdit>> formatting(DocumentFormattingParams params) {
return computeAsync((cancelChecker) -> {
String uri = params.getTextDocument().getUri();
TextDocument document = getDocument(uri, cancelChecker);
TextDocument document = getDocument(uri);
return getXMLLanguageService().format(document, null,
XMLFormattingOptions.create(params.getOptions(), getFormattingSettings(uri)));
});
Expand All @@ -249,17 +224,15 @@ public CompletableFuture<List<? extends TextEdit>> formatting(DocumentFormatting
public CompletableFuture<List<? extends TextEdit>> rangeFormatting(DocumentRangeFormattingParams params) {
return computeAsync((cancelChecker) -> {
String uri = params.getTextDocument().getUri();
TextDocument document = getDocument(uri, cancelChecker);
TextDocument document = getDocument(uri);
return getXMLLanguageService().format(document, params.getRange(),
XMLFormattingOptions.create(params.getOptions(), getFormattingSettings(uri)));
});
}

@Override
public CompletableFuture<WorkspaceEdit> rename(RenameParams params) {
return computeAsync((monitor) -> {
TextDocument document = getDocument(params.getTextDocument().getUri());
DOMDocument xmlDocument = getXMLDocument(document);
return computeDOMAsync(params.getTextDocument(), (cancelChecker, xmlDocument) -> {
return getXMLLanguageService().doRename(xmlDocument, params.getPosition(), params.getNewName());
});
}
Expand All @@ -282,7 +255,6 @@ public void didChange(DidChangeTextDocumentParams params) {
@Override
public void didClose(DidCloseTextDocumentParams params) {
documents.onDidCloseTextDocument(params);
xmlDocuments.onDocumentRemoved(params.getTextDocument().getUri());
TextDocumentIdentifier document = params.getTextDocument();
String uri = document.getUri();
xmlLanguageServer.getLanguageClient()
Expand All @@ -291,44 +263,37 @@ public void didClose(DidCloseTextDocumentParams params) {

@Override
public CompletableFuture<List<FoldingRange>> foldingRange(FoldingRangeRequestParams params) {
return computeAsync((cancelChecker) -> {
DOMDocument xmlDocument = getXMLDocument(params.getTextDocument().getUri(), cancelChecker);
return computeDOMAsync(params.getTextDocument(), (cancelChecker, xmlDocument) -> {
return getXMLLanguageService().getFoldingRanges(xmlDocument, sharedSettings.foldingSettings, cancelChecker);
});
}

@Override
public CompletableFuture<List<DocumentLink>> documentLink(DocumentLinkParams params) {
return computeAsync((cancelChecker) -> {
DOMDocument xmlDocument = getXMLDocument(params.getTextDocument().getUri(), cancelChecker);
return computeDOMAsync(params.getTextDocument(), (cancelChecker, xmlDocument) -> {
return getXMLLanguageService().findDocumentLinks(xmlDocument);
});
}

@Override
public CompletableFuture<Either<List<? extends Location>, List<? extends LocationLink>>> definition(
TextDocumentPositionParams params) {
return computeAsync((cancelChecker) -> {
DOMDocument xmlDocument = getXMLDocument(params.getTextDocument().getUri(), cancelChecker);
Either e = Either.forLeft(getXMLLanguageService().findDefinition(xmlDocument, params.getPosition()));
return e;
return computeDOMAsync(params.getTextDocument(), (cancelChecker, xmlDocument) -> {
return Either.forLeft(getXMLLanguageService().findDefinition(xmlDocument, params.getPosition()));
});
}

@Override
public CompletableFuture<List<? extends Location>> references(ReferenceParams params) {
return computeAsync((cancelChecker) -> {
TextDocument document = getDocument(params.getTextDocument().getUri(), cancelChecker);
DOMDocument xmlDocument = getXMLDocument(document);
return computeDOMAsync(params.getTextDocument(), (cancelChecker, xmlDocument) -> {
return getXMLLanguageService().findReferences(xmlDocument, params.getPosition(), params.getContext());
});
}

@Override
public CompletableFuture<List<Either<Command, CodeAction>>> codeAction(CodeActionParams params) {
return computeAsync((cancelChecker) -> {
return computeDOMAsync(params.getTextDocument(), (cancelChecker, xmlDocument) -> {
String uri = params.getTextDocument().getUri();
DOMDocument xmlDocument = getXMLDocument(params.getTextDocument().getUri(), cancelChecker);
return getXMLLanguageService()
.doCodeActions(params.getContext(), params.getRange(), xmlDocument, getFormattingSettings(uri)) //
.stream() //
Expand Down Expand Up @@ -378,13 +343,12 @@ void doSave(SaveContext context) {
context.triggerValidationIfNeeded();
}

private void triggerValidationFor(Collection<TextDocument> documents) {
private void triggerValidationFor(Collection<ModelTextDocument<DOMDocument>> documents) {
if (!documents.isEmpty()) {
xmlLanguageServer.schedule(() -> {
documents.forEach(document -> {
try {
CancelChecker cancelChecker = new TextDocumentVersionChecker(document, document.getVersion());
validate(document, cancelChecker);
validate(document.getModel().getNow(null));
} catch (CancellationException e) {
// Ignore the error and continue to validate other documents
}
Expand All @@ -393,16 +357,16 @@ private void triggerValidationFor(Collection<TextDocument> documents) {
}
}

@SuppressWarnings("unchecked")
private void triggerValidationFor(TextDocument document) {
CancelChecker cancelChecker = new TextDocumentVersionChecker(document, document.getVersion());
xmlLanguageServer.schedule(() -> {
validate(document, cancelChecker);
}, 500, TimeUnit.MILLISECONDS);
((ModelTextDocument<DOMDocument>) document).getModel().thenAcceptAsync(xmlDocument -> {
validate(xmlDocument);
});
}

private void validate(TextDocument document, CancelChecker cancelChecker) throws CancellationException {
private void validate(DOMDocument xmlDocument) throws CancellationException {
CancelChecker cancelChecker = xmlDocument.getCancelChecker();
cancelChecker.checkCanceled();
DOMDocument xmlDocument = getXMLDocument(document);
getXMLLanguageService().publishDiagnostics(xmlDocument,
params -> xmlLanguageServer.getLanguageClient().publishDiagnostics(params),
(doc) -> triggerValidationFor(doc), sharedSettings.validationSettings, cancelChecker);
Expand All @@ -420,7 +384,7 @@ public void updateSymbolSettings(XMLSymbolSettings newSettings) {
XMLSymbolSettings symbolSettings = sharedSettings.symbolSettings;
symbolSettings.setEnabled(newSettings.isEnabled());
String[] newPatterns = newSettings.getExcluded();
if(newPatterns != null) {
if (newPatterns != null) {
symbolSettings.setExcluded(newPatterns);
}
}
Expand Down Expand Up @@ -451,4 +415,46 @@ public void updateIncrementalSettings(XMLIncrementalSupportSettings settings) {
this.documents.setIncremental(sharedSettings.experimentalSettings.getIncrementalSupport().getEnabled());
}

/**
* Returns the text document from the given uri.
*
* @param uri the uri
* @return the text document from the given uri.
*/
public ModelTextDocument<DOMDocument> getDocument(String uri) {
ModelTextDocument<DOMDocument> document = documents.get(uri);
if (document == null) {
throw new CancellationException("Cannot find a text document for the uri='" + uri + "'.");
}
return document;
}

/**
* Compute the DOM Document for a given uri in a future and then apply the given
* function.
*
* @param <R>
* @param documentIdentifier the document indetifier.
* @param code a bi function that accepts a {@link CancelChecker}
* and parsed {@link DOMDocument} and returns the to
* be computed value
* @return the DOM Document for a given uri in a future and then apply the given
* function.
*/
public <R> CompletableFuture<R> computeDOMAsync(TextDocumentIdentifier documentIdentifier,
BiFunction<CancelChecker, DOMDocument, R> code) {
return computeModelAsync(getDocument(documentIdentifier.getUri()).getModel(), code);
}

private static <R, M> CompletableFuture<R> computeModelAsync(CompletableFuture<M> loadModel,
BiFunction<CancelChecker, M, R> code) {
CompletableFuture<CancelChecker> start = new CompletableFuture<>();
CompletableFuture<R> result = start.thenCombineAsync(loadModel, code);
CancelChecker cancelIndicator = () -> {
if (result.isCancelled())
throw new CancellationException();
};
start.complete(cancelIndicator);
return result;
}
}
Loading

0 comments on commit 231bf5d

Please sign in to comment.