-
Notifications
You must be signed in to change notification settings - Fork 72
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[XML+HTML] Support for autoCloseTags
Fixes #143 Signed-off-by: azerr <[email protected]>
- Loading branch information
1 parent
d70dbb0
commit 5aba9af
Showing
25 changed files
with
868 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
...dwebdeveloper.xml/src/org/eclipse/wildwebdeveloper/xml/internal/XMLLanguageServerAPI.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/******************************************************************************* | ||
* Copyright (c) 2022 Red Hat Inc. and others. | ||
* This program and the accompanying materials are made | ||
* available under the terms of the Eclipse Public License 2.0 | ||
* which is available at https://www.eclipse.org/legal/epl-2.0/ | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Contributors: | ||
* Angelo ZERR (Red Hat Inc.) - initial implementation | ||
*******************************************************************************/ | ||
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.autoclose.AutoCloseTagResponse; | ||
|
||
/** | ||
* XML language server API which defines custom LSP commands. | ||
* | ||
*/ | ||
public interface XMLLanguageServerAPI extends LanguageServer { | ||
|
||
@JsonRequest("xml/closeTag") | ||
CompletableFuture<AutoCloseTagResponse> closeTag(TextDocumentPositionParams params); | ||
|
||
} |
33 changes: 33 additions & 0 deletions
33
...per.xml/src/org/eclipse/wildwebdeveloper/xml/internal/autoclose/AutoCloseTagResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/******************************************************************************* | ||
* Copyright (c) 2022 Red Hat Inc. and others. | ||
* This program and the accompanying materials are made | ||
* available under the terms of the Eclipse Public License 2.0 | ||
* which is available at https://www.eclipse.org/legal/epl-2.0/ | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Contributors: | ||
* Angelo ZERR (Red Hat Inc.) - initial implementation | ||
*******************************************************************************/ | ||
package org.eclipse.wildwebdeveloper.xml.internal.autoclose; | ||
|
||
import org.eclipse.lsp4j.Range; | ||
|
||
/** | ||
* Auto close tag LSP response. | ||
* | ||
*/ | ||
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; | ||
} | ||
} |
207 changes: 207 additions & 0 deletions
207
...ml/src/org/eclipse/wildwebdeveloper/xml/internal/autoclose/XMLAutoCloseTagReconciler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
/******************************************************************************* | ||
* Copyright (c) 2022 Red Hat Inc. and others. | ||
* This program and the accompanying materials are made | ||
* available under the terms of the Eclipse Public License 2.0 | ||
* which is available at https://www.eclipse.org/legal/epl-2.0/ | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Contributors: | ||
* Angelo ZERR (Red Hat Inc.) - initial implementation | ||
*/ | ||
package org.eclipse.wildwebdeveloper.xml.internal.autoclose; | ||
|
||
import static org.eclipse.wildwebdeveloper.xml.internal.ui.preferences.XMLPreferenceClientConstants.XML_PREFERENCES_COMPLETION_AUTO_CLOSE_TAGS; | ||
|
||
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.Activator; | ||
import org.eclipse.wildwebdeveloper.xml.internal.XMLLanguageServerAPI; | ||
|
||
/** | ||
* {@link IReconciler} implementation used to support auto close tags , features | ||
* provides by the LemMinx XML language server with the custom 'xml/closeTag' | ||
* LSP request. | ||
* | ||
*/ | ||
public class XMLAutoCloseTagReconciler implements IReconciler { | ||
|
||
private IDocument document; | ||
|
||
private ITextViewer viewer; | ||
|
||
private Listener listener; | ||
|
||
private void autoInsert(DocumentEvent event) { | ||
if (!isEnabled()) { | ||
return; | ||
} | ||
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 boolean isEnabled() { | ||
return Activator.getDefault().getPreferenceStore().getBoolean(XML_PREFERENCES_COMPLETION_AUTO_CLOSE_TAGS); | ||
} | ||
|
||
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; | ||
} | ||
|
||
} |
Oops, something went wrong.