diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/META-INF/MANIFEST.MF b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/META-INF/MANIFEST.MF index 9d6a562b7..2ded46711 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/META-INF/MANIFEST.MF +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/META-INF/MANIFEST.MF @@ -19,7 +19,8 @@ Require-Bundle: org.eclipse.ui, org.eclipse.core.runtime;bundle-version="3.13.0", org.springsource.ide.eclipse.commons.core;bundle-version="3.9.2", io.projectreactor.reactor-core;bundle-version="3.0.7", - org.reactivestreams.reactive-streams;bundle-version="1.0.0" + org.reactivestreams.reactive-streams;bundle-version="1.0.0", + org.springframework.ide.eclipse.boot.dash Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-Activator: org.springframework.tooling.ls.eclipse.gotosymbol.GotoSymbolPlugin Bundle-ActivationPolicy: lazy diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/icons/boot-icon.png b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/icons/boot-icon.png new file mode 100644 index 000000000..de88ef0d5 Binary files /dev/null and b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/icons/boot-icon.png differ diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/icons/boot-icon@2x.png b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/icons/boot-icon@2x.png new file mode 100644 index 000000000..6aef74256 Binary files /dev/null and b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/icons/boot-icon@2x.png differ diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/icons/sample.png b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/icons/sample.png deleted file mode 100644 index 02c4b79e1..000000000 Binary files a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/icons/sample.png and /dev/null differ diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/icons/sample@2x.png b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/icons/sample@2x.png deleted file mode 100644 index c1224d1fc..000000000 Binary files a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/icons/sample@2x.png and /dev/null differ diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/plugin.xml b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/plugin.xml index 4402afac6..ee4b08a1e 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/plugin.xml +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/plugin.xml @@ -1,6 +1,18 @@ + + + + + + )element); - if (si!=null) { - return si.getName(); - } - } - return null; - } - - @Override - public void update(ViewerCell cell) { - super.update(cell); - Object obj = cell.getElement(); - if (obj instanceof Match) { - Match match = (Match) obj; - cell.setImage(symbolsLabelProvider.getImage(match.value)); - StyledString styledString = getStyledText(match); - cell.setText(styledString.getString()); - cell.setStyleRanges(styledString.getStyleRanges()); - cell.getControl().redraw(); - //^^^ Sigh... Yes, this is needed. It seems SWT/Jface isn't smart enough to itself figure out that if - //the styleranges change a redraw is needed to make the change visible. - } else { - super.update(cell); - } - } - - private StyledString getStyledText(Match element) { - SymbolInformation symbolInformation = getSymbolInformation(element); - if (symbolInformation != null) { - String name = symbolInformation.getName(); - StyledString s = new StyledString(name); - Collection highlights = FuzzyMatcher.highlights(element.query, name.toLowerCase()); - for (IRegion hl : highlights) { - s.setStyle(hl.getOffset(), hl.getLength(), stylers.bold()); - } - String locationText = getSymbolLocationText(symbolInformation); - if (locationText != null) { - s = s.append(locationText, stylers.italicColoured(SWT.COLOR_DARK_GRAY)); - } - return s; - } else { - return symbolsLabelProvider.getStyledText(element.value); - } - } - - @Override - public void dispose() { - stylers.dispose(); - symbolsLabelProvider.dispose(); - super.dispose(); - } - - protected String getSymbolLocationText(SymbolInformation symbol) { - Optional location = GotoSymbolDialog.this - .getSymbolLocation(symbol); - if (location.isPresent()) { - return " -- [" + location.get() + "]"; - } - return null; - } - } - private static final Point DEFAULT_SIZE = new Point(280, 300); private GotoSymbolDialogModel model; - private List disposables = new ArrayList<>(); + private final GotoSymbolSection content; private ITextEditor fTextEditor; /** @@ -219,26 +50,11 @@ public class GotoSymbolDialog extends PopupDialog { //For the time being I've simply disabled the menu that makes it appear like this should work. this.fTextEditor = textEditor; this.model = model; + this.content = new GotoSymbolSection(this, model); this.alignRight = alignRight; create(); } - private SymbolInformation getSymbolInformation(Match element) { - if (element.value instanceof SymbolInformation) { - return (SymbolInformation) element.value; - } - if (element.value instanceof Either) { - Either either = (Either)element.value; - if (either.isLeft()) { - Object left = either.getLeft(); - if (left instanceof SymbolInformation) { - return (SymbolInformation) left; - } - } - } - return null; - } - @Override protected IDialogSettings getDialogSettings() { if (dlgSettings==null) { @@ -247,125 +63,9 @@ public class GotoSymbolDialog extends PopupDialog { return dlgSettings; } - /** - * Determine the 'target' for the dialog's action. - */ - private SymbolInformation getTarget(TreeViewer list) { - ISelection sel = list.getSelection(); - if (sel instanceof IStructuredSelection) { - IStructuredSelection ss = (IStructuredSelection) sel; - Object selected = ss.getFirstElement(); - if (selected instanceof Match) { - SymbolInformation si = getSymbolInformation((Match) selected); - if (si!=null) { - return si; - } - } - } - //No element selected, target the first element in the list instead. - //This allows user to execute the action without explicitly selecting an element. - return getFirstElement(list); - } - - private void installWidgetListeners(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()); - } - } - } else if (e.character == '\r') { - performOk(list); - } - } - }); - - 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()); - } - - } - } else if (e.character == '\r') { - performOk(list); - } - -// if (e.keyCode == SWT.ARROW_DOWN -// && (e.stateMask & SWT.SHIFT) != 0 -// && (e.stateMask & SWT.CTRL) != 0) { -// -// list.getTree().notifyListeners(SWT.Selection, new Event()); -// } - - } - }); - - list.addDoubleClickListener(e -> performOk(list)); - } - - private Optional getSymbolLocation(SymbolInformation symbolInformation) { - String val = null; - - if (!model.fromFileProvider(symbolInformation)) { - Location location = symbolInformation.getLocation(); - - IResource targetResource = LSPEclipseUtils.findResourceFor(location.getUri()); - if (targetResource != null && targetResource.getFullPath() != null) { - val = targetResource.getFullPath().toString(); - } - } - - return val != null ? Optional.of(val) : Optional.empty(); - } - - private void performOk(TreeViewer list) { - if (model.performOk(getTarget(list))) { - close(); - } - } - - private SymbolInformation getFirstElement(TreeViewer list) { - TreeItem[] items = list.getTree().getItems(); - if (items!=null && items.length>0) { - TreeItem item = items[0]; - Object data = item.getData(); - if (data instanceof Match) { - SymbolInformation si = getSymbolInformation((Match) data); - if (si!=null) { - return si; - } - } - } - 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)); } @@ -375,66 +75,7 @@ public class GotoSymbolDialog extends PopupDialog { 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)); - pattern.setMessage(model.getSearchBoxHintMessage()); - - SwtConnect.connect(pattern, model.getSearchBox()); - - TreeViewer viewer = new TreeViewer(dialogArea, SWT.SINGLE); - ColumnViewerToolTipSupport.enableFor(viewer); - GridDataFactory.fillDefaults().grab(true, true).applyTo(viewer.getControl()); - viewer.setContentProvider(new SymbolsContentProvider()); - viewer.setLabelProvider(new GotoSymbolsLabelProvider(viewer.getTree().getFont())); - viewer.setUseHashlookup(true); - disposables.add(model.getSymbols().onChange(UIValueListener.from((e, v) -> { - if (!viewer.getControl().isDisposed()) viewer.refresh(); - }))); -//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); -// } -// } -// }); - installWidgetListeners(pattern, viewer); - - StyledText statusLabel = new StyledText(dialogArea, SWT.NONE); - // Allow for some extra space for highlight fonts - statusLabel.setLeftMargin(3); - statusLabel.setBottomMargin(2); - statusLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - - Stylers stylers = new Stylers(dialogArea.getFont()); - disposables.add(stylers); - - SwtConnect.connectHighlighted(stylers.bold(), statusLabel, model.getStatus(), Duration.ofMillis(500)); - - viewer.setInput(model); + content.createContents(dialogArea); return dialogArea; } @@ -461,6 +102,11 @@ public class GotoSymbolDialog extends PopupDialog { return new Point(control.getBounds().width, control.getBounds().height/2); } + @Override + public IRunnableContext getRunnableContext() { + return PlatformUI.getWorkbench().getProgressService(); + } + // /** // * Determines the graphical area covered by the given text region. // * diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/GotoSymbolSection.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/GotoSymbolSection.java new file mode 100644 index 000000000..c8860c4e9 --- /dev/null +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/GotoSymbolSection.java @@ -0,0 +1,401 @@ +/******************************************************************************* + * Copyright (c) 2016, 2019, 2020 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 + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Michał Niewrzał (Rogue Wave Software Inc.) - initial implementation + * Kris De Volder (Pivotal Inc) - Copied and adapted + *******************************************************************************/ +package org.springframework.tooling.ls.eclipse.gotosymbol.dialogs; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.resource.JFaceColors; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.StyledCellLabelProvider; +import org.eclipse.jface.viewers.StyledString; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.ViewerCell; +import org.eclipse.lsp4e.LSPEclipseUtils; +import org.eclipse.lsp4e.outline.SymbolsLabelProvider; +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.SymbolInformation; +import org.eclipse.lsp4j.jsonrpc.messages.Either; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.TreeItem; +import org.springframework.tooling.ls.eclipse.gotosymbol.dialogs.GotoSymbolDialogModel.Match; +import org.springsource.ide.eclipse.commons.core.util.FuzzyMatcher; +import org.springsource.ide.eclipse.commons.livexp.core.UIValueListener; +import org.springsource.ide.eclipse.commons.livexp.ui.Disposable; +import org.springsource.ide.eclipse.commons.livexp.ui.IPageWithSections; +import org.springsource.ide.eclipse.commons.livexp.ui.Stylers; +import org.springsource.ide.eclipse.commons.livexp.ui.WizardPageSection; +import org.springsource.ide.eclipse.commons.livexp.ui.util.SwtConnect; + +@SuppressWarnings("restriction") +public class GotoSymbolSection extends WizardPageSection { + + 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().getValue().toArray(); + } + return null; + } + } + + private class GotoSymbolsLabelProvider extends StyledCellLabelProvider { + + private Stylers stylers; + private SymbolsLabelProvider symbolsLabelProvider; + + public GotoSymbolsLabelProvider(Font base) { + stylers = new Stylers(base); + boolean showSymbolsLabelProviderLocation = false; /* dont show full location. we show relative location in our own implementation below */ + boolean showKindInformation = false; + symbolsLabelProvider = new SymbolsLabelProvider(showSymbolsLabelProviderLocation , showKindInformation) { + @Override + protected int getMaxSeverity(IResource resource, IDocument doc, Range range) + throws CoreException, BadLocationException { + int maxSeverity = -1; + for (IMarker marker : resource.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_ZERO)) { + int offset = marker.getAttribute(IMarker.CHAR_START, -1); + if (offset != -1) { + maxSeverity = Math.max(maxSeverity, marker.getAttribute(IMarker.SEVERITY, -1)); + } + } + return maxSeverity; + } + }; + } + + @Override + public Color getToolTipBackgroundColor(Object object) { + return JFaceColors.getInformationViewerBackgroundColor(Display.getDefault()); + } + + @Override + public Color getToolTipForegroundColor(Object object) { + return JFaceColors.getInformationViewerForegroundColor(Display.getDefault()); + } + + @Override + public String getToolTipText(Object element) { + if (element instanceof Match) { + SymbolInformation si = getSymbolInformation((Match)element); + if (si!=null) { + return si.getName(); + } + } + return null; + } + + @Override + public void update(ViewerCell cell) { + super.update(cell); + Object obj = cell.getElement(); + if (obj instanceof Match) { + Match match = (Match) obj; + cell.setImage(symbolsLabelProvider.getImage(match.value)); + StyledString styledString = getStyledText(match); + cell.setText(styledString.getString()); + cell.setStyleRanges(styledString.getStyleRanges()); + cell.getControl().redraw(); + //^^^ Sigh... Yes, this is needed. It seems SWT/Jface isn't smart enough to itself figure out that if + //the styleranges change a redraw is needed to make the change visible. + } else { + super.update(cell); + } + } + + private StyledString getStyledText(Match element) { + SymbolInformation symbolInformation = getSymbolInformation(element); + if (symbolInformation != null) { + String name = symbolInformation.getName(); + StyledString s = new StyledString(name); + Collection highlights = FuzzyMatcher.highlights(element.query, name.toLowerCase()); + for (IRegion hl : highlights) { + s.setStyle(hl.getOffset(), hl.getLength(), stylers.bold()); + } + String locationText = getSymbolLocationText(symbolInformation); + if (locationText != null) { + s = s.append(locationText, stylers.italicColoured(SWT.COLOR_DARK_GRAY)); + } + return s; + } else { + return symbolsLabelProvider.getStyledText(element.value); + } + } + + @Override + public void dispose() { + stylers.dispose(); + symbolsLabelProvider.dispose(); + super.dispose(); + } + + protected String getSymbolLocationText(SymbolInformation symbol) { + Optional location = GotoSymbolSection.this + .getSymbolLocation(symbol); + if (location.isPresent()) { + return " -- [" + location.get() + "]"; + } + return null; + } + } + + private final GotoSymbolDialogModel model; + + + public GotoSymbolSection(IPageWithSections owner, GotoSymbolDialogModel model) { + super(owner); + this.model = model; + } + + @Override + public void createContents(Composite dialogArea) { + List disposables = new ArrayList<>(); + dialogArea.addDisposeListener(de -> { + for (Disposable d : disposables) { + d.dispose(); + } + }); + + //Search box: + 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)); + pattern.setMessage(model.getSearchBoxHintMessage()); + SwtConnect.connect(pattern, model.getSearchBox()); + + //Tree viewer with results + TreeViewer viewer = new TreeViewer(dialogArea, SWT.SINGLE); + ColumnViewerToolTipSupport.enableFor(viewer); + GridDataFactory.fillDefaults().grab(true, true).applyTo(viewer.getControl()); + viewer.setContentProvider(new SymbolsContentProvider()); + viewer.setLabelProvider(new GotoSymbolsLabelProvider(viewer.getTree().getFont())); + viewer.setUseHashlookup(true); + disposables.add(model.getSymbols().onChange(UIValueListener.from((e, v) -> { + if (!viewer.getControl().isDisposed()) viewer.refresh(); + }))); +//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); +// } +// } +// }); + installWidgetListeners(pattern, viewer); + + //Status label + StyledText statusLabel = new StyledText(dialogArea, SWT.NONE); + // Allow for some extra space for highlight fonts + statusLabel.setLeftMargin(3); + statusLabel.setBottomMargin(2); + statusLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + Stylers stylers = new Stylers(dialogArea.getFont()); + disposables.add(stylers); + + SwtConnect.connectHighlighted(stylers.bold(), statusLabel, model.getStatus(), Duration.ofMillis(500)); + viewer.setInput(model); + } + + private void installWidgetListeners(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()); + } + } + } else if (e.character == '\r') { + performOk(list); + } + } + }); + + 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()); + } + + } + } else if (e.character == '\r') { + performOk(list); + } + +// if (e.keyCode == SWT.ARROW_DOWN +// && (e.stateMask & SWT.SHIFT) != 0 +// && (e.stateMask & SWT.CTRL) != 0) { +// +// list.getTree().notifyListeners(SWT.Selection, new Event()); +// } + + } + }); + + list.addDoubleClickListener(e -> performOk(list)); + } + + private void performOk(TreeViewer list) { + if (model.performOk(getTarget(list))) { + close(); + } + } + + /** + * Determine the 'target' for the dialog's action. + */ + private SymbolInformation getTarget(TreeViewer list) { + ISelection sel = list.getSelection(); + if (sel instanceof IStructuredSelection) { + IStructuredSelection ss = (IStructuredSelection) sel; + Object selected = ss.getFirstElement(); + if (selected instanceof Match) { + SymbolInformation si = getSymbolInformation((Match) selected); + if (si!=null) { + return si; + } + } + } + //No element selected, target the first element in the list instead. + //This allows user to execute the action without explicitly selecting an element. + return getFirstElement(list); + } + + private SymbolInformation getFirstElement(TreeViewer list) { + TreeItem[] items = list.getTree().getItems(); + if (items!=null && items.length>0) { + TreeItem item = items[0]; + Object data = item.getData(); + if (data instanceof Match) { + SymbolInformation si = getSymbolInformation((Match) data); + if (si!=null) { + return si; + } + } + } + return null; + } + + private SymbolInformation getSymbolInformation(Match element) { + if (element.value instanceof SymbolInformation) { + return (SymbolInformation) element.value; + } + if (element.value instanceof Either) { + Either either = (Either)element.value; + if (either.isLeft()) { + Object left = either.getLeft(); + if (left instanceof SymbolInformation) { + return (SymbolInformation) left; + } + } + } + return null; + } + + private Optional getSymbolLocation(SymbolInformation symbolInformation) { + String val = null; + + if (!model.fromFileProvider(symbolInformation)) { + Location location = symbolInformation.getLocation(); + + IResource targetResource = LSPEclipseUtils.findResourceFor(location.getUri()); + if (targetResource != null && targetResource.getFullPath() != null) { + val = targetResource.getFullPath().toString(); + } + } + + return val != null ? Optional.of(val) : Optional.empty(); + } + +} diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/InWorkspaceSymbolsProvider.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/InWorkspaceSymbolsProvider.java index 039781b6b..808ed7308 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/InWorkspaceSymbolsProvider.java +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/InWorkspaceSymbolsProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017, 2019 Pivotal, Inc. + * Copyright (c) 2017, 2019, 2020 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 @@ -12,6 +12,7 @@ package org.springframework.tooling.ls.eclipse.gotosymbol.dialogs; import java.time.Duration; import java.util.List; +import java.util.function.Supplier; import java.util.stream.Collectors; import org.eclipse.core.commands.ExecutionEvent; @@ -38,11 +39,22 @@ import reactor.core.publisher.Mono; @SuppressWarnings("restriction") public class InWorkspaceSymbolsProvider implements SymbolsProvider { + public static InWorkspaceSymbolsProvider createFor(Supplier _project) { + return new InWorkspaceSymbolsProvider(() -> { + IProject project = _project.get(); + if (project!=null) { + return LanguageServiceAccessor.getLanguageServers(project, + capabilities -> Boolean.TRUE.equals(capabilities.getWorkspaceSymbolProvider()), true); + } + return ImmutableList.of(); + }); + } + public static InWorkspaceSymbolsProvider createFor(IProject project) { List languageServers = LanguageServiceAccessor.getLanguageServers(project, capabilities -> Boolean.TRUE.equals(capabilities.getWorkspaceSymbolProvider()), true); if (!languageServers.isEmpty()) { - return new InWorkspaceSymbolsProvider(languageServers); + return new InWorkspaceSymbolsProvider(() -> languageServers); } return null; } @@ -50,10 +62,9 @@ public class InWorkspaceSymbolsProvider implements SymbolsProvider { private static final Duration TIMEOUT = Duration.ofSeconds(2); private static final int MAX_RESULTS = 200; + private Supplier> languageServers; - private List languageServers; - - public InWorkspaceSymbolsProvider(List languageServers) { + public InWorkspaceSymbolsProvider(Supplier> languageServers) { this.languageServers = languageServers; } @@ -75,7 +86,7 @@ public class InWorkspaceSymbolsProvider implements SymbolsProvider { // really use this with a single language server anyways. WorkspaceSymbolParams params = new WorkspaceSymbolParams(query); - Flux> symbols = Flux.fromIterable(this.languageServers) + Flux> symbols = Flux.fromIterable(this.languageServers.get()) .flatMap(server -> Mono.fromFuture(server.getWorkspaceService().symbol(params)) .timeout(TIMEOUT) .doOnError(e -> log(e)) diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/view/SpringSymbolsView.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/view/SpringSymbolsView.java new file mode 100644 index 000000000..8727e1a23 --- /dev/null +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/view/SpringSymbolsView.java @@ -0,0 +1,129 @@ +package org.springframework.tooling.ls.eclipse.gotosymbol.view; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.IActionBars; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.ISelectionListener; +import org.eclipse.ui.ISelectionService; +import org.eclipse.ui.IWorkbenchPart; +import org.springframework.ide.eclipse.boot.dash.views.sections.ViewPartWithSections; +import org.springframework.tooling.ls.eclipse.gotosymbol.dialogs.GotoSymbolSection; +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.ui.DescriptionSection; +import org.springsource.ide.eclipse.commons.livexp.ui.IPageSection; +import org.springsource.ide.eclipse.commons.livexp.ui.InfoFieldSection; +import org.springsource.ide.eclipse.commons.livexp.ui.StringFieldSection; +import org.springsource.ide.eclipse.commons.livexp.util.Log; + +public class SpringSymbolsView extends ViewPartWithSections { + + /** + * Adds scroll support to the whole view. You probably want to disable this + * if view is broken into pieces that have their own scrollbars + */ + private static final boolean ENABLE_SCROLLING = false; + + private final SpringSymbolsViewModel model = new SpringSymbolsViewModel(); + + private final LiveExpression projectAsString = model.currentProject.apply(p -> { + System.out.println("project = "+p); + return p==null ? "null" : p.getName(); + }); + + private ISelectionListener selectionListener = new ISelectionListener() { + + @Override + public void selectionChanged(IWorkbenchPart arg0, ISelection selection) { + if (selection instanceof IStructuredSelection) { + IStructuredSelection ss = (IStructuredSelection) selection; + Object element = ss.getFirstElement(); + IProject project = getProject(element); + if (project!=null) { + model.currentProject.setValue(project); + } + } else if (selection instanceof ITextSelection) { + //Let's assume the selection is in the active editor + try { + IEditorPart editor = getSite().getWorkbenchWindow().getActivePage().getActiveEditor(); + if (editor!=null) { + IEditorInput input = editor.getEditorInput(); + IResource resource = input.getAdapter(IResource.class); + if (resource != null) { + IProject project = resource.getProject(); + if (project!=null) { + model.currentProject.setValue(project); + } + } + } + } catch (Exception e) { + Log.log(e); + } + } + } + + private IProject getProject(Object element) { + if (element instanceof IResource) { + return ((IResource) element).getProject(); + } else if (element instanceof IAdaptable) { + IResource resource = ((IAdaptable) element).getAdapter(IResource.class); + if (resource!=null) { + return resource.getProject(); + } + } + return null; + } + }; + + public SpringSymbolsView() { + super(ENABLE_SCROLLING); + } + + @Override + protected List createSections() throws CoreException { + List sections = new ArrayList<>(); + sections.add(new InfoFieldSection(this, "Project", projectAsString)); + sections.add(new GotoSymbolSection(this, model.gotoSymbols)); + ISelectionService selectitonService = getSite().getWorkbenchWindow().getSelectionService(); + selectitonService.addSelectionListener(selectionListener); + return sections; + } + + /** + * This is a callback that will allow us to create the viewer and initialize + * it. + */ + public void createPartControl(Composite parent) { + super.createPartControl(parent); + contributeToActionBars(); + } + + private void contributeToActionBars() { + IActionBars bars = getViewSite().getActionBars(); + fillLocalPullDown(bars.getMenuManager()); + fillLocalToolBar(bars.getToolBarManager()); + } + + /** + * Fills the pull-down menu for this view (accessible from the toolbar) + */ + private void fillLocalPullDown(IMenuManager manager) { + } + + private void fillLocalToolBar(IToolBarManager manager) { + } + +} diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/view/SpringSymbolsViewModel.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/view/SpringSymbolsViewModel.java new file mode 100644 index 000000000..a55e65bea --- /dev/null +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/view/SpringSymbolsViewModel.java @@ -0,0 +1,13 @@ +package org.springframework.tooling.ls.eclipse.gotosymbol.view; + +import org.eclipse.core.resources.IProject; +import org.springframework.tooling.ls.eclipse.gotosymbol.dialogs.GotoSymbolDialogModel; +import org.springframework.tooling.ls.eclipse.gotosymbol.dialogs.InWorkspaceSymbolsProvider; +import org.springsource.ide.eclipse.commons.livexp.core.LiveVariable; + +public class SpringSymbolsViewModel { + + public final LiveVariable currentProject = new LiveVariable<>(); + public final GotoSymbolDialogModel gotoSymbols = new GotoSymbolDialogModel(null, InWorkspaceSymbolsProvider.createFor(currentProject::getValue)); + +}