diff --git a/bundles/org.eclipse.cdt.lsp/META-INF/MANIFEST.MF b/bundles/org.eclipse.cdt.lsp/META-INF/MANIFEST.MF
index eaef9c94..cc8e33ed 100644
--- a/bundles/org.eclipse.cdt.lsp/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.cdt.lsp/META-INF/MANIFEST.MF
@@ -7,7 +7,9 @@ Export-Package: org.eclipse.cdt.lsp,
org.eclipse.cdt.lsp.editor,
org.eclipse.cdt.lsp.server,
org.eclipse.cdt.lsp.server.enable,
- org.eclipse.cdt.lsp.services
+ org.eclipse.cdt.lsp.services,
+ org.eclipse.cdt.lsp.services.ast,
+ org.eclipse.cdt.lsp.services.symbolinfo
Bundle-Activator: org.eclipse.cdt.lsp.LspPlugin
Bundle-Vendor: %Bundle-Vendor
Require-Bundle: org.eclipse.ui,
diff --git a/bundles/org.eclipse.cdt.lsp/src/org/eclipse/cdt/lsp/services/ClangdLanguageServer.java b/bundles/org.eclipse.cdt.lsp/src/org/eclipse/cdt/lsp/services/ClangdLanguageServer.java
index 82b5397d..e7162154 100644
--- a/bundles/org.eclipse.cdt.lsp/src/org/eclipse/cdt/lsp/services/ClangdLanguageServer.java
+++ b/bundles/org.eclipse.cdt.lsp/src/org/eclipse/cdt/lsp/services/ClangdLanguageServer.java
@@ -9,24 +9,32 @@
*
* Contributors:
* Dominic Scharfe (COSEDA Technologies GmbH) - initial implementation
+ * Dietrich Travkin (Solunar GmbH) - extensions for AST and symbol info
*******************************************************************************/
package org.eclipse.cdt.lsp.services;
import java.util.concurrent.CompletableFuture;
+import org.eclipse.cdt.lsp.services.ast.AstNode;
+import org.eclipse.cdt.lsp.services.ast.AstParams;
+import org.eclipse.cdt.lsp.services.symbolinfo.SymbolDetails;
import org.eclipse.lsp4j.TextDocumentIdentifier;
+import org.eclipse.lsp4j.TextDocumentPositionParams;
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest;
import org.eclipse.lsp4j.services.LanguageServer;
/**
* Interface extending the {@link LanguageServer} with clangd extensions.
+ * More details about LSP usage and extension see the
+ *
+ * org.eclipse.lsp4j project's documentation.
*
* @see https://clangd.llvm.org/extensions
*/
public interface ClangdLanguageServer extends LanguageServer {
/**
- * The switchSourceHeader request is sent from the client to the server to
+ * The textDocument/switchSourceHeader request is sent from the client to the server to
*
* - get the corresponding header if a source file was provided
* - get the source file if a header was provided
@@ -39,4 +47,31 @@ public interface ClangdLanguageServer extends LanguageServer {
*/
@JsonRequest(value = "textDocument/switchSourceHeader")
CompletableFuture switchSourceHeader(TextDocumentIdentifier textDocument);
+
+ /**
+ * The textDocument/ast request is sent from the client to the server in order to get
+ * details about the program structure (so called abstract syntax tree or AST) in a C++ file.
+ * The structure can be requested for the whole file or for a certain range.
+ *
+ * @param astParameters request parameters containing the document identifier and requested documented range
+ * @return the abstract syntax tree root node (with child hierarchy) for the requested document and range
+ *
+ * @see https://clangd.llvm.org/extensions#ast
+ */
+ @JsonRequest(value = "textDocument/ast")
+ CompletableFuture getAst(AstParams astParameters);
+
+ /**
+ * The textDocument/symbolInfo request is sent from the client to the server in order to access
+ * details about the element under the cursor. The response provides details like the element's name,
+ * its parent container's name, and some clangd-specific element IDs (e.g. the "unified symbol resolution"
+ * identifier).
+ *
+ * @param positionParameters request parameters containing the document identifier and the current cursor position
+ * @return the details about the symbol on the given position
+ *
+ * @see https://clangd.llvm.org/extensions#symbol-info-request
+ */
+ @JsonRequest(value = "textDocument/symbolInfo")
+ CompletableFuture getSymbolInfo(TextDocumentPositionParams positionParameters);
}
diff --git a/bundles/org.eclipse.cdt.lsp/src/org/eclipse/cdt/lsp/services/ast/AstNode.java b/bundles/org.eclipse.cdt.lsp/src/org/eclipse/cdt/lsp/services/ast/AstNode.java
new file mode 100644
index 00000000..a69f2c17
--- /dev/null
+++ b/bundles/org.eclipse.cdt.lsp/src/org/eclipse/cdt/lsp/services/ast/AstNode.java
@@ -0,0 +1,167 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Advantest Europe GmbH and others.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Dietrich Travkin (Solunar GmbH) - Initial implementation
+ *******************************************************************************/
+package org.eclipse.cdt.lsp.services.ast;
+
+import java.util.Arrays;
+
+import org.eclipse.cdt.lsp.services.ClangdLanguageServer;
+import org.eclipse.lsp4j.Range;
+import org.eclipse.lsp4j.jsonrpc.util.Preconditions;
+import org.eclipse.lsp4j.jsonrpc.util.ToStringBuilder;
+import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
+
+/**
+ * Return type for the textDocument/ast request.
+ * This class was generated by the org.eclipse.lsp4j.generator bundle
+ * using xtend (see {@link org.eclipse.lsp4j.generator.JsonRpcData JsonRpcData} and
+ * the documentation).
+ *
+ * @see {@link ClangdLanguageServer#getAst(AstParams)}
+ */
+public class AstNode {
+
+ @NonNull
+ private String role;
+
+ @NonNull
+ private String kind;
+
+ private String detail;
+
+ private String arcana;
+
+ @NonNull
+ private Range range;
+
+ private AstNode[] children;
+
+ public AstNode() {
+
+ }
+
+ @NonNull
+ public String getRole() {
+ return role;
+ }
+
+ public void setRole(@NonNull final String role) {
+ this.role = Preconditions.checkNotNull(role, "role"); //$NON-NLS-1$
+ }
+
+ @NonNull
+ public String getKind() {
+ return this.kind;
+ }
+
+ public void setKind(@NonNull final String kind) {
+ this.kind = Preconditions.checkNotNull(kind, "kind"); //$NON-NLS-1$
+ }
+
+ public String getDetail() {
+ return detail;
+ }
+
+ public void setDetail(final String detail) {
+ this.detail = detail;
+ }
+
+ public String getArcana() {
+ return arcana;
+ }
+
+ public void setArcana(final String arcana) {
+ this.arcana = arcana;
+ }
+
+ @NonNull
+ public Range getRange() {
+ return range;
+ }
+
+ public void setRange(@NonNull final Range range) {
+ this.range = Preconditions.checkNotNull(range, "range"); //$NON-NLS-1$
+ }
+
+ public AstNode[] getChildren() {
+ return children;
+ }
+
+ public void setChildren(final AstNode[] children) {
+ this.children = children;
+ }
+
+ @Override
+ public String toString() {
+ ToStringBuilder b = new ToStringBuilder(this);
+ b.add("role", this.role); //$NON-NLS-1$
+ b.add("kind", this.kind); //$NON-NLS-1$
+ b.add("detail", this.detail); //$NON-NLS-1$
+ b.add("arcana", this.arcana); //$NON-NLS-1$
+ b.add("range", this.range); //$NON-NLS-1$
+ b.add("children", this.children); //$NON-NLS-1$
+ return b.toString();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ AstNode other = (AstNode) obj;
+ if (this.role == null) {
+ if (other.role != null)
+ return false;
+ } else if (!this.role.equals(other.role))
+ return false;
+ if (this.kind == null) {
+ if (other.kind != null)
+ return false;
+ } else if (!this.kind.equals(other.kind))
+ return false;
+ if (this.detail == null) {
+ if (other.detail != null)
+ return false;
+ } else if (!this.detail.equals(other.detail))
+ return false;
+ if (this.arcana == null) {
+ if (other.arcana != null)
+ return false;
+ } else if (!this.arcana.equals(other.arcana))
+ return false;
+ if (this.range == null) {
+ if (other.range != null)
+ return false;
+ } else if (!this.range.equals(other.range))
+ return false;
+ if (this.children == null) {
+ if (other.children != null)
+ return false;
+ } else if (!Arrays.deepEquals(this.children, other.children))
+ return false;
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((this.role == null) ? 0 : this.role.hashCode());
+ result = prime * result + ((this.kind == null) ? 0 : this.kind.hashCode());
+ result = prime * result + ((this.detail == null) ? 0 : this.detail.hashCode());
+ result = prime * result + ((this.arcana == null) ? 0 : this.arcana.hashCode());
+ result = prime * result + ((this.range == null) ? 0 : this.range.hashCode());
+ return prime * result + ((this.children == null) ? 0 : Arrays.deepHashCode(this.children));
+ }
+}
diff --git a/bundles/org.eclipse.cdt.lsp/src/org/eclipse/cdt/lsp/services/ast/AstParams.java b/bundles/org.eclipse.cdt.lsp/src/org/eclipse/cdt/lsp/services/ast/AstParams.java
new file mode 100644
index 00000000..4ebc56a7
--- /dev/null
+++ b/bundles/org.eclipse.cdt.lsp/src/org/eclipse/cdt/lsp/services/ast/AstParams.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Advantest Europe GmbH and others.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Dietrich Travkin (Solunar GmbH) - Initial implementation
+ *******************************************************************************/
+package org.eclipse.cdt.lsp.services.ast;
+
+import org.eclipse.cdt.lsp.services.ClangdLanguageServer;
+import org.eclipse.lsp4j.Range;
+import org.eclipse.lsp4j.TextDocumentIdentifier;
+import org.eclipse.lsp4j.jsonrpc.util.Preconditions;
+import org.eclipse.lsp4j.jsonrpc.util.ToStringBuilder;
+import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
+
+/**
+ * Parameter object type for the textDocument/ast request.
+ * This class was generated by the org.eclipse.lsp4j.generator bundle
+ * using xtend (see {@link org.eclipse.lsp4j.generator.JsonRpcData JsonRpcData} and
+ * the documentation).
+ *
+ * @see {@link ClangdLanguageServer#getAst(AstParams)}
+ */
+public class AstParams {
+
+ @NonNull
+ private TextDocumentIdentifier textDocument;
+
+ @NonNull
+ private Range range;
+
+ public AstParams() {
+ }
+
+ public AstParams(@NonNull final TextDocumentIdentifier textDocument, @NonNull final Range range) {
+ this.textDocument = Preconditions.checkNotNull(textDocument, "textDocument"); //$NON-NLS-1$
+ this.range = Preconditions.checkNotNull(range, "range"); //$NON-NLS-1$
+ }
+
+ @NonNull
+ public TextDocumentIdentifier getTextDocument() {
+ return this.textDocument;
+ }
+
+ public void setTextDocument(@NonNull final TextDocumentIdentifier textDocument) {
+ this.textDocument = Preconditions.checkNotNull(textDocument, "textDocument"); //$NON-NLS-1$
+ }
+
+ @NonNull
+ public Range getRange() {
+ return range;
+ }
+
+ public void setRange(@NonNull Range range) {
+ this.range = Preconditions.checkNotNull(range, "range"); //$NON-NLS-1$
+ }
+
+ @Override
+ public String toString() {
+ ToStringBuilder b = new ToStringBuilder(this);
+ b.add("textDocument", getTextDocument()); //$NON-NLS-1$
+ b.add("range", getRange()); //$NON-NLS-1$
+ return b.toString();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ AstParams other = (AstParams) obj;
+ if (this.getTextDocument() == null) {
+ if (other.getTextDocument() != null)
+ return false;
+ } else if (!this.getTextDocument().equals(other.getTextDocument()))
+ return false;
+ if (this.range == null) {
+ if (other.range != null)
+ return false;
+ } else if (!this.range.equals(other.range))
+ return false;
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((this.getTextDocument() == null) ? 0 : this.getTextDocument().hashCode());
+ return prime * result + ((this.range == null) ? 0 : this.range.hashCode());
+ }
+}
diff --git a/bundles/org.eclipse.cdt.lsp/src/org/eclipse/cdt/lsp/services/symbolinfo/RangeAndUri.java b/bundles/org.eclipse.cdt.lsp/src/org/eclipse/cdt/lsp/services/symbolinfo/RangeAndUri.java
new file mode 100644
index 00000000..16ba99b3
--- /dev/null
+++ b/bundles/org.eclipse.cdt.lsp/src/org/eclipse/cdt/lsp/services/symbolinfo/RangeAndUri.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Advantest Europe GmbH and others.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Dietrich Travkin (Solunar GmbH) - Initial implementation
+ *******************************************************************************/
+package org.eclipse.cdt.lsp.services.symbolinfo;
+
+import org.eclipse.cdt.lsp.services.ClangdLanguageServer;
+import org.eclipse.lsp4j.Range;
+import org.eclipse.lsp4j.jsonrpc.util.Preconditions;
+import org.eclipse.lsp4j.jsonrpc.util.ToStringBuilder;
+import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
+
+/**
+ * Data type used in {@link SymbolDetails} for the textDocument/symbolInfo request.
+ * This class was generated by the org.eclipse.lsp4j.generator bundle
+ * using xtend (see {@link org.eclipse.lsp4j.generator.JsonRpcData JsonRpcData} and
+ * the documentation).
+ *
+ * @see {@link SymbolDetails}
+ * @see {@link ClangdLanguageServer#getSymbolInfo(org.eclipse.lsp4j.TextDocumentPositionParams)}
+ */
+public class RangeAndUri {
+ @NonNull
+ private Range range;
+
+ @NonNull
+ private String uri;
+
+ @NonNull
+ public Range getRange() {
+ return this.range;
+ }
+
+ public void setRange(@NonNull final Range range) {
+ this.range = Preconditions.checkNotNull(range, "range"); //$NON-NLS-1$
+ }
+
+ @NonNull
+ public String getUri() {
+ return this.uri;
+ }
+
+ public void setUri(@NonNull final String uri) {
+ this.uri = Preconditions.checkNotNull(uri, "uri"); //$NON-NLS-1$
+ }
+
+ @Override
+ public String toString() {
+ ToStringBuilder b = new ToStringBuilder(this);
+ b.add("range", this.range); //$NON-NLS-1$
+ b.add("uri", this.uri); //$NON-NLS-1$
+ return b.toString();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ RangeAndUri other = (RangeAndUri) obj;
+ if (this.range == null) {
+ if (other.range != null)
+ return false;
+ } else if (!this.range.equals(other.range))
+ return false;
+ if (this.uri == null) {
+ if (other.uri != null)
+ return false;
+ } else if (!this.uri.equals(other.uri))
+ return false;
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((this.range == null) ? 0 : this.range.hashCode());
+ return prime * result + ((this.uri == null) ? 0 : this.uri.hashCode());
+ }
+}
diff --git a/bundles/org.eclipse.cdt.lsp/src/org/eclipse/cdt/lsp/services/symbolinfo/SymbolDetails.java b/bundles/org.eclipse.cdt.lsp/src/org/eclipse/cdt/lsp/services/symbolinfo/SymbolDetails.java
new file mode 100644
index 00000000..4aa59811
--- /dev/null
+++ b/bundles/org.eclipse.cdt.lsp/src/org/eclipse/cdt/lsp/services/symbolinfo/SymbolDetails.java
@@ -0,0 +1,160 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Advantest Europe GmbH and others.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Dietrich Travkin (Solunar GmbH) - Initial implementation
+ *******************************************************************************/
+package org.eclipse.cdt.lsp.services.symbolinfo;
+
+import org.eclipse.cdt.lsp.services.ClangdLanguageServer;
+import org.eclipse.lsp4j.jsonrpc.util.Preconditions;
+import org.eclipse.lsp4j.jsonrpc.util.ToStringBuilder;
+import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
+
+/**
+ * Return type for the textDocument/symbolInfo request.
+ * This class was generated by the org.eclipse.lsp4j.generator bundle
+ * using xtend (see {@link org.eclipse.lsp4j.generator.JsonRpcData JsonRpcData} and
+ * the documentation).
+ *
+ * @see {@link ClangdLanguageServer#getSymbolInfo(org.eclipse.lsp4j.TextDocumentPositionParams)}
+ */
+public class SymbolDetails {
+
+ @NonNull
+ private String name;
+
+ @NonNull
+ private String containerName;
+
+ @NonNull
+ private String usr;
+
+ private String id;
+
+ private RangeAndUri declarationRange;
+
+ private RangeAndUri definitionRange;
+
+ @NonNull
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(@NonNull final String name) {
+ this.name = Preconditions.checkNotNull(name, "name"); //$NON-NLS-1$
+ }
+
+ @NonNull
+ public String getContainerName() {
+ return this.containerName;
+ }
+
+ public void setContainerName(@NonNull final String containerName) {
+ this.containerName = Preconditions.checkNotNull(containerName, "containerName"); //$NON-NLS-1$
+ }
+
+ @NonNull
+ public String getUsr() {
+ return this.usr;
+ }
+
+ public void setUsr(@NonNull final String usr) {
+ this.usr = Preconditions.checkNotNull(usr, "usr"); //$NON-NLS-1$
+ }
+
+ public String getId() {
+ return this.id;
+ }
+
+ public void setId(final String id) {
+ this.id = id;
+ }
+
+ public RangeAndUri getDeclarationRange() {
+ return this.declarationRange;
+ }
+
+ public void setDeclarationRange(final RangeAndUri declarationRange) {
+ this.declarationRange = declarationRange;
+ }
+
+ public RangeAndUri getDefinitionRange() {
+ return this.definitionRange;
+ }
+
+ public void setDefinitionRange(final RangeAndUri definitionRange) {
+ this.definitionRange = definitionRange;
+ }
+
+ @Override
+ public String toString() {
+ ToStringBuilder b = new ToStringBuilder(this);
+ b.add("name", this.name); //$NON-NLS-1$
+ b.add("containerName", this.containerName); //$NON-NLS-1$
+ b.add("usr", this.usr); //$NON-NLS-1$
+ b.add("id", this.id); //$NON-NLS-1$
+ b.add("declarationRange", this.declarationRange); //$NON-NLS-1$
+ b.add("definitionRange", this.definitionRange); //$NON-NLS-1$
+ return b.toString();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ SymbolDetails other = (SymbolDetails) obj;
+ if (this.name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!this.name.equals(other.name))
+ return false;
+ if (this.containerName == null) {
+ if (other.containerName != null)
+ return false;
+ } else if (!this.containerName.equals(other.containerName))
+ return false;
+ if (this.usr == null) {
+ if (other.usr != null)
+ return false;
+ } else if (!this.usr.equals(other.usr))
+ return false;
+ if (this.id == null) {
+ if (other.id != null)
+ return false;
+ } else if (!this.id.equals(other.id))
+ return false;
+ if (this.declarationRange == null) {
+ if (other.declarationRange != null)
+ return false;
+ } else if (!this.declarationRange.equals(other.declarationRange))
+ return false;
+ if (this.definitionRange == null) {
+ if (other.definitionRange != null)
+ return false;
+ } else if (!this.definitionRange.equals(other.definitionRange))
+ return false;
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((this.name == null) ? 0 : this.name.hashCode());
+ result = prime * result + ((this.containerName == null) ? 0 : this.containerName.hashCode());
+ result = prime * result + ((this.usr == null) ? 0 : this.usr.hashCode());
+ result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
+ result = prime * result + ((this.declarationRange == null) ? 0 : this.declarationRange.hashCode());
+ return prime * result + ((this.definitionRange == null) ? 0 : this.definitionRange.hashCode());
+ }
+}