Skip to content

feat: LSP hover with resolved CFN properties and template locations - #1669

Merged
megha-narayanan merged 7 commits into
aws:feat/cdk-lspfrom
megha-narayanan:feat/explorer-lsp-hover
Jul 6, 2026
Merged

feat: LSP hover with resolved CFN properties and template locations#1669
megha-narayanan merged 7 commits into
aws:feat/cdk-lspfrom
megha-narayanan:feat/explorer-lsp-hover

Conversation

@megha-narayanan

@megha-narayanan megha-narayanan commented Jun 24, 2026

Copy link
Copy Markdown
Contributor
  • Adds LSP hover on construct creation lines showing the resolved CloudFormation logical ID, resource type, construct path, and top-level CFN properties with values
  • Each property value links to its exact line in the synthesized template; the logical ID header links to the resource block
  • Multi-resource constructs show auxiliaries inline (≤5 listed individually, >5 collapsed to a type histogram like "6× Subnet, 3× RouteTable")
  • Extends template-ranges.ts with resolveResourceRanges / indexTemplateRanges to resolve per-property character ranges in a single parse
  • Exports cfnProperties from the construct tree builder so the hover has access to resolved property values

example hover:
Screenshot 2026-06-24 at 12 12 52 PM

Screenshot 2026-06-24 at 12 13 44 PM

Fixes #

Checklist

  • This change contains a major version upgrade for a dependency and I confirm all breaking changes are addressed
    • Release notes for the new version:

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

@github-actions github-actions Bot added the p2 label Jun 24, 2026
@aws-cdk-automation
aws-cdk-automation requested a review from a team June 24, 2026 14:39
@megha-narayanan megha-narayanan changed the title feat: LSP hover with resolved CFN properties and template locs feat: LSP hover with resolved CFN properties and template locations Jun 24, 2026
@megha-narayanan
megha-narayanan marked this pull request as ready for review June 24, 2026 16:15
megha-narayanan and others added 5 commits June 30, 2026 10:45
…te links

Hovering a construct's creation line surfaces its synthesized CloudFormation property values, with each value linking to its location in the template. Adds the cfnProperties attribute to the construct tree, resolveResourceRanges for per-property template ranges, a pure hover content builder, and the onHover server wiring.
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
First-letter-upper only capitalized the first character, so properties whose CloudFormation name has an uppercase acronym run (SSESpecification, EnableECSManagedTags, TemplateURL) did not link and rendered as plain text. The L1 camelCase name and the template PascalCase name differ only in letter case, so key both sides lower-cased. Removes pascalCase.

@ShadowCat567 ShadowCat567 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall, have some comments about variable names and UI details

* in which case values render without links.
*/
export interface HoverLinks {
readonly blocks: Record<string, LinkTarget>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does blocks refer to in this context? Can it get a more descriptive name?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocks is the name that was used for the chunk of the template associated with a resource from the template range resolver. Renamed to resourceLocations which better describes what these are, a map from construct path to the location of that resource's definition in the synthesized template. Pushed.

}

/** Group auxiliary resources by short CFN type, most common first, e.g. "8× Subnet". */
function histogram(nodes: readonly ResourceConstruct[]): string {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this look like in the hover UI?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

realized I needed to include the case where the default is same depth as everything else, so just pushed that bit of code, but it looks like this:

Image

@ShadowCat567 ShadowCat567 Jul 6, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have a test for that? (default same depth as the other constructs)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added

if (nodes.length === 0) {
return undefined;
}
const value = selection === undefined

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can selection instead be called defaultChild? Since it seems like the logic here revolves around whether the construct we are hovering over has a default child or not

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not TOTALLY the default child, selectPrimary picks the uniquely shallowest resource on the line, which is usually the default child but could be any uniquely-shallow resource. Renamed to primaryResource, which feels more descriptive than selection without implying the default child relationship specifically. Looks ok?

const properties = primary.cfnProperties ?? {};
const keys = Object.keys(properties);
const lines = keys.slice(0, MAX_PROPERTIES).map((key) => {
const value = `\`${renderValue(properties[key])}\``;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For nested properties like S3Bucket and S3Key in Code for Lambda functions, any particular reason why you render it like:

code: { s3bucket, s3key }

instead of:

code: {
   s3bucket: <bucket name>
   s3key: <key zip file>
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mostly just to save hover space, nested CFN values are usually intrinsics or deeply nested objects that make the tooltip very long. Showing key names felt like a good middle ground, and the property value is clickable, so it links to the exact line in the template where the full expansion is visible. happy to expand shallow objects if feels too terse, lmk.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have the link to get the information, then this should be fine

…imary selection for VPC

- Rename `blocks` → `resourceLocations` for clarity
- Rename `selection` → `primaryResource` to avoid confusion with defaultChild
- Fix selectPrimary to recognize the default child as primary when peers
  exist at the same depth (e.g. VPC + IGW + VPCGW all at depth 4)

/**
* Builds the hover for the resource(s) created on a line, given the primary
* primaryResource. When one resource is the construct's primary (default) child, its

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this comment is confusing if the primary resource is not actually the default child (default child of construct), can you change the wording of this comment and comments like this to describe what you mean by primary child and probably remove the reference to default child if primary child != default child

const properties = primary.cfnProperties ?? {};
const keys = Object.keys(properties);
const lines = keys.slice(0, MAX_PROPERTIES).map((key) => {
const value = `\`${renderValue(properties[key])}\``;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have the link to get the information, then this should be fine

@megha-narayanan
megha-narayanan merged commit 9aa9168 into aws:feat/cdk-lsp Jul 6, 2026
7 of 8 checks passed
@megha-narayanan
megha-narayanan deleted the feat/explorer-lsp-hover branch July 6, 2026 20:15
megha-narayanan added a commit that referenced this pull request Jul 8, 2026
…#1711)

startServer now requires toolkitBindingsFactory (added in #1634/#1669),
but the cdk lsp command still called it with only { readable, writable
}, breaking the build. Expose startLspServer() from cdk-explorer (wires
the Toolkit bindings and starts the server) and call it from the CLI.
main.ts becomes that exported function, making cdk lsp the single LSP
entrypoint.

Fixes #

### Checklist
- [ ] This change contains a major version upgrade for a dependency and
I confirm all breaking changes are addressed
  - Release notes for the new version:

---
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache-2.0 license
megha-narayanan added a commit that referenced this pull request Jul 8, 2026
Brings merged LSP work (#1697, #1669, #1670, #1711, #1715) into the explorer
branch. CLI command map now carries both 'cdk explore' and 'cdk lsp';
regenerated the user-input files from cli-config.ts. express remains a bundled
dependency of the explorer web server.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants