Skip to content

Commit

Permalink
[XML] Support for autoCloseTags
Browse files Browse the repository at this point in the history
Fixes eclipse-wildwebdeveloper#143

Signed-off-by: azerr <azerr@redhat.com>
  • Loading branch information
angelozerr committed Sep 8, 2022
1 parent adf04ab commit 8ef3c0b
Show file tree
Hide file tree
Showing 6 changed files with 288 additions and 0 deletions.
8 changes: 8 additions & 0 deletions org.eclipse.wildwebdeveloper.xml/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@
</presentationReconciler>
</extension>

<extension
point="org.eclipse.ui.genericeditor.reconcilers">
<reconciler
class="org.eclipse.wildwebdeveloper.xml.internal.lsp.ClosingTagReconciler"
contentType="org.eclipse.core.runtime.xml">
</reconciler>
</extension>

<!-- XML Language -->
<extension
Expand Down Expand Up @@ -49,6 +56,7 @@
point="org.eclipse.lsp4e.languageServer">
<server
class="org.eclipse.wildwebdeveloper.xml.internal.XMLLanguageServer"
serverInterface="org.eclipse.wildwebdeveloper.xml.internal.XMLLanguageServerAPI"
clientImpl="org.eclipse.wildwebdeveloper.xml.internal.XmlLanguageClientImpl"
id="org.eclipse.wildwebdeveloper.xml"
label="XML Language Server"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.eclipse.wildwebdeveloper.xml.internal;

import java.util.concurrent.CompletableFuture;

import org.eclipse.lsp4j.TextDocumentPositionParams;
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest;
import org.eclipse.lsp4j.services.LanguageServer;
import org.eclipse.wildwebdeveloper.xml.internal.lsp.AutoCloseTagResponse;

public interface XMLLanguageServerAPI extends LanguageServer {

@JsonRequest("xml/closeTag")
CompletableFuture<AutoCloseTagResponse> closeTag(TextDocumentPositionParams params);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.eclipse.wildwebdeveloper.xml.internal.lsp;

import org.eclipse.lsp4j.Range;

public class AutoCloseTagResponse {
public String snippet;
public Range range;

public AutoCloseTagResponse(String snippet, Range range) {
this.snippet = snippet;
this.range = range;
}

public AutoCloseTagResponse(String snippet) {
this.snippet = snippet;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.eclipse.wildwebdeveloper.xml.internal.lsp;

import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.ITextViewer;

public class ClosingTagReconciler extends NoThreadReconciler {

public ClosingTagReconciler() {
super(new ClosingTagReconcilingStrategy());
}

@Override
public void install(ITextViewer textViewer) {
super.install(textViewer);
((ClosingTagReconcilingStrategy)getReconcilingStrategy(null)).install(textViewer);
}

@Override
protected void documentChanged(DocumentEvent e) {
((ClosingTagReconcilingStrategy)getReconcilingStrategy(null)).documentChanged(e);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package org.eclipse.wildwebdeveloper.xml.internal.lsp;

import java.net.URI;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.reconciler.DirtyRegion;
import org.eclipse.jface.text.reconciler.IReconcilingStrategy;
import org.eclipse.lsp4e.LSPEclipseUtils;
import org.eclipse.lsp4e.LanguageServiceAccessor;
import org.eclipse.lsp4e.LanguageServiceAccessor.LSPDocumentInfo;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.TextDocumentPositionParams;
import org.eclipse.swt.widgets.Display;
import org.eclipse.wildwebdeveloper.xml.internal.XMLLanguageServerAPI;

public class ClosingTagReconcilingStrategy implements IReconcilingStrategy {


private ITextViewer viewer;

@Override
public void reconcile(IRegion region) {

}

public void update(DocumentEvent event) {
if (event == null || viewer == null) {
return;
}
IDocument document = event.getDocument();
if (document == null || event == null || event.getLength() != 0 || event.getText().length() != 1) {
return;
}

int offset = event.getOffset() + 1;
char c = event.getText().charAt(0);
if (c != '>' && c != '/') {
return;
}
URI uri = LSPEclipseUtils.toUri(document);
if (uri == null) {
return;
}

TextDocumentIdentifier identifier = new TextDocumentIdentifier(uri.toString());
Optional<LSPDocumentInfo> info = LanguageServiceAccessor
.getLSPDocumentInfosFor(document, (capabilities) -> true).stream()
.filter(doc -> (doc.getLanguageClient() instanceof XMLLanguageServerAPI)).findAny();
if (!info.isEmpty()) {
final Display display = viewer.getTextWidget().getShell().getDisplay();
CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(200);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
TextDocumentPositionParams params = LSPEclipseUtils.toTextDocumentPosistionParams(uri, offset,
document);
((XMLLanguageServerAPI) info.get().getLanguageClient()).closeTag(params).thenAccept(r -> {
if (r != null) {
display.asyncExec(() -> {
try {
String text = r.snippet.replace("$0", "");
document.replace(offset, 0, text);
} catch (BadLocationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});

}
});
} catch (BadLocationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
});
}
}

@Override
public void reconcile(DirtyRegion arg0, IRegion arg1) {

}

@Override
public void setDocument(IDocument document) {
}

public void install(ITextViewer viewer) {
this.viewer = viewer;
}

public void uninstall() {

}

public void documentChanged(DocumentEvent e) {
update(e);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package org.eclipse.wildwebdeveloper.xml.internal.lsp;

import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.ITextInputListener;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.reconciler.IReconciler;
import org.eclipse.jface.text.reconciler.IReconcilingStrategy;
import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension;

public class NoThreadReconciler implements IReconciler {

/** The text viewer's document */
private IDocument document;
/** The text viewer */
private ITextViewer textViewer;

private IReconcilingStrategy strategy;
private Listener fListener;

public NoThreadReconciler(IReconcilingStrategy strategy) {
this.strategy = strategy;
}

/**
* Internal document listener and text input listener.
*/
class Listener implements IDocumentListener, ITextInputListener {

/*
* @see IDocumentListener#documentAboutToBeChanged(DocumentEvent)
*/
@Override
public void documentAboutToBeChanged(DocumentEvent e) {
}

/*
* @see IDocumentListener#documentChanged(DocumentEvent)
*/
@Override
public void documentChanged(DocumentEvent e) {
NoThreadReconciler.this.documentChanged(e);
}

/*
* @see ITextInputListener#inputDocumentAboutToBeChanged(IDocument, IDocument)
*/
@Override
public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {

if (oldInput == document) {

if (document != null)
document.removeDocumentListener(this);
document = null;
}
}

/*
* @see ITextInputListener#inputDocumentChanged(IDocument, IDocument)
*/
@Override
public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {

document = newInput;
if (document == null)
return;
reconcilerDocumentChanged(document);

document.addDocumentListener(this);
initialProcess();
}

}

@Override
public void install(ITextViewer textViewer) {
this.textViewer = textViewer;
fListener = new Listener();
textViewer.addTextInputListener(fListener);
}

protected void documentChanged(DocumentEvent e) {
// TODO Auto-generated method stub

}

@Override
public void uninstall() {
if (fListener != null) {
textViewer.removeTextInputListener(fListener);
if (document != null)
document.removeDocumentListener(fListener);
fListener = null;
}
this.textViewer = null;
}

protected void reconcilerDocumentChanged(IDocument document) {
strategy.setDocument(document);
}

@Override
public IReconcilingStrategy getReconcilingStrategy(String contentType) {
return strategy;
}

protected void initialProcess() {
if (strategy instanceof IReconcilingStrategyExtension) {
IReconcilingStrategyExtension extension = (IReconcilingStrategyExtension) strategy;
extension.initialReconcile();
}
}
}

0 comments on commit 8ef3c0b

Please sign in to comment.