From 1b4b2b76ff9eccbee3d09fecbe72bb87b45af9b9 Mon Sep 17 00:00:00 2001 From: aboyko Date: Wed, 21 May 2025 17:45:35 -0700 Subject: [PATCH] Hierarchical doc symbols in Eclipse by default --- .../boot/ls/prefs/PrefsInitializer.java | 2 +- .../.settings/org.eclipse.jdt.core.prefs | 6 +-- .../dialogs/GotoSymbolDialogModel.java | 37 +++++++++++++++++-- .../gotosymbol/dialogs/GotoSymbolSection.java | 31 +++------------- .../gotosymbol/dialogs/SymbolContainer.java | 15 +++++++- 5 files changed, 57 insertions(+), 34 deletions(-) diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/prefs/PrefsInitializer.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/prefs/PrefsInitializer.java index f71b799b2..7aa55ae6e 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/prefs/PrefsInitializer.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/prefs/PrefsInitializer.java @@ -64,7 +64,7 @@ public class PrefsInitializer extends AbstractPreferenceInitializer { preferenceStore.setDefault(Constants.PREF_PROPS_COMPLETIONS_ELIDE_PREFIX, false); preferenceStore.setDefault(Constants.PREF_CRON_INLAY_HINTS, true); preferenceStore.setDefault(Constants.PREF_COMPLETION_JAVA_INJECT_BEAN, true); - preferenceStore.setDefault(Constants.PREF_BEANS_STRUCTURE_TREE, false); + preferenceStore.setDefault(Constants.PREF_BEANS_STRUCTURE_TREE, true); preferenceStore.setDefault(Constants.PREF_SYMBOLS_FROM_NEW_INDEX, true); } diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/.settings/org.eclipse.jdt.core.prefs b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/.settings/org.eclipse.jdt.core.prefs index 7adc0fb9a..d4540a53f 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/.settings/org.eclipse.jdt.core.prefs +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/.settings/org.eclipse.jdt.core.prefs @@ -1,10 +1,10 @@ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 -org.eclipse.jdt.core.compiler.compliance=11 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 +org.eclipse.jdt.core.compiler.compliance=17 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning org.eclipse.jdt.core.compiler.release=enabled -org.eclipse.jdt.core.compiler.source=11 +org.eclipse.jdt.core.compiler.source=17 diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/GotoSymbolDialogModel.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/GotoSymbolDialogModel.java index f567fc54b..e3231f2f0 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/GotoSymbolDialogModel.java +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/GotoSymbolDialogModel.java @@ -17,12 +17,18 @@ import java.util.Comparator; import java.util.List; import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; import org.eclipse.lsp4e.LSPEclipseUtils; +import org.eclipse.lsp4e.LanguageServerPlugin; +import org.eclipse.lsp4e.ui.UI; import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.WorkspaceSymbolLocation; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.texteditor.ITextEditor; import org.springframework.tooling.ls.eclipse.gotosymbol.GotoSymbolPlugin; import org.springframework.tooling.ls.eclipse.gotosymbol.favourites.FavouritesPreference; import org.springsource.ide.eclipse.commons.core.util.FuzzyMatcher; @@ -59,11 +65,13 @@ public class GotoSymbolDialogModel { final double score; final String query; final T value; - public Match(double score, String query, T value) { + final List> children; + public Match(double score, String query, T value, List> children) { super(); this.score = score; this.query = query; this.value = value; + this.children = children; } } @@ -114,6 +122,21 @@ public class GotoSymbolDialogModel { IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); LSPEclipseUtils.openInEditor(location, page); + } else if (symbolInformation.isDocumentSymbol()) { + ITextEditor editor = UI.getActiveTextEditor(); + if (editor != null) { + IDocument doc = LSPEclipseUtils.getDocument(editor); + if (doc != null) { + try { + Range range = symbolInformation.getDocumentSymbol().getSelectionRange(); + int offset = LSPEclipseUtils.toOffset(range.getStart(), doc); + int endOffset = LSPEclipseUtils.toOffset(range.getEnd(), doc); + editor.selectAndReveal(offset, endOffset - offset); + } catch (BadLocationException e) { + LanguageServerPlugin.logError(e); + } + } + } } return true; }; @@ -185,8 +208,13 @@ public class GotoSymbolDialogModel { query = ""; } query = query.toLowerCase(); + + return computeMatches(unfilteredSymbols.getValues(), query); + } + private List> computeMatches(Collection c, String query) { List> matches = new ArrayList<>(); - for (SymbolContainer symbol : unfilteredSymbols.getValues()) { + for (SymbolContainer symbol : c) { + String name = symbol.getName(); if (name != null) { @@ -194,8 +222,9 @@ public class GotoSymbolDialogModel { } double score = FuzzyMatcher.matchScore(query, name); - if (score != 0.0) { - matches.add(new Match(score, query, symbol)); + List> childrenMatches = computeMatches(symbol.getChildren(), query); + if (score != 0.0 || !childrenMatches.isEmpty()) { + matches.add(new Match(score, query, symbol, childrenMatches)); } } Collections.sort(matches, MATCH_COMPARATOR); 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 index 91db44ca4..1055e6290 100644 --- 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 @@ -88,6 +88,9 @@ public class GotoSymbolSection extends WizardPageSection { @Override public Object[] getChildren(Object parentElement) { + if (parentElement instanceof Match m) { + return m.children.toArray(new Object[m.children.size()]); + } return null; } @@ -98,6 +101,9 @@ public class GotoSymbolSection extends WizardPageSection { @Override public boolean hasChildren(Object element) { + if (element instanceof Match m ) { + return !m.children.isEmpty(); + } return false; } @@ -302,31 +308,6 @@ public class GotoSymbolSection extends WizardPageSection { 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 diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/SymbolContainer.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/SymbolContainer.java index dbffb0446..5f7c4863e 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/SymbolContainer.java +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/SymbolContainer.java @@ -10,6 +10,8 @@ *******************************************************************************/ package org.springframework.tooling.ls.eclipse.gotosymbol.dialogs; +import java.util.List; + import org.eclipse.lsp4j.DocumentSymbol; import org.eclipse.lsp4j.SymbolInformation; import org.eclipse.lsp4j.WorkspaceSymbol; @@ -20,6 +22,8 @@ public class SymbolContainer { private DocumentSymbol documentSymbol; private WorkspaceSymbol workspaceSymbol; + private List children = List.of(); + public static SymbolContainer fromSymbolInformation(SymbolInformation symbolInformation) { return new SymbolContainer(symbolInformation); } @@ -38,6 +42,11 @@ public class SymbolContainer { private SymbolContainer(DocumentSymbol documentSymbol) { this.documentSymbol = documentSymbol; + if (documentSymbol.getChildren() == null) { + this.children = List.of(); + } else { + this.children = documentSymbol.getChildren().stream().map(SymbolContainer::new).toList(); + } } private SymbolContainer(WorkspaceSymbol workspaceSymbol) { @@ -100,5 +109,9 @@ public class SymbolContainer { return workspaceSymbol; } } - + + public List getChildren() { + return children; + } + }