Skip to content

feat(code-action): add quote/unquote refactoring for attribute names#756

Merged
takeokunn merged 6 commits into
mainfrom
feature/add-code-action-1
Jan 4, 2026
Merged

feat(code-action): add quote/unquote refactoring for attribute names#756
takeokunn merged 6 commits into
mainfrom
feature/add-code-action-1

Conversation

@takeokunn

Copy link
Copy Markdown
Collaborator

Summary

Add refactoring code actions to quote/unquote attribute names in attribute sets.

  • { foo = 1; } → Offer "Quote attribute name" → { "foo" = 1; }
  • { "bar" = 1; } → Offer "Unquote attribute name" → { bar = 1; }

This is part of the code actions initiative (#466, #755).

Changes

File Description
nixd/lib/Controller/CodeAction.cpp Add quote/unquote refactoring logic
nixd/lib/Controller/LifeTime.cpp Register refactor.rewrite capability
nixd/lspserver/include/lspserver/Protocol.h Add REFACTOR_REWRITE_KIND constant
nixd/lspserver/src/Protocol.cpp Define REFACTOR_REWRITE_KIND
nixd/tools/nixd/test/code-action/*.md 11 test files for various cases
nixd/tools/nixd/test/initialize.md Update capability expectations

Behavior

Quote action offered when:

  • Cursor is on an unquoted attribute name (foo)
  • Parent is an attribute set (not a let binding)

Unquote action offered when:

  • Cursor is on a quoted static attribute name ("foo")
  • The string is a valid Nix identifier (starts with letter/underscore, contains only valid chars)
  • The string is not a Nix keyword (if, then, else, etc.)

Explicitly excluded:

  • Let bindings (let foo = 1; in foo) - no actions offered
  • Invalid identifiers ("123", "foo.bar") - no unquote action
  • Keywords ("true", "if") - no unquote action

Test Plan

  • All existing tests pass
  • New lit tests for each code action scenario (11 test files)
  • Manual testing in editor
CleanShot 2026-01-04 at 14 39 08@2x CleanShot 2026-01-04 at 14 38 43@2x

Related

- Implement quote/unquote attribute name refactoring code actions
- Add isValidNixIdentifier helper to validate unquoted names
- Register "refactor.rewrite" kind in code action provider and protocol
- Update tests to reflect new code action kinds
- Add tests for quoting and unquoting attribute names in various cases
- Cover keywords, let bindings, empty strings, hyphens, invalid chars, digits, keywords, quote chars, and underscores
- Update initialize.md and semantic-tokens/initialize.md to reference new code action tests
- Add "refactor.rewrite" to codeActionKinds in initialize.md tests
- Ensure semantic-tokens/initialize.md reflects new supported code action kind
@takeokunn takeokunn requested a review from inclyc as a code owner January 4, 2026 05:42
Comment thread nixd/lib/Controller/CodeAction.cpp Outdated
if (!AttrNameNode)
return;

// Only offer quote/unquote for attribute sets, not for let bindings.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It is OK to offer quote/unquote for let-bindings?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

b7bdf80 00780a5
You're right, it's fine. Nix syntax is identical for both contexts. Removed the restriction and added tests.

@inclyc inclyc added enhancement New feature or request nixd:controller labels Jan 4, 2026
- Prevent quote/unquote code actions from being offered on let bindings
- Add tests to verify quote/unquote are not available for let bindings
- Add tests to ensure quote/unquote still work for attribute sets
…d or keyword let bindings

- Add test for let bindings with invalid identifiers (e.g., starting with digit)
- Add test for let bindings using keywords (e.g., "true")
- Verify code action result is empty in both cases
@takeokunn takeokunn merged commit 3335d12 into main Jan 4, 2026
19 checks passed
@takeokunn takeokunn deleted the feature/add-code-action-1 branch January 4, 2026 09:39
takeokunn added a commit that referenced this pull request Jan 4, 2026
…757)

## Summary

Adds a new "Flatten nested attribute set" code action that transforms
nested attribute sets into dot notation:

```nix
# Before
{ foo = { bar = 1; }; }

# After
{ foo.bar = 1; }
```

This follows the existing pattern established in #756 for code actions.

## Changes

| File | Description |

|-----------------------------------------------------------|------------------------------------------------------------------------------------------|
| nixd/lib/Controller/CodeAction.cpp | Add getFlattenableBinds() helper
and addFlattenAttrsAction() for the flatten refactoring |
| nixd/tools/nixd/test/code-action/flatten-attrs.md | Basic test for
simple nested attribute flattening |
| nixd/tools/nixd/test/code-action/flatten-attrs-multi.md | Test for
nested sets with multiple bindings |
| nixd/tools/nixd/test/code-action/flatten-attrs-deep.md | Test for
deeply nested attribute paths |
| nixd/tools/nixd/test/code-action/flatten-attrs-rec.md | Test that rec
sets are excluded |
| nixd/tools/nixd/test/code-action/flatten-attrs-inherit.md | Test that
sets with inherit are excluded |
| nixd/tools/nixd/test/code-action/flatten-attrs-dynamic.md | Test that
dynamic ${} names are excluded |
| nixd/tools/nixd/test/code-action/flatten-attrs-empty.md | Test that
empty nested sets are excluded |

## Behavior

Flatten action offered when:

- Cursor is on a binding whose value is an attribute set
- All attribute names (outer and inner) are static identifiers
- The nested set contains at least one binding

## Explicitly excluded:

- Recursive attribute sets (rec { ... }): Flattening would change
semantics
- Inherited bindings (inherit x;): Cannot be represented in dot notation
- Dynamic attribute names (${expr}): Cannot be statically flattened
- Empty nested sets ({ foo = {}; }): No meaningful transformation

## Test Plan

- Added 7 lit tests covering positive and negative cases
- Tests verify the action is offered only when appropriate
- Tests verify correct transformation output for various input shapes
- All existing tests pass

<img width="954" height="424" alt="CleanShot 2026-01-04 at 20 12 35@2x"
src="https://github.com/user-attachments/assets/e0ccbeb2-705a-44dd-8e76-0eaa06f65b48"
/>
<img width="846" height="412" alt="CleanShot 2026-01-04 at 20 14 09@2x"
src="https://github.com/user-attachments/assets/0ce5109c-e640-4c46-b642-3225ae574561"
/>


## Related

- Issue: #466
- Draft PR: #755 (this implements Split 2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request nixd:controller

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants