WIP: goto symbol in file dialog

This commit is contained in:
Kris De Volder
2017-09-28 16:39:52 -07:00
parent 7c4c3f50f5
commit adced19b09
17 changed files with 851 additions and 1 deletions

View File

@@ -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);

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.springframework.tooling.ls.eclipse.gotosymbol</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,6 @@
source.. = src/
output.. = bin/
bin.includes = plugin.xml,\
META-INF/,\
.,\
icons/

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
<extension
point="org.eclipse.ui.commands">
<category
name="STS4"
id="org.springframework.tooling.ls.eclipse.gotosymbol.commands.category">
</category>
<command
name="Goto Symbol"
categoryId="org.springframework.tooling.ls.eclipse.gotosymbol.commands.category"
defaultHandler="org.springframework.tooling.ls.eclipse.gotosymbol.handlers.GotoSymbolHandler"
id="org.springframework.tooling.ls.eclipse.gotosymbol.command">
</command>
</extension>
<extension
point="org.eclipse.ui.handlers">
<handler
commandId="org.springframework.tooling.ls.eclipse.gotosymbol.command"
class="org.springframework.tooling.ls.eclipse.gotosymbol.handlers.GotoSymbolHandler">
</handler>
</extension>
<extension
point="org.eclipse.ui.bindings">
<key
commandId="org.springframework.tooling.ls.eclipse.gotosymbol.command"
contextId="org.eclipse.ui.contexts.window"
sequence="M1+6"
schemeId="org.eclipse.ui.defaultAcceleratorConfiguration">
</key>
</extension>
<!-- TODO: stuff below comes from 'Hello world Command' example generated by Eclipse
Should figure out proper way to integrate into menus and toolbars etc. So commented out
The command is currently accessible only via 'Quick Access' search box or keyboard shortcut
<extension
point="org.eclipse.ui.menus">
<menuContribution
locationURI="menu:org.eclipse.ui.main.menu?after=additions">
<menu
label="Sample Menu"
mnemonic="M"
id="org.springframework.tooling.ls.eclipse.gotosymbol.menus.sampleMenu">
<command
commandId="org.springframework.tooling.ls.eclipse.gotosymbol.command"
mnemonic="G"
id="org.springframework.tooling.ls.eclipse.gotosymbol.menus.sampleCommand">
</command>
</menu>
</menuContribution>
<menuContribution
locationURI="toolbar:org.eclipse.ui.main.toolbar?after=additions">
<toolbar
id="org.springframework.tooling.ls.eclipse.gotosymbol.toolbars.sampleToolbar">
<command
commandId="org.springframework.tooling.ls.eclipse.gotosymbol.commands.sampleCommand"
icon="icons/sample.png"
tooltip="Say hello world"
id="org.springframework.tooling.ls.eclipse.gotosymbol.toolbars.sampleCommand">
</command>
</toolbar>
</menuContribution>
</extension>
-->
</plugin>

View File

@@ -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<Disposable> 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));
}
}
}

View File

@@ -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<String> status = new LiveVariable<>();
private final LiveVariable<SymbolsProvider> symbolsProvider = new LiveVariable<>(null);
private final LiveVariable<String> searchBox = new LiveVariable<>("");
private final ObservableSet<SymbolInformation> unfilteredSymbols = new ObservableSet<SymbolInformation>(ImmutableSet.of(), AsyncMode.ASYNC, AsyncMode.SYNC) {
//Note: fetching is 'slow' so is done asynchronously
{
dependsOn(searchBox);
dependsOn(symbolsProvider);
}
@Override
protected ImmutableSet<SymbolInformation> 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<SymbolInformation> 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<SymbolInformation> filteredSymbols = new ObservableSet<SymbolInformation>() {
//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<SymbolInformation> compute() {
char[] query = searchBox.getValue().toCharArray();
ImmutableSet.Builder<SymbolInformation> 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<String> summary(ImmutableSet<SymbolInformation> values) {
return values.stream().map(SymbolInformation::getName).collect(Collectors.toList());
}
public ObservableSet<SymbolInformation> getSymbols() {
return filteredSymbols;
}
public LiveVariable<String> getSearchBox() {
return searchBox;
}
public LiveExpression<String> getStatus() {
return status;
}
}

View File

@@ -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<SymbolInformation> fetchFor(String query) {
// TODO Auto-generated method stub
return null;
}
}

View File

@@ -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<SymbolInformation> fetchFor(String query);
}

View File

@@ -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<LSPDocumentInfo> 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<List<? extends SymbolInformation>> symbols = info.getLanguageClient()
.getTextDocumentService().documentSymbol(params);
symbols.thenAccept((List<? extends SymbolInformation> t) -> {
shell.getDisplay().asyncExec(() -> {
List<SymbolInformation> 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<LSPDocumentInfo> infos = LanguageServiceAccessor.getLSPDocumentInfosFor(
LSPEclipseUtils.getDocument((ITextEditor) part),
(capabilities) -> Boolean.TRUE.equals(capabilities.getDocumentSymbolProvider()));
return !infos.isEmpty();
}
return false;
}
}

View File

@@ -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<String> model) {
ModifyListener widgetListener = (ModifyEvent e) -> {
model.setValue(widget.getText());
};
ValueListener<String> modelListener = new UIValueListener<String>() {
@Override
protected void uiGotValue(LiveExpression<String> 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<String> model) {
ValueListener<String> modelListener = new UIValueListener<String>() {
@Override
protected void uiGotValue(LiveExpression<String> exp, String value) {
String newText = model.getValue();
if (newText==null) {
newText = "";
}
widget.setText(newText);
}
};
model.addListener(modelListener);
widget.addDisposeListener(xx -> model.removeListener(modelListener));
}
}

View File

@@ -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
*/

View File

@@ -310,7 +310,9 @@ public class SimpleTextDocumentService implements TextDocumentService {
}
return Mono.fromCallable(() -> {
server.waitForReconcile();
return documentSymbolHandler.handle(params);
List<? extends SymbolInformation> 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<? extends SymbolInformation>)l);