/** * Copyright (c) 2018 Angelo ZERR * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v20.html * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * Angelo Zerr <[email protected]> - initial API and implementation */ package org.eclipse.lemminx; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertIterableEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Collectors; import org.eclipse.lemminx.client.CodeLensKind; import org.eclipse.lemminx.client.CodeLensKindCapabilities; import org.eclipse.lemminx.client.ExtendedCodeLensCapabilities; import org.eclipse.lemminx.commons.BadLocationException; import org.eclipse.lemminx.commons.TextDocument; import org.eclipse.lemminx.customservice.AutoCloseTagResponse; import org.eclipse.lemminx.dom.DOMDocument; import org.eclipse.lemminx.dom.DOMParser; import org.eclipse.lemminx.extensions.contentmodel.settings.ContentModelSettings; import org.eclipse.lemminx.extensions.contentmodel.settings.XMLValidationSettings; import org.eclipse.lemminx.extensions.generators.FileContentGeneratorManager; import org.eclipse.lemminx.extensions.generators.FileContentGeneratorSettings; import org.eclipse.lemminx.services.XMLLanguageService; import org.eclipse.lemminx.services.extensions.diagnostics.IXMLErrorCode; import org.eclipse.lemminx.services.extensions.save.AbstractSaveContext; import org.eclipse.lemminx.settings.SharedSettings; import org.eclipse.lemminx.settings.XMLCodeLensSettings; import org.eclipse.lemminx.settings.XMLSymbolSettings; import org.eclipse.lemminx.utils.StringUtils; import org.eclipse.lsp4j.CodeAction; import org.eclipse.lsp4j.CodeActionContext; import org.eclipse.lsp4j.CodeLens; import org.eclipse.lsp4j.Command; import org.eclipse.lsp4j.CompletionItem; import org.eclipse.lsp4j.CompletionList; import org.eclipse.lsp4j.CreateFile; import org.eclipse.lsp4j.CreateFileOptions; import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.DocumentHighlight; import org.eclipse.lsp4j.DocumentHighlightKind; import org.eclipse.lsp4j.DocumentLink; import org.eclipse.lsp4j.DocumentSymbol; import org.eclipse.lsp4j.Hover; import org.eclipse.lsp4j.HoverCapabilities; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.LocationLink; import org.eclipse.lsp4j.MarkedString; import org.eclipse.lsp4j.MarkupContent; import org.eclipse.lsp4j.MarkupKind; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.PublishDiagnosticsParams; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.ReferenceContext; import org.eclipse.lsp4j.ResourceOperation; import org.eclipse.lsp4j.SymbolKind; import org.eclipse.lsp4j.TextDocumentEdit; import org.eclipse.lsp4j.TextEdit; import org.eclipse.lsp4j.VersionedTextDocumentIdentifier; import org.eclipse.lsp4j.WorkspaceEdit; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.junit.jupiter.api.Assertions; /** * XML * */ public class XMLAssert { // ------------------- Completion assert public static final int COMMENT_SNIPPETS = 1; public static final int CDATA_SNIPPETS = 1; public static final int DOCTYPE_SNIPPETS = 5; public static final int DTDNODE_SNIPPETS = 4; public static final int NEW_XML_SNIPPETS = 7; public static final int NEW_XSD_SNIPPETS = 1; public static final int XML_DECLARATION_SNIPPETS = 2; public static final int PROCESSING_INSTRUCTION_SNIPPETS = 2; public static final int REGION_SNIPPETS = 2; public static final int CATALOG_SNIPPETS = 3; private static final String FILE_URI = "test.xml"; public static class SettingsSaveContext extends AbstractSaveContext { public SettingsSaveContext(Object settings) { super(settings); } @Override public DOMDocument getDocument(String uri) { return null; } @Override public void collectDocumentToValidate(Predicate<DOMDocument> validateDocumentPredicate) { } } public static void testCompletionFor(String value, CompletionItem... expectedItems) throws BadLocationException { testCompletionFor(value, null, expectedItems); } public static void testCompletionFor(String value, String catalogPath, CompletionItem... expectedItems) throws BadLocationException { testCompletionFor(value, catalogPath, null, null, expectedItems); } public static void testCompletionFor(String value, int expectedCount, CompletionItem... expectedItems) throws BadLocationException { testCompletionFor(value, null, null, expectedCount, expectedItems); } public static void testCompletionFor(String value, String catalogPath, String fileURI, Integer expectedCount, CompletionItem... expectedItems) throws BadLocationException { testCompletionFor(new XMLLanguageService(), value, catalogPath, null, fileURI, expectedCount, true, expectedItems); } public static void testCompletionFor(String value, boolean autoCloseTags, CompletionItem... expectedItems) throws BadLocationException { testCompletionFor(new XMLLanguageService(), value, null, null, null, null, autoCloseTags, expectedItems); } public static void testCompletionFor(XMLLanguageService xmlLanguageService, String value, String catalogPath, Consumer<XMLLanguageService> customConfiguration, String fileURI, Integer expectedCount, boolean autoCloseTags, CompletionItem... expectedItems) throws BadLocationException { SharedSettings settings = new SharedSettings(); settings.getCompletionSettings().setAutoCloseTags(autoCloseTags); testCompletionFor(xmlLanguageService, value, catalogPath, customConfiguration, fileURI, expectedCount, settings, expectedItems); } public static void testCompletionFor(XMLLanguageService xmlLanguageService, String value, String catalogPath, Consumer<XMLLanguageService> customConfiguration, String fileURI, Integer expectedCount, SharedSettings sharedSettings, CompletionItem... expectedItems) throws BadLocationException { int offset = value.indexOf('|'); value = value.substring(0, offset) + value.substring(offset + 1); TextDocument document = new TextDocument(value, fileURI != null ? fileURI : "test://test/test.xml"); Position position = document.positionAt(offset); DOMDocument htmlDoc = DOMParser.getInstance().parse(document, xmlLanguageService.getResolverExtensionManager()); xmlLanguageService.setDocumentProvider((uri) -> htmlDoc); ContentModelSettings cmSettings = new ContentModelSettings(); cmSettings.setUseCache(false); // Configure XML catalog for XML schema if (catalogPath != null) { cmSettings.setCatalogs(new String[] { catalogPath }); } xmlLanguageService.doSave(new SettingsSaveContext(cmSettings)); xmlLanguageService.initializeIfNeeded(); if (customConfiguration != null) { customConfiguration.accept(xmlLanguageService); } CompletionList list = xmlLanguageService.doComplete(htmlDoc, position, sharedSettings); // no duplicate labels List<String> labels = list.getItems().stream().map(i -> i.getLabel()).sorted().collect(Collectors.toList()); String previous = null; for (String label : labels) { if (expectedCount != null) { continue; } assertNotEquals(previous, label, () -> { return "Duplicate label " + label + " in " + labels.stream().collect(Collectors.joining(",")) + "}"; }); previous = label; } if (expectedCount != null) { assertEquals(expectedCount.intValue(), list.getItems().size()); } if (expectedItems != null) { for (CompletionItem item : expectedItems) { assertCompletion(list, item, document, offset, expectedCount); } } } private static void assertCompletion(CompletionList completions, CompletionItem expected, TextDocument document, int offset, Integer expectedCount) { List<CompletionItem> matches = completions.getItems().stream().filter(completion -> { return expected.getLabel().equals(completion.getLabel()); }).collect(Collectors.toList()); if (expectedCount != null) { assertTrue(matches.size() >= 1, () -> { return expected.getLabel() + " should only exist once: Actual: " + completions.getItems().stream().map(c -> c.getLabel()).collect(Collectors.joining(",")); }); } else { assertEquals(1, matches.size(), () -> { return expected.getLabel() + " should only exist once: Actual: " + completions.getItems().stream().map(c -> c.getLabel()).collect(Collectors.joining(",")); }); } CompletionItem match = getCompletionMatch(matches, expected); if (expected.getTextEdit() != null && match.getTextEdit() != null) { if (expected.getTextEdit().getNewText() != null) { assertEquals(expected.getTextEdit().getNewText(), match.getTextEdit().getNewText()); } Range r = expected.getTextEdit().getRange(); if (r != null && r.getStart() != null && r.getEnd() != null) { assertEquals(expected.getTextEdit().getRange(), match.getTextEdit().getRange()); } } if (expected.getFilterText() != null && match.getFilterText() != null) { assertEquals(expected.getFilterText(), match.getFilterText()); } if (expected.getDocumentation() != null) { assertEquals(expected.getDocumentation(), match.getDocumentation()); } } private static CompletionItem getCompletionMatch(List<CompletionItem> matches, CompletionItem expected) { for (CompletionItem item : matches) { if (expected.getTextEdit().getNewText().equals(item.getTextEdit().getNewText())) { return item; } } return matches.get(0); } public static CompletionItem c(String label, TextEdit textEdit, String filterText, String documentation) { return c(label, textEdit, filterText, documentation, null); } public static CompletionItem c(String label, TextEdit textEdit, String filterText, String documentation, String kind) { CompletionItem item = new CompletionItem(); item.setLabel(label); item.setFilterText(filterText); item.setTextEdit(textEdit); if (kind == null) { item.setDocumentation(documentation); } else { item.setDocumentation(new MarkupContent(kind, documentation)); } return item; } public static CompletionItem c(String label, TextEdit textEdit, String filterText) { CompletionItem item = new CompletionItem(); item.setLabel(label); item.setFilterText(filterText); item.setTextEdit(textEdit); return item; } public static CompletionItem c(String label, String newText) { return c(label, newText, null); } public static CompletionItem c(String label, String newText, String filterText) { return c(label, newText, new Range(), filterText); } public static CompletionItem c(String label, String newText, Range range, String filterText) { return c(label, new TextEdit(range, newText), filterText); } public static void testTagCompletion(String value, String expected) throws BadLocationException { int offset = value.indexOf('|'); value = value.substring(0, offset) + value.substring(offset + 1); XMLLanguageService ls = new XMLLanguageService(); TextDocument document = new TextDocument(value, "test://test/test.html"); Position position = document.positionAt(offset); DOMDocument htmlDoc = DOMParser.getInstance().parse(document, ls.getResolverExtensionManager()); AutoCloseTagResponse response = ls.doTagComplete(htmlDoc, position); if (expected == null) { assertNull(response); return; } String actual = response.snippet; assertEquals(expected, actual); } // ------------------- Diagnostics assert public static void testDiagnosticsFor(String xml, Diagnostic... expected) { testDiagnosticsFor(xml, null, expected); } public static void testDiagnosticsFor(String xml, String catalogPath, Diagnostic... expected) { testDiagnosticsFor(xml, catalogPath, null, null, expected); } public static void testDiagnosticsFor(String xml, String catalogPath, Consumer<XMLLanguageService> configuration, String fileURI, Diagnostic... expected) { testDiagnosticsFor(xml, catalogPath, configuration, fileURI, true, expected); } public static void testDiagnosticsFor(String xml, String catalogPath, Consumer<XMLLanguageService> configuration, String fileURI, boolean filter, Diagnostic... expected) { ContentModelSettings settings = new ContentModelSettings(); settings.setUseCache(false); XMLValidationSettings problems = new XMLValidationSettings(); problems.setNoGrammar("ignore"); settings.setValidation(problems); if (catalogPath != null) { // Configure XML catalog for XML schema settings.setCatalogs(new String[] { catalogPath }); } testDiagnosticsFor(xml, catalogPath, configuration, fileURI, filter, settings, expected); } public static void testDiagnosticsFor(String xml, String catalogPath, Consumer<XMLLanguageService> configuration, String fileURI, boolean filter, ContentModelSettings settings, Diagnostic... expected) { testDiagnosticsFor(new XMLLanguageService(), xml, catalogPath, configuration, fileURI, filter, settings, expected); } public static void testDiagnosticsFor(XMLLanguageService xmlLanguageService, String xml, String catalogPath, Consumer<XMLLanguageService> configuration, String fileURI, boolean filter, ContentModelSettings settings, Diagnostic... expected) { TextDocument document = new TextDocument(xml, fileURI != null ? fileURI : "test.xml"); DOMDocument xmlDocument = DOMParser.getInstance().parse(document, xmlLanguageService.getResolverExtensionManager()); xmlLanguageService.setDocumentProvider((uri) -> xmlDocument); xmlLanguageService.doSave(new SettingsSaveContext(settings)); if (configuration != null) { xmlLanguageService.initializeIfNeeded(); configuration.accept(xmlLanguageService); } List<Diagnostic> actual = xmlLanguageService.doDiagnostics(xmlDocument, () -> { }, settings.getValidation()); if (expected == null) { assertTrue(actual.isEmpty()); return; } assertDiagnostics(actual, Arrays.asList(expected), filter); } public static void assertDiagnostics(List<Diagnostic> actual, Diagnostic... expected) { assertDiagnostics(actual, Arrays.asList(expected), true); } public static void assertDiagnostics(List<Diagnostic> actual, List<Diagnostic> expected, boolean filter) { List<Diagnostic> received = actual; final boolean filterMessage; if (expected != null && !expected.isEmpty() && !StringUtils.isEmpty(expected.get(0).getMessage())) { filterMessage = true; } else { filterMessage = false; } if (filter) { received = actual.stream().map(d -> { Diagnostic simpler = new Diagnostic(d.getRange(), ""); simpler.setCode(d.getCode()); if (filterMessage) { simpler.setMessage(d.getMessage()); } return simpler; }).collect(Collectors.toList()); } assertIterableEquals(expected, received, "Unexpected diagnostics:\n" + actual); } public static Diagnostic d(int startLine, int startCharacter, int endLine, int endCharacter, IXMLErrorCode code) { return d(startLine, startCharacter, endLine, endCharacter, code, ""); } public static Diagnostic d(int startLine, int startCharacter, int endCharacter, IXMLErrorCode code) { // Diagnostic on 1 line return d(startLine, startCharacter, startLine, endCharacter, code); } public static Diagnostic d(int startLine, int startCharacter, int endLine, int endCharacter, IXMLErrorCode code, String message) { // Diagnostic on 1 line return new Diagnostic(r(startLine, startCharacter, endLine, endCharacter), message, null, null, code.getCode()); } public static Range r(int startLine, int startCharacter, int endLine, int endCharacter) { return new Range(new Position(startLine, startCharacter), new Position(endLine, endCharacter)); } public static ContentModelSettings getContentModelSettings(boolean isEnabled, boolean isSchema) { ContentModelSettings settings = new ContentModelSettings(); settings.setUseCache(false); XMLValidationSettings problems = new XMLValidationSettings(); problems.setNoGrammar("ignore"); settings.setValidation(problems); XMLValidationSettings diagnostics = new XMLValidationSettings(); diagnostics.setEnabled(isEnabled); diagnostics.setSchema(isSchema); settings.setValidation(diagnostics); return settings; } // ------------------- Publish Diagnostics assert public static void testPublishDiagnosticsFor(String xml, String fileURI, Consumer<XMLLanguageService> configuration, PublishDiagnosticsParams... expected) { List<PublishDiagnosticsParams> actual = new ArrayList<>(); XMLLanguageService xmlLanguageService = new XMLLanguageService(); if (configuration != null) { xmlLanguageService.initializeIfNeeded(); configuration.accept(xmlLanguageService); } DOMDocument xmlDocument = DOMParser.getInstance().parse(xml, fileURI, xmlLanguageService.getResolverExtensionManager()); xmlLanguageService.setDocumentProvider((uri) -> xmlDocument); publishDiagnostics(xmlDocument, actual, xmlLanguageService); assertEquals(expected.length, actual.size()); for (int i = 0; i < expected.length; i++) { assertEquals(fileURI, actual.get(i).getUri()); assertDiagnostics(actual.get(i).getDiagnostics(), expected[i].getDiagnostics(), false); } } public static void publishDiagnostics(DOMDocument xmlDocument, List<PublishDiagnosticsParams> actual, XMLLanguageService languageService) { CompletableFuture<Path> error = languageService.publishDiagnostics(xmlDocument, params -> { actual.add(params); }, (doc) -> { // Retrigger validation publishDiagnostics(xmlDocument, actual, languageService); }, null, () -> { }); if (error != null) { try { error.join(); // Wait for 500 ms to collect the last params Thread.sleep(200); } catch (Exception e) { e.printStackTrace(); } } } public static PublishDiagnosticsParams pd(String uri, Diagnostic... diagnostics) { return new PublishDiagnosticsParams(uri, Arrays.asList(diagnostics)); } // ------------------- CodeAction assert public static void testCodeActionsFor(String xml, Diagnostic diagnostic, CodeAction... expected) throws BadLocationException { testCodeActionsFor(xml, diagnostic, (String) null, expected); } public static void testCodeActionsFor(String xml, Diagnostic diagnostic, SharedSettings settings, CodeAction... expected) throws BadLocationException { testCodeActionsFor(xml, diagnostic, null, settings, expected); } public static void testCodeActionsFor(String xml, Diagnostic diagnostic, String catalogPath, CodeAction... expected) throws BadLocationException { SharedSettings settings = new SharedSettings(); settings.getFormattingSettings().setTabSize(4); settings.getFormattingSettings().setInsertSpaces(false); testCodeActionsFor(xml, diagnostic, catalogPath, settings, expected); } public static void testCodeActionsFor(String xml, Diagnostic diagnostic, String catalogPath, SharedSettings sharedSettings, CodeAction... expected) throws BadLocationException { int offset = xml.indexOf('|'); Range range = null; if (offset != -1) { xml = xml.substring(0, offset) + xml.substring(offset + 1); } TextDocument document = new TextDocument(xml.toString(), FILE_URI); if (offset != -1) { Position position = document.positionAt(offset); range = new Range(position, position); } else { range = diagnostic.getRange(); } XMLLanguageService xmlLanguageService = new XMLLanguageService(); ContentModelSettings cmSettings = new ContentModelSettings(); cmSettings.setUseCache(false); if (catalogPath != null) { // Configure XML catalog for XML schema cmSettings.setCatalogs(new String[] { catalogPath }); } xmlLanguageService.doSave(new SettingsSaveContext(cmSettings)); CodeActionContext context = new CodeActionContext(); context.setDiagnostics(Arrays.asList(diagnostic)); DOMDocument xmlDoc = DOMParser.getInstance().parse(document, xmlLanguageService.getResolverExtensionManager()); List<CodeAction> actual = xmlLanguageService.doCodeActions(context, range, xmlDoc, sharedSettings); assertCodeActions(actual, expected); } public static void assertCodeActions(List<CodeAction> actual, CodeAction... expected) { actual.stream().forEach(ca -> { // we don't want to compare title, etc ca.setCommand(null); ca.setKind(null); ca.setTitle(""); if (ca.getDiagnostics() != null) { ca.getDiagnostics().forEach(d -> { d.setSeverity(null); d.setMessage(""); d.setSource(null); }); } }); assertEquals(expected.length, actual.size()); assertArrayEquals(expected, actual.toArray()); } public static CodeAction ca(Diagnostic d, TextEdit... te) { CodeAction codeAction = new CodeAction(); codeAction.setTitle(""); codeAction.setDiagnostics(Arrays.asList(d)); VersionedTextDocumentIdentifier versionedTextDocumentIdentifier = new VersionedTextDocumentIdentifier(FILE_URI, 0); TextDocumentEdit textDocumentEdit = new TextDocumentEdit(versionedTextDocumentIdentifier, Arrays.asList(te)); WorkspaceEdit workspaceEdit = new WorkspaceEdit(Collections.singletonList(Either.forLeft(textDocumentEdit))); codeAction.setEdit(workspaceEdit); return codeAction; } public static CodeAction ca(Diagnostic d, Either<TextDocumentEdit, ResourceOperation>... ops) { CodeAction codeAction = new CodeAction(); codeAction.setDiagnostics(Collections.singletonList(d)); codeAction.setEdit(new WorkspaceEdit(Arrays.asList(ops))); codeAction.setTitle(""); return codeAction; } public static TextEdit te(int startLine, int startCharacter, int endLine, int endCharacter, String newText) { TextEdit textEdit = new TextEdit(); textEdit.setNewText(newText); textEdit.setRange(r(startLine, startCharacter, endLine, endCharacter)); return textEdit; } public static Either<TextDocumentEdit, ResourceOperation> createFile(String uri, boolean overwrite) { CreateFileOptions options = new CreateFileOptions(); options.setIgnoreIfExists(!overwrite); options.setOverwrite(overwrite); return Either.forRight(new CreateFile(uri, options)); } public static Either<TextDocumentEdit, ResourceOperation> teOp(String uri, int startLine, int startChar, int endLine, int endChar, String newText) { return Either.forLeft(new TextDocumentEdit(new VersionedTextDocumentIdentifier(uri, 0), Collections.singletonList(te(startLine, startChar, endLine, endChar, newText)))); } // ------------------- Hover assert public static void assertHover(String value) throws BadLocationException { assertHover(value, null, null); } public static void assertHover(String value, String expectedHoverLabel, Range expectedHoverRange) throws BadLocationException { assertHover(value, null, expectedHoverLabel, expectedHoverRange); } public static void assertHover(String value, String fileURI, String expectedHoverLabel, Range expectedHoverRange) throws BadLocationException { assertHover(new XMLLanguageService(), value, null, fileURI, expectedHoverLabel, expectedHoverRange); } public static void assertHover(XMLLanguageService xmlLanguageService, String value, String catalogPath, String fileURI, String expectedHoverLabel, Range expectedHoverRange) throws BadLocationException { ContentModelSettings settings = new ContentModelSettings(); settings.setUseCache(false); assertHover(xmlLanguageService, value, catalogPath, fileURI, expectedHoverLabel, expectedHoverRange, settings); } public static void assertHover(XMLLanguageService xmlLanguageService, String value, String catalogPath, String fileURI, String expectedHoverLabel, Range expectedHoverRange, SharedSettings sharedSettings) throws BadLocationException { ContentModelSettings settings = new ContentModelSettings(); settings.setUseCache(false); assertHover(xmlLanguageService, value, catalogPath, fileURI, expectedHoverLabel, expectedHoverRange, settings, sharedSettings); } public static void assertHover(XMLLanguageService xmlLanguageService, String value, String catalogPath, String fileURI, String expectedHoverLabel, Range expectedHoverRange, ContentModelSettings settings) throws BadLocationException { SharedSettings sharedSettings = new SharedSettings(); HoverCapabilities capabilities = new HoverCapabilities(Arrays.asList(MarkupKind.MARKDOWN), false); sharedSettings.getHoverSettings().setCapabilities(capabilities); assertHover(xmlLanguageService, value, catalogPath, fileURI, expectedHoverLabel, expectedHoverRange, settings, sharedSettings); } public static void assertHover(XMLLanguageService xmlLanguageService, String value, String catalogPath, String fileURI, String expectedHoverLabel, Range expectedHoverRange, ContentModelSettings settings, SharedSettings sharedSettings) throws BadLocationException { int offset = value.indexOf("|"); value = value.substring(0, offset) + value.substring(offset + 1); TextDocument document = new TextDocument(value, fileURI != null ? fileURI : "test://test/test.html"); Position position = document.positionAt(offset); DOMDocument htmlDoc = DOMParser.getInstance().parse(document, xmlLanguageService.getResolverExtensionManager()); // Configure XML catalog for XML schema if (catalogPath != null) { settings.setCatalogs(new String[] { catalogPath }); } xmlLanguageService.doSave(new SettingsSaveContext(settings)); Hover hover = xmlLanguageService.doHover(htmlDoc, position, sharedSettings); if (expectedHoverLabel == null) { assertNull(hover); } else { String actualHoverLabel = getHoverLabel(hover); assertEquals(expectedHoverLabel, actualHoverLabel); if (expectedHoverRange != null) { assertEquals(expectedHoverRange, hover.getRange()); } } } private static String getHoverLabel(Hover hover) { Either<List<Either<String, MarkedString>>, MarkupContent> contents = hover != null ? hover.getContents() : null; if (contents == null) { return null; } return contents.getRight().getValue(); } // ------------------- Links assert public static void testDocumentLinkFor(String xml, String fileURI, DocumentLink... expected) { TextDocument document = new TextDocument(xml, fileURI != null ? fileURI : "test.xml"); XMLLanguageService xmlLanguageService = new XMLLanguageService(); ContentModelSettings settings = new ContentModelSettings(); settings.setUseCache(false); xmlLanguageService.doSave(new SettingsSaveContext(settings)); DOMDocument xmlDocument = DOMParser.getInstance().parse(document, xmlLanguageService.getResolverExtensionManager()); xmlLanguageService.setDocumentProvider((uri) -> xmlDocument); List<DocumentLink> actual = xmlLanguageService.findDocumentLinks(xmlDocument); assertDocumentLinks(actual, expected); } public static DocumentLink dl(Range range, String target) { return new DocumentLink(range, target); } public static void assertDocumentLinks(List<DocumentLink> actual, DocumentLink... expected) { assertEquals(expected.length, actual.size()); for (int i = 0; i < expected.length; i++) { assertEquals(expected[i].getRange(), actual.get(i).getRange(), " Range test '" + i + "' link"); assertEquals(Paths.get(expected[i].getTarget()).toUri().toString(), actual.get(i).getTarget(), " Target test '" + i + "' link"); } } // ------------------- DocumentSymbol assert public static void testDocumentSymbolsFor(String xml, DocumentSymbol... expected) { testDocumentSymbolsFor(xml, null, new XMLSymbolSettings(), expected); } public static void testDocumentSymbolsFor(String xml, XMLSymbolSettings symbolSettings, DocumentSymbol... expected) { testDocumentSymbolsFor(xml, null, symbolSettings, expected); } public static void testDocumentSymbolsFor(String xml, String fileURI, DocumentSymbol... expected) { testDocumentSymbolsFor(xml, fileURI, new XMLSymbolSettings(), expected); } public static void testDocumentSymbolsFor(String xml, String fileURI, XMLSymbolSettings symbolSettings, DocumentSymbol... expected) { TextDocument document = new TextDocument(xml, fileURI != null ? fileURI : "test.xml"); XMLLanguageService xmlLanguageService = new XMLLanguageService(); ContentModelSettings settings = new ContentModelSettings(); settings.setUseCache(false); xmlLanguageService.doSave(new SettingsSaveContext(settings)); DOMDocument xmlDocument = DOMParser.getInstance().parse(document, xmlLanguageService.getResolverExtensionManager()); xmlLanguageService.setDocumentProvider((uri) -> xmlDocument); List<DocumentSymbol> actual = xmlLanguageService.findDocumentSymbols(xmlDocument, symbolSettings); assertDocumentSymbols(actual, expected); } public static DocumentSymbol ds(final String name, final SymbolKind kind, final Range range, final Range selectionRange, final String detail, final List<DocumentSymbol> children) { return new DocumentSymbol(name, kind, range, selectionRange, detail, children); } public static void assertDocumentSymbols(List<DocumentSymbol> actual, DocumentSymbol... expected) { assertEquals(expected.length, actual.size()); assertArrayEquals(expected, actual.toArray()); } // ------------------- Definition assert public static void testDefinitionFor(String xml, LocationLink... expected) throws BadLocationException { testDefinitionFor(xml, null, expected); } public static void testDefinitionFor(String value, String fileURI, LocationLink... expected) throws BadLocationException { int offset = value.indexOf('|'); value = value.substring(0, offset) + value.substring(offset + 1); TextDocument document = new TextDocument(value, fileURI != null ? fileURI : "test://test/test.xml"); Position position = document.positionAt(offset); XMLLanguageService xmlLanguageService = new XMLLanguageService(); ContentModelSettings settings = new ContentModelSettings(); settings.setUseCache(false); xmlLanguageService.doSave(new SettingsSaveContext(settings)); DOMDocument xmlDocument = DOMParser.getInstance().parse(document, xmlLanguageService.getResolverExtensionManager()); xmlLanguageService.setDocumentProvider((uri) -> xmlDocument); List<? extends LocationLink> actual = xmlLanguageService.findDefinition(xmlDocument, position, () -> { }); assertLocationLink(actual, expected); } public static LocationLink ll(final String uri, final Range originRange, Range targetRange) { return new LocationLink(uri, targetRange, targetRange, originRange); } public static void assertLocationLink(List<? extends LocationLink> actual, LocationLink... expected) { assertEquals(expected.length, actual.size()); for (int i = 0; i < expected.length; i++) { actual.get(i).setTargetUri(actual.get(i).getTargetUri().replaceAll("file:///", "file:/")); expected[i].setTargetUri(expected[i].getTargetUri().replaceAll("file:///", "file:/")); } assertArrayEquals(expected, actual.toArray()); } // ------------------- Type Definition assert public static void testTypeDefinitionFor(XMLLanguageService xmlLanguageService, String value, String fileURI, LocationLink... expected) throws BadLocationException { testTypeDefinitionFor(xmlLanguageService, null, value, fileURI, expected); } public static void testTypeDefinitionFor(XMLLanguageService xmlLanguageService, String catalogPath, String value, String fileURI, LocationLink... expected) throws BadLocationException { testTypeDefinitionFor(xmlLanguageService, catalogPath, null, value, fileURI, expected); } public static void testTypeDefinitionFor(XMLLanguageService xmlLanguageService, String catalogPath, Consumer<XMLLanguageService> customConfiguration, String value, String fileURI, LocationLink... expected) throws BadLocationException { int offset = value.indexOf('|'); value = value.substring(0, offset) + value.substring(offset + 1); TextDocument document = new TextDocument(value, fileURI != null ? fileURI : "test://test/test.xml"); Position position = document.positionAt(offset); ContentModelSettings cmSettings = new ContentModelSettings(); cmSettings.setUseCache(false); // Configure XML catalog for XML schema if (catalogPath != null) { cmSettings.setCatalogs(new String[] { catalogPath }); } xmlLanguageService.doSave(new SettingsSaveContext(cmSettings)); xmlLanguageService.initializeIfNeeded(); if (customConfiguration != null) { customConfiguration.accept(xmlLanguageService); } DOMDocument xmlDocument = DOMParser.getInstance().parse(document, xmlLanguageService.getResolverExtensionManager()); xmlLanguageService.setDocumentProvider((uri) -> xmlDocument); List<? extends LocationLink> actual = xmlLanguageService.findTypeDefinition(xmlDocument, position, () -> { }); assertLocationLink(actual, expected); } // ------------------- Reference assert public static void testReferencesFor(String xml, Location... expected) throws BadLocationException { testReferencesFor(xml, null, expected); } public static void testReferencesFor(String value, String fileURI, Location... expected) throws BadLocationException { int offset = value.indexOf('|'); value = value.substring(0, offset) + value.substring(offset + 1); TextDocument document = new TextDocument(value, fileURI != null ? fileURI : "test://test/test.xml"); Position position = document.positionAt(offset); XMLLanguageService xmlLanguageService = new XMLLanguageService(); ContentModelSettings settings = new ContentModelSettings(); settings.setUseCache(false); xmlLanguageService.doSave(new SettingsSaveContext(settings)); DOMDocument xmlDocument = DOMParser.getInstance().parse(document, xmlLanguageService.getResolverExtensionManager()); xmlLanguageService.setDocumentProvider((uri) -> xmlDocument); List<? extends Location> actual = xmlLanguageService.findReferences(xmlDocument, position, new ReferenceContext(), () -> { }); assertLocation(actual, expected); } public static Location l(final String uri, final Range range) { return new Location(uri, range); } public static void assertLocation(List<? extends Location> actual, Location... expected) { assertEquals(expected.length, actual.size()); assertArrayEquals(expected, actual.toArray()); } // ------------------- CodeLens assert public static void testCodeLensFor(String xml, CodeLens... expected) throws BadLocationException { testCodeLensFor(xml, null, expected); } public static void testCodeLensFor(String value, String fileURI, CodeLens... expected) throws BadLocationException { TextDocument document = new TextDocument(value, fileURI != null ? fileURI : "test://test/test.xml"); XMLLanguageService xmlLanguageService = new XMLLanguageService(); ContentModelSettings settings = new ContentModelSettings(); settings.setUseCache(false); xmlLanguageService.doSave(new SettingsSaveContext(settings)); DOMDocument xmlDocument = DOMParser.getInstance().parse(document, xmlLanguageService.getResolverExtensionManager()); xmlLanguageService.setDocumentProvider((uri) -> xmlDocument); XMLCodeLensSettings codeLensSettings = new XMLCodeLensSettings(); ExtendedCodeLensCapabilities codeLensCapabilities = new ExtendedCodeLensCapabilities( new CodeLensKindCapabilities(Arrays.asList(CodeLensKind.References))); codeLensSettings.setCodeLens(codeLensCapabilities); List<? extends CodeLens> actual = xmlLanguageService.getCodeLens(xmlDocument, codeLensSettings, () -> { }); assertCodeLens(actual, expected); } public static CodeLens cl(Range range, String title, String command) { return new CodeLens(range, new Command(title, command), null); } public static void assertCodeLens(List<? extends CodeLens> actual, CodeLens... expected) { assertEquals(expected.length, actual.size()); for (int i = 0; i < expected.length; i++) { assertEquals(expected[i].getRange(), actual.get(i).getRange()); Command expectedCommand = expected[i].getCommand(); Command actualCommand = actual.get(i).getCommand(); if (expectedCommand != null && actualCommand != null) { assertEquals(expectedCommand.getTitle(), actualCommand.getTitle()); assertEquals(expectedCommand.getCommand(), actualCommand.getCommand()); } assertEquals(expected[i].getData(), actual.get(i).getData()); } } // ------------------- Highlights assert public static void testHighlightsFor(String xml, DocumentHighlight... expected) throws BadLocationException { testHighlightsFor(xml, null, expected); } public static void testHighlightsFor(String value, String fileURI, DocumentHighlight... expected) throws BadLocationException { int offset = value.indexOf('|'); value = value.substring(0, offset) + value.substring(offset + 1); TextDocument document = new TextDocument(value, fileURI != null ? fileURI : "test://test/test.xml"); Position position = document.positionAt(offset); XMLLanguageService xmlLanguageService = new XMLLanguageService(); ContentModelSettings settings = new ContentModelSettings(); settings.setUseCache(false); xmlLanguageService.doSave(new SettingsSaveContext(settings)); DOMDocument xmlDocument = DOMParser.getInstance().parse(document, xmlLanguageService.getResolverExtensionManager()); xmlLanguageService.setDocumentProvider((uri) -> xmlDocument); List<? extends DocumentHighlight> actual = xmlLanguageService.findDocumentHighlights(xmlDocument, position, () -> { }); assertDocumentHighlight(actual, expected); } public static void assertDocumentHighlight(List<? extends DocumentHighlight> actual, DocumentHighlight... expected) { assertEquals(expected.length, actual.size()); assertArrayEquals(expected, actual.toArray()); } public static DocumentHighlight hl(Range range) { return hl(range, DocumentHighlightKind.Read); } public static DocumentHighlight hl(Range range, DocumentHighlightKind kind) { return new DocumentHighlight(range, kind); } public static void assertHighlights(String value, int[] expectedMatches, String elementName) throws BadLocationException { int offset = value.indexOf("|"); value = value.substring(0, offset) + value.substring(offset + 1); DOMDocument document = DOMParser.getInstance().parse(value, "test://test/test.html", null); Position position = document.positionAt(offset); XMLLanguageService languageService = new XMLLanguageService(); List<DocumentHighlight> highlights = languageService.findDocumentHighlights(document, position); assertEquals(expectedMatches.length, highlights.size()); for (int i = 0; i < highlights.size(); i++) { DocumentHighlight highlight = highlights.get(i); int actualStartOffset = document.offsetAt(highlight.getRange().getStart()); assertEquals(expectedMatches[i], actualStartOffset); int actualEndOffset = document.offsetAt(highlight.getRange().getEnd()); assertEquals(expectedMatches[i] + (elementName != null ? elementName.length() : 0), actualEndOffset); assertEquals(elementName, document.getText().substring(actualStartOffset, actualEndOffset).toLowerCase()); } } // ------------------- Rename assert public static void assertRename(String value, String newText) throws BadLocationException { assertRename(value, newText, Collections.emptyList()); } public static void assertRename(String value, String newText, List<TextEdit> expectedEdits) throws BadLocationException { int offset = value.indexOf("|"); value = value.substring(0, offset) + value.substring(offset + 1); DOMDocument document = DOMParser.getInstance().parse(value, "test://test/test.html", null); Position position = document.positionAt(offset); XMLLanguageService languageService = new XMLLanguageService(); WorkspaceEdit workspaceEdit = languageService.doRename(document, position, newText); List<TextEdit> actualEdits = workspaceEdit.getChanges().get("test://test/test.html"); assertArrayEquals(expectedEdits.toArray(), actualEdits.toArray()); } // ------------------- Generator assert public static void assertGrammarGenerator(String xml, FileContentGeneratorSettings grammarSettings, String expected) { DOMDocument document = DOMParser.getInstance().parse(xml, "test.xml", null); SharedSettings sharedSettings = new SharedSettings(); XMLLanguageService languageService = new XMLLanguageService(); FileContentGeneratorManager manager = new FileContentGeneratorManager(languageService); String actual = manager.generate(document, sharedSettings, grammarSettings); Assertions.assertEquals(expected, actual); } }