Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide Document Lifecycle Participant for tracking didOpen, did* #1091

Merged
merged 1 commit into from
Jul 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/LemMinX-Extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ The [LemMinx Extensions API](https://github.com/eclipse/lemminx/tree/master/org.
- Formatter with [IFormatterParticipant](https://github.com/eclipse/lemminx/blob/master/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/format/IFormatterParticipant.java)
- Symbols with [ISymbolsProviderParticipant](https://github.com/eclipse/lemminx/blob/master/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/ISymbolsProviderParticipant.java)
- Monitoring workspace folders with [IWorkspaceServiceParticipant](https://github.com/eclipse/lemminx/blob/master/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/IWorkspaceServiceParticipant.java)
- Monitoring document lifecycle (didOpen, didChange, etc) with [IDocumentLifecycleParticipant](https://github.com/eclipse/lemminx/blob/master/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/IDocumentLifecycleParticipant.java)

## XML Language Server services available for extensions
XML Language Server extension may need to use standard Language Server features such as commands, documents and ability to manipulate documents. These are available to extensions indirectly via specialized service API.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import com.google.gson.JsonPrimitive;

import org.eclipse.lemminx.client.ExtendedClientCapabilities;
import org.eclipse.lemminx.client.LimitExceededWarner;
import org.eclipse.lemminx.client.LimitFeature;
Expand Down Expand Up @@ -91,6 +91,7 @@
import org.eclipse.lsp4j.SelectionRangeParams;
import org.eclipse.lsp4j.SymbolInformation;
import org.eclipse.lsp4j.TextDocumentClientCapabilities;
import org.eclipse.lsp4j.TextDocumentContentChangeEvent;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.TextEdit;
import org.eclipse.lsp4j.TypeDefinitionParams;
Expand All @@ -100,17 +101,31 @@
import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
import org.eclipse.lsp4j.services.TextDocumentService;

import com.google.gson.JsonPrimitive;

/**
* XML text document service.
*
*/
public class XMLTextDocumentService implements TextDocumentService {

private static final Logger LOGGER = Logger.getLogger(XMLTextDocumentService.class.getName());

private final XMLLanguageServer xmlLanguageServer;
private final TextDocuments<ModelTextDocument<DOMDocument>> documents;
private SharedSettings sharedSettings;
private LimitExceededWarner limitExceededWarner;

/**
* Enumeration for Validation triggered by.
*
*/
private static enum TriggeredBy {
didOpen, //
didChange, //
Other;
}

/**
* Save context.
*/
Expand Down Expand Up @@ -341,7 +356,7 @@ public CompletableFuture<WorkspaceEdit> rename(RenameParams params) {
@Override
public void didOpen(DidOpenTextDocumentParams params) {
TextDocument document = documents.onDidOpenTextDocument(params);
triggerValidationFor(document);
triggerValidationFor(document, TriggeredBy.didOpen);
}

/**
Expand All @@ -350,17 +365,39 @@ public void didOpen(DidOpenTextDocumentParams params) {
@Override
public void didChange(DidChangeTextDocumentParams params) {
TextDocument document = documents.onDidChangeTextDocument(params);
triggerValidationFor(document);
triggerValidationFor(document, TriggeredBy.didChange, params.getContentChanges());
}

@Override
public void didClose(DidCloseTextDocumentParams params) {
TextDocumentIdentifier identifier = params.getTextDocument();
String uri = identifier.getUri();
DOMDocument xmlDocument = getNowDOMDocument(uri);
// Remove the document from the cache
documents.onDidCloseTextDocument(params);
TextDocumentIdentifier document = params.getTextDocument();
String uri = document.getUri();
// Publish empty errors from the document
xmlLanguageServer.getLanguageClient()
.publishDiagnostics(new PublishDiagnosticsParams(uri, Collections.emptyList()));
getLimitExceededWarner().evictValue(uri);
// Manage didClose document lifecycle participants
if (xmlDocument != null) {
getXMLLanguageService().getDocumentLifecycleParticipants().forEach(participant -> {
try {
participant.didClose(xmlDocument);
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Error while processing didClose for the participant '"
+ participant.getClass().getName() + "'.", e);
}
});
}
}

private DOMDocument getNowDOMDocument(String uri) {
TextDocument document = documents.get(uri);
if (document != null) {
return ((ModelTextDocument<DOMDocument>) document).getModel().getNow(null);
}
return null;
}

@Override
Expand Down Expand Up @@ -483,6 +520,19 @@ public void didSave(DidSaveTextDocumentParams params) {
// A document was saved, collect documents to revalidate
SaveContext context = new SaveContext(params.getTextDocument().getUri());
doSave(context);

// Manage didSave document lifecycle participants
final DOMDocument xmlDocument = getNowDOMDocument(params.getTextDocument().getUri());
datho7561 marked this conversation as resolved.
Show resolved Hide resolved
if (xmlDocument != null) {
getXMLLanguageService().getDocumentLifecycleParticipants().forEach(participant -> {
try {
participant.didSave(xmlDocument);
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Error while processing didSave for the participant '"
+ participant.getClass().getName() + "'.", e);
}
});
}
return null;
});
}
Expand Down Expand Up @@ -526,19 +576,52 @@ private void triggerValidationFor(Collection<ModelTextDocument<DOMDocument>> doc
}
}

private void triggerValidationFor(TextDocument document, TriggeredBy triggeredBy) {
triggerValidationFor(document, triggeredBy, null);
}

@SuppressWarnings("unchecked")
private void triggerValidationFor(TextDocument document) {
((ModelTextDocument<DOMDocument>) document).getModel().thenAcceptAsync(xmlDocument -> {
validate(xmlDocument);
});
private void triggerValidationFor(TextDocument document, TriggeredBy triggeredBy,
List<TextDocumentContentChangeEvent> changeEvents) {
((ModelTextDocument<DOMDocument>) document).getModel()//
.thenAcceptAsync(xmlDocument -> {
// Validate the DOM document
validate(xmlDocument);
// Manage didOpen, didChange document lifecycle participants
switch (triggeredBy) {
case didOpen:
getXMLLanguageService().getDocumentLifecycleParticipants().forEach(participant -> {
try {
participant.didOpen(xmlDocument);
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Error while processing didOpen for the participant '"
+ participant.getClass().getName() + "'.", e);
}
});
break;
case didChange:
getXMLLanguageService().getDocumentLifecycleParticipants().forEach(participant -> {
try {
participant.didChange(xmlDocument);
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Error while processing didChange for the participant '"
+ participant.getClass().getName() + "'.", e);
}
});
break;
default:
// Do nothing
}
});
}

void validate(DOMDocument xmlDocument) throws CancellationException {
CancelChecker cancelChecker = xmlDocument.getCancelChecker();
cancelChecker.checkCanceled();
getXMLLanguageService().publishDiagnostics(xmlDocument,
params -> xmlLanguageServer.getLanguageClient().publishDiagnostics(params),
(doc) -> triggerValidationFor(doc), sharedSettings.getValidationSettings(), cancelChecker);
(doc) -> triggerValidationFor(doc, TriggeredBy.Other), sharedSettings.getValidationSettings(),
cancelChecker);
}

private XMLLanguageService getXMLLanguageService() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*******************************************************************************
* Copyright (c) 2021 Red Hat Inc. and others.
* All rights reserved. This program and the accompanying materials
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.lemminx.services.extensions;

import org.eclipse.lemminx.dom.DOMDocument;

/**
* Document LifecycleService participant API.
*
* @since 0.18.0
*/
public interface IDocumentLifecycleParticipant {

/**
* Handler called when an XML document is opened.
*
* @param document the DOM document
*/
void didOpen(DOMDocument document);

/**
* Handler called when an XML document is changed.
*
* @param document the DOM document
*/
void didChange(DOMDocument document);

/**
* Handler called when an XML document is saved.
*
* @param document the DOM document
*/
void didSave(DOMDocument document);

/**
* Handler called when an XML document is closed.
*
* @param document the DOM document
*/
void didClose(DOMDocument document);

}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public class XMLExtensionsRegistry implements IComponentProvider {
private final List<IFormatterParticipant> formatterParticipants;
private final List<ISymbolsProviderParticipant> symbolsProviderParticipants;
private final List<IWorkspaceServiceParticipant> workspaceServiceParticipants;
private final List<IDocumentLifecycleParticipant> documentLifecycleParticipants;
private IXMLDocumentProvider documentProvider;
private IXMLValidationService validationService;
private IXMLCommandService commandService;
Expand Down Expand Up @@ -89,6 +90,7 @@ public XMLExtensionsRegistry() {
formatterParticipants = new ArrayList<>();
symbolsProviderParticipants = new ArrayList<>();
workspaceServiceParticipants = new ArrayList<>();
documentLifecycleParticipants = new ArrayList<>();
resolverExtensionManager = new URIResolverExtensionManager();
components = new HashMap<>();
registerComponent(resolverExtensionManager);
Expand Down Expand Up @@ -201,6 +203,8 @@ public Collection<ISymbolsProviderParticipant> getSymbolsProviderParticipants()
}

/**
* Return the registered workspace service participants.
*
* @return the registered workspace service participants.
* @since 0.14.2
*/
Expand All @@ -209,6 +213,17 @@ public Collection<IWorkspaceServiceParticipant> getWorkspaceServiceParticipants(
return workspaceServiceParticipants;
}

/**
* Return the registered document lifecycle participants.
*
* @return the registered document lifecycle participants.
* @since 0.18.0
*/
public List<IDocumentLifecycleParticipant> getDocumentLifecycleParticipants() {
initializeIfNeeded();
return documentLifecycleParticipants;
}

public void initializeIfNeeded() {
if (initialized) {
return;
Expand Down Expand Up @@ -373,9 +388,10 @@ public void registerSymbolsProviderParticipant(ISymbolsProviderParticipant symbo
public void unregisterSymbolsProviderParticipant(ISymbolsProviderParticipant symbolsProviderParticipant) {
symbolsProviderParticipants.remove(symbolsProviderParticipant);
}

/**
* Register a new workspace service participant
*
* @param workspaceServiceParticipant the participant to register
* @since 0.14.2
*/
Expand All @@ -385,13 +401,34 @@ public void registerWorkspaceServiceParticipant(IWorkspaceServiceParticipant wor

/**
* Unregister a new workspace service participant.
*
* @param workspaceServiceParticipant the participant to unregister
* @since 0.14.2
*/
public void unregisterWorkspaceServiceParticipant(IWorkspaceServiceParticipant workspaceServiceParticipant) {
workspaceServiceParticipants.remove(workspaceServiceParticipant);
}

/**
* Register a new document lifecycle participant
*
* @param documentLifecycleParticipant the participant to register
* @since 0.18.0
*/
public void registerDocumentLifecycleParticipant(IDocumentLifecycleParticipant documentLifecycleParticipant) {
documentLifecycleParticipants.add(documentLifecycleParticipant);
}

/**
* Unregister a new document lifecycle participant.
*
* @param documentLifecycleParticipant the participant to unregister
* @since 0.18.0
*/
public void unregisterDocumentLifecycleParticipant(IDocumentLifecycleParticipant documentLifecycleParticipant) {
documentLifecycleParticipants.remove(documentLifecycleParticipant);
}

/**
* Returns the XML Document provider and null otherwise.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,17 @@

import org.eclipse.lemminx.customservice.ActionableNotification;
import org.eclipse.lemminx.services.extensions.commands.IXMLCommandService.IDelegateCommandHandler;
import org.eclipse.lsp4j.DidChangeTextDocumentParams;
import org.eclipse.lsp4j.DidCloseTextDocumentParams;
import org.eclipse.lsp4j.DidOpenTextDocumentParams;
import org.eclipse.lsp4j.DidSaveTextDocumentParams;
import org.eclipse.lsp4j.ExecuteCommandParams;
import org.eclipse.lsp4j.MessageParams;
import org.eclipse.lsp4j.PublishDiagnosticsParams;
import org.eclipse.lsp4j.TextDocumentContentChangeEvent;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.TextDocumentItem;
import org.eclipse.lsp4j.VersionedTextDocumentIdentifier;

/**
* Mock XML Language server which helps to track show messages, actionable
Expand Down Expand Up @@ -82,4 +87,41 @@ public TextDocumentIdentifier didOpen(String fileURI, String xml) {
}
return xmlIdentifier;
}

public TextDocumentIdentifier didChange(String fileURI, List<TextDocumentContentChangeEvent> contentChanges) {
TextDocumentIdentifier xmlIdentifier = new TextDocumentIdentifier(fileURI);
DidChangeTextDocumentParams params = new DidChangeTextDocumentParams(
new VersionedTextDocumentIdentifier(xmlIdentifier.getUri(), 1), contentChanges);
XMLTextDocumentService textDocumentService = (XMLTextDocumentService) super.getTextDocumentService();
textDocumentService.didChange(params);
try {
// Force the parse of DOM document
textDocumentService.getDocument(params.getTextDocument().getUri()).getModel().get();
} catch (Exception e) {
e.printStackTrace();
}
return xmlIdentifier;
}

public TextDocumentIdentifier didClose(String fileURI) {
TextDocumentIdentifier xmlIdentifier = new TextDocumentIdentifier(fileURI);
DidCloseTextDocumentParams params = new DidCloseTextDocumentParams(xmlIdentifier);
XMLTextDocumentService textDocumentService = (XMLTextDocumentService) super.getTextDocumentService();
textDocumentService.didClose(params);
return xmlIdentifier;
}

public TextDocumentIdentifier didSave(String fileURI) {
TextDocumentIdentifier xmlIdentifier = new TextDocumentIdentifier(fileURI);
DidSaveTextDocumentParams params = new DidSaveTextDocumentParams(xmlIdentifier);
XMLTextDocumentService textDocumentService = (XMLTextDocumentService) super.getTextDocumentService();
textDocumentService.didSave(params);
try {
// Force the parse of DOM document
textDocumentService.getDocument(params.getTextDocument().getUri()).getModel().get();
} catch (Exception e) {
e.printStackTrace();
}
return xmlIdentifier;
}
}
Loading