Skip to content

Commit

Permalink
[XML+HTML] 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 Oct 18, 2022
1 parent d70dbb0 commit 9ef3d59
Show file tree
Hide file tree
Showing 30 changed files with 1,032 additions and 154 deletions.
Original file line number Diff line number Diff line change
@@ -1,98 +1,160 @@
/*******************************************************************************
* Copyright (c) 2019 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:
* Mickael Istria (Red Hat Inc.) - initial implementation
*******************************************************************************/
package org.eclipse.wildwebdeveloper.tests;

import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.ByteArrayInputStream;
import java.util.concurrent.atomic.AtomicReference;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.tests.harness.util.DisplayHelper;
import org.eclipse.ui.texteditor.ITextEditor;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

@ExtendWith(AllCleanRule.class)
public class TestHTML {

@Test
public void testHTMLFile() throws Exception {
final IProject project = ResourcesPlugin.getWorkspace().getRoot()
.getProject("testHTMLFile" + System.currentTimeMillis());
project.create(null);
project.open(null);
final IFile file = project.getFile("blah.html");
file.create(new ByteArrayInputStream("FAIL".getBytes()), true, null);
ITextEditor editor = (ITextEditor) IDE
.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), file);
editor.getDocumentProvider().getDocument(editor.getEditorInput()).set("<style\n<html><");
assertTrue(new DisplayHelper() {
@Override
protected boolean condition() {
try {
return file.findMarkers("org.eclipse.lsp4e.diagnostic", true, IResource.DEPTH_ZERO).length != 0;
} catch (CoreException e) {
return false;
}
}
}.waitForCondition(PlatformUI.getWorkbench().getDisplay(), 5000), "Diagnostic not published");
}

@Test
public void testFormat() throws Exception {
final IProject project = ResourcesPlugin.getWorkspace().getRoot()
.getProject("testHTMLFile" + System.currentTimeMillis());
project.create(null);
project.open(null);
final IFile file = project.getFile("blah.html");
file.create(new ByteArrayInputStream("<html><body><a></a></body></html>".getBytes()), true, null);
ITextEditor editor = (ITextEditor) IDE
.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), file);
editor.setFocus();
editor.getSelectionProvider().setSelection(new TextSelection(0, 0));
IHandlerService handlerService = PlatformUI.getWorkbench().getService(IHandlerService.class);
assertTrue(
PlatformUI.getWorkbench().getService(ICommandService.class).getCommand("org.eclipse.lsp4e.format").isEnabled());
AtomicReference<Exception> ex = new AtomicReference<>();
new DisplayHelper() {
@Override
protected boolean condition() {
try {
handlerService.executeCommand("org.eclipse.lsp4e.format", null);
return true;
} catch (Exception e) {
return false;
}
}
}.waitForCondition(editor.getSite().getShell().getDisplay(), 3000);
if (ex.get() != null) {
throw ex.get();
}
new DisplayHelper() {
@Override
protected boolean condition() {
return editor.getDocumentProvider().getDocument(editor.getEditorInput()).getNumberOfLines() > 1;
}
}.waitForCondition(editor.getSite().getShell().getDisplay(), 3000);
}
}
/*******************************************************************************
* Copyright (c) 2019 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:
* Mickael Istria (Red Hat Inc.) - initial implementation
*******************************************************************************/
package org.eclipse.wildwebdeveloper.tests;

import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.ByteArrayInputStream;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.lsp4e.operations.completion.LSContentAssistProcessor;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.tests.harness.util.DisplayHelper;
import org.eclipse.ui.texteditor.AbstractTextEditor;
import org.eclipse.ui.texteditor.ITextEditor;
import org.junit.Test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;

@ExtendWith(AllCleanRule.class)
public class TestXML {

private IProject project;
private ICompletionProposal[] proposals;

@BeforeEach
public void setUpProject() throws CoreException {
this.project = ResourcesPlugin.getWorkspace().getRoot().getProject(getClass().getName() + System.nanoTime());
project.create(null);
project.open(null);
}

@Test
public void testXMLFile() throws Exception {
final IFile file = project.getFile("blah.xml");
file.create(new ByteArrayInputStream("FAIL".getBytes()), true, null);
ITextEditor editor = (ITextEditor) IDE
.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), file);
editor.getDocumentProvider().getDocument(editor.getEditorInput()).set("<plugin></");
assertTrue(new DisplayHelper() {
@Override
protected boolean condition() {
try {
return file.findMarkers("org.eclipse.lsp4e.diagnostic", true, IResource.DEPTH_ZERO).length != 0;
} catch (CoreException e) {
return false;
}
}
}.waitForCondition(PlatformUI.getWorkbench().getDisplay(), 5000), "Diagnostic not published");
}

@Test
public void testXSLFile() throws Exception {
final IFile file = project.getFile("blah.xsl");
file.create(new ByteArrayInputStream("FAIL".getBytes()), true, null);
ITextEditor editor = (ITextEditor) IDE
.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), file);
editor.getDocumentProvider().getDocument(editor.getEditorInput()).set("FAIL");
assertTrue(new DisplayHelper() {
@Override
protected boolean condition() {
try {
return file.findMarkers("org.eclipse.lsp4e.diagnostic", true, IResource.DEPTH_ZERO).length != 0;
} catch (CoreException e) {
return false;
}
}
}.waitForCondition(PlatformUI.getWorkbench().getDisplay(), 5000), "Diagnostic not published");
}

@Test
public void testXSDFile() throws Exception {
final IFile file = project.getFile("blah.xsd");
file.create(new ByteArrayInputStream("FAIL".getBytes()), true, null);
ITextEditor editor = (ITextEditor) IDE
.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), file);
editor.getDocumentProvider().getDocument(editor.getEditorInput()).set("a<");
assertTrue(new DisplayHelper() {
@Override
protected boolean condition() {
try {
return file.findMarkers("org.eclipse.lsp4e.diagnostic", true, IResource.DEPTH_ZERO).length != 0;
} catch (CoreException e) {
return false;
}
}
}.waitForCondition(PlatformUI.getWorkbench().getDisplay(), 5000), "Diagnostic not published");
}

@Test
public void testDTDFile() throws Exception {
final IFile file = project.getFile("blah.dtd");
file.create(new ByteArrayInputStream("FAIL".getBytes()), true, null);
ITextEditor editor = (ITextEditor) IDE
.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), file);
editor.getDocumentProvider().getDocument(editor.getEditorInput()).set("<!--<!-- -->");
assertTrue(new DisplayHelper() {
@Override
protected boolean condition() {
try {
return file.findMarkers("org.eclipse.lsp4e.diagnostic", true, IResource.DEPTH_ZERO).length != 0;
} catch (CoreException e) {
return false;
}
}
}.waitForCondition(PlatformUI.getWorkbench().getDisplay(), 5000), "Diagnostic not published");
}

@Test
public void testComplexXML() throws Exception {
final IFile file = project.getFile("blah.xml");
String content = "<layout:BlockLayoutCell\n" + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \n"
+ " xsi:schemaLocation=\"sap.ui.layout https://openui5.hana.ondemand.com/downloads/schemas/sap.ui.layout.xsd\"\n"
+ " xmlns:layout=\"sap.ui.layout\">\n" + " |\n" + "</layout:BlockLayoutCell>";
int offset = content.indexOf('|');
content = content.replace("|", "");
file.create(new ByteArrayInputStream(content.getBytes()), true, null);
AbstractTextEditor editor = (AbstractTextEditor) IDE.openEditor(
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), file,
"org.eclipse.ui.genericeditor.GenericEditor");
editor.getSelectionProvider().setSelection(new TextSelection(offset, 0));
LSContentAssistProcessor processor = new LSContentAssistProcessor();
proposals = processor.computeCompletionProposals(Utils.getViewer(editor), offset);
DisplayHelper.sleep(editor.getSite().getShell().getDisplay(), 2000);
assertTrue(proposals.length > 1);
}

@Test
public void autoCloseTags() throws Exception {
final IFile file = project.getFile("autoCloseTags.xml");
file.create(new ByteArrayInputStream("<foo".getBytes()), true, null);
ITextEditor editor = (ITextEditor) IDE
.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), file);
IDocument document = editor.getDocumentProvider().getDocument(editor.getEditorInput());
document.replace(4, 0, ">");
assertTrue(new DisplayHelper() {
@Override
protected boolean condition() {
return "<foo></foo>".equals(document.get());
}
}.waitForCondition(PlatformUI.getWorkbench().getDisplay(), 5000), "Autoclose not done");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.lsp4e.operations.completion.LSContentAssistProcessor;
Expand Down Expand Up @@ -140,4 +141,20 @@ public void testComplexXML() throws Exception {
DisplayHelper.sleep(editor.getSite().getShell().getDisplay(), 2000);
assertTrue(proposals.length > 1);
}

@Test
public void autoCloseTags() throws Exception {
final IFile file = project.getFile("autoCloseTags.xml");
file.create(new ByteArrayInputStream("<foo".getBytes()), true, null);
ITextEditor editor = (ITextEditor) IDE
.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), file);
IDocument document = editor.getDocumentProvider().getDocument(editor.getEditorInput());
document.replace(4, 0, ">");
assertTrue(new DisplayHelper() {
@Override
protected boolean condition() {
return "<foo></foo>".equals(document.get());
}
}.waitForCondition(PlatformUI.getWorkbench().getDisplay(), 5000), "Autoclose not done");
}
}
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.autoclose.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
Expand Up @@ -39,7 +39,7 @@
import org.eclipse.lsp4e.LanguageServiceAccessor;
import org.eclipse.lsp4e.server.ProcessStreamConnectionProvider;
import org.eclipse.lsp4j.DidChangeConfigurationParams;
import org.eclipse.wildwebdeveloper.xml.internal.ui.preferences.XMLPreferenceConstants;
import org.eclipse.wildwebdeveloper.xml.internal.ui.preferences.XMLPreferenceServerConstants;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;

Expand All @@ -53,7 +53,7 @@ public class XMLLanguageServer extends ProcessStreamConnectionProvider {
private static final LanguageServerDefinition lemminxDefinition = LanguageServersRegistry.getInstance()
.getDefinition("org.eclipse.wildwebdeveloper.xml");
private static final IPropertyChangeListener psListener = event -> {
XMLPreferenceConstants.getLemminxPreference(event).ifPresent(pref -> {
XMLPreferenceServerConstants.getLemminxPreference(event).ifPresent(pref -> {
Map<String, Object> config = mergeCustomInitializationOptions(
extensionJarRegistry.getInitiatizationOptions());

Expand Down Expand Up @@ -165,7 +165,7 @@ public Object getInitializationOptions(URI rootUri) {

private static Map<String, Object> mergeCustomInitializationOptions(Map<String, Object> defaults) {
Map<String, Object> xmlOpts = new HashMap<>(defaults);
XMLPreferenceConstants.storePreferencesToLemminxOptions(store, xmlOpts);
XMLPreferenceServerConstants.storePreferencesToLemminxOptions(store, xmlOpts);
return Map.of(SETTINGS_KEY, Map.of(XML_KEY, xmlOpts));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*******************************************************************************
* Copyright (c) 2020 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:
* Victor Rubezhny (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);

}
Loading

0 comments on commit 9ef3d59

Please sign in to comment.