diff --git a/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPDocumentSymbolFeature.java b/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPDocumentSymbolFeature.java index cdd7fa6d2..27f8247b8 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPDocumentSymbolFeature.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPDocumentSymbolFeature.java @@ -12,6 +12,8 @@ import com.intellij.ide.structureView.StructureViewTreeElement; import com.intellij.openapi.editor.Document; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.lsp4ij.features.documentSymbol.DocumentSymbolData; @@ -127,4 +129,10 @@ public boolean canNavigate(@NotNull DocumentSymbol documentSymbol, var selectionRange = documentSymbol.getSelectionRange(); return selectionRange != null && selectionRange.getStart() != null; } + + public boolean isSame(@NotNull DocumentSymbolData documentSymbolData, + @NotNull PsiElement psiElement) { + TextRange textRange = documentSymbolData.getSelectionTextRange() != null ? documentSymbolData.getSelectionTextRange() : documentSymbolData.getTextRange(); + return textRange.equals(psiElement.getTextRange()); + } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/documentSymbol/DocumentSymbolData.java b/src/main/java/com/redhat/devtools/lsp4ij/features/documentSymbol/DocumentSymbolData.java index 002b19829..a6b50a648 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/documentSymbol/DocumentSymbolData.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/documentSymbol/DocumentSymbolData.java @@ -25,6 +25,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.Icon; +import java.util.Objects; /** @@ -39,6 +40,7 @@ public class DocumentSymbolData extends FakePsiElement { private final DocumentSymbolData parent; private final @NotNull LanguageServerItem languageServer; private volatile TextRange textRange = null; + private volatile TextRange selectionTextRange = null; private DocumentSymbolData[] cachedChildren; public DocumentSymbolData(@NotNull DocumentSymbol documentSymbol, @@ -95,6 +97,21 @@ public TextRange getTextRange() { return textRange; } + public @Nullable TextRange getSelectionTextRange() { + if (documentSymbol.getSelectionRange() == null) { + return null; + } + if (selectionTextRange == null) { + synchronized (this) { + if (selectionTextRange == null) { + Range range = documentSymbol.getSelectionRange(); + Document document = LSPIJUtils.getDocument(psiFile); + this.selectionTextRange = (document != null) ? LSPIJUtils.toTextRange(range, document) : null; + } + } + } + return selectionTextRange; + } @Override public void navigate(boolean requestFocus) { getClientFeatures().getDocumentSymbolFeature().navigate(documentSymbol, psiFile, requestFocus); @@ -124,6 +141,26 @@ public boolean canNavigate() { return cachedChildren; } + @Override + public boolean equals(Object o) { + if (o == null) { + return false; + } + if (getClass() == o.getClass()) { + DocumentSymbolData that = (DocumentSymbolData) o; + return Objects.equals(documentSymbol, that.documentSymbol); + } + if (o instanceof PsiElement psiElement) { + return getClientFeatures().getDocumentSymbolFeature().isSame(this, psiElement); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(documentSymbol); + } + public @NotNull LSPClientFeatures getClientFeatures() { return languageServer.getClientFeatures(); } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/documentSymbol/LSPDocumentSymbolStructureViewModel.java b/src/main/java/com/redhat/devtools/lsp4ij/features/documentSymbol/LSPDocumentSymbolStructureViewModel.java index 86e20f01d..2b8ac973f 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/documentSymbol/LSPDocumentSymbolStructureViewModel.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/documentSymbol/LSPDocumentSymbolStructureViewModel.java @@ -15,10 +15,12 @@ import com.intellij.ide.structureView.StructureViewTreeElement; import com.intellij.ide.structureView.impl.common.PsiTreeElementBase; import com.intellij.openapi.editor.Editor; +import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.util.ArrayUtil; import com.redhat.devtools.lsp4ij.LSPFileSupport; import com.redhat.devtools.lsp4ij.client.indexing.ProjectIndexingManager; +import com.redhat.devtools.lsp4ij.features.semanticTokens.viewProvider.LSPSemanticTokenPsiElement; import com.redhat.devtools.lsp4ij.internal.PsiFileChangedException; import org.eclipse.lsp4j.DocumentSymbolParams; import org.eclipse.lsp4j.TextDocumentIdentifier; @@ -55,6 +57,12 @@ public boolean isAlwaysLeaf(StructureViewTreeElement element) { return element.getChildren().length == 0; } + @Override + protected Class @NotNull [] getSuitableClasses() { + // Any PSI element + return new Class[] {PsiElement.class}; + } + @Override public void dispose() { super.dispose(); diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/documentSymbol/LSPStructureViewBuilderProvider.java b/src/main/java/com/redhat/devtools/lsp4ij/features/documentSymbol/LSPStructureViewBuilderProvider.java new file mode 100644 index 000000000..1d13ad5dd --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/documentSymbol/LSPStructureViewBuilderProvider.java @@ -0,0 +1,37 @@ +package com.redhat.devtools.lsp4ij.features.documentSymbol; + +import com.intellij.ide.structureView.StructureViewBuilder; +import com.intellij.ide.structureView.StructureViewBuilderProvider; +import com.intellij.ide.structureView.logical.PhysicalAndLogicalStructureViewBuilder; +import com.intellij.lang.LanguageStructureViewBuilder; +import com.intellij.lang.PsiStructureViewFactory; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.fileTypes.impl.AbstractFileType; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.redhat.devtools.lsp4ij.LanguageServersRegistry; +import com.redhat.devtools.lsp4ij.client.ExecuteLSPFeatureStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class LSPStructureViewBuilderProvider implements StructureViewBuilderProvider { + @Override + public @Nullable StructureViewBuilder getStructureViewBuilder(@NotNull FileType fileType, @NotNull VirtualFile file, @NotNull Project project) { + if (!(fileType instanceof AbstractFileType)) { + return null; + } + if (!LanguageServersRegistry.getInstance().isFileSupported(file, project)) { + // The file is not associated to a language server, don't execute the LSP feature. + return null; + } + PsiFile psiFile = PsiManager.getInstance(project).findFile(file); + if (psiFile == null) return null; + + PsiStructureViewFactory factory = LanguageStructureViewBuilder.getInstance().forLanguage(psiFile.getLanguage()); + if (factory == null) return null; + StructureViewBuilder physicalBuilder = factory.getStructureViewBuilder(psiFile); + return PhysicalAndLogicalStructureViewBuilder.Companion.wrapPhysicalBuilderIfPossible(physicalBuilder, psiFile); + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 0f00684f3..24c1c55c3 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -437,6 +437,10 @@ implementationClass="com.redhat.devtools.lsp4ij.features.documentLink.LSPDocumentLinkDocumentationProvider"/> +