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 13, 2022
1 parent 3703333 commit bc11965
Show file tree
Hide file tree
Showing 9 changed files with 492 additions and 1 deletion.
3 changes: 2 additions & 1 deletion org.eclipse.wildwebdeveloper.xml/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ Require-Bundle: org.eclipse.tm4e.registry;bundle-version="0.3.0",
org.eclipse.ui.genericeditor;bundle-version="1.0.0",
org.eclipse.core.net;bundle-version="1.3.0",
org.eclipse.lsp4j.jsonrpc,
org.eclipse.text
org.eclipse.text,
org.eclipse.jface.text;bundle-version="3.20.100"
Bundle-ActivationPolicy: lazy
Bundle-Activator: org.eclipse.wildwebdeveloper.xml.internal.Activator
Export-Package: org.eclipse.wildwebdeveloper.xml;x-friends:="org.eclipse.m2e.editor.lemminx"
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.XMLAutoCloseTagReconciler"
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,180 @@
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.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.lsp4e.LSPEclipseUtils;
import org.eclipse.lsp4e.LanguageServiceAccessor;
import org.eclipse.lsp4e.LanguageServiceAccessor.LSPDocumentInfo;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
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 XMLAutoCloseTagReconciler implements IReconciler {

private IDocument document;

private ITextViewer viewer;

private Listener listener;

private void autoInsert(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()) {
// The document is bound with XML language server, consumes the xml/closeTag
final Display display = viewer.getTextWidget().getDisplay();
CompletableFuture.supplyAsync(() -> {
try {
// Wait for textDocument/didChange
Thread.sleep(100);
} catch (InterruptedException ex) {
Thread.interrupted();
}
try {
TextDocumentPositionParams params = LSPEclipseUtils.toTextDocumentPosistionParams(uri, offset,
document);
// consumes xml/closeTag from XML language server
((XMLLanguageServerAPI) info.get().getLanguageClient()).closeTag(params).thenAccept(r -> {
if (r != null) {
display.asyncExec(() -> {
try {
// we receive a text like
// $0</foo>
// $0 should be used for set the cursor.
String text = r.snippet.replace("$0", "");
int replaceLength = getReplaceLength(r.range, document);
document.replace(offset, replaceLength, text);
} catch (BadLocationException e) {
// Do nothing
}
});

}
});
} catch (BadLocationException e) {
// Do nothing
}
return null;
});
}
}

private static int getReplaceLength(Range range, IDocument document) throws BadLocationException {
if (range == null) {
return 0;
}
Position start = range.getStart();
Position end = range.getEnd();
if (start.getLine() == end.getLine()) {
return end.getCharacter() - start.getCharacter();
}
int startOffset = LSPEclipseUtils.toOffset(start, document);
int endOffset = LSPEclipseUtils.toOffset(end, document);
return endOffset - startOffset;
}

/**
* 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) {
autoInsert(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;
}
document.addDocumentListener(this);
}

}

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

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

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

}
8 changes: 8 additions & 0 deletions org.eclipse.wildwebdeveloper/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@
point="org.eclipse.lsp4e.languageServer">
<server
class="org.eclipse.wildwebdeveloper.html.HTMLLanguageServer"
serverInterface="org.eclipse.wildwebdeveloper.html.HTMLLanguageServerAPI"
id="org.eclipse.wildwebdeveloper.html"
label="HTML Language Server (VSCode)"
singleton="true">
Expand Down Expand Up @@ -325,6 +326,13 @@
<icon contentType="org.eclipse.wildwebdeveloper.html" icon="icons/htmlEditorIcon.png"/>
</extension>

<extension
point="org.eclipse.ui.genericeditor.reconcilers">
<reconciler
class="org.eclipse.wildwebdeveloper.html.HTMLAutoInsertReconciler"
contentType="org.eclipse.wildwebdeveloper.html">
</reconciler>
</extension>
<!-- JavaScript/TypeScript Language -->
<extension
point="org.eclipse.core.contenttype.contentTypes">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.eclipse.wildwebdeveloper.html;

import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.TextDocumentIdentifier;

/**
*
* @author azerr
* @see <a href=
* "https://github.com/microsoft/vscode/blob/9b80ed652434a6e82b3ac28c1a9a01132c8faea3/extensions/html-language-features/client/src/htmlClient.ts#L31">https://github.com/microsoft/vscode/blob/9b80ed652434a6e82b3ac28c1a9a01132c8faea3/extensions/html-language-features/client/src/htmlClient.ts#L31</a>
*/
public class AutoInsertParams {

/**
* The auto insert kind
*/
private String kind; // 'autoQuote' | 'autoClose';
/**
* The text document.
*/
private TextDocumentIdentifier textDocument;
/**
* The position inside the text document.
*/
private Position position;

public String getKind() {
return kind;
}

public void setKind(String kind) {
this.kind = kind;
}

public TextDocumentIdentifier getTextDocument() {
return textDocument;
}

public void setTextDocument(TextDocumentIdentifier textDocument) {
this.textDocument = textDocument;
}

public Position getPosition() {
return position;
}

public void setPosition(Position position) {
this.position = position;
}

}
Loading

0 comments on commit bc11965

Please sign in to comment.