diff --git a/atom-extensions/atom-spring-boot/package.json b/atom-extensions/atom-spring-boot/package.json index 853964d23..db9232845 100644 --- a/atom-extensions/atom-spring-boot/package.json +++ b/atom-extensions/atom-spring-boot/package.json @@ -35,6 +35,16 @@ "default": false, "description": "Enable/Disable Support for Spring XML Config files" }, + "support-spring-xml-config.hyperlinks": { + "type": "boolean", + "description": "Enable/Disable Hyperlinks in Spring XML Config file editor", + "default": true + }, + "support-spring-xml-config.content-assist": { + "type": "boolean", + "description": "Enable/Disable Content Assist in Spring XML Config file editor", + "default": true + }, "support-spring-xml-config.scan-folders-globs": { "type": "string", "default": "**/src/main/**", diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/plugin.xml b/eclipse-language-servers/org.springframework.tooling.boot.ls/plugin.xml index dd1877610..02e917d2a 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/plugin.xml +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/plugin.xml @@ -109,9 +109,21 @@ + + + + diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/BootLanguageServerPreferencesPage.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/BootJavaPreferencesPage.java similarity index 65% rename from eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/BootLanguageServerPreferencesPage.java rename to eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/BootJavaPreferencesPage.java index cb461c824..91160d109 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/BootLanguageServerPreferencesPage.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/BootJavaPreferencesPage.java @@ -12,18 +12,12 @@ package org.springframework.tooling.boot.ls; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.InstanceScope; -import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.preference.BooleanFieldEditor; import org.eclipse.jface.preference.FieldEditorPreferencePage; import org.eclipse.jface.preference.IPreferenceStore; -import org.eclipse.jface.preference.StringFieldEditor; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Group; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; import org.springframework.tooling.ls.eclipse.commons.LanguageServerCommonsActivator; @@ -35,11 +29,7 @@ import org.springframework.tooling.ls.eclipse.commons.preferences.PreferenceCons * @author Alex Boyko * */ -public class BootLanguageServerPreferencesPage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { - - public BootLanguageServerPreferencesPage() { - super(GRID); - } +public class BootJavaPreferencesPage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { /** * Starts a preference change listener that keeps code mining preferences in sync with @@ -84,48 +74,22 @@ public class BootLanguageServerPreferencesPage extends FieldEditorPreferencePage setPreferenceStore(BootLanguageServerPlugin.getDefault().getPreferenceStore()); } - @Override - protected void adjustGridLayout() { - // Keep empty - } - @Override protected void createFieldEditors() { final IPreferenceStore commonsLsPrefs = LanguageServerCommonsActivator.getInstance().getPreferenceStore(); - Composite contents = new Composite(getFieldEditorParent(), SWT.NONE); - GridData gd = new GridData(GridData.FILL_BOTH); - contents.setLayoutData(gd); - contents.setLayout(new GridLayout()); + Composite fieldEditorParent = getFieldEditorParent(); - Group liveBeansGroup = new Group(contents, SWT.NONE); - liveBeansGroup.setText("Spring Boot Live Beans"); - liveBeansGroup.setLayout(new GridLayout(1, false)); - liveBeansGroup.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create()); - - addField(new BooleanFieldEditor(Constants.PREF_BOOT_HINTS, "Live Boot Hint Decorators", liveBeansGroup)); - addField(new BooleanFieldEditor(PreferenceConstants.HIGHLIGHT_CODELENS_PREFS, "Highlights CodeLens", liveBeansGroup) { + addField(new BooleanFieldEditor(Constants.PREF_SCAN_JAVA_TEST_SOURCES, "Scan Java test sources", fieldEditorParent)); + addField(new BooleanFieldEditor(Constants.PREF_BOOT_HINTS, "Live Boot Hint Decorators", fieldEditorParent)); + addField(new BooleanFieldEditor(PreferenceConstants.HIGHLIGHT_CODELENS_PREFS, "Highlights CodeLens", fieldEditorParent) { @Override public IPreferenceStore getPreferenceStore() { return commonsLsPrefs; } }); - addField(new BooleanFieldEditor(Constants.PREF_CHANGE_DETECTION, "Live Boot Change Detection", liveBeansGroup)); + addField(new BooleanFieldEditor(Constants.PREF_CHANGE_DETECTION, "Live Boot Change Detection", fieldEditorParent)); - Group symbolGroup = new Group(contents, SWT.NONE); - symbolGroup.setText("Spring Symbols"); - symbolGroup.setLayout(new GridLayout(1, false)); - symbolGroup.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create()); - - addField(new BooleanFieldEditor(Constants.PREF_SCAN_JAVA_TEST_SOURCES, "Scan Java test sources", symbolGroup)); - addField(new BooleanFieldEditor(Constants.PREF_SUPPORT_SPRING_XML_CONFIGS, "Scan Spring XML Config files (experimental)", symbolGroup)); - - Composite scanFoldersComposite = new Composite(symbolGroup, SWT.NONE); - scanFoldersComposite.setFont(symbolGroup.getFont()); - scanFoldersComposite.setLayoutData(GridDataFactory.swtDefaults().span(2, 1).align(GridData.FILL, GridData.BEGINNING).grab(true, false).create()); - scanFoldersComposite.setLayout(new GridLayout(2, false)); - addField(new StringFieldEditor(Constants.PREF_XML_CONFIGS_SCAN_FOLDERS, "Scan Spring XML in folders:", scanFoldersComposite)); - } } diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/BootLanguageServerPlugin.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/BootLanguageServerPlugin.java index b4430f406..1234dede8 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/BootLanguageServerPlugin.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/BootLanguageServerPlugin.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017, 2018 Pivotal, Inc. + * Copyright (c) 2017, 2019 Pivotal, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -45,7 +45,7 @@ public class BootLanguageServerPlugin extends AbstractUIPlugin { plugin = this; super.start(context); deactivateDuplicateKeybindings(); - BootLanguageServerPreferencesPage.manageCodeMiningPreferences(); + BootJavaPreferencesPage.manageCodeMiningPreferences(); } @Override diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/Constants.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/Constants.java index 2ea6c1139..58b4f443a 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/Constants.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/Constants.java @@ -21,6 +21,8 @@ public class Constants { public static final String PREF_SUPPORT_SPRING_XML_CONFIGS = "boot-java.support-spring-xml-config.on"; public static final String PREF_XML_CONFIGS_SCAN_FOLDERS = "boot-java.support-spring-xml-config.scan-folders-globs"; public static final String PREF_SCAN_JAVA_TEST_SOURCES = "boot-java.scan-java-test-sources"; + public static final String PREF_XML_CONFIGS_HYPERLINKS = "boot-java.support-spring-xml-config.hyperlinks"; + public static final String PREF_XML_CONFIGS_CONTENT_ASSIST = "boot-java.support-spring-xml-config.content-assist"; public static final String PREF_CHANGE_DETECTION = "boot-java.change-detection.on"; } diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/DelegatingStreamConnectionProvider.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/DelegatingStreamConnectionProvider.java index cbfffdd38..22cf1a29d 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/DelegatingStreamConnectionProvider.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/DelegatingStreamConnectionProvider.java @@ -213,6 +213,8 @@ public class DelegatingStreamConnectionProvider implements StreamConnectionProvi bootHint.put("on", preferenceStore.getBoolean(Constants.PREF_BOOT_HINTS)); supportXML.put("on", preferenceStore.getBoolean(Constants.PREF_SUPPORT_SPRING_XML_CONFIGS)); supportXML.put("scan-folders-globs", preferenceStore.getString(Constants.PREF_XML_CONFIGS_SCAN_FOLDERS)); + supportXML.put("hyperlinks", preferenceStore.getString(Constants.PREF_XML_CONFIGS_HYPERLINKS)); + supportXML.put("content-assist", preferenceStore.getString(Constants.PREF_XML_CONFIGS_CONTENT_ASSIST)); bootChangeDetection.put("on", preferenceStore.getBoolean(Constants.PREF_CHANGE_DETECTION)); scanTestJavaSources.put("on", preferenceStore.getBoolean(Constants.PREF_SCAN_JAVA_TEST_SOURCES)); diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/PrefsInitializer.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/PrefsInitializer.java index eb1ddfaa5..2791ce68c 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/PrefsInitializer.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/PrefsInitializer.java @@ -29,6 +29,8 @@ public class PrefsInitializer extends AbstractPreferenceInitializer { IPreferenceStore preferenceStore = BootLanguageServerPlugin.getDefault().getPreferenceStore(); preferenceStore.setDefault(Constants.PREF_BOOT_HINTS, true); preferenceStore.setDefault(Constants.PREF_SUPPORT_SPRING_XML_CONFIGS, false); + preferenceStore.setDefault(Constants.PREF_XML_CONFIGS_HYPERLINKS, true); + preferenceStore.setDefault(Constants.PREF_XML_CONFIGS_CONTENT_ASSIST, true); preferenceStore.setDefault(Constants.PREF_XML_CONFIGS_SCAN_FOLDERS, "**/src/main/**"); preferenceStore.setDefault(Constants.PREF_CHANGE_DETECTION, false); preferenceStore.setDefault(Constants.PREF_SCAN_JAVA_TEST_SOURCES, false); diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/SpringBootLanguageServerPreferencePage.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/SpringBootLanguageServerPreferencePage.java new file mode 100644 index 000000000..8d5b9cdf5 --- /dev/null +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/SpringBootLanguageServerPreferencePage.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2019 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.tooling.boot.ls; + +import org.eclipse.jface.preference.PreferencePage; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; + +public class SpringBootLanguageServerPreferencePage extends PreferencePage implements IWorkbenchPreferencePage { + + public SpringBootLanguageServerPreferencePage() { + } + + public SpringBootLanguageServerPreferencePage(String title) { + super(title); + } + + public SpringBootLanguageServerPreferencePage(String title, ImageDescriptor image) { + super(title, image); + } + + @Override + public void init(IWorkbench workbench) { + } + + @Override + protected Control createContents(Composite parent) { + return new Composite(parent, SWT.NONE); + } + +} diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/XmlConfigPreferencePage.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/XmlConfigPreferencePage.java new file mode 100644 index 000000000..2a5ab4627 --- /dev/null +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/XmlConfigPreferencePage.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2019 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.tooling.boot.ls; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.preference.BooleanFieldEditor; +import org.eclipse.jface.preference.FieldEditorPreferencePage; +import org.eclipse.jface.preference.StringFieldEditor; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; + +/** + * + * @author Alex Boyko + * + */ +public class XmlConfigPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { + + private List updateXmlSettingControlsEnablement = Collections.emptyList(); + private BooleanFieldEditor enableEditor; + + public XmlConfigPreferencePage() { + super(GRID); + } + + @Override + protected void adjustGridLayout() { + } + + @Override + public void init(IWorkbench workbench) { + setPreferenceStore(BootLanguageServerPlugin.getDefault().getPreferenceStore()); + } + + + @Override + public void propertyChange(PropertyChangeEvent event) { + super.propertyChange(event); + if (enableEditor == event.getSource()) { + updateXmlSettingControlsEnablement.forEach(r -> r.run()); + } + } + + @Override + protected void performDefaults() { + super.performDefaults(); + updateXmlSettingControlsEnablement.forEach(r -> r.run()); + } + + @Override + protected void createFieldEditors() { + Composite fieldEditorParent = getFieldEditorParent(); + final boolean xmlEnabled = getPreferenceStore().getBoolean(Constants.PREF_SUPPORT_SPRING_XML_CONFIGS); + updateXmlSettingControlsEnablement = new ArrayList<>(); + + enableEditor = new BooleanFieldEditor(Constants.PREF_SUPPORT_SPRING_XML_CONFIGS, "Enable Spring XML Config files support", fieldEditorParent); + addField(enableEditor); + + Label label = new Label(fieldEditorParent, SWT.SEPARATOR | SWT.HORIZONTAL); + label.setLayoutData(GridDataFactory.swtDefaults().grab(true, false).span(2, 1).align(SWT.FILL, SWT.BEGINNING).create()); + + Composite settingsComposite = new Composite(fieldEditorParent, SWT.NONE); + settingsComposite.setLayoutData(GridDataFactory.swtDefaults().grab(true, true).align(SWT.FILL, SWT.BEGINNING).create()); + settingsComposite.setLayout(new GridLayout(1, false)); + + BooleanFieldEditor caFieldEditor = new BooleanFieldEditor(Constants.PREF_XML_CONFIGS_CONTENT_ASSIST, "Content Assist in editor", settingsComposite); + addField(caFieldEditor); + caFieldEditor.setEnabled(xmlEnabled, settingsComposite); + updateXmlSettingControlsEnablement.add(() -> caFieldEditor.setEnabled(enableEditor.getBooleanValue(), settingsComposite)); + + BooleanFieldEditor hyperlinkEditor = new BooleanFieldEditor(Constants.PREF_XML_CONFIGS_HYPERLINKS, "Hyperlinks in editor", settingsComposite); + addField(hyperlinkEditor); + hyperlinkEditor.setEnabled(xmlEnabled, settingsComposite); + updateXmlSettingControlsEnablement.add(() -> hyperlinkEditor.setEnabled(enableEditor.getBooleanValue(), settingsComposite)); + + Composite scanFoldersComposite = new Composite(settingsComposite, SWT.NONE); + scanFoldersComposite.setFont(settingsComposite.getFont()); + scanFoldersComposite.setLayoutData(GridDataFactory.swtDefaults().span(2, 1).align(GridData.FILL, GridData.BEGINNING).grab(true, false).create()); + scanFoldersComposite.setLayout(new GridLayout(2, false)); + StringFieldEditor scanGlobEditor = new StringFieldEditor(Constants.PREF_XML_CONFIGS_SCAN_FOLDERS, "Scan XML in folders for symbols:", scanFoldersComposite); + addField(scanGlobEditor); + scanGlobEditor.setEnabled(xmlEnabled, scanFoldersComposite); + updateXmlSettingControlsEnablement.add(() -> scanGlobEditor.setEnabled(enableEditor.getBooleanValue(), scanFoldersComposite)); + + } + +} diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/xml/XMLContentAssistProposalComputer.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/xml/XMLContentAssistProposalComputer.java index fd0cc64ad..feb2cdcea 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/xml/XMLContentAssistProposalComputer.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/xml/XMLContentAssistProposalComputer.java @@ -62,7 +62,8 @@ public class XMLContentAssistProposalComputer implements IAsyncCompletionProposa @Override public List computeContextInformation(CompletionProposalInvocationContext context, IProgressMonitor monitor) { - if (!BootLanguageServerPlugin.getDefault().getPreferenceStore().getBoolean(Constants.PREF_SUPPORT_SPRING_XML_CONFIGS)) { + if (!BootLanguageServerPlugin.getDefault().getPreferenceStore().getBoolean(Constants.PREF_SUPPORT_SPRING_XML_CONFIGS) + || !BootLanguageServerPlugin.getDefault().getPreferenceStore().getBoolean(Constants.PREF_XML_CONFIGS_CONTENT_ASSIST)) { return Collections.emptyList(); } diff --git a/headless-services/commons/language-server-starter/src/main/java/org/springframework/ide/vscode/languageserver/starter/LanguageServerAutoConf.java b/headless-services/commons/language-server-starter/src/main/java/org/springframework/ide/vscode/languageserver/starter/LanguageServerAutoConf.java index 0c4cf72d9..d9940b6ba 100644 --- a/headless-services/commons/language-server-starter/src/main/java/org/springframework/ide/vscode/languageserver/starter/LanguageServerAutoConf.java +++ b/headless-services/commons/language-server-starter/src/main/java/org/springframework/ide/vscode/languageserver/starter/LanguageServerAutoConf.java @@ -72,7 +72,7 @@ public class LanguageServerAutoConf { @ConditionalOnBean(DefinitionHandler.class) @Bean - InitializingBean registerDefintionHandler(SimpleTextDocumentService documents, + InitializingBean registerDefinitionHandler(SimpleTextDocumentService documents, List definitionHandlers) { if (definitionHandlers.size() == 1) { return () -> documents.onDefinition(definitionHandlers.get(0)); diff --git a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java index 6b6197aec..e35f435fc 100644 --- a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java +++ b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java @@ -750,6 +750,19 @@ public class Editor { assertEquals(ImmutableSet.copyOf(expectedLocations), ImmutableSet.copyOf(definitions)); } + + public void assertNoLinkTargets(String hoverOver) throws Exception { + int pos = getRawText().indexOf(hoverOver); + if (pos>=0) { + pos += hoverOver.length() / 2; + } + assertTrue("Not found in editor: '"+hoverOver+"'", pos>=0); + + TextDocumentPositionParams params = new TextDocumentPositionParams(new TextDocumentIdentifier(getUri()), doc.toPosition(pos)); + List definitions = harness.getDefinitions(params); + + assertTrue(definitions == null || definitions.isEmpty()); + } @Deprecated public void assertLinkTargets(String hoverOver, String... expecteds) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaConfig.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaConfig.java index 85a39cdf5..7aa2c16d2 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaConfig.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaConfig.java @@ -82,6 +82,16 @@ public class BootJavaConfig implements InitializingBean { return enabled != null && enabled.booleanValue(); } + public boolean areXmlHyperlinksEnabled() { + Boolean enabled = settings.getBoolean("boot-java", "support-spring-xml-config", "hyperlinks"); + return enabled != null && enabled.booleanValue(); + } + + public boolean isXmlContentAssistEnabled() { + Boolean enabled = settings.getBoolean("boot-java", "support-spring-xml-config", "content-assist"); + return enabled != null && enabled.booleanValue(); + } + public void handleConfigurationChange(Settings newConfig) { this.settings = newConfig; listeners.fire(null); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java index 2106b11d9..0d6948545 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java @@ -20,6 +20,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -29,6 +30,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.function.Predicate; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.commons.io.FileUtils; import org.eclipse.lsp4j.SymbolInformation; @@ -64,6 +66,7 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument; import org.springframework.stereotype.Component; import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableList; /** * @author Martin Lippert @@ -80,14 +83,11 @@ public class SpringSymbolIndex implements InitializingBean { private static final String QUERY_PARAM_LOCATION_PREFIX = "locationPrefix:"; private static final int MAX_NUMBER_OF_SYMBOLS_IN_RESPONSE = 50; - private final List symbols = new ArrayList<>(); - private final List addonInformation = new ArrayList<>(); + private final List symbols = new ArrayList<>(); - private final ConcurrentMap> symbolsByDoc = new ConcurrentHashMap<>(); - private final ConcurrentMap> addonInformationByDoc = new ConcurrentHashMap<>(); + private final ConcurrentMap> symbolsByDoc = new ConcurrentHashMap<>(); - private final ConcurrentMap> symbolsByProject = new ConcurrentHashMap<>(); - private final ConcurrentMap> addonInformationByProject = new ConcurrentHashMap<>(); + private final ConcurrentMap> symbolsByProject = new ConcurrentHashMap<>(); private final ExecutorService updateQueue = Executors.newSingleThreadExecutor(); private SpringIndexer[] indexer; @@ -403,17 +403,26 @@ public class SpringSymbolIndex implements InitializingBean { } } else { synchronized(this.symbols) { - List subList = this.symbols.subList(0, Math.min(MAX_NUMBER_OF_SYMBOLS_IN_RESPONSE, this.symbols.size())); - return new ArrayList(subList); + return this.symbols.stream().map(s -> s.getSymbol()).limit(Math.min(MAX_NUMBER_OF_SYMBOLS_IN_RESPONSE, this.symbols.size())).collect(Collectors.toList()); } } } + + public Stream getSymbols(Predicate filter) { + return symbols.parallelStream() + .filter(filter) + .map(enhanced -> enhanced.getSymbol()); + } public List getSymbols(String docURI) { - List docSymbols = this.symbolsByDoc.get(docURI); + List docSymbols = this.symbolsByDoc.get(docURI); if (docSymbols != null) { synchronized(docSymbols) { - return new ArrayList(docSymbols); + ImmutableList.Builder builder = ImmutableList.builder(); + for (EnhancedSymbolInformation enhanced : docSymbols) { + builder.add(enhanced.getSymbol()); + } + return builder.build(); } } else { @@ -423,8 +432,13 @@ public class SpringSymbolIndex implements InitializingBean { public List getAllAdditionalInformation(Predicate filter) { if (filter != null) { - synchronized(addonInformation) { - return addonInformation.stream().filter(filter).collect(Collectors.toList()); + synchronized(symbols) { + return symbols.stream() + .map(s -> s.getAdditionalInformation()) + .filter(Objects::nonNull) + .flatMap(i -> Arrays.stream(i)) + .filter(filter) + .collect(Collectors.toList()); } } else { @@ -433,11 +447,18 @@ public class SpringSymbolIndex implements InitializingBean { } public List getAdditonalInformation(String docURI) { - List info = this.addonInformationByDoc.get(docURI); + List info = this.symbolsByDoc.get(docURI); if (info != null) { synchronized(info) { - return new ArrayList<>(info); + ImmutableList.Builder builder = ImmutableList.builder(); + for (EnhancedSymbolInformation enhanced : info) { + SymbolAddOnInformation[] additionalInformation = enhanced.getAdditionalInformation(); + if (additionalInformation != null) { + builder.add(additionalInformation); + } + } + return builder.build(); } } else { @@ -458,7 +479,7 @@ public class SpringSymbolIndex implements InitializingBean { }, this.updateQueue); } - private List searchMatchingSymbols(List allsymbols, String query, int maxNumberOfSymbolsInResponse) { + private List searchMatchingSymbols(List allsymbols, String query, int maxNumberOfSymbolsInResponse) { long limit = maxNumberOfSymbolsInResponse; String locationPrefix = ""; @@ -484,6 +505,7 @@ public class SpringSymbolIndex implements InitializingBean { String finalLocationPrefix = locationPrefix; return allsymbols.stream() + .map(enhanced -> enhanced.getSymbol()) .filter(symbol -> symbol.getLocation().getUri().startsWith(finalLocationPrefix)) .filter(symbol -> StringUtil.containsCharactersCaseInsensitive(symbol.getName(), finalQuery)) .limit(limit) @@ -603,41 +625,25 @@ public class SpringSymbolIndex implements InitializingBean { private void addSymbol(IJavaProject project, String docURI, EnhancedSymbolInformation enhancedSymbol) { synchronized(this.symbols) { - symbols.add(enhancedSymbol.getSymbol()); + symbols.add(enhancedSymbol); } - List docSymbols = symbolsByDoc.computeIfAbsent(docURI, s -> new ArrayList()); + List docSymbols = symbolsByDoc.computeIfAbsent(docURI, s -> new ArrayList()); synchronized(docSymbols) { - docSymbols.add(enhancedSymbol.getSymbol()); + docSymbols.add(enhancedSymbol); } - List projectSymbols = symbolsByProject.computeIfAbsent(project.getElementName(), s -> new ArrayList()); + List projectSymbols = symbolsByProject.computeIfAbsent(project.getElementName(), s -> new ArrayList()); synchronized(projectSymbols) { - projectSymbols.add(enhancedSymbol.getSymbol()); - } - - if (enhancedSymbol.getAdditionalInformation() != null) { - synchronized(addonInformation) { - addonInformation.addAll(Arrays.asList(enhancedSymbol.getAdditionalInformation())); - } - - List infoByDoc = addonInformationByDoc.computeIfAbsent(docURI, s -> new ArrayList()); - synchronized(infoByDoc) { - infoByDoc.addAll(Arrays.asList(enhancedSymbol.getAdditionalInformation())); - } - - List infoByProject = addonInformationByProject.computeIfAbsent(project.getElementName(), s -> new ArrayList()); - synchronized(infoByProject) { - infoByProject.addAll(Arrays.asList(enhancedSymbol.getAdditionalInformation())); - } + projectSymbols.add(enhancedSymbol); } } private void removeSymbolsByDoc(IJavaProject project, String docURI) { - List oldSymbols = symbolsByDoc.remove(docURI); + List oldSymbols = symbolsByDoc.remove(docURI); if (oldSymbols != null) { - List copy = null; + List copy = null; synchronized(oldSymbols) { copy = new ArrayList<>(oldSymbols); } @@ -646,7 +652,7 @@ public class SpringSymbolIndex implements InitializingBean { this.symbols.removeAll(copy); } - List projectSymbols = symbolsByProject.get(project.getElementName()); + List projectSymbols = symbolsByProject.get(project.getElementName()); if (projectSymbols != null) { synchronized(projectSymbols) { projectSymbols.removeAll(copy); @@ -654,25 +660,6 @@ public class SpringSymbolIndex implements InitializingBean { } } - List oldAddOnInformation = addonInformationByDoc.remove(docURI); - if (oldAddOnInformation != null) { - - List copy = null; - synchronized(oldAddOnInformation) { - copy = new ArrayList<>(oldAddOnInformation); - } - - synchronized(addonInformation) { - addonInformation.removeAll(copy); - } - - List projectAddOns = addonInformationByProject.get(project.getElementName()); - if (projectAddOns != null) { - synchronized(projectAddOns) { - projectAddOns.removeAll(copy); - } - } - } } private void removeSymbolsByProject(IJavaProject project) { @@ -680,10 +667,10 @@ public class SpringSymbolIndex implements InitializingBean { if (project.getElementName() == null) { return; } - List oldSymbols = symbolsByProject.remove(project.getElementName()); + List oldSymbols = symbolsByProject.remove(project.getElementName()); if (oldSymbols != null) { - List copy = null; + List copy = null; synchronized(oldSymbols) { copy = new ArrayList<>(oldSymbols); } @@ -696,7 +683,7 @@ public class SpringSymbolIndex implements InitializingBean { Iterator docIter = keySet.iterator(); while (docIter.hasNext()) { String docURI = docIter.next(); - List docSymbols = symbolsByDoc.get(docURI); + List docSymbols = symbolsByDoc.get(docURI); synchronized(docSymbols) { docSymbols.removeAll(copy); @@ -707,32 +694,5 @@ public class SpringSymbolIndex implements InitializingBean { } } - List oldAddInInformation = addonInformationByProject.remove(project.getElementName()); - if (oldAddInInformation != null) { - - List copy = null; - synchronized(oldAddInInformation) { - copy = new ArrayList<>(oldAddInInformation); - } - - synchronized(this.addonInformation) { - addonInformation.removeAll(copy); - } - - Set keySet = addonInformationByDoc.keySet(); - Iterator docIter = keySet.iterator(); - while (docIter.hasNext()) { - String docURI = docIter.next(); - List docAddons = addonInformationByDoc.get(docURI); - - synchronized(docAddons) { - docAddons.removeAll(copy); - - if (docAddons.isEmpty()) { - docIter.remove(); - } - } - } - } } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/XmlBeansConfigDefinitionHandler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/XmlBeansConfigDefinitionHandler.java new file mode 100644 index 000000000..0659b5473 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/XmlBeansConfigDefinitionHandler.java @@ -0,0 +1,152 @@ +/******************************************************************************* + * Copyright (c) 2019 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.app; + +import static org.springframework.ide.vscode.boot.xml.XmlConfigConstants.BEANS_NAMESPACE; +import static org.springframework.ide.vscode.boot.xml.XmlConfigConstants.BEAN_ELEMENT; +import static org.springframework.ide.vscode.boot.xml.XmlConfigConstants.CLASS_ATTRIBUTE; +import static org.springframework.ide.vscode.boot.xml.XmlConfigConstants.NAME_ATTRIBUTE; +import static org.springframework.ide.vscode.boot.xml.XmlConfigConstants.PROPERTY_ELEMENT; +import static org.springframework.ide.vscode.boot.xml.XmlConfigConstants.REF_ATTRIBUTE; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.TextDocumentPositionParams; +import org.eclipse.lsp4xml.dom.DOMAttr; +import org.eclipse.lsp4xml.dom.DOMDocument; +import org.eclipse.lsp4xml.dom.DOMNode; +import org.eclipse.lsp4xml.dom.DOMParser; +import org.eclipse.lsp4xml.dom.parser.Scanner; +import org.eclipse.lsp4xml.dom.parser.TokenType; +import org.eclipse.lsp4xml.dom.parser.XMLScanner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.boot.java.links.JavaElementLocationProvider; +import org.springframework.ide.vscode.boot.xml.XMLElementKey; +import org.springframework.ide.vscode.boot.xml.hyperlinks.BeanRefHyperlinkProvider; +import org.springframework.ide.vscode.boot.xml.hyperlinks.JavaTypeHyperlinkProvider; +import org.springframework.ide.vscode.boot.xml.hyperlinks.PropertyNameHyperlinkProvider; +import org.springframework.ide.vscode.boot.xml.hyperlinks.XMLHyperlinkProvider; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.util.DefinitionHandler; +import org.springframework.ide.vscode.commons.languageserver.util.LanguageSpecific; +import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; +import org.springframework.ide.vscode.commons.util.text.LanguageId; +import org.springframework.ide.vscode.commons.util.text.TextDocument; +import org.springframework.stereotype.Component; + +import com.google.common.collect.ImmutableList; + +/** + * + * @author Alex Boyko + * + */ +@Component +public class XmlBeansConfigDefinitionHandler implements DefinitionHandler, LanguageSpecific { + + private static final Logger log = LoggerFactory.getLogger(XmlBeansConfigDefinitionHandler.class); + + private final Map> hyperlinkProviders; + + private final SimpleTextDocumentService documents; + private final BootJavaConfig config; + + public XmlBeansConfigDefinitionHandler(SimpleTextDocumentService documents, + BootJavaConfig config, + JavaElementLocationProvider locationProvider, + SpringSymbolIndex symbolIndex, + BootLanguageServerParams serverParams) { + this.documents = documents; + this.config = config; + JavaProjectFinder projectFinder = serverParams.projectFinder; + + hyperlinkProviders = new HashMap<>(); + hyperlinkProviders.put(new XMLElementKey(BEANS_NAMESPACE, null, BEAN_ELEMENT, CLASS_ATTRIBUTE), Arrays.asList(new JavaTypeHyperlinkProvider(projectFinder, locationProvider))); + hyperlinkProviders.put(new XMLElementKey(BEANS_NAMESPACE, BEAN_ELEMENT, PROPERTY_ELEMENT, NAME_ATTRIBUTE), Arrays.asList(new PropertyNameHyperlinkProvider(projectFinder, locationProvider))); + hyperlinkProviders.put(new XMLElementKey(BEANS_NAMESPACE, BEAN_ELEMENT, PROPERTY_ELEMENT, REF_ATTRIBUTE), Arrays.asList(new BeanRefHyperlinkProvider(projectFinder, symbolIndex, documents))); + + } + + @Override + public Collection supportedLanguages() { + return Arrays.asList(LanguageId.XML); + } + + @Override + public List handle(TextDocumentPositionParams position) { + try { + if (config.isSpringXMLSupportEnabled() && config.areXmlHyperlinksEnabled()) { + TextDocument doc = documents.get(position); + if (doc != null) { + String content = doc.get(); + + DOMParser parser = DOMParser.getInstance(); + DOMDocument dom = parser.parse(content, "", null); + + int offset = doc.toOffset(position.getPosition()); + + DOMNode node = dom.findNodeBefore(offset); + + if (node != null) { + String namespace = node.getNamespaceURI(); + + Scanner scanner = XMLScanner.createScanner(content, node.getStart(), false); + TokenType token = scanner.scan(); + while (token != TokenType.EOS && scanner.getTokenOffset() <= offset) { + switch (token) { + case AttributeValue: + if (scanner.getTokenOffset() <= offset && offset <= scanner.getTokenEnd()) { + DOMAttr attributeAt = dom.findAttrAt(offset); + + if (attributeAt != null) { + XMLElementKey key = new XMLElementKey(namespace, null, node.getLocalName(), attributeAt.getNodeName()); + + if (!hyperlinkProviders.containsKey(key)) { + DOMNode parentNode = node.getParentNode(); + String parentNodeName = parentNode != null ? parentNode.getLocalName() : null; + key = new XMLElementKey(namespace, parentNodeName, node.getLocalName(), attributeAt.getNodeName()); + } + + List providers = hyperlinkProviders.get(key); + if (providers != null) { + ImmutableList.Builder listBuilder = ImmutableList.builder(); + for (XMLHyperlinkProvider provider : providers) { + Location location = provider.getDefinition(doc, namespace, node, attributeAt); + if (location != null) { + listBuilder.add(location); + } + } + return listBuilder.build(); + } + } + } + break; + default: + break; + } + token = scanner.scan(); + } + } + } + } + } catch (Exception e) { + log.error("{}", e); + } + return null; + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/SpringXMLCompletionEngine.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/SpringXMLCompletionEngine.java index 5899cbe70..3d8b37f6a 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/SpringXMLCompletionEngine.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/SpringXMLCompletionEngine.java @@ -10,6 +10,8 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.xml; +import static org.springframework.ide.vscode.boot.xml.XmlConfigConstants.*; + import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -38,16 +40,7 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument; */ public class SpringXMLCompletionEngine implements ICompletionEngine { - private static final String BEANS_NAMESPACE = "http://www.springframework.org/schema/beans"; - - private static final String BEAN_ELEMENT = "bean"; - private static final String CLASS_ATTRIBUTE = "class"; - - private static final String PROPERTY_ELEMENT = "property"; - private static final String NAME_ATTRIBUTE = "name"; - private static final String REF_ATTRIBUTE = "ref"; - - private final Map completionProviders; + private final Map completionProviders; private final BootJavaConfig config; public SpringXMLCompletionEngine(SpringXMLLanguageServerComponents springXMLLanguageServerComponents, @@ -56,14 +49,14 @@ public class SpringXMLCompletionEngine implements ICompletionEngine { this.config = config; this.completionProviders = new HashMap<>(); - this.completionProviders.put(new XMLCompletionProviderKey(BEANS_NAMESPACE, null, BEAN_ELEMENT, CLASS_ATTRIBUTE), new TypeCompletionProposalProvider(server, projectFinder, true, true, false, false)); - this.completionProviders.put(new XMLCompletionProviderKey(BEANS_NAMESPACE, BEAN_ELEMENT, PROPERTY_ELEMENT, NAME_ATTRIBUTE), new PropertyNameCompletionProposalProvider(projectFinder)); - this.completionProviders.put(new XMLCompletionProviderKey(BEANS_NAMESPACE, BEAN_ELEMENT, PROPERTY_ELEMENT, REF_ATTRIBUTE), new BeanRefCompletionProposalProvider(projectFinder, symbolIndex)); + this.completionProviders.put(new XMLElementKey(BEANS_NAMESPACE, null, BEAN_ELEMENT, CLASS_ATTRIBUTE), new TypeCompletionProposalProvider(server, projectFinder, true, true, false, false)); + this.completionProviders.put(new XMLElementKey(BEANS_NAMESPACE, BEAN_ELEMENT, PROPERTY_ELEMENT, NAME_ATTRIBUTE), new PropertyNameCompletionProposalProvider(projectFinder)); + this.completionProviders.put(new XMLElementKey(BEANS_NAMESPACE, BEAN_ELEMENT, PROPERTY_ELEMENT, REF_ATTRIBUTE), new BeanRefCompletionProposalProvider(projectFinder, symbolIndex)); } @Override public Collection getCompletions(TextDocument doc, int offset) throws Exception { - if (!config.isSpringXMLSupportEnabled()) { + if (!config.isSpringXMLSupportEnabled() || !config.isXmlContentAssistEnabled()) { return Collections.emptyList(); } @@ -86,12 +79,12 @@ public class SpringXMLCompletionEngine implements ICompletionEngine { DOMAttr attributeAt = dom.findAttrAt(offset); if (attributeAt != null) { - XMLCompletionProviderKey key = new XMLCompletionProviderKey(namespace, null, node.getLocalName(), attributeAt.getNodeName()); + XMLElementKey key = new XMLElementKey(namespace, null, node.getLocalName(), attributeAt.getNodeName()); if (!this.completionProviders.containsKey(key)) { DOMNode parentNode = node.getParentNode(); String parentNodeName = parentNode != null ? parentNode.getLocalName() : null; - key = new XMLCompletionProviderKey(namespace, parentNodeName, node.getLocalName(), attributeAt.getNodeName()); + key = new XMLElementKey(namespace, parentNodeName, node.getLocalName(), attributeAt.getNodeName()); } XMLCompletionProvider completionProvider = this.completionProviders.get(key); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/XMLCompletionProviderKey.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/XMLElementKey.java similarity index 91% rename from headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/XMLCompletionProviderKey.java rename to headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/XMLElementKey.java index 6712ea1db..c6a1e048d 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/XMLCompletionProviderKey.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/XMLElementKey.java @@ -13,14 +13,14 @@ package org.springframework.ide.vscode.boot.xml; /** * @author Martin Lippert */ -public class XMLCompletionProviderKey { +public class XMLElementKey { private final String namespaceURI; private final String elementName; private final String attributeName; private final String parentNodeName; - public XMLCompletionProviderKey(String namespaceURI, String parentNodeName, String elementName, String attributeName) { + public XMLElementKey(String namespaceURI, String parentNodeName, String elementName, String attributeName) { super(); this.namespaceURI = namespaceURI; this.parentNodeName = parentNodeName; @@ -63,7 +63,7 @@ public class XMLCompletionProviderKey { return false; if (getClass() != obj.getClass()) return false; - XMLCompletionProviderKey other = (XMLCompletionProviderKey) obj; + XMLElementKey other = (XMLElementKey) obj; if (attributeName == null) { if (other.attributeName != null) return false; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/XmlConfigConstants.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/XmlConfigConstants.java new file mode 100644 index 000000000..fa46f20f2 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/XmlConfigConstants.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2019 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.xml; + +/** + * + * @author Alex Boyko + * + */ +public class XmlConfigConstants { + + public static final String BEANS_NAMESPACE = "http://www.springframework.org/schema/beans"; + + public static final String BEAN_ELEMENT = "bean"; + public static final String CLASS_ATTRIBUTE = "class"; + + public static final String PROPERTY_ELEMENT = "property"; + public static final String NAME_ATTRIBUTE = "name"; + public static final String REF_ATTRIBUTE = "ref"; + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/completions/PropertyNameCompletionProposalProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/completions/PropertyNameCompletionProposalProvider.java index 9852d9866..7308ab19c 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/completions/PropertyNameCompletionProposalProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/completions/PropertyNameCompletionProposalProvider.java @@ -10,6 +10,9 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.xml.completions; +import static org.springframework.ide.vscode.boot.xml.XmlConfigConstants.BEAN_ELEMENT; +import static org.springframework.ide.vscode.boot.xml.XmlConfigConstants.CLASS_ATTRIBUTE; + import java.util.Collection; import java.util.Collections; import java.util.Optional; @@ -76,11 +79,11 @@ public class PropertyNameCompletionProposalProvider implements XMLCompletionProv return Collections.emptyList(); } - private String identifyBeanClass(DOMNode node) { + public static String identifyBeanClass(DOMNode node) { DOMNode parentNode = node.getParentNode(); if (parentNode != null) { - if ("bean".equals(parentNode.getLocalName())) { - String beanClassAttribute = parentNode.getAttribute("class"); + if (BEAN_ELEMENT.equals(parentNode.getLocalName())) { + String beanClassAttribute = parentNode.getAttribute(CLASS_ATTRIBUTE); return beanClassAttribute; } } @@ -113,7 +116,7 @@ public class PropertyNameCompletionProposalProvider implements XMLCompletionProv && method.getElementName().length() > 3; } - private String getPropertyName(IMethod method) { + public static String getPropertyName(IMethod method) { String methodName = method.getElementName(); if (methodName.startsWith("set")) { String propertyName = methodName.substring(3); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/hyperlinks/BeanRefHyperlinkProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/hyperlinks/BeanRefHyperlinkProvider.java new file mode 100644 index 000000000..1424fe7ef --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/hyperlinks/BeanRefHyperlinkProvider.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2019 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.xml.hyperlinks; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.SymbolInformation; +import org.eclipse.lsp4xml.dom.DOMAttr; +import org.eclipse.lsp4xml.dom.DOMNode; +import org.springframework.ide.vscode.boot.app.SpringSymbolIndex; +import org.springframework.ide.vscode.boot.java.beans.BeansSymbolAddOnInformation; +import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformation; +import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; +import org.springframework.ide.vscode.commons.util.text.TextDocument; + +/** + * + * @author Alex Boyko + * + */ +public class BeanRefHyperlinkProvider implements XMLHyperlinkProvider { + + private final JavaProjectFinder projectFinder; + private final SpringSymbolIndex symbolIndex; + private final SimpleTextDocumentService documents; + + public BeanRefHyperlinkProvider(JavaProjectFinder projectFinder, SpringSymbolIndex symbolIndex, SimpleTextDocumentService documents) { + this.projectFinder = projectFinder; + this.symbolIndex = symbolIndex; + this.documents = documents; + } + + @Override + public Location getDefinition(TextDocument doc, String namespace, DOMNode node, DOMAttr attributeAt) { + Optional foundProject = this.projectFinder.find(doc.getId()); + if (foundProject.isPresent()) { + final IJavaProject project = foundProject.get(); + List symbols = symbolIndex.getSymbols(data -> symbolsFilter(data, attributeAt.getValue())).collect(Collectors.toList()); + if (!symbols.isEmpty()) { + for (SymbolInformation symbol : symbols) { + Location location = symbol.getLocation(); + if (project == documents.get(location.getUri())) { + return location; + } + } + return symbols.get(0).getLocation(); + } + } + return null; + } + + private boolean symbolsFilter(EnhancedSymbolInformation data, String beanId) { + SymbolAddOnInformation[] additionalInformation = data.getAdditionalInformation(); + if (additionalInformation != null) { + for (SymbolAddOnInformation info : additionalInformation) { + if (info instanceof BeansSymbolAddOnInformation) { + return beanId.equals(((BeansSymbolAddOnInformation)info).getBeanID()); + } + } + } + return false; + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/hyperlinks/JavaTypeHyperlinkProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/hyperlinks/JavaTypeHyperlinkProvider.java new file mode 100644 index 000000000..75b41208a --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/hyperlinks/JavaTypeHyperlinkProvider.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2019 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.xml.hyperlinks; + +import java.util.Optional; + +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4xml.dom.DOMAttr; +import org.eclipse.lsp4xml.dom.DOMNode; +import org.springframework.ide.vscode.boot.java.links.JavaElementLocationProvider; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.java.IType; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.util.text.TextDocument; + +/** + * + * @author Alex Boyko + * + */ +public class JavaTypeHyperlinkProvider implements XMLHyperlinkProvider { + + private final JavaProjectFinder projectFinder; + private final JavaElementLocationProvider locationProvider; + + public JavaTypeHyperlinkProvider(JavaProjectFinder projectFinder, JavaElementLocationProvider locationProvider) { + this.projectFinder = projectFinder; + this.locationProvider = locationProvider; + } + + @Override + public Location getDefinition(TextDocument doc, String namespace, DOMNode node, DOMAttr attributeAt) { + Optional foundProject = this.projectFinder.find(doc.getId()); + if (foundProject.isPresent()) { + IJavaProject project = foundProject.get(); + String fqName = attributeAt.getValue(); + if (fqName != null) { + IType type = project.getIndex().findType(fqName); + if (type != null) { + return locationProvider.findLocation(project, type); + } + } + } + return null; + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/hyperlinks/PropertyNameHyperlinkProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/hyperlinks/PropertyNameHyperlinkProvider.java new file mode 100644 index 000000000..92ab27ead --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/hyperlinks/PropertyNameHyperlinkProvider.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2019 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.xml.hyperlinks; + +import java.util.Optional; + +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4xml.dom.DOMAttr; +import org.eclipse.lsp4xml.dom.DOMNode; +import org.springframework.ide.vscode.boot.java.links.JavaElementLocationProvider; +import org.springframework.ide.vscode.boot.xml.completions.PropertyNameCompletionProposalProvider; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.java.IType; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.util.text.TextDocument; + +/** + * + * @author Alex Boyko + * + */ +public class PropertyNameHyperlinkProvider implements XMLHyperlinkProvider { + + private final JavaProjectFinder projectFinder; + private final JavaElementLocationProvider locationProvider; + + public PropertyNameHyperlinkProvider(JavaProjectFinder projectFinder, JavaElementLocationProvider locationProvider) { + this.projectFinder = projectFinder; + this.locationProvider = locationProvider; + } + + @Override + public Location getDefinition(TextDocument doc, String namespace, DOMNode node, DOMAttr attributeAt) { + Optional foundProject = this.projectFinder.find(doc.getId()); + String propertyName = attributeAt.getValue(); + if (foundProject.isPresent() && propertyName != null && !propertyName.isEmpty()) { + IJavaProject project = foundProject.get(); + String beanClass = PropertyNameCompletionProposalProvider.identifyBeanClass(node); + if (beanClass != null && beanClass.length() > 0) { + IType beanType = project.getIndex().findType(beanClass); + + if (beanType != null) { + return beanType.getMethods() + .filter(method -> propertyName.equals(PropertyNameCompletionProposalProvider.getPropertyName(method))) + .map(method -> locationProvider.findLocation(project, method)) + .findFirst() + .orElse(null); + } + } + } + return null; + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/hyperlinks/XMLHyperlinkProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/hyperlinks/XMLHyperlinkProvider.java new file mode 100644 index 000000000..a8315fbd0 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/xml/hyperlinks/XMLHyperlinkProvider.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2019 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.xml.hyperlinks; + +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4xml.dom.DOMAttr; +import org.eclipse.lsp4xml.dom.DOMNode; +import org.springframework.ide.vscode.commons.util.text.TextDocument; + +/** + * + * @author Alex Boyko + * + */ +public interface XMLHyperlinkProvider { + + Location getDefinition(TextDocument doc, String namespace, DOMNode node, DOMAttr attributeAt); + +} diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/bootiful/XmlBeansTestConf.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/bootiful/XmlBeansTestConf.java new file mode 100644 index 000000000..fcb31bf89 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/bootiful/XmlBeansTestConf.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2019 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.bootiful; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.ide.vscode.boot.app.BootLanguageServerParams; +import org.springframework.ide.vscode.boot.editor.harness.PropertyIndexHarness; +import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider; +import org.springframework.ide.vscode.boot.java.links.JavaDocumentUriProvider; +import org.springframework.ide.vscode.boot.java.links.SourceLinkFactory; +import org.springframework.ide.vscode.boot.java.links.SourceLinks; +import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache; +import org.springframework.ide.vscode.boot.java.utils.SymbolCache; +import org.springframework.ide.vscode.boot.java.utils.SymbolCacheVoid; +import org.springframework.ide.vscode.boot.java.utils.test.MockProjectObserver; +import org.springframework.ide.vscode.boot.metadata.DefaultSpringPropertyIndexProvider; +import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry; +import org.springframework.ide.vscode.boot.test.DefinitionLinkAsserts; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; +import org.springframework.ide.vscode.commons.util.text.LanguageId; +import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness; + +@Configuration +@Import(AdHocPropertyHarnessTestConf.class) +public class XmlBeansTestConf { + + @Bean public SymbolCache symbolCache() { + return new SymbolCacheVoid(); + } + + @Bean PropertyIndexHarness indexHarness(ValueProviderRegistry valueProviders) { + return new PropertyIndexHarness(valueProviders); + } + + @Bean JavaProjectFinder projectFinder(BootLanguageServerParams serverParams) { + return serverParams.projectFinder; + } + + @Bean BootLanguageServerHarness harness(SimpleLanguageServer server, BootLanguageServerParams serverParams, PropertyIndexHarness indexHarness, JavaProjectFinder projectFinder) throws Exception { + return new BootLanguageServerHarness(server, serverParams, indexHarness, projectFinder, LanguageId.JAVA, ".java"); + } + + @Bean BootLanguageServerParams serverParams(SimpleLanguageServer server, ValueProviderRegistry valueProviders, PropertyIndexHarness indexHarness) { + BootLanguageServerParams testDefaults = BootLanguageServerParams.createTestDefault(server, valueProviders); + return new BootLanguageServerParams( + indexHarness.getProjectFinder(), + new MockProjectObserver(), + testDefaults.indexProvider, + testDefaults.typeUtilProvider, + testDefaults.watchDogInterval + ); + } + + @Bean DefaultSpringPropertyIndexProvider indexProvider(BootLanguageServerParams serverParams) { + return (DefaultSpringPropertyIndexProvider) serverParams.indexProvider; + } + + @Bean DefinitionLinkAsserts definitionLinkAsserts(JavaDocumentUriProvider javaDocumentUriProvider, CompilationUnitCache cuCache) { + return new DefinitionLinkAsserts(javaDocumentUriProvider, cuCache); + } + + @Bean SourceLinks sourceLinks() { + return SourceLinkFactory.NO_SOURCE_LINKS; + } + + @Bean RunningAppProvider runningAppProvider() { + return RunningAppProvider.NULL; + } + + @Bean MockProjectObserver mockProjectObserver(BootLanguageServerParams params) { + return (MockProjectObserver) params.projectObserver; + } + +} diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerXMLProjectTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerXMLProjectTest.java index 612eae801..d1d30fd88 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerXMLProjectTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerXMLProjectTest.java @@ -14,13 +14,13 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.File; +import java.nio.file.Paths; import java.util.Iterator; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import org.eclipse.lsp4j.SymbolInformation; -import org.eclipse.lsp4j.TextDocumentIdentifier; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -28,12 +28,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Import; import org.springframework.ide.vscode.boot.app.SpringSymbolIndex; import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest; -import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf; +import org.springframework.ide.vscode.boot.bootiful.XmlBeansTestConf; import org.springframework.ide.vscode.boot.java.beans.BeansSymbolAddOnInformation; import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation; import org.springframework.ide.vscode.boot.java.utils.SymbolIndexConfig; import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness; import org.springframework.ide.vscode.project.harness.ProjectsHarness; import org.springframework.test.context.junit4.SpringRunner; @@ -43,15 +42,15 @@ import org.springframework.test.context.junit4.SpringRunner; */ @RunWith(SpringRunner.class) @BootLanguageServerTest -@Import(SymbolProviderTestConf.class) +@Import(XmlBeansTestConf.class) public class SpringIndexerXMLProjectTest { @Autowired private BootLanguageServerHarness harness; @Autowired private SpringSymbolIndex indexer; - @Autowired private JavaProjectFinder projectFinder; + @Autowired private MockProjectObserver projectObserver; + private ProjectsHarness projects = ProjectsHarness.INSTANCE; private File directory; - private String projectDir; private IJavaProject project; @Before @@ -63,14 +62,15 @@ public class SpringIndexerXMLProjectTest { .build()) .get(5, TimeUnit.SECONDS);; - directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-xml-project/").toURI()); - projectDir = directory.toURI().toString(); - + project = projects.mavenProject("test-annotation-indexing-xml-project"); + harness.useProject(project); + directory = Paths.get(project.getLocationUri()).toFile(); + // trigger project creation - project = projectFinder.find(new TextDocumentIdentifier(projectDir)).get(); - + projectObserver.doWithListeners(l -> l.created(project)); + CompletableFuture initProject = indexer.waitOperation(); - initProject.get(50000, TimeUnit.SECONDS); + initProject.get(5, TimeUnit.SECONDS); } @Test diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/XmlBeansHyperlinkTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/XmlBeansHyperlinkTest.java new file mode 100644 index 000000000..310081d88 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/XmlBeansHyperlinkTest.java @@ -0,0 +1,194 @@ +/******************************************************************************* + * Copyright (c) 2019 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.utils.test; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.lsp4j.DidChangeConfigurationParams; +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.Range; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Import; +import org.springframework.ide.vscode.boot.app.SpringSymbolIndex; +import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest; +import org.springframework.ide.vscode.boot.bootiful.XmlBeansTestConf; +import org.springframework.ide.vscode.boot.test.DefinitionLinkAsserts; +import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; +import org.springframework.ide.vscode.commons.util.UriUtil; +import org.springframework.ide.vscode.commons.util.text.LanguageId; +import org.springframework.ide.vscode.languageserver.testharness.Editor; +import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness; +import org.springframework.ide.vscode.project.harness.ProjectsHarness; +import org.springframework.test.context.junit4.SpringRunner; + +import com.google.gson.Gson; + +/** + * @author Alex Boyko + */ +@RunWith(SpringRunner.class) +@BootLanguageServerTest +@Import(XmlBeansTestConf.class) +public class XmlBeansHyperlinkTest { + + @Autowired private BootLanguageServerHarness harness; + @Autowired private SpringSymbolIndex indexer; + @Autowired private DefinitionLinkAsserts definitionLinkAsserts; + @Autowired private MockProjectObserver projectObserver; + + private ProjectsHarness projects = ProjectsHarness.INSTANCE; + private MavenJavaProject project; + + @Before + public void setup() throws Exception { + harness.intialize(null); + + Map supportXML = new HashMap<>(); + supportXML.put("on", true); + supportXML.put("hyperlinks", true); + supportXML.put("scan-folders-globs", "**/src/main/**"); + Map bootJavaObj = new HashMap<>(); + bootJavaObj.put("support-spring-xml-config", supportXML); + Map settings = new HashMap<>(); + settings.put("boot-java", bootJavaObj); + + harness.getServer().getWorkspaceService().didChangeConfiguration(new DidChangeConfigurationParams(new Gson().toJsonTree(settings))); + + project = projects.mavenProject("test-xml-hyperlinks"); + + harness.useProject(project); + + projectObserver.doWithListeners(l -> l.created(project)); + + CompletableFuture initProject = indexer.waitOperation(); + initProject.get(5, TimeUnit.SECONDS); + } + + @Test + public void testBeanClassHyperlink() throws Exception { + Path xmlFilePath = Paths.get(project.getLocationUri()).resolve("beans.xml"); + Editor editor = harness.newEditor(LanguageId.XML, + "\n" + + "\n" + + + "\n" + + "\n", + UriUtil.toUri(xmlFilePath.toFile()).toString() + ); + definitionLinkAsserts.assertLinkTargets(editor, "u.t.r.SimpleObj", project, "u.t.r.SimpleObj"); + } + + @Test + public void testBeanPropertyNameHyperlink() throws Exception { + Path xmlFilePath = Paths.get(project.getLocationUri()).resolve("beans.xml"); + Editor editor = harness.newEditor(LanguageId.XML, + "\n" + + "\n" + + + "\n" + + "\n" + + "\n", + UriUtil.toUri(xmlFilePath.toFile()).toString() + ); + definitionLinkAsserts.assertLinkTargets(editor, "age", project, DefinitionLinkAsserts.method("u.t.r.TestBean", "setAge", "int")); + } + + @Test + public void testBeanRefHyperlink() throws Exception { + Path xmlFilePath = Paths.get(project.getLocationUri()).resolve("beans.xml"); + Editor editor = harness.newEditor(LanguageId.XML, + "\n" + + "\n" + + + "\n" + + "\n" + + "\n", + UriUtil.toUri(xmlFilePath.toFile()).toString() + ); + Path rootContextFilePath = Paths.get(project.getLocationUri()).resolve("src/main/webapp/WEB-INF/spring/root-context.xml"); + Location expectedLocation = new Location(); + expectedLocation.setUri(UriUtil.toUri(rootContextFilePath.toFile()).toString()); + expectedLocation.setRange(new Range(new Position(6,7), new Position(6, 21))); + editor.assertLinkTargets("simpleObj", Collections.singleton(expectedLocation)); + } + + @Test + public void testNoHyperlinkWhenXmlSupportOff() throws Exception { + Map supportXML = new HashMap<>(); + supportXML.put("on", false); + supportXML.put("hyperlinks", true); + supportXML.put("scan-folders-globs", "**/src/main/**"); + Map bootJavaObj = new HashMap<>(); + bootJavaObj.put("support-spring-xml-config", supportXML); + Map settings = new HashMap<>(); + settings.put("boot-java", bootJavaObj); + + harness.getServer().getWorkspaceService().didChangeConfiguration(new DidChangeConfigurationParams(new Gson().toJsonTree(settings))); + + Path xmlFilePath = Paths.get(project.getLocationUri()).resolve("beans.xml"); + Editor editor = harness.newEditor(LanguageId.XML, + "\n" + + "\n" + + + "\n" + + "\n", + UriUtil.toUri(xmlFilePath.toFile()).toString() + ); + editor.assertNoLinkTargets("u.t.r.SimpleObj"); + } + + @Test + public void testNoHyperlinkWhenHyperlinksOff() throws Exception { + Map supportXML = new HashMap<>(); + supportXML.put("on", true); + supportXML.put("hyperlinks", false); + supportXML.put("scan-folders-globs", "**/src/main/**"); + Map bootJavaObj = new HashMap<>(); + bootJavaObj.put("support-spring-xml-config", supportXML); + Map settings = new HashMap<>(); + settings.put("boot-java", bootJavaObj); + + harness.getServer().getWorkspaceService().didChangeConfiguration(new DidChangeConfigurationParams(new Gson().toJsonTree(settings))); + + Path xmlFilePath = Paths.get(project.getLocationUri()).resolve("beans.xml"); + Editor editor = harness.newEditor(LanguageId.XML, + "\n" + + "\n" + + + "\n" + + "\n", + UriUtil.toUri(xmlFilePath.toFile()).toString() + ); + editor.assertNoLinkTargets("u.t.r.SimpleObj"); + } +} diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/mvnw b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/mvnw new file mode 100755 index 000000000..02217b1ec --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/mvnw @@ -0,0 +1,233 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # + # Look for the Apple JDKs first to preserve the existing behaviour, and then look + # for the new JDKs provided by Oracle. + # + if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then + # + # Apple JDKs + # + export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then + # + # Apple JDKs + # + export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then + # + # Oracle JDKs + # + export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then + # + # Apple JDKs + # + export JAVA_HOME=`/usr/libexec/java_home` + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + local basedir=$(pwd) + local wdir=$(pwd) + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + wdir=$(cd "$wdir/.."; pwd) + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} "$@" diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/mvnw.cmd b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/mvnw.cmd new file mode 100644 index 000000000..4b98b78c4 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/mvnw.cmd @@ -0,0 +1,145 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +set MAVEN_CMD_LINE_ARGS=%* + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar"" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS% +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% \ No newline at end of file diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/pom.xml b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/pom.xml new file mode 100644 index 000000000..28fcfd3e1 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/pom.xml @@ -0,0 +1,158 @@ + + + 4.0.0 + u.t + test-xml-hyperlinks + test-xml-hyperlinks + war + 1.0.0-BUILD-SNAPSHOT + + 1.6 + 3.1.1.RELEASE + 1.6.10 + 1.6.6 + + + + + org.springframework + spring-context + ${org.springframework-version} + + + + commons-logging + commons-logging + + + + + org.springframework + spring-webmvc + ${org.springframework-version} + + + + + org.aspectj + aspectjrt + ${org.aspectj-version} + + + + + org.slf4j + slf4j-api + ${org.slf4j-version} + + + org.slf4j + jcl-over-slf4j + ${org.slf4j-version} + runtime + + + org.slf4j + slf4j-log4j12 + ${org.slf4j-version} + runtime + + + log4j + log4j + 1.2.15 + + + javax.mail + mail + + + javax.jms + jms + + + com.sun.jdmk + jmxtools + + + com.sun.jmx + jmxri + + + runtime + + + + + javax.inject + javax.inject + 1 + + + + + javax.servlet + servlet-api + 2.5 + provided + + + javax.servlet.jsp + jsp-api + 2.1 + provided + + + javax.servlet + jstl + 1.2 + + + + + junit + junit + 4.7 + test + + + + + + maven-eclipse-plugin + 2.9 + + + org.springframework.ide.eclipse.core.springnature + + + org.springframework.ide.eclipse.core.springbuilder + + true + true + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.5.1 + + 1.6 + 1.6 + -Xlint:all + true + true + + + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + org.test.int1.Main + + + + + diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/main/java/u/t/r/HomeController.java b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/main/java/u/t/r/HomeController.java new file mode 100644 index 000000000..175525533 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/main/java/u/t/r/HomeController.java @@ -0,0 +1,39 @@ +package u.t.r; + +import java.text.DateFormat; +import java.util.Date; +import java.util.Locale; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +/** + * Handles requests for the application home page. + */ +@Controller +public class HomeController { + + private static final Logger logger = LoggerFactory.getLogger(HomeController.class); + + /** + * Simply selects the home view to render by returning its name. + */ + @RequestMapping(value = "/", method = RequestMethod.GET) + public String home(Locale locale, Model model) { + logger.info("Welcome home! The client locale is {}.", locale); + + Date date = new Date(); + DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale); + + String formattedDate = dateFormat.format(date); + + model.addAttribute("serverTime", formattedDate ); + + return "home"; + } + +} diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/main/java/u/t/r/SimpleObj.java b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/main/java/u/t/r/SimpleObj.java new file mode 100644 index 000000000..937d84573 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/main/java/u/t/r/SimpleObj.java @@ -0,0 +1,5 @@ +package u.t.r; + +public class SimpleObj { + +} diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/main/java/u/t/r/TestBean.java b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/main/java/u/t/r/TestBean.java new file mode 100644 index 000000000..23f3d04cf --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/main/java/u/t/r/TestBean.java @@ -0,0 +1,35 @@ +package u.t.r; + +public class TestBean { + + private int age = 5; + + private TestBean spouse; + + private SimpleObj simple; + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public TestBean getSpouse() { + return spouse; + } + + public void setSpouse(TestBean spouse) { + this.spouse = spouse; + } + + public SimpleObj getSimple() { + return simple; + } + + public void setSimple(SimpleObj simple) { + this.simple = simple; + } + +} diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/main/resources/log4j.xml b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/main/resources/log4j.xml new file mode 100644 index 000000000..44388f63f --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/main/resources/log4j.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml new file mode 100644 index 000000000..a61484643 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/main/webapp/WEB-INF/spring/root-context.xml b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/main/webapp/WEB-INF/spring/root-context.xml new file mode 100644 index 000000000..de379cede --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/main/webapp/WEB-INF/spring/root-context.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/main/webapp/WEB-INF/views/home.jsp b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/main/webapp/WEB-INF/views/home.jsp new file mode 100644 index 000000000..478338366 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/main/webapp/WEB-INF/views/home.jsp @@ -0,0 +1,14 @@ +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ page session="false" %> + + + Home + + +

+ Hello world! +

+ +

The time on the server is ${serverTime}.

+ + diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/main/webapp/WEB-INF/web.xml b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..c9975d53c --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,33 @@ + + + + + + contextConfigLocation + /WEB-INF/spring/root-context.xml + + + + + org.springframework.web.context.ContextLoaderListener + + + + + appServlet + org.springframework.web.servlet.DispatcherServlet + + contextConfigLocation + /WEB-INF/spring/appServlet/servlet-context.xml + + 1 + + + + appServlet + / + + + diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/test/resources/log4j.xml b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/test/resources/log4j.xml new file mode 100644 index 000000000..05978314c --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-xml-hyperlinks/src/test/resources/log4j.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/theia-extensions/theia-spring-boot/spring-boot/src/browser/boot-preferences.ts b/theia-extensions/theia-spring-boot/spring-boot/src/browser/boot-preferences.ts index 741ae9da3..724c27b79 100644 --- a/theia-extensions/theia-spring-boot/spring-boot/src/browser/boot-preferences.ts +++ b/theia-extensions/theia-spring-boot/spring-boot/src/browser/boot-preferences.ts @@ -36,6 +36,16 @@ export const BootConfigSchema: PreferenceSchema = { description: 'Enable/Disable Support for Spring XML Config files', default: false }, + 'boot-java.support-spring-xml-config.hyperlinks': { + type: 'boolean', + description: 'Enable/Disable Hyperlinks in Spring XML Config file editor', + default: true + }, + 'boot-java.support-spring-xml-config.content-assist': { + type: 'boolean', + description: 'Enable/Disable Content Assist in Spring XML Config file editor', + default: true + }, 'boot-java.support-spring-xml-config.scan-folders-globs': { type: 'string', description: 'Scan Spring XML in folders', @@ -68,6 +78,8 @@ export interface BootConfiguration { 'boot-java.boot-hints.on': boolean; 'boot-java.scan-java-test-sources.on': boolean; 'boot-java.support-spring-xml-config.on': boolean; + 'boot-java.support-spring-xml-config.hyperlinks': boolean; + 'boot-java.support-spring-xml-config.content-assist': boolean; 'boot-java.support-spring-xml-config.scan-folders-globs': string; 'boot-java.change-detection.on': boolean; 'boot-java.highlight-codelens.on': boolean; diff --git a/vscode-extensions/vscode-spring-boot/package.json b/vscode-extensions/vscode-spring-boot/package.json index 4836d240a..b161acb7a 100644 --- a/vscode-extensions/vscode-spring-boot/package.json +++ b/vscode-extensions/vscode-spring-boot/package.json @@ -83,6 +83,16 @@ "default": false, "description": "Enable/Disable Support for Spring XML Config files" }, + "boot-java.support-spring-xml-config.hyperlinks": { + "type": "boolean", + "description": "Enable/Disable Hyperlinks in Spring XML Config file editor", + "default": true + }, + "boot-java.support-spring-xml-config.content-assist": { + "type": "boolean", + "description": "Enable/Disable Content Assist in Spring XML Config file editor", + "default": true + }, "boot-java.support-spring-xml-config.scan-folders-globs": { "type": "string", "default": "**/src/main/**",