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 19, 2022
1 parent d70dbb0 commit 55c7612
Show file tree
Hide file tree
Showing 30 changed files with 1,004 additions and 154 deletions.
Original file line number Diff line number Diff line change
@@ -1,98 +1,119 @@
/*******************************************************************************
* 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 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.IDocument;
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);
}

@Test
public void autoCloseTags() throws Exception {
final IProject project = ResourcesPlugin.getWorkspace().getRoot()
.getProject("testHTMLFile" + System.currentTimeMillis());
project.create(null);
project.open(null);
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) 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);

}
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;
}
}
Loading

0 comments on commit 55c7612

Please sign in to comment.