diff --git a/eclipse-language-servers/org.springframework.tooling.boot.java.ls/src/org/springframework/tooling/boot/java/ls/SpringBootJavaLanguageServer.java b/eclipse-language-servers/org.springframework.tooling.boot.java.ls/src/org/springframework/tooling/boot/java/ls/SpringBootJavaLanguageServer.java index 07984d9b3..f464c68e6 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.java.ls/src/org/springframework/tooling/boot/java/ls/SpringBootJavaLanguageServer.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.java.ls/src/org/springframework/tooling/boot/java/ls/SpringBootJavaLanguageServer.java @@ -101,6 +101,10 @@ public class SpringBootJavaLanguageServer extends ProcessStreamConnectionProvide } protected String getLanguageServerJARLocation() { + String fromSysprop = System.getProperty("boot-java-ls-jar", null); + if (fromSysprop!=null) { + return fromSysprop; + } String languageServer = "boot-java-language-server-" + Constants.LANGUAGE_SERVER_VERSION; Bundle bundle = Platform.getBundle(Constants.PLUGIN_ID); diff --git a/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/.classpath b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/.classpath new file mode 100644 index 000000000..eca7bdba8 --- /dev/null +++ b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/.project b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/.project new file mode 100644 index 000000000..1f5f63896 --- /dev/null +++ b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/.project @@ -0,0 +1,28 @@ + + + org.springframework.tooling.ls.eclipse.gotosymbol + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/.settings/org.eclipse.jdt.core.prefs b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..71c295fb2 --- /dev/null +++ b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,106 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled +org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore +org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull +org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= +org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullable.secondary= +org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.APILeak=warning +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=warning +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning +org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning +org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error +org.eclipse.jdt.core.compiler.problem.nullReference=warning +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore +org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning +org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled +org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedImport=warning +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/META-INF/MANIFEST.MF b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/META-INF/MANIFEST.MF new file mode 100644 index 000000000..56cb4edc9 --- /dev/null +++ b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/META-INF/MANIFEST.MF @@ -0,0 +1,22 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Gotosymbol +Bundle-SymbolicName: org.springframework.tooling.ls.eclipse.gotosymbol;singleton:=true +Bundle-Version: 1.0.0.qualifier +Require-Bundle: org.eclipse.ui, + org.eclipse.ui.workbench.texteditor, + org.eclipse.lsp4e, + org.eclipse.lsp4j, + org.eclipse.jface.text;bundle-version="3.12.0", + org.eclipse.core.resources;bundle-version="3.12.0", + org.eclipse.core.filebuffers, + org.eclipse.ui.navigator, + org.eclipse.equinox.registry, + io.projectreactor.reactor-core, + org.springsource.ide.eclipse.commons.livexp, + com.google.guava, + org.eclipse.core.jobs, + org.eclipse.core.runtime;bundle-version="3.13.0", + org.springframework.ide.eclipse.boot, + org.springsource.ide.eclipse.commons.core +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 diff --git a/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/build.properties b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/build.properties new file mode 100644 index 000000000..0d3d3a745 --- /dev/null +++ b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/build.properties @@ -0,0 +1,6 @@ +source.. = src/ +output.. = bin/ +bin.includes = plugin.xml,\ + META-INF/,\ + .,\ + icons/ diff --git a/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/icons/sample.png b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/icons/sample.png new file mode 100644 index 000000000..02c4b79e1 Binary files /dev/null and b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/icons/sample.png differ diff --git a/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/icons/sample@2x.png b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/icons/sample@2x.png new file mode 100644 index 000000000..c1224d1fc Binary files /dev/null and b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/icons/sample@2x.png differ diff --git a/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/plugin.xml b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/plugin.xml new file mode 100644 index 000000000..cd8f82e9b --- /dev/null +++ b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/plugin.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/GotoSymbolDialog.java b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/GotoSymbolDialog.java new file mode 100644 index 000000000..9362fcc1f --- /dev/null +++ b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/GotoSymbolDialog.java @@ -0,0 +1,242 @@ +/******************************************************************************* + * Copyright (c) 2016 Rogue Wave Software Inc. and others. + * 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Michał Niewrzał (Rogue Wave Software Inc.) - initial implementation + * Kris De Volder (Pivotal Inc) - Copied and adapted from + *******************************************************************************/ +package org.springframework.tooling.ls.eclipse.gotosymbol.dialogs; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.Platform; +import org.eclipse.jface.dialogs.PopupDialog; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.lsp4e.outline.SymbolsLabelProvider; +import org.eclipse.lsp4j.SymbolInformation; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.TreeItem; +import org.eclipse.ui.texteditor.ITextEditor; +import org.springframework.tooling.ls.eclipse.gotosymbol.util.SwtConnect; +import org.springsource.ide.eclipse.commons.livexp.ui.Disposable; + +@SuppressWarnings("restriction") +public class GotoSymbolDialog extends PopupDialog { + + private static final boolean DEBUG = (""+Platform.getLocation()).contains("kdvolder"); + + private static void debug(String string) { + if (DEBUG) { + System.out.println(string); + } + } + + private static class SymbolsContentProvider implements ITreeContentProvider { + + @Override + public Object[] getChildren(Object parentElement) { + return null; + } + + @Override + public Object getParent(Object element) { + return null; + } + + @Override + public boolean hasChildren(Object element) { + return false; + } + + @Override + public Object[] getElements(Object inputElement) { + if (inputElement instanceof GotoSymbolDialogModel) { + GotoSymbolDialogModel model = (GotoSymbolDialogModel) inputElement; + return model.getSymbols().getValues().toArray(); + } + return null; + } + } + + private GotoSymbolDialogModel model; + private List disposables = new ArrayList<>(); + private ITextEditor fTextEditor; + + public GotoSymbolDialog(Shell parentShell, ITextEditor textEditor, GotoSymbolDialogModel model) { + super(parentShell, PopupDialog.INFOPOPUP_SHELLSTYLE, true, true, true, false, false, null, null); + this.fTextEditor = textEditor; + this.model = model; + create(); + } + + private void installKeyListeners(Text pattern, TreeViewer list) { + pattern.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent e) { + if (e.keyCode == SWT.ARROW_DOWN) { + if (list.getTree().getItemCount() > 0) { + list.getTree().setFocus(); + TreeItem[] items = list.getTree().getItems(); + if (items!=null && items.length>0) { + list.getTree().setSelection(items[0]); + //programatic selection may not fire selection events so... + list.getTree().notifyListeners(SWT.Selection, + new Event()); + } + } + } + } + }); + + list.getTree().addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent e) { + + if (e.keyCode == SWT.ARROW_UP && (e.stateMask & SWT.SHIFT) == 0 + && (e.stateMask & SWT.CTRL) == 0) { + StructuredSelection selection = (StructuredSelection) list + .getSelection(); + + if (selection.size() == 1) { + Object element = selection.getFirstElement(); + if (element.equals(getFirstElement(list))) { + pattern.setFocus(); + list.setSelection(new StructuredSelection()); + list.getTree().notifyListeners(SWT.Selection, + new Event()); + } + + } + } + +// if (e.keyCode == SWT.ARROW_DOWN +// && (e.stateMask & SWT.SHIFT) != 0 +// && (e.stateMask & SWT.CTRL) != 0) { +// +// list.getTree().notifyListeners(SWT.Selection, new Event()); +// } + + } + }); + } + + private Object getFirstElement(TreeViewer list) { + TreeItem[] items = list.getTree().getItems(); + if (items!=null && items.length>0) { + TreeItem item = items[0]; + return item.getData(); + } + return null; + } + + @Override + protected Control createDialogArea(Composite parent) { + Composite dialogArea = new Composite(parent, SWT.NONE); + dialogArea.addDisposeListener(de -> { + for (Disposable d : disposables) { + d.dispose(); + } + }); + if (parent.getLayout() instanceof GridLayout) { + dialogArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + } + + GridLayout layout = new GridLayout(); + layout.marginHeight = 0; + layout.marginWidth = 0; + dialogArea.setLayout(layout); + + Text pattern = new Text(dialogArea, SWT.SINGLE | SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL); +// pattern.getAccessible().addAccessibleListener(new AccessibleAdapter() { +// public void getName(AccessibleEvent e) { +// e.result = LegacyActionTools.removeMnemonics(headerLabel) +// .getText()); +// } +// }); + pattern.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + SwtConnect.connect(pattern, model.getSearchBox()); + + TreeViewer viewer = new TreeViewer(dialogArea); + GridDataFactory.fillDefaults().grab(true, true).applyTo(viewer.getControl()); + viewer.setContentProvider(new SymbolsContentProvider()); + viewer.setLabelProvider(new SymbolsLabelProvider()); + viewer.setUseHashlookup(true); + disposables.add(model.getSymbols().onChange((e, v) -> { + viewer.refresh(); + })); + if (DEBUG) { + viewer.addSelectionChangedListener(event -> { + IStructuredSelection selection = (IStructuredSelection) event.getSelection(); + if (selection.isEmpty()) { + debug("selected NONE"); + } else { + SymbolInformation symbolInformation = (SymbolInformation) selection.getFirstElement(); + debug("selected = "+symbolInformation.getName()); + } + }); + } +//TODO: somehow show selection in local file, (but not in other file ?) +// viewer.addSelectionChangedListener(event -> { +// IStructuredSelection selection = (IStructuredSelection) event.getSelection(); +// if (selection.isEmpty()) { +// return; +// } +// SymbolInformation symbolInformation = (SymbolInformation) selection.getFirstElement(); +// Location location = symbolInformation.getLocation(); +// +// IResource targetResource = LSPEclipseUtils.findResourceFor(location.getUri()); +// if (targetResource == null) { +// return; +// } +// IDocument targetDocument = FileBuffers.getTextFileBufferManager() +// .getTextFileBuffer(targetResource.getFullPath(), LocationKind.IFILE).getDocument(); +// if (targetDocument != null) { +// try { +// int offset = LSPEclipseUtils.toOffset(location.getRange().getStart(), targetDocument); +// int endOffset = LSPEclipseUtils.toOffset(location.getRange().getEnd(), targetDocument); +// fTextEditor.selectAndReveal(offset, endOffset - offset); +// } catch (BadLocationException e) { +// LanguageServerPlugin.logError(e); +// } +// } +// }); + installKeyListeners(pattern, viewer); + + Label statusLabel = new Label(dialogArea, SWT.NONE); + statusLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + SwtConnect.connect(statusLabel, model.getStatus()); + + viewer.setInput(model); + return dialogArea; + } + + @Override + protected void configureShell(Shell shell) { + super.configureShell(shell); + + shell.setSize(280, 300); + Control control = fTextEditor.getAdapter(Control.class); + if (control != null) { + shell.setLocation( + control.toDisplay(control.getBounds().width - shell.getSize().x, control.getLocation().y)); + } + } + +} diff --git a/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/GotoSymbolDialogModel.java b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/GotoSymbolDialogModel.java new file mode 100644 index 000000000..721ac1045 --- /dev/null +++ b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/GotoSymbolDialogModel.java @@ -0,0 +1,145 @@ +/******************************************************************************* + * Copyright (c) 2017 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.tooling.ls.eclipse.gotosymbol.dialogs; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.core.runtime.Platform; +import org.eclipse.lsp4j.SymbolInformation; +import org.springsource.ide.eclipse.commons.livexp.core.AsyncLiveExpression.AsyncMode; +import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil; +import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression; +import org.springsource.ide.eclipse.commons.livexp.core.LiveVariable; +import org.springsource.ide.eclipse.commons.livexp.core.ObservableSet; +import org.springsource.ide.eclipse.commons.core.util.StringUtil; + +import com.google.common.collect.ImmutableSet; + +import org.springframework.ide.eclipse.boot.util.Log; + +public class GotoSymbolDialogModel { + + private static final boolean DEBUG = (""+Platform.getLocation()).contains("kdvolder"); + + private static void debug(String string) { + if (DEBUG) { + System.out.println(string); + } + } + + + //TODO: support for switching between local/workspace symbols. + + private final LiveVariable status = new LiveVariable<>(); + private final LiveVariable symbolsProvider = new LiveVariable<>(null); + private final LiveVariable searchBox = new LiveVariable<>(""); + private final ObservableSet unfilteredSymbols = new ObservableSet(ImmutableSet.of(), AsyncMode.ASYNC, AsyncMode.SYNC) { + //Note: fetching is 'slow' so is done asynchronously + { + dependsOn(searchBox); + dependsOn(symbolsProvider); + } + + @Override + protected ImmutableSet compute() { + status.setValue("Fetching symbols..."); + try { + debug("Fetching symbols "); + SymbolsProvider sp = symbolsProvider.getValue(); + if (sp!=null) { + String query = searchBox.getValue(); + debug("Fetching symbols... from symbol provider, for '"+query+"'"); + Collection fetched = sp.fetchFor(query); + if (DEBUG) { + fetched.stream().forEach(sym -> debug("symbol: "+sym.getName())); + } + StringBuilder msg = new StringBuilder(); + msg.append("Fetched "); + msg.append(fetched.size()); + msg.append(" symbols"); + if (StringUtil.hasText(query)) { + msg.append(" matching '"); + msg.append(query); + msg.append("'"); + } + debug(msg.toString()); + status.setValue(msg.toString()); + return ImmutableSet.copyOf(fetched); + } else { + status.setValue("No symbol provider"); + } + } catch (Exception e) { + Log.log(e); + status.setValue(ExceptionUtil.getMessage(e)); + } + return ImmutableSet.of(); + } + }; + private ObservableSet filteredSymbols = new ObservableSet() { + //Note: filtering is 'fast' so is done synchronously + { + dependsOn(searchBox); + dependsOn(unfilteredSymbols); + } + + private boolean containsCharacters(char[] symbolChars, char[] queryChars) { + int symbolindex = 0; + int queryindex = 0; + + while (queryindex < queryChars.length && symbolindex < symbolChars.length) { + if (symbolChars[symbolindex] == queryChars[queryindex]) { + queryindex++; + } + symbolindex++; + } + + return queryindex == queryChars.length; + } + + @Override + protected ImmutableSet compute() { + char[] query = searchBox.getValue().toCharArray(); + ImmutableSet.Builder builder = ImmutableSet.builder(); + unfilteredSymbols.getValues().stream().filter(sym -> containsCharacters(sym.getName().toCharArray(), query)).forEach(builder::add); + return builder.build(); + } + }; + + public GotoSymbolDialogModel(SymbolsProvider symbolsProvider) { + this.symbolsProvider.setValue(symbolsProvider); + if (DEBUG) { + searchBox.addListener((e, v) -> { + debug("searchBox = "+v); + }); + unfilteredSymbols.addListener((e, v) -> debug("raw = "+summary(filteredSymbols.getValues()))); + filteredSymbols.addListener((e, v) -> debug("filtered = "+summary(filteredSymbols.getValues()))); + } + } + + private List summary(ImmutableSet values) { + return values.stream().map(SymbolInformation::getName).collect(Collectors.toList()); + } + + public ObservableSet getSymbols() { + return filteredSymbols; + } + + public LiveVariable getSearchBox() { + return searchBox; + } + + public LiveExpression getStatus() { + return status; + } + +} diff --git a/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/InFileSymbolsProvider.java b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/InFileSymbolsProvider.java new file mode 100644 index 000000000..94c846fbc --- /dev/null +++ b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/InFileSymbolsProvider.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2017 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.tooling.ls.eclipse.gotosymbol.dialogs; + +import java.util.Collection; + +import org.eclipse.lsp4j.SymbolInformation; +import org.eclipse.ui.texteditor.ITextEditor; + +public class InFileSymbolsProvider implements SymbolsProvider { + + private ITextEditor target; + + public InFileSymbolsProvider(ITextEditor target) { + super(); + this.target = target; + } + + @Override + public Collection fetchFor(String query) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/SymbolsProvider.java b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/SymbolsProvider.java new file mode 100644 index 000000000..31abba0e5 --- /dev/null +++ b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/SymbolsProvider.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2017 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.tooling.ls.eclipse.gotosymbol.dialogs; + +import java.util.Collection; + +import org.eclipse.lsp4j.SymbolInformation; + +/** + * An SymbolsProvider can fetch symbols from a service (typically, a language server + * operation is used to fetch symbol data. This operation may be slow and should be + * called only from a background job. + */ +public interface SymbolsProvider { + Collection fetchFor(String query); +} \ No newline at end of file diff --git a/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/handlers/GotoSymbolHandler.java b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/handlers/GotoSymbolHandler.java new file mode 100644 index 000000000..d472d74ca --- /dev/null +++ b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/handlers/GotoSymbolHandler.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2016 Rogue Wave Software Inc. and others. + * 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Michał Niewrzał (Rogue Wave Software Inc.) - initial implementation + * Kris De Volder (Pivotal) - copied and adapted from org.eclipse.lsp4e.operations.symbols.LSPSymbolInFileDialog + *******************************************************************************/ +package org.springframework.tooling.ls.eclipse.gotosymbol.handlers; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.lsp4e.LSPEclipseUtils; +import org.eclipse.lsp4e.LanguageServiceAccessor; +import org.eclipse.lsp4e.LanguageServiceAccessor.LSPDocumentInfo; +import org.eclipse.lsp4j.DocumentSymbolParams; +import org.eclipse.lsp4j.SymbolInformation; +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.handlers.HandlerUtil; +import org.eclipse.ui.texteditor.ITextEditor; +import org.springframework.tooling.ls.eclipse.gotosymbol.dialogs.GotoSymbolDialog; +import org.springframework.tooling.ls.eclipse.gotosymbol.dialogs.GotoSymbolDialogModel; + +import com.google.common.collect.ImmutableList; + +/** + * Our sample handler extends AbstractHandler, an IHandler base class. + * @see org.eclipse.core.commands.IHandler + * @see org.eclipse.core.commands.AbstractHandler + */ +@SuppressWarnings("restriction") +public class GotoSymbolHandler extends AbstractHandler { + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + System.out.println("GotoSymbolHandler.execute"); + IEditorPart part = HandlerUtil.getActiveEditor(event); + if (part instanceof ITextEditor) { + final ITextEditor textEditor = (ITextEditor) part; + Collection infos = LanguageServiceAccessor.getLSPDocumentInfosFor( + LSPEclipseUtils.getDocument(textEditor), + capabilities -> Boolean.TRUE.equals(capabilities.getDocumentSymbolProvider())); + if (infos.isEmpty()) { + return null; + } + // TODO maybe consider better strategy such as iterating on all LS until we have a good result + LSPDocumentInfo info = infos.iterator().next(); + final Shell shell = HandlerUtil.getActiveShell(event); + DocumentSymbolParams params = new DocumentSymbolParams( + new TextDocumentIdentifier(info.getFileUri().toString())); + CompletableFuture> symbols = info.getLanguageClient() + .getTextDocumentService().documentSymbol(params); + + symbols.thenAccept((List t) -> { + shell.getDisplay().asyncExec(() -> { + List allSymbols = ImmutableList.copyOf(t); + GotoSymbolDialogModel model = new GotoSymbolDialogModel(query -> allSymbols); + GotoSymbolDialog dialog = new GotoSymbolDialog(shell, textEditor, model); + dialog.open(); + }); + }); + } + return null; + } + + @Override + public boolean isEnabled() { + boolean r = _isEnabled(); + System.out.println("isEnabled = "+r); + return r; + } + public boolean _isEnabled() { + + IWorkbenchPart part = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActivePart(); + if (part instanceof ITextEditor) { + List infos = LanguageServiceAccessor.getLSPDocumentInfosFor( + LSPEclipseUtils.getDocument((ITextEditor) part), + (capabilities) -> Boolean.TRUE.equals(capabilities.getDocumentSymbolProvider())); + return !infos.isEmpty(); + } + return false; + } +} diff --git a/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/util/SwtConnect.java b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/util/SwtConnect.java new file mode 100644 index 000000000..a35551fc0 --- /dev/null +++ b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/util/SwtConnect.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2017 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.tooling.ls.eclipse.gotosymbol.util; + +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression; +import org.springsource.ide.eclipse.commons.livexp.core.LiveVariable; +import org.springsource.ide.eclipse.commons.livexp.core.UIValueListener; +import org.springsource.ide.eclipse.commons.livexp.core.ValueListener; +import org.springsource.ide.eclipse.commons.livexp.ui.Disposable; + +/** + * Static helper methods for attaching LiveExps to SWT widgets. + */ +public class SwtConnect { + + public static void connect(Text widget, LiveVariable model) { + ModifyListener widgetListener = (ModifyEvent e) -> { + model.setValue(widget.getText()); + }; + ValueListener modelListener = new UIValueListener() { + @Override + protected void uiGotValue(LiveExpression exp, String value) { + String newText = model.getValue(); + if (newText==null) { + newText = ""; + } + String oldText = widget.getText(); + if (!newText.equals(oldText)) { + widget.setText(newText); + } + } + }; + widget.addModifyListener(widgetListener); + model.addListener(modelListener); + widget.addDisposeListener(e -> { + model.removeListener(modelListener); + }); + + } + + public static void connect(Label widget, LiveExpression model) { + ValueListener modelListener = new UIValueListener() { + @Override + protected void uiGotValue(LiveExpression exp, String value) { + String newText = model.getValue(); + if (newText==null) { + newText = ""; + } + widget.setText(newText); + } + }; + model.addListener(modelListener); + widget.addDisposeListener(xx -> model.removeListener(modelListener)); + } + +} diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java index 7b45e9e2b..70808a8c3 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java @@ -49,6 +49,8 @@ import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguage import org.springframework.ide.vscode.commons.util.text.LanguageId; import org.springframework.ide.vscode.commons.util.text.TextDocument; +import com.google.common.collect.ImmutableList; + /** * @author Martin Lippert */ diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java index 57f545b95..0a8d64eb5 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java @@ -310,7 +310,9 @@ public class SimpleTextDocumentService implements TextDocumentService { } return Mono.fromCallable(() -> { server.waitForReconcile(); - return documentSymbolHandler.handle(params); + List r = documentSymbolHandler.handle(params); + //handle it when symbolHandler is sloppy and returns null instead of empty list. + return r == null ? ImmutableList.of() : r; }) .toFuture() .thenApply(l -> (List)l);