diff --git a/README.md b/README.md index bbcb34456..0d3c88371 100644 --- a/README.md +++ b/README.md @@ -6,87 +6,26 @@

-# WIP Note 🚧 - -`nixd-next` is still WIP! Please see https://github.com/nix-community/nixd/issues/283 for the migration plan. - -[Released versions](https://github.com/nix-community/nixd/releases/) contain stable nixd code. If you encountered any problem with this nightly version, please use to our released version in nixpkgs. - -The following description is suitable for stable releases, but outdated for `nixd-next` (i.e. this version). - ## About -This is a Nix language server that directly uses (i.e., is linked with) the official Nix library (https://github.com/NixOS/nix). +This is a feature-rich nix language server interoperating with C++ nix. Some notable features provided by linking with the Nix library include: - Nixpkgs option support, for all option system (NixOS/home-manager/flake-parts). -- Diagnostics and evaluation that produce identical results as the real Nix command. +- Nixpkgs package complete, lazily evaluated. - Shared eval caches (flake, file) with your system's Nix. -- Native support for cross-file analysis (goto definition to locations in nixpkgs). -- Precise Nix language support. We do not maintain "yet another parser & evaluator". -- Support for built-ins, including Nix plugins. - - -## Features Preview - - -
Home-manager options auto-completion & goto declaration - -![options-example](https://github.com/nix-community/nixd/assets/36667224/43e00a8e-c2e6-4598-b188-f5e95d708256) - -See how to configure option system: https://github.com/nix-community/nixd/blob/main/nixd/docs/user-guide.md#options - -
- -
Write a package using nixd - -![write-package](https://github.com/nix-community/nixd/assets/36667224/a974c60e-096e-4964-a5d4-fc926963d577) - -
- -
Native cross-file analysis - -![package](nixd/docs/images/3e4fc99c-7a20-42be-a337-d1746239c731.png) - -We support goto-definition on nix derivations! -Just `Ctrl + click` to see where is a package defined. - -![goto-def-pkg-2](https://github.com/nix-community/nixd/assets/36667224/726c711f-cf75-48f4-9f3b-40dd1b9f53be) - -And also for nix lambda: - -![lambda-location](https://github.com/nix-community/nixd/assets/36667224/5792da0b-8152-4e51-9b0e-0387b045eeb5) - -See how to configure the evaluator for cross-file analysis: https://github.com/nix-community/nixd/blob/main/nixd/docs/user-guide.md#evaluation - -
- -
Handle evaluations exactly same as nix evaluator - -![infinte-recursion](nixd/docs/images/9ed5e08a-e439-4b09-ba78-d83dc0a8a03f.png) - -
- -
Support *all* builtins - -![eval-builtin-json](nixd/docs/images/59655838-36a8-4145-9717-f2009e0efef9.png) - -And diagnostic: - -![eval-builtin-diagnostic](nixd/docs/images/f6e10994-41e4-4a03-84a2-ef275fb402fd.png) +- Support for cross-file analysis (goto definition to locations in nixpkgs). -
## Get Started You can *try nixd without installation*. -We have tested some working & reproducible [editor environments](/nixd/docs/editors/editors.md) and example [configurations & workspaces](/nixd/docs/examples). +We have tested some working & reproducible [editor environments](/nixd/docs/editors/editors.md). ## Resources - [Editor Setup](nixd/docs/editor-setup.md) - [User Guide](nixd/docs/user-guide.md) -- [Configuration Examples](nixd/docs/examples) - [Developers' Manual](nixd/docs/dev.md) (internal design, contributing): - Project matrix room: https://matrix.to/#/#nixd:matrix.org diff --git a/nixd/docs/dev.md b/nixd/docs/dev.md index 50dc8fbd7..97514dc50 100644 --- a/nixd/docs/dev.md +++ b/nixd/docs/dev.md @@ -44,43 +44,6 @@ digraph { └─────────────────┘ ``` -#### How does language information being collected? - -Nix expressions are evaluated using the `nix::Expr::eval` virtual method, with dynamic name bindings and lookups performed in `Env`s. -The resulting evaluation is stored in `Value`s. - -Link: https://github.com/NixOS/nix/blob/61ddfa154bcfa522819781d23e40e984f38dfdeb/src/libexpr/nixexpr.hh#L161 - -It is mandatory to preserve the information for language services, rather than discarding it away (what nix itself do). -The codebase achieves this by inheriting all Nix AST node classes and overriding the virtual `eval` method to call the super-class `eval` and then invoke a custom callback. - -##### Why does nix evaluator see your data structure, instead of parsing file by itself? - -Nix parsing and evaluation are cached, and the caching interface is public. -When you open a file in your workspace, we first parse it to obtain normal Nix abstract syntax trees (ASTs). -We then recursively rewrite all AST nodes with our own data structure, creating a new tree. -Finally, we call `cacheFile` in Nix to inject our own data structure into the evaluator. - - -#### How does cross-file analysis work? - -For example, you write a NixOS module: - -```nix -{ config, lib, pkgs, ... }: - -{ - # Some stuff -} -``` - -The file itself is a valid Nix expression, specifically an `ExprLambda`. However, it is important to know the arguments that are passed when invoking lambdas. This is necessary when writing NixOS configurations, as it helps to determine what can be used in `pkgs` and the library functions in `lib`. - -Here's how it works: - -When you open the file, `nixd` will parse it for you, and rewrite and inject it into the Nix evaluator (as mentioned earlier). Then, the top-level evaluation process begins, and the lambdas are evaluated. Our callback function is invoked, and the necessary information is collected in the callbacks. - - ### Testing This project is tested by "unit tests" and "regression tests". diff --git a/nixd/docs/examples/README.md b/nixd/docs/examples/README.md deleted file mode 100644 index 06111bd7c..000000000 --- a/nixd/docs/examples/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Configuration Examples - -These are examples of `.nixd.json` of some scenarios. - -**Note: .nixd.json must be the working directory of nixd process.** - -i.e. - -For vim users: - -do not: - -``` -vi nixd/nixd/docs/examples/options/nixos/module.nix -``` - -instead: - -``` -cd nixd/nixd/docs/examples/options/nixos -vi module.nix -``` - -For vscode users: please open each folder as your "workspace". diff --git a/nixd/docs/examples/flake/.nixd.json b/nixd/docs/examples/flake/.nixd.json deleted file mode 100644 index 66d6778fe..000000000 --- a/nixd/docs/examples/flake/.nixd.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "eval": { - "target": { - "args": [ - "-f", - "default.nix" - ], - "installable": "foo" - } - } -} diff --git a/nixd/docs/examples/flake/default.nix b/nixd/docs/examples/flake/default.nix deleted file mode 100644 index 2cccff28d..000000000 --- a/nixd/docs/examples/flake/default.nix +++ /dev/null @@ -1,10 +0,0 @@ -(import - ( - let lock = builtins.fromJSON (builtins.readFile ./flake.lock); in - fetchTarball { - url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; - sha256 = lock.nodes.flake-compat.locked.narHash; - } - ) - { src = ./.; } -).defaultNix diff --git a/nixd/docs/examples/flake/flake.lock b/nixd/docs/examples/flake/flake.lock deleted file mode 100644 index f7c5d9790..000000000 --- a/nixd/docs/examples/flake/flake.lock +++ /dev/null @@ -1,44 +0,0 @@ -{ - "nodes": { - "flake-compat": { - "flake": false, - "locked": { - "lastModified": 1687265871, - "narHash": "sha256-P8AOiQk/XN8/ia4289hDHlTfWB70cRQ5pc9GRfmEdpc=", - "owner": "inclyc", - "repo": "flake-compat", - "rev": "70e56389c58bbd300d11778913b255477ebbae22", - "type": "github" - }, - "original": { - "owner": "inclyc", - "repo": "flake-compat", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1686960236, - "narHash": "sha256-AYCC9rXNLpUWzD9hm+askOfpliLEC9kwAo7ITJc4HIw=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "04af42f3b31dba0ef742d254456dc4c14eedac86", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "flake-compat": "flake-compat", - "nixpkgs": "nixpkgs" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/nixd/docs/examples/flake/flake.nix b/nixd/docs/examples/flake/flake.nix deleted file mode 100644 index b345fc62e..000000000 --- a/nixd/docs/examples/flake/flake.nix +++ /dev/null @@ -1,15 +0,0 @@ -{ - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - # use this fork for git repository - flake-compat = { - url = "github:inclyc/flake-compat"; - flake = false; - }; - }; - - outputs = { nixpkgs, ... }: { - foo = nixpkgs.lib.mkIf true; - # ^ goto definition works here (and more)! - }; -} diff --git a/nixd/docs/examples/options/home-manager/.nixd.json b/nixd/docs/examples/options/home-manager/.nixd.json deleted file mode 100644 index 2bd4adef8..000000000 --- a/nixd/docs/examples/options/home-manager/.nixd.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "options": { - "enable": true, - "target": { - "args": [], - "installable": ".#foo.options" - } - } -} diff --git a/nixd/docs/examples/options/home-manager/flake.lock b/nixd/docs/examples/options/home-manager/flake.lock deleted file mode 100644 index 1eb0cb801..000000000 --- a/nixd/docs/examples/options/home-manager/flake.lock +++ /dev/null @@ -1,48 +0,0 @@ -{ - "nodes": { - "home-manager": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1687301540, - "narHash": "sha256-vFbCrE9WlOSVpyAT5VNR3bqMB7W7sDzMNDcO6JqtmBw=", - "owner": "nix-community", - "repo": "home-manager", - "rev": "9a76fb9a852fdf9edd3b0aabc119efa1d618f969", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "home-manager", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1686960236, - "narHash": "sha256-AYCC9rXNLpUWzD9hm+askOfpliLEC9kwAo7ITJc4HIw=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "04af42f3b31dba0ef742d254456dc4c14eedac86", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "home-manager": "home-manager", - "nixpkgs": "nixpkgs" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/nixd/docs/examples/options/home-manager/flake.nix b/nixd/docs/examples/options/home-manager/flake.nix deleted file mode 100644 index 3fd857c26..000000000 --- a/nixd/docs/examples/options/home-manager/flake.nix +++ /dev/null @@ -1,34 +0,0 @@ -{ - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - home-manager = { - url = "github:nix-community/home-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - - outputs = { home-manager, nixpkgs, ... }: rec { - system = builtins.currentSystem; - foo = home-manager.lib.homeManagerConfiguration { - pkgs = nixpkgs.legacyPackages."${system}"; - modules = [ - ({ config, ... }: { - - # <--- Can you see completion lists here? - - # try: - -# home.| -# xdg.| -# nixpkgs.| - - - home.stateVersion = "22.11"; - home.username = "lyc"; - home.homeDirectory = "/home/lyc"; - - }) - ]; - }; - }; -} diff --git a/nixd/docs/examples/options/nixos/.nixd.json b/nixd/docs/examples/options/nixos/.nixd.json deleted file mode 100644 index 2bb568b66..000000000 --- a/nixd/docs/examples/options/nixos/.nixd.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "options": { - "enable": true, - "target": { - "args": [ - "--expr", - "(import { configuration = ./module.nix; }).options" - ], - "installable": "" - } - } -} diff --git a/nixd/docs/examples/options/nixos/module.nix b/nixd/docs/examples/options/nixos/module.nix deleted file mode 100644 index 32fd34ad3..000000000 --- a/nixd/docs/examples/options/nixos/module.nix +++ /dev/null @@ -1,20 +0,0 @@ -{ config, pkgs, ... }: - -{ - - # <-- Can you see completion list here? - - fileSystems."/" = - { - device = "/dev/disk/by-label/nixos"; - fsType = "btrfs"; - }; - - fileSystems."/boot" = - { - device = "/dev/disk/by-label/EFI"; - fsType = "vfat"; - }; - - boot.loader.grub.devices = [ "/foo" ]; -} diff --git a/nixd/docs/examples/package/.nixd.json b/nixd/docs/examples/package/.nixd.json deleted file mode 100644 index d11b5c48e..000000000 --- a/nixd/docs/examples/package/.nixd.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "eval": { - "target": { - "args": [ - "--expr", - "with import { }; callPackage ./package.nix { }" - ] - } - } -} diff --git a/nixd/docs/examples/package/package.nix b/nixd/docs/examples/package/package.nix deleted file mode 100644 index fbf777964..000000000 --- a/nixd/docs/examples/package/package.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ lib -, stdenv -}: - -## Type: -# stdenv.| you can see the completion list -# ^<-- this is your cursor -stdenv -# ^ -# . diff --git a/nixd/docs/user-guide.md b/nixd/docs/user-guide.md index 17cb37208..835bdec5a 100644 --- a/nixd/docs/user-guide.md +++ b/nixd/docs/user-guide.md @@ -1,9 +1,8 @@ ## User Guide ### Installation -At this time (2023-06-14), nixd is under rapid development and it is highly recommended to install nixd from source. -Package `nixd` can be found in [nixpkgs](https://github.com/NixOS/nixpkgs), there are different ways to install nixd, pick your favourite: +Package `nixd` can be found in [nixpkgs](https://github.com/NixOS/nixpkgs).
NixOS Configuration @@ -43,7 +42,7 @@ nix profile install github:nixos/nixpkgs#nixd
-And our flake.nix provides a package named `nixd`, and an overlay to nixpkgs that add the `nixd` package. +And our flake.nix provides a package named `nixd` with "unstable" experience. Note that please do NOT override nixpkgs revision for nixd inputs. The source code have tested on specific version on NixOS/nix, which may not work at your version. @@ -65,186 +64,63 @@ nix build -L .# ### Configuration -- [Configuration Examples](/nixd/docs/examples) - We support LSP standard `workspace/configuration` for server configurations. -Configuration overview: - -```jsonc -{ - // The evaluation section, provide auto completion for dynamic bindings. - "eval": { - "target": { - // Accept args as "nix eval" - "args": [], - // "nix eval" - "installable": "" - }, - // Extra depth for evaluation - "depth": 0, - // The number of workers for evaluation task. - "workers": 3 - }, - "formatting": { - // Which command you would like to do formatting - "command": "nixpkgs-fmt" - }, - // Tell the language server your desired option set, for completion - // This is lazily evaluated. - "options": { - // Enable option completion task. - // If you are writing a package, disable this - "enable": true, - "target": { - // Accept args as "nix eval" - "args": [], - // "nix eval" - "installable": "" - } - } -} -``` +### Default configuration -Note: we support a configuration file named `.nixd.json` at your workspace directory. -This is a feature requested by nvim users. +Most important part of package/options features is the path of "nixpkgs". +By default, this is search via standard nix search path. +That is, find nixpkgs via ``. -Typically, you can write a nix file, and evaluate the result into `.nixd.json`, because json does not support comments: +For nix-channels users: nixos option & package features shall work out of box, without any extra effort. +For nix-flake users: (suggestion) you can set $NIX_PATH env to your flake input. e.g. ```nix -# .nixd.nix +{ inputs, ... }: { - eval = { - # Example target for writing a package. - target = { - args = [ "--expr" "with import { }; callPackage ./somePackage.nix { }" ]; - installable = ""; - }; - # Force thunks - depth = 10; - }; - formatting.command = "nixpkgs-fmt"; - options = { - enable = true; - target = { - args = [ ]; - # Example installable for flake-parts, nixos, and home-manager - - # flake-parts - installable = "/flakeref#debug.options"; - - # nixOS configuration - installable = "/flakeref#nixosConfigurations..options"; - - # home-manager configuration - installable = "/flakeref#homeConfigurations..options"; - }; - }; -} - -``` - -```console -nix eval --json --file .nixd.nix > .nixd.json -``` - -`.nixd.json`, the configuration of `nixd`, supports [json schema](https://json-schema.org/). -So if your editor supports -[LSP](https://microsoft.github.io/language-server-protocol/implementors/servers/), -you can get completions, diagnostics and more[^json-schema]: - -![completion](https://github.com/nix-community/nixd/assets/32936898/013367c6-63d8-4bba-9057-c5701c2f36c1) - -![diagnostic](https://github.com/nix-community/nixd/assets/32936898/d285eb69-f023-4573-a8e5-8d5501ae3d16) - -[^json-schema]: These pictures were captured in [neovim](https://neovim.org/) -with the plugin [coc-json](https://github.com/neoclide/coc-json). - -#### Evaluation - -##### Target - -Unlike any other nix lsp implementation, you may need to explicitly specify a `installable` in your workspace. -The language server will consider the `installable` is your desired "object file", and it is the cross-file analysis pivot. -For example, here is a nix pacakge: - -```nix -{ stdenv -, lib -}: - -stdenv.mkDerivation { - pname = "..."; - version = "..."; + # NixOS configuration. + nix.nixPath = [ "nixpkgs=${inputs.nixpkgs}" ]; } ``` -From a language perspective, the lambda expression only accepts one argument and returns an AttrSet, without any special properties of a "package". -So how do we know what argument it will accept? - -For nixd, you can write a project-specific *installable* that will be evaluated. -e.g. - -```sh -nix eval --expr "with import { }; callPackage ./some-package.nix { }" -``` - -We accept the same argument as `nix eval`, and perform evaluation for language analysis. - - -```jsonc -{ - "eval": { - "target": { - // Same as: - // nix eval --expr "..." - "args": [ - "--expr", - "with import { }; callPackage ./some-package.nix { } " - ], - // AttrPath - "installable": "" - } - } -} -``` +### The configuration -This is much similar to `compile_commands.json` in C/C++ world. - -Here is the demo video that I used the above installable in my workspace: - -![write-package](https://github.com/nix-community/nixd/assets/36667224/a974c60e-096e-4964-a5d4-fc926963d577) - -##### Depth - -Nix evaluator will be lazily peform evaluation on your specified task[^nix-evaluation-peformance]. - -[^nix-evaluation-peformance]: https://wiki.nixos.org/wiki/Nix_Evaluation_Performance - -As for language service, we have an custom extension to nix evaluator that allows you to force thunks being evaluated in a desired depth. +Configuration overview: ```jsonc { - "eval": { - "depth": 5 - } -} -``` - -##### Workers - -Nixd evals your project concurrently. -You can specify how many workers will be used for language tasks, e.g. parsing & evaluation. + "nixpkgs": { + // For flake. + // "expr": "import (builtins.getFlake \"/home/lyc/workspace/CS/OS/NixOS/flakes\").inputs.nixpkgs { } " + + // This expression will be interpreted as "nixpkgs" toplevel + // Nixd provides package, lib completion/information from it. + /// + // Resouces Usage: Entries are lazily evaluated, entire nixpkgs takes 200~300MB for just "names". + /// Package documentation, versions, are evaluated by-need. + "expr": "import { }" + }, + "formatting": { + // Which command you would like to do formatting + "command": [ "nixpkgs-fmt" ] + }, + // Tell the language server your desired option set, for completion + // This is lazily evaluated. + "options": { // Map of eval information + // If this is ommited, default search path () will be used. + "nixos": { // This name "nixos" could be arbitary. + // The expression to eval, intepret it as option declarations. + "expr": "(builtins.getFlake \"/home/lyc/flakes\").nixosConfigurations.adrastea.options" + }, -```jsonc -{ - "eval": { - "workers": 5 + // By default there is no home-manager options completion, thus you can add this entry. + "home-manager": { + "expr": "(builtins.getFlake \"/home/lyc/flakes\").homeConfigurations.\"lyc@adrastea\".options" } + } } ``` -The default value is `std::thread::hardware_concurrency()`. #### Format @@ -254,7 +130,7 @@ To configure which command will be used for formatting, you can change the "form { "formatting": { // The external command to be invoked for formatting - "command": "" + "command": [ "some-command" ] } } ``` @@ -280,53 +156,21 @@ In our option system, you need to specify which option set you'd like to use. ```jsonc { - "options": { - // Disable it if you are not writting modules. - "enable": true, - "target": { - "args": [], - // Example of NixOS options. - "installable": "#nixosConfigurations..options" + // Tell the language server your desired option set, for completion + // This is lazily evaluated. + "options": { // Map of eval information + // If this is ommited, default search path () will be used. + "nixos": { // This name "nixos" could be arbitary. + // The expression to eval, intepret it as option declarations. + "expr": "(builtins.getFlake \"/home/lyc/flakes\").nixosConfigurations.adrastea.options" + }, + + // By default there is no home-manager options completion, thus you can add this entry. + "home-manager": { + "expr": "(builtins.getFlake \"/home/lyc/flakes\").homeConfigurations.\"lyc@adrastea\".options" } } } ``` -
Options auto completion - -**Home-manager Options** - -![hm-docs](https://github.com/nix-community/nixd/assets/36667224/38039c75-379f-463f-aac8-e33ff71eea38) - -**NixOS Options** - -![nixos-option-docs](https://github.com/nix-community/nixd/assets/36667224/ca4ed4dc-469f-4c2b-9dea-7beab9a417e8) - - -
- - -### FAQ - -#### How to use nixd in my *flake*? - -The *eval* subsystems requires flakes evaluating **in-place** to get language callbacks. -The *options* subsystem does not need to eval flakes in-place. - -However, nix flakes are now hardcoded being evaluated in your store, e.g. `/nix/store`. -That is, we cannot hack caches by injecting our own data structre. -So basically language callbacks (i.e. dynamic bindings & values) are not available. - -Actually we are waiting for [Source tree abstraction (by edolstra)](https://github.com/NixOS/nix/pull/6530), to handle this issue. - -If you would like to use `nixd` in your personal flake, you can use `flake-compat` to turn your project in a "non-flake" installable. - -Note that `flake-compat` by edolstra will fetch a git project in nix store, that will break everything just as the same case as normal flakes (i.e. not being evaluated in-pllace). -Here we have a fork of `flake-compat`, won't fetch git repositories at `github:inclyc/flake-compat`. - -So tldr, to use `nixd` in your flake project, you have to: - -1. Turn your project into a legacy one, by using `flake-compat` -2. Use `inclyc/flake-compat` which will not fetch git repository in nix store -We have a working example [here](/nixd/docs/examples/flake/)