Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions nixd/lib/Controller/CodeAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "CodeActions/AttrName.h"
#include "CodeActions/ExtractToFile.h"
#include "CodeActions/FlattenAttrs.h"
#include "CodeActions/InheritToBinding.h"
#include "CodeActions/JsonToNix.h"
#include "CodeActions/NoogleDoc.h"
#include "CodeActions/PackAttrs.h"
Expand Down Expand Up @@ -72,6 +73,8 @@ void Controller::onCodeAction(const lspserver::CodeActionParams &Params,
addFlattenAttrsAction(*N, *TU->parentMap(), FileURI, TU->src(),
Actions);
addPackAttrsAction(*N, *TU->parentMap(), FileURI, TU->src(), Actions);
addInheritToBindingAction(*N, *TU->parentMap(), FileURI, TU->src(),
Actions);
addNoogleDocAction(*N, *TU->parentMap(), Actions);

// Extract to file requires variable lookup analysis
Expand Down
66 changes: 66 additions & 0 deletions nixd/lib/Controller/CodeActions/InheritToBinding.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/// \file
/// \brief Implementation of inherit to explicit binding code action.

#include "InheritToBinding.h"
#include "Utils.h"

#include "../Convert.h"

#include <nixf/Basic/Nodes/Attrs.h>

#include <sstream>

namespace nixd {

void addInheritToBindingAction(const nixf::Node &N,
const nixf::ParentMapAnalysis &PM,
const std::string &FileURI, llvm::StringRef Src,
std::vector<lspserver::CodeAction> &Actions) {
// Find if we're inside an Inherit node
const nixf::Node *InheritNode = PM.upTo(N, nixf::Node::NK_Inherit);
if (!InheritNode)
return;

const auto &Inherit = static_cast<const nixf::Inherit &>(*InheritNode);

// Get the list of names in the inherit statement
const auto &Names = Inherit.names();

// Only handle single-name inherit statements
// Multi-name inherits would require more complex handling
if (Names.size() != 1)
return;

const auto &Name = Names[0];

// Defensive null check
if (!Name)
return;

// Only handle static names (not interpolated)
if (!Name->isStatic())
return;

const std::string &AttrName = Name->staticName();

// Build the replacement text
std::ostringstream NewText;
NewText << AttrName << " = ";

if (Inherit.expr()) {
// inherit (expr) name; -> name = expr.name;
NewText << Inherit.expr()->src(Src) << "." << AttrName;
} else {
// inherit name; -> name = name;
NewText << AttrName;
}
NewText << ";";

// Create the code action
Actions.emplace_back(createSingleEditAction(
"Convert to explicit binding",
lspserver::CodeAction::REFACTOR_REWRITE_KIND, FileURI,
toLSPRange(Src, Inherit.range()), NewText.str()));
}

} // namespace nixd
37 changes: 37 additions & 0 deletions nixd/lib/Controller/CodeActions/InheritToBinding.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/// \file
/// \brief Code action for converting inherit to explicit binding.
///
/// Offers to convert inherit statements to explicit bindings:
/// - { inherit x; } -> { x = x; }
/// - { inherit (b) a; } -> { a = b.a; }

#pragma once

#include <lspserver/Protocol.h>

#include <nixf/Sema/ParentMap.h>

#include <llvm/ADT/StringRef.h>

#include <string>
#include <vector>

namespace nixf {
class Node;
} // namespace nixf

namespace nixd {

/// \brief Add code action to convert inherit to explicit binding.
///
/// This action converts inherit statements to explicit bindings:
/// - Simple inherit: { inherit x; } -> { x = x; }
/// - Inherit from expression: { inherit (b) a; } -> { a = b.a; }
///
/// The action is only offered when the inherit has exactly one name.
void addInheritToBindingAction(const nixf::Node &N,
const nixf::ParentMapAnalysis &PM,
const std::string &FileURI, llvm::StringRef Src,
std::vector<lspserver::CodeAction> &Actions);

} // namespace nixd
1 change: 1 addition & 0 deletions nixd/lib/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ libnixd_lib = library(
'Controller/CodeActions/AttrName.cpp',
'Controller/CodeActions/ExtractToFile.cpp',
'Controller/CodeActions/FlattenAttrs.cpp',
'Controller/CodeActions/InheritToBinding.cpp',
'Controller/CodeActions/JsonToNix.cpp',
'Controller/CodeActions/NoogleDoc.cpp',
'Controller/CodeActions/PackAttrs.cpp',
Expand Down
68 changes: 68 additions & 0 deletions nixd/tools/nixd/test/code-action/inherit-to-binding/basic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# RUN: nixd --lit-test < %s | FileCheck %s

<-- initialize(0)

```json
{
"jsonrpc":"2.0",
"id":0,
"method":"initialize",
"params":{
"processId":123,
"rootPath":"",
"capabilities":{
},
"trace":"off"
}
}
```


<-- textDocument/didOpen

```nix file:///basic-inherit.nix
{ inherit x; }
```

<-- textDocument/codeAction(2)


```json
{
"jsonrpc":"2.0",
"id":2,
"method":"textDocument/codeAction",
"params":{
"textDocument":{
"uri":"file:///basic-inherit.nix"
},
"range":{
"start":{
"line": 0,
"character":10
},
"end":{
"line":0,
"character":11
}
},
"context":{
"diagnostics":[],
"triggerKind":2
}
}
}
```

```
CHECK: "id": 2,
CHECK-NEXT: "jsonrpc": "2.0",
CHECK-NEXT: "result": [
CHECK: "newText": "x = x;",
CHECK: "kind": "refactor.rewrite",
CHECK-NEXT: "title": "Convert to explicit binding"
```

```json
{"jsonrpc":"2.0","method":"exit"}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# RUN: nixd --lit-test < %s | FileCheck %s

<-- initialize(0)

```json
{
"jsonrpc":"2.0",
"id":0,
"method":"initialize",
"params":{
"processId":123,
"rootPath":"",
"capabilities":{
},
"trace":"off"
}
}
```


<-- textDocument/didOpen

```nix file:///inherit-complex-expr.nix
{ inherit (pkgs.lib) mkOption; }
```

<-- textDocument/codeAction(2)


```json
{
"jsonrpc":"2.0",
"id":2,
"method":"textDocument/codeAction",
"params":{
"textDocument":{
"uri":"file:///inherit-complex-expr.nix"
},
"range":{
"start":{
"line": 0,
"character":21
},
"end":{
"line":0,
"character":29
}
},
"context":{
"diagnostics":[],
"triggerKind":2
}
}
}
```

```
CHECK: "id": 2,
CHECK-NEXT: "jsonrpc": "2.0",
CHECK-NEXT: "result": [
CHECK: "newText": "mkOption = pkgs.lib.mkOption;",
CHECK: "kind": "refactor.rewrite",
CHECK-NEXT: "title": "Convert to explicit binding"
```

```json
{"jsonrpc":"2.0","method":"exit"}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# RUN: nixd --lit-test < %s | FileCheck %s

<-- initialize(0)

```json
{
"jsonrpc":"2.0",
"id":0,
"method":"initialize",
"params":{
"processId":123,
"rootPath":"",
"capabilities":{
},
"trace":"off"
}
}
```


<-- textDocument/didOpen

```nix file:///inherit-long-name.nix
{ inherit fooBar; }
```

<-- textDocument/codeAction(2)


```json
{
"jsonrpc":"2.0",
"id":2,
"method":"textDocument/codeAction",
"params":{
"textDocument":{
"uri":"file:///inherit-long-name.nix"
},
"range":{
"start":{
"line": 0,
"character":10
},
"end":{
"line":0,
"character":16
}
},
"context":{
"diagnostics":[],
"triggerKind":2
}
}
}
```

```
CHECK: "id": 2,
CHECK-NEXT: "jsonrpc": "2.0",
CHECK-NEXT: "result": [
CHECK: "newText": "fooBar = fooBar;",
CHECK: "kind": "refactor.rewrite",
CHECK-NEXT: "title": "Convert to explicit binding"
```

```json
{"jsonrpc":"2.0","method":"exit"}
```
Loading