Compare commits

...

No commits in common. "main" and "0.0.2" have entirely different histories.
main ... 0.0.2

27 changed files with 745 additions and 1939 deletions

19
.direnv/bin/nix-direnv-reload Executable file
View File

@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -e
if [[ ! -d "/home/lemarsu/prog/nix/neoflake" ]]; then
echo "Cannot find source directory; Did you move it?"
echo "(Looking for "/home/lemarsu/prog/nix/neoflake")"
echo 'Cannot force reload with this script - use "direnv reload" manually and then try again'
exit 1
fi
# rebuild the cache forcefully
_nix_direnv_force_reload=1 direnv exec "/home/lemarsu/prog/nix/neoflake" true
# Update the mtime for .envrc.
# This will cause direnv to reload again - but without re-building.
touch "/home/lemarsu/prog/nix/neoflake/.envrc"
# Also update the timestamp of whatever profile_rc we have.
# This makes sure that we know we are up to date.
touch -r "/home/lemarsu/prog/nix/neoflake/.envrc" "/home/lemarsu/prog/nix/neoflake/.direnv"/*.rc

View File

@ -1,146 +0,0 @@
# Changelog
All notable changes to this project will be documented in this file.
## [0.0.10] - 2024-06-09
### Features
- Runtime is now optional
- [**breaking**] `init.lua` configuration is now a `mkNeovimPkg`'s option
- Add option init config object to generate `init.lua`
### Miscellaneous Tasks
- Add alejandra to nix shell
## [0.0.9] - 2024-06-09
### Features
- Sloth command has now completion
- Add describe subcommand
- Introduce mkPluginsFromInputs function
### Bug Fixes
- Init/config are loaded to on-the-fly build plugins
- Plugins with init are now loaded as they should be
### Refactor
- Filters are now fully dynamic
### Miscellaneous Tasks
- Remove sourcesWith fn, using lib.fileset
- Move back to flake-utils
## [0.0.8] - 2024-06-04
### Features
- Can load lazy plugins on events
- Can load lazy plugins on keymaps
### Bug Fixes
- Move files instead of copying them
- Sloth list was broken since refactoring
### Miscellaneous Tasks
- Switch from nixpkgs `23.11` to `unstable`
## [0.0.7] - 2024-06-03
### Features
- Add `version` Sloth subcommand
- Add `viAlias` and `vimAlias` mkNeovimPkg options
- Add `vimdiffAlias` and `nvimdiffAlias` mkNeovimPkg options
### Bug Fixes
- Init function is called before plugin is loaded
### Documentation
- Fix url in the import example
### Refactor
- Extract all dep logic in Dep object
- Extract Sloth command code in its own module
- Split Sloth subcommands in their own files
- Deps provides accessors via metatables
- Deps are now loaded via a FSM
## [0.0.6] - 2024-05-30
### Features
- Can load lazy plugins
- Can load lazy plugins via placeholder `cmd`s
- Loading plugins respect dependencies option
- Can load lazy plugins on FileType autocommand
- Introduce new Sloth command with list subcommand
- Add `Sloth load` subcommand
### Bug Fixes
- Prevent `setup` function to be called twice
### Refactor
- Plugin normalization
### Miscellaneous Tasks
- Delete dead code
- Add CHANGELOG with `git-cliff`
## [0.0.5] - 2024-05-28
### Features
- Add real documentation
### Bug Fixes
- Give default init.lua if none provided
- Remove namePrefix and nameSuffix
### Miscellaneous Tasks
- Refactor lua generation
- Remove dead code
## [0.0.4] - 2024-05-28
### Features
- Generating plugins lua definition from nix
- Simplify lua api
## [0.0.3] - 2024-05-26
### Features
- Rename neofalke to sloth-flake.nvim
- Check inputs of functions with yants
## [0.0.2] - 2024-05-26
### Features
- Can give dependencies to new package
- Can build package with all config
- Remove neovim from nix-shell
- Neoflake and runtime plugin have their version
### Miscellaneous Tasks
- Release v0.0.2
<!-- generated by git-cliff -->

342
README.md
View File

@ -1,335 +1,15 @@
# sloth-flake.nvim
# TODO
A [neovim] plugin and configuration management plugin, highly inspired by [lazy], using [nix].
## MVP
<!-- TOC GFM -->
- [X] Find a way to handle local plugins (like spos.nvim)
- [X] Generate static init.nvim file
- [Description](#description)
- [Features](#features)
- [SemVer](#semver)
- [Installation](#installation)
- [Flake installation](#flake-installation)
- [Usage](#usage)
- [Documentation](#documentation)
- [nix](#nix)
- [`mkPluginsFromInputs`](#mkpluginsfrominputs)
- [`mkNeovimPkg`](#mkneovimpkg)
- [neovim (lua)](#neovim-lua)
- [Using default `init.lua`](#using-default-initlua)
- [Using your own `init.lua`](#using-your-own-initlua)
- [`:Sloth` command](#sloth-command)
- [`list` subcommand](#list-subcommand)
- [`load` subcommand](#load-subcommand)
- [`version` subcommand](#version-subcommand)
- [API](#api)
## Backlog
<!-- TOC -->
## Description
> `sloth-flake.nvim` is an alpha software. Please use caution when using it.
`sloth-flake.nvim` is both
- a [neovim] plugin to manage your plugins
- a [nix] flake to build your neovim package with your own configuration and plugins.
> :information_source: If you never heard of [nix] or you never used it, then this plugin is not for you.
## Features
- [X] Declare your plugin via `nix` files
- [X] Declare nix package as dependencies
- [X] Generate nix package from local files
- [X] Generate default `init.lua`
- [X] Accepts your own `init.lua`
- [X] Lazy load your plugins
- [X] on command
- [X] on filetype
- [X] on event
- [X] on keybinding
- [X] load plugins in order (via plugin `dependencies` property)
- [X] Have a `:Sloth` command to load or query your plugins
- [ ] Generate spell files on build (maybe)
## SemVer
This project will respect SemVer in the end, but will adopt a slightly different semantics in the beginning.
This project is considered alpha as long as the version is `0.0.x`, therefore
you can expect breaking change on each version. You can see the version as
`0.0.MAJOR`.
When this project will hit `0.1.0`, the project will be in beta phase. If a new
version have breaking change, then it will be `0.2.0` and `0.1.1` otherwise.
You can see the version as `0.MAJOR.MINOR`.
When this project will hit `1.0.0`, it will then follow full SemVer
(`MAJOR.MINOR.PATCH`).
## Installation
Only flake installation is supported.
### Flake installation
Import `sloth-flake.nvim` flake latest version:
```nix
inputs.sloth-flake.url = "github:lemarsu/sloth-flake.nvim";
```
You can give a specific version:
```nix
inputs.sloth-flake.url = "github:lemarsu/sloth-flake.nvim?tag=0.0.5";
```
## Usage
Once installed, you can call the `sloth-flake.lib.mkNeovimPkg` to build your neovim package.
`mkNeovimPkg` requires a *set* as argument with at least `pkgs` that represents your nixpkgs.
```nix
sloth-flake.lib.mkNeovimPkg {
inherit pkgs;
viAlias = true;
vimAlias = true;
runtime = {
version = "0.1";
src = sloth-flake.lib.sourcesWith ./. [
./after
./colors
./ftplugin
./lua
./plugin
./queries
];
};
dependencies = [
rust-vim
vim-openscad
./lsp
{
plugin = telescope-nvim;
config = ./telescope.lua;
}
];
}
```
## Documentation
### nix
#### `mkPluginsFromInputs`
`mkPluginsFromInputs` is a helper function that converts flake inputs (starting by `"plugin-"` by default) into vimPlugins.
Example:
```nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
sloth-flake.url = "github:lemarsu/sloth-flake.nvim";
utils.url = "github:numtide/flake-utils";
plugin-base64 = {
url = "github:moevis/base64.nvim";
flake = false;
};
plugin-cellular-automaton = {
url = "github:eandrju/cellular-automaton.nvim";
flake = false;
};
};
outputs = {utils, sloth-flake, ...}@inputs: let
fu = utils.lib;
in fu.eachDefaultSystem (system: let
plugins = sloth-flake.lib.mkPluginsFromInputs {
inherit pkgs inputs;
};
in {
packages = rec {
default = neovim;
neovim = pkgs.callPackage ./package.nix {
# By adding `plugins` here, we can use `with plugins; base64 cellular-automaton`,
inherit plugins sloth-flake;
};
};
};
}
```
The function accepts the following attributes:
- `pkgs` **REQUIRED** the nixpkgs set.
- `inputs` **REQUIRED** the inputs from a flake.
- `predicate` **optional** predicate to filter input by name. By default, it's
filter inputs with `hasPrefix "plugin-"`.
- `nameMap` **optional** function to change the key of the input in the resulting
set. By default, the 7 first letters are removed, so to remove the "plugin-"
prefix.
- `buildVimPlugin` **optional** function used to build the plugin. Defaults to
`pkgs.vimUtils.buildVimPlugin`.
#### `mkNeovimPkg`
`mkNeovimPkg` requires only the `pkgs` argument.
Here's a list of all accepted arguments
| name | default | description |
|-------------------------|-------------------------|---------------------------------------------------------------------|
| `pkgs` | N/A | The nixpkgs set. **REQUIRED** |
| `package` | `pkgs.neovim-unwrapped` | The unwrapped neovim package to use |
| `init` | `null` | The `init.lua` of your config (string, path or init config object)¹ |
| `runtime` | `{}` | Your Runtime configuration (see below) |
| `dependencies` | `[]` | A list of your dependencies (see below) |
| `dependenciesExtraArgs` | `{}` | Extra arguments to load your dependencies in other files |
| `extraLuaPackages` | `null` | Extra lua packages needed for the plugin |
| `viAlias` | `false` | Wether to create a `vi` alias to run neovim |
| `vimAlias` | `false` | Wether to create a `vim` alias to run neovim |
| `vimdiffAlias` | `false` | Wether to create a `vimdiff` alias to run neovim in diff mode |
| `nvimdiffAlias` | `false` | Wether to create a `nvimdiff` alias to run neovim in diff mode |
> ¹ If you give your own `init.lua` as string or path, you'll have to call `sloth-flake` lua plugin yourself. See more below.
The init configuration object accepts the following properties:
| name | default | description |
|------------|---------|----------------------------------------------------------------------------|
| `init` | `null` | Lua code call before plugins init. String or path. |
| `postInit` | `null` | Lua code call after plugins init and before plugin config. String or path. |
| `config` | `null` | Luas cod call after plugins config. String or path. |
The Runtime configuration object accepts the following properties:
| name | default | description |
|-----------|---------|--------------------------------|
| `version` | `null` | The version of your runtime |
| `src` | `null` | The content of your runtime |
The dependencies is a list of element of either:
- path: the path of the file to load other dependencies
- package: a nix package of a neovim/vim plugin
- Plugin configuration object: an object describing a plugin and its configuration.
The Plugin configuration object accepts the following properties:
| name | default | description |
|--------------------|---------|----------------------------------------------------------------|
| `plugin` | N/A | The plugin to load² **REQUIRED** |
| `init` | `null` | Lua code (as string of path) to call before loading the plugin |
| `config` | `null` | Lua code (as string of path) to call after loading the plugin |
| `dependencies` | `[]` | The plugin dependencies³ |
| `extraLuaPackages` | `null` | Extra lua packages needed for the plugin |
| `lazy` | `false` | Should the plugin be loaded lazily |
| `cmd` | `[]` | Command to put as place_holder to load the lazy plugin⁴ |
| `ft` | `[]` | Filetype to watch to load the lazy plugin⁴ |
| `events` | `[]` | Events to watch to load the lazy plugin⁴⁵ |
| `keymaps` | `[]` | Keymaps that when press will load the lazy plugin⁴⁶ |
> ² The plugin can be either a nix package or an object with only `name` and
> `src` as properties. The latter will be used to create a nix package of your
> plugin on the fly.
> ³ `nix` handles the installation of your plugin, therefore, this list is
> **NOT** to declare dependencies that the nix package of the plugin doesn't
> know. This will tell `sloth-flake` in what order your plugins should be
> loaded.
> ⁴ Setting this property implicitly set `lazy` to `true`.
> ⁵ Events can be a string, an Event object or a list of either. You can
> represent a simple event name and pattern with the following string format :
> "BufRead *.md".
> ⁶ Keymaps can be a string, an Keymap object or a list of either.
The Event configuration object accepts the following properties:
| name | default | description |
|-----------|---------|----------------------------------------------------------------|
| `name` | N/A | The name of the event as string or list of string **REQUIRED** |
| `pattern` | `null` | Pattern (as string or list of string) associated to the event |
The Keymap configuration object accepts the following properties:
| name | default | description |
|-----------|---------|----------------------------------------------------|
| `mode` | `"n"` | The mode of the keymap as string or list of string |
| `mapping` | N/A | The actual mapping **REQUIRED** |
### neovim (lua)
#### Using default `init.lua`
If your neovim configuration is only plugin configuration, `sloth-flake` will
give you a default `init.lua` that will load `sloth-flake` plugin that will, in
turn, load all your plugins. If you need more control over configuration
startup, please look at next section.
#### Using your own `init.lua`
You can provide your `init.lua`, but you must call `sloth-flake` your self.
```lua
-- Requiring sloth-flake plugin
local sloth_flake = require 'sloth-flake'
-- Before this point, no plugin nor configuration is loaded
-- You can configure what you need before loading any plugin.
sloth_flake.setup {
-- This function is called after calling all optional `init` functions of your plugins
-- but before loading those plugins
post_init = function()
-- [...]
end
}
-- From here, your plugins are loaded and their optional `config` function called
```
#### `:Sloth` command
`sloth-flake` give a `Sloth` command that you can call to gather some
informations about or load your plugins.
Usage: `Sloth [command] [args...]`
If no arguments are given, the `Sloth` command will call the `list` subcommand.
##### `list` subcommand
Summary: List plugins.
Usage: `Sloth list [filter]`
- `filter`: filter the list of plugins.
- `all`: list all declared plugins. Same as if no filter is given.
- `loaded`: list only loaded plugins.
- `notloaded`: list only not loaded plugins.
##### `load` subcommand
Summary: Load lazy plugins.
Usage: `Sloth load <plugin> [plugin [...]]`
- `plugin`: a plugin to load
##### `version` subcommand
Summary: Return Sloth version
Usage: `Sloth version`
#### API
The lua API is not really defined yet. This documentation will be completed then.
[neovim]: http://neovim.io/
[nix]: https://nixos.org
[lazy]: https://github.com/folke/lazy.nvim
- [ ] handle dependencies
- [ ] Explore how lazy load plugins
- [ ] load manual plugins
- [ ] play with hm dag
- [ ] respect dependencies in loading
- [ ] Generate spell files on build ?

View File

@ -1 +1 @@
0.0.10
0.0.2

View File

@ -1,87 +0,0 @@
# git-cliff ~ default configuration file
# https://git-cliff.org/docs/configuration
#
# Lines starting with "#" are comments.
# Configuration options are organized into tables and keys.
# See documentation for more information on available options.
[changelog]
# changelog header
header = """
# Changelog\n
All notable changes to this project will be documented in this file.\n
"""
# template for the changelog body
# https://keats.github.io/tera/docs/#introduction
body = """
{% if version %}\
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
{% else %}\
## [unreleased]
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | striptags | trim | upper_first }}
{% for commit in commits %}
- {% if commit.breaking %}[**breaking**] {% endif %}{{ commit.message | upper_first }}\
{% endfor %}
{% endfor %}\n
"""
# remove the leading and trailing whitespace from the template
trim = true
# changelog footer
footer = """
<!-- generated by git-cliff -->
"""
# postprocessors
postprocessors = [
# { pattern = '<REPO>', replace = "https://github.com/orhun/git-cliff" }, # replace repository URL
]
[git]
# parse the commits based on https://www.conventionalcommits.org
conventional_commits = true
# filter out the commits that are not conventional
filter_unconventional = true
# process each line of a commit as an individual commit
split_commits = false
# regex for preprocessing the commit messages
commit_preprocessors = [
# { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](<REPO>/issues/${2}))"}, # replace issue numbers
]
# regex for parsing and grouping commits
commit_parsers = [
{ message = "^feat", group = "<!-- 1 -->Features" },
{ message = "^fix", group = "<!-- 2 -->Bug Fixes" },
{ message = "^doc", group = "<!-- 3 -->Documentation" },
{ message = "^perf", group = "<!-- 4 -->Performance" },
{ message = "^refactor", group = "<!-- 5 -->Refactor" },
{ message = "^style", group = "<!-- 6 -->Styling" },
{ message = "^test", group = "<!-- 7 -->Testing" },
{ message = "^chore\\(release\\): prepare for", skip = true },
{ message = "^chore\\(deps\\)", skip = true },
{ message = "^chore\\(pr\\)", skip = true },
{ message = "^chore\\(pull\\)", skip = true },
{ message = "^chore: bump version", skip = true },
{ message = "^chore|ci", group = "<!-- 8 -->Miscellaneous Tasks" },
{ message = "^flake\\.lock: Update", skip = true },
{ body = ".*security", group = "<!-- 9 -->Security" },
{ message = "^revert", group = "<!-- 10 -->Revert" },
]
# protect breaking changes from being skipped due to matching a skipping commit_parser
protect_breaking_commits = false
# filter out the commits that are not matched by commit parsers
filter_commits = false
# regex for matching git tags
tag_pattern = "[0-9].*"
breaking_always_bump_major = false
# regex for skipping tags
skip_tags = "v0.1.0-beta.1"
# regex for ignoring tags
ignore_tags = ""
# sort the tags topologically
topo_order = false
# sort the commits inside sections by oldest/newest order
sort_commits = "oldest"
# limit the number of commits included in the changelog.
# limit_commits = 42

View File

@ -50,11 +50,29 @@
"systems": "systems"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"lastModified": 1701680307,
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1694529238,
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
"type": "github"
},
"original": {
@ -86,11 +104,11 @@
"rust-overlay": "rust-overlay"
},
"locked": {
"lastModified": 1717086091,
"narHash": "sha256-GmsEQa4HZeMfec37LZnwG/Lt/XmqFLXsjv5QWojeNiM=",
"lastModified": 1704611696,
"narHash": "sha256-4ZCgV5oHdEc3q+XaIzy//gh20uC/aSuAtMU9bsfgLZk=",
"owner": "oxalica",
"repo": "nil",
"rev": "ab3ddb8f063774cf7e22eb610f5ecfdb77309f3c",
"rev": "059d33a24bb76d2048740bcce936362bf54b5bc9",
"type": "github"
},
"original": {
@ -101,11 +119,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1716977081,
"narHash": "sha256-pFe5jLeIPlKEln5n2h998d7cpzXFdbrBMRe3suz4K1o=",
"lastModified": 1704161960,
"narHash": "sha256-QGua89Pmq+FBAro8NriTuoO/wNaUtugt29/qqA8zeeM=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "ac82a513e55582291805d6f09d35b6d8b60637a1",
"rev": "63143ac2c9186be6d9da6035fa22620018c85932",
"type": "github"
},
"original": {
@ -117,42 +135,26 @@
},
"nixpkgs_2": {
"locked": {
"lastModified": 1717196966,
"narHash": "sha256-yZKhxVIKd2lsbOqYd5iDoUIwsRZFqE87smE2Vzf6Ck0=",
"lastModified": 1711668574,
"narHash": "sha256-u1dfs0ASQIEr1icTVrsKwg2xToIpn7ZXxW3RHfHxshg=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "57610d2f8f0937f39dbd72251e9614b1561942d8",
"rev": "219951b495fc2eac67b1456824cc1ec1fd2ee659",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"ref": "nixos-23.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1660438583,
"narHash": "sha256-rJUTYxFKlWUJI3njAwEc1pKAVooAViZGJvsgqfh/q/E=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "bbd8f7cd87d0b29294ef3072ffdbd61d60f05da4",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"root": {
"inputs": {
"alejandra": "alejandra",
"nil": "nil",
"nixpkgs": "nixpkgs_2",
"utils": "utils",
"yants": "yants"
"utils": "utils"
}
},
"rust-analyzer-src": {
@ -184,11 +186,11 @@
]
},
"locked": {
"lastModified": 1717035469,
"narHash": "sha256-MzH+yjKULH3HCRj9QCTwBvqq4LZkR0ZqRE/QfGOGC2E=",
"lastModified": 1704593904,
"narHash": "sha256-nDoXZDTRdgF3b4n3m011y99nYFewvOl9UpzFvP8Rb3c=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "095702e63a40e86f339d11864da9dc965b70a01e",
"rev": "c36fd70a99decfa6e110c86f296a97613034a680",
"type": "github"
},
"original": {
@ -229,37 +231,20 @@
},
"utils": {
"inputs": {
"systems": "systems_2"
"flake-utils": "flake-utils_2"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"lastModified": 1696281284,
"narHash": "sha256-xcmtTmoiiAOSk4abifbtqVZk0iwBcqJfg47iUbkwhcE=",
"owner": "gytis-ivaskevicius",
"repo": "flake-utils-plus",
"rev": "6cf1e312fb259693c4930d07ca3cbe1d07ef4a48",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"yants": {
"inputs": {
"nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1686863218,
"narHash": "sha256-kooxYm3/3ornWtVBNHM3Zh020gACUyFX2G0VQXnB+mk=",
"owner": "divnix",
"repo": "yants",
"rev": "8f0da0dba57149676aa4817ec0c880fbde7a648d",
"type": "github"
},
"original": {
"owner": "divnix",
"repo": "yants",
"owner": "gytis-ivaskevicius",
"ref": "v1.4.0",
"repo": "flake-utils-plus",
"type": "github"
}
}

View File

@ -2,10 +2,9 @@
description = "My neovim configuration";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
utils.url = "github:numtide/flake-utils";
nixpkgs.url = "github:nixos/nixpkgs/nixos-23.11";
utils.url = "github:gytis-ivaskevicius/flake-utils-plus/v1.4.0";
nil.url = "github:oxalica/nil";
yants.url = "github:divnix/yants";
alejandra = {
url = "github:kamadorueda/alejandra/3.0.0";
inputs.nixpkgs.follows = "nixpkgs";
@ -14,31 +13,29 @@
outputs = {
self,
alejandra,
nixpkgs,
utils,
yants,
alejandra,
...
} @ inputs: let
inherit (builtins) readFile replaceStrings;
fu = utils.lib;
versionFile = replaceStrings ["\n"] [""] (readFile ./VERSION);
version =
if self.sourceInfo ? dirtyShortRev
then "${versionFile}-${self.sourceInfo.dirtyShortRev}"
else versionFile;
versionFile = builtins.replaceStrings ["\n"] [""] (builtins.readFile ./VERSION);
forSystem = system: let
pkgs = nixpkgs.legacyPackages.${system};
formatter = alejandra.defaultPackage.${system};
inherit (inputs.nil.packages.${system}) nil;
in {
inherit formatter;
devShells.default = import ./shell.nix {
inherit pkgs nil formatter;
};
};
version = if self.sourceInfo ? dirtyShortRev
then "${versionFile}-${self.sourceInfo.dirtyShortRev}"
else versionFile;
in
(fu.eachDefaultSystem forSystem)
// {lib = import ./lib {inherit version yants;};};
utils.lib.mkFlake {
inherit self inputs;
outputsBuilder = channel: let
system = channel.nixpkgs.system;
in {
formatter = alejandra.defaultPackage.${channel.nixpkgs.system};
devShells.default = import ./shell.nix {
pkgs = channel.nixpkgs;
inherit (inputs.nil.packages.${system}) nil;
};
};
lib = import ./lib.nix {inherit version;};
};
}

152
hm-file-type.nix Normal file
View File

@ -0,0 +1,152 @@
# Imported from home-manager. Couldn't find another way to access it
{
homeDirectory,
lib,
pkgs,
}: let
inherit
(lib)
hasPrefix
hm
literalExpression
mkDefault
mkIf
mkOption
removePrefix
types
;
in rec {
# Constructs a type suitable for a `home.file."specific/path"` like option. The
# target path may be either absolute or relative, in which case it
# is relative the `basePath` argument (which itself must be an
# absolute path).
#
# Arguments:
# - opt the name of the option, for self-references
# - basePathDesc docbook compatible description of the base path
# - basePath the file base path
fileTypeSubmodule = opt: basePathDesc: basePath:
types.submodule ({
name,
config,
...
}: {
options = {
enable = mkOption {
type = types.bool;
default = true;
description = ''
Whether this file should be generated. This option allows specific
files to be disabled.
'';
};
target = mkOption {
type = types.str;
apply = p: let
absPath =
if hasPrefix "/" p
then p
else "${basePath}/${p}";
in
removePrefix (homeDirectory + "/") absPath;
defaultText = literalExpression "name";
description = ''
Path to target file relative to ${basePathDesc}.
'';
};
text = mkOption {
default = null;
type = types.nullOr types.lines;
description = ''
Text of the file. If this option is null then
[](#opt-${opt}._name_.source)
must be set.
'';
};
source = mkOption {
type = types.path;
description = ''
Path of the source file or directory. If
[](#opt-${opt}._name_.text)
is non-null then this option will automatically point to a file
containing that text.
'';
};
executable = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Set the execute bit. If `null`, defaults to the mode
of the {var}`source` file or to `false`
for files created through the {var}`text` option.
'';
};
recursive = mkOption {
type = types.bool;
default = false;
description = ''
If the file source is a directory, then this option
determines whether the directory should be recursively
linked to the target location. This option has no effect
if the source is a file.
If `false` (the default) then the target
will be a symbolic link to the source directory. If
`true` then the target will be a
directory structure matching the source's but whose leafs
are symbolic links to the files of the source directory.
'';
};
onChange = mkOption {
type = types.lines;
default = "";
description = ''
Shell commands to run when file has changed between
generations. The script will be run
*after* the new files have been linked
into place.
Note, this code is always run when `recursive` is
enabled.
'';
};
force = mkOption {
type = types.bool;
default = false;
visible = false;
description = ''
Whether the target path should be unconditionally replaced
by the managed file source. Warning, this will silently
delete the target regardless of whether it is a file or
link.
'';
};
};
config = {
target = mkDefault name;
source = mkIf (config.text != null) (mkDefault (pkgs.writeTextFile {
inherit (config) text;
executable = config.executable == true; # can be null
name = hm.strings.storeFileName name;
}));
};
});
# Constructs a type suitable for a `home.file` like option. The
# target path may be either absolute or relative, in which case it
# is relative the `basePath` argument (which itself must be an
# absolute path).
#
# Arguments:
# - opt the name of the option, for self-references
# - basePathDesc docbook compatible description of the base path
# - basePath the file base path
fileTypeAttrSet = opt: basePathDesc: basePath:
types.attrsOf (fileTypeSubmodule opt basePathDesc basePath);
}

477
lib.nix Normal file
View File

@ -0,0 +1,477 @@
{version}: {
mkNeovimPkg = {
pkgs,
package ? pkgs.neovim-unwrapped,
namePrefix ? "",
nameSuffix ? "",
dependencies ? [],
dependenciesExtraArgs ? {},
runtime ? {},
...
}: let
inherit (builtins) isPath;
inherit (pkgs) lib vimUtils;
callPackage = lib.callPackageWith (pkgs // dependenciesExtraArgs);
inherit (lib.lists) concatMap filter foldl' map optional reverseList;
inherit (lib.attrsets) attrNames optionalAttrs;
inherit (lib.strings) concatStringsSep fileContents hasSuffix removePrefix removeSuffix replaceStrings;
inherit (lib.sources) sourceByRegex;
# inherit (lib.debug) traceIf traceSeq traceVal traceValSeq traceValFn;
remotePluginToNeovimPlugin = p:
vimUtils.buildVimPlugin rec {
inherit (p) src name;
pname = name;
};
defaultPlugin = {
enabled = true;
init = null;
config = null;
};
withPluginDefaults = dep: defaultPlugin // dep;
normalizePlugin = dep: let
p =
if ! dep ? plugin
then {plugin = dep;}
else let
inherit (dep) plugin;
in
if attrNames plugin == ["name" "src"]
then {plugin = remotePluginToNeovimPlugin plugin;}
else dep;
in
withPluginDefaults p;
normalizeOrImportPlugin = dep:
if isPath dep
then normalizePlugins (callPackage dep {})
else [(normalizePlugin dep)];
normalizePlugins = concatMap normalizeOrImportPlugin;
wrapLuaInFunction = section: lua: ''
-- begin ${section}
(function()
${removeSuffix "\n" lua}
end)();
-- end ${section}
'';
getLua = type: p: let
pluginName =
if p.plugin ? name
then p.plugin.name
else baseNameOf p.plugin;
content = p.${type};
textContent =
if isPath content
then fileContents content
else content;
in
optional (! isNull content)
(wrapLuaInFunction "${type} for ${pluginName}" textContent);
getAllLua = type:
concatStringsSep "\n"
(concatMap (getLua type) plugins);
neoflake.plugin = vimUtils.buildVimPlugin {
inherit version;
pname = "neoflake";
src = ./lua/neoflake;
buildPhase = ''
dir=lua/neoflake
mkdir -p $dir
mv init.lua $dir
cat <<'LUA' > $dir/initialize.lua
return function ()
${getAllLua "init"}
end
LUA
cat <<'LUA' > $dir/config.lua
return function()
${getAllLua "config"}
end
LUA
'';
};
runtimePlugin.plugin = vimUtils.buildVimPlugin ({
inherit (runtime) src;
}
// (optionalAttrs (isNull runtime.version) {
name = "runtime";
})
// (optionalAttrs (! isNull runtime.version) {
inherit (runtime) version;
pname = "runtime";
}));
plugins = normalizePlugins (dependencies ++ [runtimePlugin neoflake]);
extractPlugin = map (p: p.plugin);
customRC = let
rc =
if runtime ? init
then runtime.init
else "";
in
if isPath rc
then lib.fileContents rc
else rc;
neovimConfig =
pkgs.neovimUtils.makeNeovimConfig {
inherit customRC;
plugins = extractPlugin plugins;
}
// {
luaRcContent = customRC;
};
pkg = pkgs.wrapNeovimUnstable package (removeAttrs neovimConfig ["manifestRc" "neovimRcContent"]);
# TODO nameSuffix is buggy :'(
name = "${namePrefix}${pkg.name}${nameSuffix}";
in
pkg // {inherit name;};
sourcesWith = path: paths: let
samePath = a: let a' = builtins.toString a; in b: a' == builtins.toString b;
isRoot = samePath "/";
isInPath = path: subPath:
if isRoot subPath
then false
else (samePath path subPath) || (isInPath path (builtins.dirOf subPath));
filter = src: _type: builtins.any (includePath: isInPath includePath src) paths;
in
builtins.path {
inherit path filter;
};
mkNeovimModule = {
pluginsDir ? null,
attrName ? "neoflake",
self,
}: {
config,
lib,
pkgs,
...
}: let
cfg = config.${attrName};
inherit (builtins) baseNameOf isPath;
inherit (lib) mkEnableOption mkIf mkOption types;
# inherit (lib.debug) traceIf traceSeq traceVal traceValSeq traceValFn;
inherit (lib.attrsets) attrNames optionalAttrs;
inherit (lib.lists) concatMap filter foldl' map optional reverseList;
inherit (lib.strings) concatStringsSep fileContents hasSuffix removePrefix removeSuffix replaceStrings;
hm-file-type = import ./hm-file-type.nix {
inherit (config.home) homeDirectory;
inherit lib pkgs;
};
inherit (hm-file-type) fileTypeSubmodule;
verbatimSubmodule = types.submodule {
options = {
path = mkOption {
description = "path to copy from. Must be included in the flake folder.";
type = types.path;
};
dest = mkOption {
description = "dest into `.config/nvim`.";
type = types.str;
};
};
};
remotePluginConfig = types.addCheck (types.submodule {
options = {
name = mkOption {
type = types.str;
};
src = mkOption {
type = types.path;
};
};
}) (mod: attrNames mod == ["name" "src"]);
pluginWithConfigType = types.submodule {
options = {
enabled =
mkEnableOption "enabled"
// {
description = ''
Whether this plugin should be enabled. This option allows specific
plugins to be disabled.
'';
default = true;
};
init = mkOption {
type = types.nullOr (fileTypeSubmodule "${attrName}.plugins._.init" "{var}`xdg.configHome/nvim`" "nvim");
description = "Script to init this plugin. Run before plugin load.";
default = null;
};
config = mkOption {
type = types.nullOr (fileTypeSubmodule "${attrName}.plugins._.config" "{var}`xdg.configHome/nvim`" "nvim");
description = "Script to configure this plugin. Run after plugin load.";
default = null;
};
main = mkOption {
type = with types; nullOr str;
description = "Name of the main module to load.";
default = null;
};
## Lazy options
lazy = mkOption {
type = types.bool;
description = "Should this plugin be load lazily ?";
default = false;
};
events = mkOption {
type = with types; listOf str;
description = "List of events on which the plugin should be loaded";
default = [];
};
commands = mkOption {
type = with types; listOf str;
description = "List of commands on which the plugin should be loaded";
default = [];
};
filetypes = mkOption {
type = with types; listOf str;
description = "List of filetypes on which the plugin should be loaded";
default = [];
};
keys = mkOption {
type = with types; listOf str;
description = "List of keystrokes on which the plugin should be loaded";
default = [];
};
priority = mkOption {
type = with types; listOf str;
description = ''
Priority of the module. Influence the order of loading plugins.
Highest values get loaded before.
'';
default = [];
};
dependencies = mkOption {
# Should we accept strings?
# type = with types; listOf (either strings package);
type = with types; listOf package;
description = ''
Give the list of packages that should be loaded before the current one.
'';
};
plugin = mkOption {
type = with types; oneOf [path remotePluginConfig package];
description = "The actual vim plugin package to load";
};
};
};
hasNixSuffix = hasSuffix ".nix";
pluginNixFiles =
if isNull pluginsDir
then []
else filter hasNixSuffix (lib.fileset.toList pluginsDir);
pathToNeovimPlugin = src: let
normalizeName = replaceStrings ["."] ["-"];
in
pkgs.vimUtils.buildVimPlugin rec {
inherit src;
pname = normalizeName (baseNameOf src);
name = pname;
};
remotePluginToNeovimPlugin = p:
pkgs.vimUtils.buildVimPlugin rec {
inherit (p) src name;
pname = name;
};
mkPlugin = plugin:
if plugin ? plugin
then let
p = plugin.plugin;
in
if isPath p
then pathToNeovimPlugin p
else if attrNames p == ["name" "src"]
then remotePluginToNeovimPlugin p
else p
else plugin;
in {
# imports = map wrapImport pluginNixFiles;
imports = pluginNixFiles;
options.${attrName} = {
enable = mkEnableOption "${attrName} module";
plugins = mkOption {
description = "List all plugins to load";
type = with types; listOf (oneOf [package pluginWithConfigType]);
};
includesVerbatim = mkOption {
description = "Includes files as is in final .config/nvim.";
type = with types; listOf (either path verbatimSubmodule);
default = [];
};
defaultConfig = {
enable = mkOption {
description = "generate default configuration";
type = types.bool;
default = true;
};
};
};
config = let
defaultPlugin = {
enabled = true;
init = null;
config = null;
};
wrapIfNeeded = p:
if p ? plugin
then p
else {plugin = p;};
normalizedPlugins = map (p: defaultPlugin // (wrapIfNeeded p)) cfg.plugins;
getText = submodule:
if ! isNull submodule.text
then submodule.text
else fileContents submodule.source;
wrapLuaInFunction = section: lua: ''
-- begin ${section}
(function()
${removeSuffix "\n" lua}
end)();
-- end ${section}
'';
pluginName = p:
if p.plugin ? name
then p.plugin.name
else baseNameOf p.plugin;
getInitText = p:
optional (!(isNull p.init))
(wrapLuaInFunction "init for ${pluginName p}" (getText p.init));
getConfigText = p:
optional (!(isNull p.config))
(wrapLuaInFunction "config for ${pluginName p}" (getText p.config));
initLua =
concatStringsSep "\n"
(concatMap getInitText normalizedPlugins);
configLua =
concatStringsSep "\n"
(concatMap getConfigText normalizedPlugins);
pathToString = filePath: let
keyName = key: {
inherit key;
name = baseNameOf key;
};
list = map (n: n.name) (builtins.genericClosure {
startSet = [(keyName filePath)];
operator = item: let
parent = dirOf item.key;
in [(keyName parent)];
});
in
concatStringsSep "/" (reverseList list);
normalizeVerbatim = def:
if def ? path && def ? dest
then def
else if ! isPath def
then abort "Not a path nor a verbatim"
else let
fileStr = pathToString def;
root = pathToString self.outPath;
in {
path = def;
dest = removePrefix (root + "/") fileStr;
};
normalizedVerbatim = map normalizeVerbatim cfg.includesVerbatim;
verbatimFiles =
foldl'
(memo: verbatim:
memo
// {
"nvim/${verbatim.dest}" = {
source = verbatim.path;
recursive = true;
};
}) {}
normalizedVerbatim;
neoflakeFiles = let
prefix = "nvim/lua/neoflake";
in {
${prefix} = {
source = ./lua/neoflake;
recursive = true;
};
"${prefix}/initialize.lua".text = ''
return function ()
${initLua}
end
'';
"${prefix}/config.lua".text = ''
return function ()
${configLua}
end
'';
};
defaultConfig = optionalAttrs cfg.defaultConfig.enable {
"nvim/init.lua".source = ./lua/default_init.lua;
};
in
mkIf cfg.enable {
programs.neovim = {
enable = true;
vimAlias = true;
viAlias = true;
defaultEditor = true;
withPython3 = true;
withNodeJs = true;
plugins = map mkPlugin cfg.plugins;
};
xdg.configFile =
verbatimFiles
// neoflakeFiles
// defaultConfig;
};
};
}

View File

@ -1,9 +0,0 @@
{
version,
yants,
}: let
types = import ./types.nix {inherit yants;};
in {
mkNeovimPkg = import ./mkNeovimPkg.nix {inherit version types;};
mkPluginsFromInputs = import ./mkPluginsFromInputs.nix;
}

View File

@ -1,196 +0,0 @@
{
pkgs,
lib,
vimUtils,
dependenciesExtraArgs,
types,
...
}: let
inherit (builtins) foldl' isPath isList isString mapAttrs match elemAt;
inherit (lib.attrsets) attrNames optionalAttrs;
inherit (lib.lists) concatMap;
inherit (lib.strings) fileContents splitString;
lua = callPackage ./lua.nix {};
callPackage = lib.callPackageWith (pkgs // dependenciesExtraArgs);
hasMatch = pattern: str: isList (match pattern str);
wrapArray = value:
if isList value
then value
else [value];
defaultPlugin = {
enabled = true;
init = null;
config = null;
dependencies = [];
lazy = false;
cmd = [];
ft = [];
events = [];
keymaps = [];
};
remotePluginToNeovimPlugin = p:
vimUtils.buildVimPlugin rec {
inherit (p) src name;
pname = name;
};
defaultKeymap = {mode = "n";};
normalizeKeymap = keymap: let
value = (
if isString keymap
then {mapping = keymap;}
else keymap
);
in
mapAttrs (_: wrapArray) (defaultKeymap // value);
normalizeKeymaps = keymaps:
if isList keymaps
then map normalizeKeymap keymaps
else [(normalizeKeymap keymaps)];
normalizeEvent = event: let
value =
if ! isString event
then event
else if ! hasMatch ".* .*" event
then {name = event;}
else let
part = elemAt (splitString " " event);
in {
name = part 0;
pattern = part 1;
};
in
mapAttrs (_: wrapArray) value;
normalizeEvents = events:
if isList events
then map normalizeEvent events
else [(normalizeEvent events)];
withPluginDefaults = dep: defaultPlugin // dep;
normalizePlugin = d: let
dep = types.dependency d;
plugin =
if ! dep ? plugin
then {plugin = dep;}
else let
inherit (dep) plugin;
in
if attrNames plugin == ["name" "src"]
then dep // {plugin = remotePluginToNeovimPlugin plugin;}
else dep;
p = withPluginDefaults plugin;
in
p
// rec {
hasCommands = p.cmd != [];
hasFileTypes = p.ft != [];
keymaps = normalizeKeymaps p.keymaps;
hasKeymaps = p.keymaps != [];
events = normalizeEvents p.events;
hasEvents = p.events != [];
lazy = p.lazy || hasCommands || hasFileTypes || hasEvents || hasKeymaps;
optional = lazy || p.init != null;
};
normalizeOrImportPlugin = dep:
if isPath dep
then normalizePlugins (callPackage dep {})
else [(normalizePlugin dep)];
normalizePlugins = concatMap normalizeOrImportPlugin;
mkRuntimePlugin = {
src,
version,
...
}:
vimUtils.buildVimPlugin ({
inherit src;
}
// (optionalAttrs (isNull version) {
name = "runtime";
})
// (optionalAttrs (! isNull version) {
inherit version;
pname = "runtime";
}));
mkSlothFlakePlugin = version: plugins:
vimUtils.buildVimPlugin {
inherit version;
pname = "sloth-flake";
src = with lib.fileset;
toSource {
root = ../.;
fileset = ../lua/sloth-flake;
};
buildPhase = ''
dir=lua/sloth-flake
cat <<'LUA' > $dir/dependencies.lua
${pluginsLuaDef plugins}
LUA
cat <<'LUA' > $dir/version.lua
${versionLua version}
LUA
'';
};
versionLua = version: with lua; nix2lua (return (lambda (return version)));
textOrContent = content:
if isPath content
then fileContents content
else content;
pluginLuaDef = memo: plugin: let
mkTypeFn = type: let
content = textOrContent plugin.${type};
in
optionalAttrs (! isNull plugin.${type}) {
${type} = with lua; lambda (raw content);
};
pluginName = plugin:
if plugin ? pname
then plugin.pname
else plugin.name;
name = pluginName plugin.plugin;
in
memo
// {
${name} =
{
name = pluginName plugin.plugin;
dependencies = map pluginName plugin.dependencies;
}
// (mkTypeFn "init")
// (mkTypeFn "config")
// (optionalAttrs plugin.lazy {
lazy = true;
})
// (optionalAttrs plugin.hasCommands {
inherit (plugin) cmd;
})
// (optionalAttrs plugin.hasFileTypes {
inherit (plugin) ft;
})
// (optionalAttrs plugin.hasEvents {
inherit (plugin) events;
})
// (optionalAttrs plugin.hasKeymaps {
inherit (plugin) keymaps;
});
};
pluginsLuaDef = plugins:
with lua; nix2lua (return (foldl' pluginLuaDef {} plugins));
in {
inherit normalizePlugin;
inherit normalizePlugins;
inherit mkSlothFlakePlugin;
inherit mkRuntimePlugin;
inherit textOrContent;
}

View File

@ -1,92 +0,0 @@
{...}: let
inherit (builtins) match isNull typeOf concatStringsSep attrNames concatMap;
commaJoin = concatStringsSep ", ";
wrapNotNull' = val: value:
if isNull val
then []
else [value];
wrapNotNull = val: wrapNotNull' val val;
toLua = {
ast = {
raw = {data, ...}: data;
return = {data, ...}: "return ${nix2lua data}";
fn = {
data,
name,
args,
...
}: "function ${name}(${commaJoin args})\n${nix2lua data}\nend";
};
type = rec {
null = _: "nil";
string = data: ''"${data}"'';
path = string;
lambda = data: builtins.trace "Skipping function" null;
int = data: toString data;
bool = data:
if data
then "true"
else "false";
ast = data: let
astType = data.__ast;
in
if toLua.ast ? ${astType}
then toLua.ast.${astType} data
else abort ''Unknown ast type ${astType}'';
list = data: let
nix2luaList = val: wrapNotNull (nix2lua val);
listContent = commaJoin (concatMap nix2luaList data);
in "{ ${listContent} }";
set = data: let
mkKeyValue = key: let
value = data.${key};
luaKey =
if isNull (match "[a-zA-Z_][a-zA-Z_0-9]+" key)
then ''["${key}"]''
else key;
luaValue = nix2lua value;
in
wrapNotNull' luaValue ''
${luaKey} = ${luaValue},
'';
attrsContent = concatMap mkKeyValue (attrNames data);
in ''
{
${concatStringsSep "" attrsContent}}
'';
};
};
newAst = type: set: set // {__ast = type;};
nix2lua = data: let
type = typeOf data;
in
if data ? __ast
then toLua.type.ast data
else if toLua.type ? ${type}
then toLua.type.${type} data
else abort ''Type "${type}"'';
in rec {
inherit nix2lua;
raw = data: newAst "raw" {inherit data;};
functionWithArgs = name: args: data:
newAst "fn" {
inherit name data args;
};
function = name: data: functionWithArgs name [] data;
lambda = function "";
return = data: newAst "return" {inherit data;};
}

View File

@ -1,115 +0,0 @@
{
version,
types,
}: {
pkgs,
package ? pkgs.neovim-unwrapped,
dependencies ? [],
dependenciesExtraArgs ? {},
extraLuaPackages ? (_: []),
runtime ? null,
init ? null,
viAlias ? false,
vimAlias ? false,
vimdiffAlias ? false,
nvimdiffAlias ? false,
...
} @ config: let
inherit (builtins) isString isPath map;
inherit (pkgs) callPackage bash lib;
inherit (lib.strings) optionalString;
inherit (lib.lists) concatMap optional;
inherit (lib.trivial) flip;
inherit (lib.attrsets) optionalAttrs;
deps = callPackage ./deps.nix {inherit dependenciesExtraArgs types;};
normalizedPlugins = deps.normalizePlugins dependencies;
sloth-flake = deps.mkSlothFlakePlugin version normalizedPlugins;
runtimePlugin = deps.mkRuntimePlugin runtime;
plugins =
normalizedPlugins
++ (
deps.normalizePlugins
([sloth-flake] ++ (optional (runtime != null) runtimePlugin))
);
extractPlugin = p: {inherit (p) optional plugin;};
extractPlugins = map extractPlugin;
extractLuaPackageFn = plugin:
optional (plugin ? extraLuaPackages) plugin.extraLuaPackages;
extractLuaPackagesFn = plugins: let
fnList = concatMap extractLuaPackageFn plugins;
concatPackages = ps: fn: fn ps;
in
ps: concatMap (concatPackages ps) fnList;
buildInit = {
init ? null,
postInit ? null,
config ? null,
}: let
initStr = optionalString (! isNull init) ''
(function()
${deps.textOrContent init}
end)();
'';
slothCall =
if isNull postInit
then "require('sloth-flake').setup {}"
else ''
require('sloth-flake').setup {
post_init = function()
${deps.textOrContent postInit}
end,
};
'';
configStr = optionalString (! isNull config) ''
(function()
${deps.textOrContent config}
end)()
'';
in ''
-- Generated by sloth-flake
${initStr}
${slothCall}
${configStr}
'';
customRC =
if isString init || isPath init
then deps.textOrContent init
else buildInit (optionalAttrs (! isNull init) init);
neovimConfig =
pkgs.neovimUtils.makeNeovimConfig {
inherit customRC;
plugins = extractPlugins plugins;
extraLuaPackages = ps:
(extractLuaPackagesFn plugins ps) ++ (extraLuaPackages ps);
}
// {luaRcContent = customRC;};
params =
removeAttrs neovimConfig ["manifestRc" "neovimRcContent"]
// {inherit viAlias vimAlias;};
pkg = pkgs.wrapNeovimUnstable package params;
mkDiffAlias = name:
(flip optionalString) ''
cat <<SH > $out/bin/${name}
#!${bash}/bin/bash
exec $out/bin/nvim -d "\''${@}"
SH
chmod 555 $out/bin/${name}
'';
in
builtins.seq (types.mkNeovimPkgOptions config) (pkg.overrideAttrs (final: super: {
postBuild =
super.postBuild
+ (mkDiffAlias "vimdiff" vimdiffAlias)
+ (mkDiffAlias "nvimdiff" nvimdiffAlias);
}))

View File

@ -1,20 +0,0 @@
{
pkgs,
inputs,
predicate ? pkgs.lib.strings.hasPrefix "plugin-",
nameMap ? builtins.substring 7 (-1),
buildVimPlugin ? pkgs.vimUtils.buildVimPlugin,
}: let
inherit (builtins) attrNames filter foldl' mapAttrs;
names = filter predicate (attrNames inputs);
mkPlugin = m: k: let
name = nameMap k;
pluginDef = {
inherit name;
src = inputs.${k};
};
in
m // {${name} = pluginDef;};
plugins = foldl' mkPlugin {} names;
in
mapAttrs (_: buildVimPlugin) plugins

View File

@ -1,138 +0,0 @@
{yants, ...}: let
stringList = with yants; list string;
stringOrStringList = with yants; either string stringList;
stringOrStringListOr = type:
with yants;
option (eitherN [string type (list (either string type))]);
in rec {
# The runtime object
runtimeType = with yants;
struct "runtime" {
# The version of the runtime
version = option string;
# The content of the runtime directory
src = any;
};
neovimInitType = with yants;
struct "neovimInit" {
# Lua code to call before plugins loaded
init = option (either string path);
# Lua code called after init but before import
postInit = option (either string path);
# Lua code called after all plugins are loaded
config = option (either string path);
};
# As simple remote plugin definition
basicPluginType = with yants;
struct "basicPlugin" {
# The name of your plugin.
name = string;
# The sources of your plugin
# TODO What is the type of a source ?
src = any;
};
eventType = with yants;
struct "event" {
# The name of the event
name = stringOrStringList;
# The pattern of the event
pattern = stringOrStringList;
};
keymapType = with yants;
struct "keymap" {
# The mode of the keymap
mode = option stringOrStringList;
# The mapping of the keymap
mapping = stringOrStringList;
};
# The plugin type of dependencies
pluginType = with yants;
struct "plugin" {
# Whether this plugin should be enabled. This option allows specific
# plugins to be disabled.
# enable = option bool;
# The init configuration of your plugin.
# This should be called before loading your plugin.
init = option (either path string);
# The configuration of your plugin.
# This should be called after loading your plugin.
config = option (either path string);
# Ensure thoses plugins are loaded before the current one
plugin = either drv basicPluginType;
# Ensure thoses plugins are loaded before the current one
dependencies = option (list drv);
# Ensure those packages are available
extraLuaPackages = option function;
# Should this plugin be load lazily ?
lazy = option bool;
# List of events on which the plugin should be loaded
events = option (stringOrStringListOr eventType);
# List of commands on which the plugin should be loaded
cmd = option stringList;
# List of filetypes on which the plugin should be loaded
ft = option stringList;
# List of keystrokes on which the plugin should be loaded
keymaps = option (stringOrStringListOr keymapType);
# Priority of the module. Influence the order of loading plugins.
# Highest values get loaded before.
# priority = option int;
};
# A dependency.
# TODO Complete doc
dependency = with yants; eitherN [path drv pluginType];
mkNeovimPkgOptions = with yants;
struct "mkNeovimPkgOptions" {
# The configuration of mkNeovimPkg
pkgs = attrs any;
# The neovim package to wrap with your conifguration.
# Default is pkgs.neovim-unwrapped
package = option drv;
# init.lua configuration
init = option (eitherN [string path neovimInitType]);
# An array of dependencies.
dependencies = option (list dependency);
# Extra argument to pass to dependencies files
dependenciesExtraArgs = option (attrs any);
# Ensure those packages are available
extraLuaPackages = option function;
# Runtime configuration
runtime = option runtimeType;
# Create a vi alias
viAlias = option bool;
# Create a vim alias
vimAlias = option bool;
# Create a vimdiff alias to run neovim in diff mode
vimdiffAlias = option bool;
# Create a nvimdiff alias to run neovim in diff mode
nvimdiffAlias = option bool;
};
}

View File

@ -1,5 +1,11 @@
-- Generated by sloth-flake
local sloth_flake = require 'sloth-flake'
-- Generated by neoflake
local neoflake = require 'neoflake'
-- Initialize plugins
sloth_flake.setup()
neoflake.init()
-- Should load plugins
neoflake.load()
-- Load plugins configuration
neoflake.config()

10
lua/neoflake/init.lua Normal file
View File

@ -0,0 +1,10 @@
local M = {
init = require('neoflake.initialize'),
config = require('neoflake.config'),
}
function M.load()
-- Not implemented yet
end
return M

View File

@ -1,74 +0,0 @@
local Dep = require 'sloth-flake.dep'
local utils = require 'sloth-flake.utils'
local function yesno(value)
return value and "Yes" or "No"
end
local function list(items)
if items == nil or #items == 0 then
return "None"
end
return vim.iter(items):join(', ')
end
local function describe(dep)
utils.info('Name: %s', dep.name)
utils.info('Is loaded: %s', yesno(dep.is_loaded))
utils.info('Is lazy: %s', yesno(dep.is_lazy))
utils.info('Has init: %s', yesno(dep.init))
utils.info('Has config: %s', yesno(dep.config))
utils.info('Dependencies: %s', list(dep.dependency_names))
utils.info('Filetypes: %s', list(dep.ft))
utils.info('Commands: %s', list(dep.cmd))
if dep.events == nil then
utils.info('Events: None')
else
utils.info('Events:')
for _, event in ipairs(dep.events) do
for _, name in ipairs(event.name) do
for _, pattern in ipairs(event.pattern) do
utils.info(' - %s %s', name, pattern)
end
end
end
end
if dep.keymaps == nil then
utils.info('Keymaps: None')
else
utils.info('Keymaps:')
for _, keymap in ipairs(dep.keymaps) do
for _, mode in ipairs(keymap.mode) do
for _, mapping in ipairs(keymap.mapping) do
utils.info(' - %s %s', mode, mapping)
end
end
end
end
end
return {
complete = function(line)
if line.arg_idx == 3 then
local prefix = line.args[line.arg_idx].arg
return vim.iter(Dep.all()):map(function(name)
return name
end):filter(function(name)
return vim.startswith(name, prefix)
end):totable()
end
end,
cmd = function(plugins)
if #plugins == 0 then
utils.error("You should at least give a plugin to describe!")
return
end
local plugin = plugins[1]
local dep = Dep.get(plugin)
if dep == nil then
return utils.error([[Unknown plugin "%s"]], plugin)
end
describe(dep)
end,
}

View File

@ -1,87 +0,0 @@
local Dep = require 'sloth-flake.dep'
local M = {}
local commands = {
list = require 'sloth-flake.command.list',
load = require 'sloth-flake.command.load',
version = require 'sloth-flake.command.version',
describe = require 'sloth-flake.command.describe',
}
local function parse_line(line, cursor_pos)
local raw_args = vim.split(line, ' +', { trimempty = true })
local parse_pos = 1
local args = vim.iter(raw_args):map(function(arg)
local start, stop = string.find(line, arg, parse_pos, { plain = true })
parse_pos = stop
return {
arg = arg,
start = start,
stop = stop,
}
end):totable()
parse_pos = 1
local arg_idx = 1
vim.iter(args):find(function(arg)
if cursor_pos < arg.start then
arg_idx = arg_idx - 1
return true
elseif cursor_pos <= arg.stop then
return true
end
arg_idx = arg_idx + 1
return false
end)
arg_idx = arg_idx < 1 and 1 or arg_idx
if arg_idx > #args then
args[#args + 1] = {
arg = "",
start = line:len(),
stop = line:len(),
}
end
return {
line = line,
args = args,
arg_idx = arg_idx,
pos = cursor_pos,
in_arg_pos = args[arg_idx] and cursor_pos - args[arg_idx].start + 1,
}
end
-- print(vim.inspect(parse_line('Sloth ', 6)))
local function sloth_cmd_complete(arg_lead, cmd_line, cursor_pos)
local parsed_line = parse_line(cmd_line, cursor_pos)
local arg = parsed_line.args[parsed_line.arg_idx]
if parsed_line.arg_idx == 2 then
return vim.iter(commands):map(function(name, command)
return vim.startswith(name, arg.arg) and name or nil
end):totable()
elseif parsed_line.arg_idx > 2 then
local cmd = parsed_line.args[2].arg
local command = commands[cmd]
return command and command.complete(parsed_line)
end
end
local function sloth_cmd(param)
local args = param.fargs
local cmd = args[1] or "list";
table.remove(args, 1)
local command = commands[cmd]
if command then
command.cmd(args)
else
vim.api.nvim_err_writeln(string.format([[No Sloth subcommand "%s"]], cmd))
end
end
function M.register()
vim.api.nvim_create_user_command('Sloth', sloth_cmd, {
nargs = '*',
complete = sloth_cmd_complete
})
end
return M

View File

@ -1,55 +0,0 @@
local Dep = require 'sloth-flake.dep'
local utils = require 'sloth-flake.utils'
local filters = {
all = {
filter = function(iter)
-- Nothing to do
return iter
end
},
loaded = {
filter = function(iter)
return iter:filter(function(dep)
return Dep.get(dep).is_loaded
end)
end
},
notloaded = {
filter = function(iter)
return iter:filter(function(dep)
return not Dep.get(dep).is_loaded
end)
end
},
}
return {
complete = function(line)
if line.arg_idx == 3 then
local prefix = line.args[3].arg
return vim.iter(vim.tbl_keys(filters)):filter(function(name)
return vim.startswith(name, prefix)
end):totable()
end
end,
cmd = function(args)
local filter_name = args[1] or "all"
local filter = filters[filter_name]
if not filter then
utils.error([[No Sloth list filter "%s".]], cmd)
utils.error("Filters are: %s", vim.iter(vim.tbl_keys(filters)):join(', '))
return
end
local deps = vim.iter(Dep.all()):map(function(_, dep)
return dep.name
end)
deps = filter.filter(deps):totable()
table.sort(deps)
for _, dep in ipairs(deps) do
print(string.format("- %s", dep))
end
end,
}

View File

@ -1,32 +0,0 @@
local Dep = require 'sloth-flake.dep'
local utils = require 'sloth-flake.utils'
return {
complete = function(line)
local previous_deps = vim.iter(line.args):enumerate():map(function(i, arg)
if i < 3 or i > line.arg_idx then return end
return arg.arg
end):totable()
local prefix = line.args[line.arg_idx].arg
return vim.iter(Dep.all()):filter(function(name, dep)
return vim.startswith(name, prefix) and not dep.is_loaded
and not vim.list_contains(previous_deps, name)
end):map(function(name)
return name
end):totable()
end,
cmd = function(plugins)
if #plugins == 0 then
utils.error("You should at least give a plugin to load!")
return
end
for _, plugin in ipairs(plugins) do
local dep = Dep.get(plugin)
if dep ~= nil then
dep:load()
end
end
end,
}

View File

@ -1,7 +0,0 @@
return {
complete = function() end,
cmd = function()
local version = require('sloth-flake.version')
print(string.format('Sloth v%s', version()))
end,
}

View File

@ -1,268 +0,0 @@
local raw_deps = require 'sloth-flake.dependencies'
local state_machine = require 'sloth-flake.state_machine'
local M = {}
local State = state_machine.build_states {
'NotLoaded',
'Shimed',
'Inited',
'Imported',
'Loaded',
}
function M.new(values)
local self = {
values = values,
}
self.sm = state_machine.build(State, {
enter = {
[State.Shimed] = function()
return self:_shim()
end,
[State.Inited] = function()
for _, dep in ipairs(self.dependencies) do
dep:init()
end
local init = self.values.init or function() end
init()
end,
[State.Imported] = function()
for _, dep in ipairs(self.dependencies) do
dep:import()
end
if self.is_lazy or self.values.init then
vim.cmd("packadd " .. self.name)
end
end,
[State.Loaded] = function()
for _, dep in ipairs(self.dependencies) do
dep:config()
end
local config = self.values.config or function() end
config()
end
},
exit = {
[State.Shimed] = function()
self:_unshim()
end,
},
events = {
shim = { from = State.NotLoaded, to = State.Shimed, },
init = { from = { State.NotLoaded, State.Shimed }, to = State.Inited, },
import = { from = State.Inited, to = State.Imported, },
config = { from = State.Imported, to = State.Loaded, },
},
})
return setmetatable(self, {
__index = function(self, k)
local fn = M[k]
if fn then
return fn
end
fn = M['get_' .. k]
if fn then
return fn(self)
end
end,
__newindex = function(self, k, v)
-- Ignore new values
end
})
end
function M:get_name()
return self.values.name
end
function M:get_dependencies()
local ret = {}
for _, name in ipairs(self.values.dependencies) do
local dep = M.get(name)
if dep ~= nil then
ret[#ret + 1] = dep
end
end
return ret
end
function M:get_dependency_names()
local ret = {}
for _, name in ipairs(self.values.dependencies) do
ret[#ret + 1] = name
end
return ret
end
function M:get_cmd()
return self.values.cmd
end
function M:get_ft()
return self.values.ft
end
function M:get_events()
return self.values.events
end
function M:get_keymaps()
return self.values.keymaps
end
function M:get_is_lazy()
return self.values.lazy or false
end
function M:get_state()
return self.sm.state
end
function M:get_is_imported()
return self.state >= State.Imported
end
function M:get_is_loaded()
return self.state >= State.Loaded
end
function M:get_has_events()
return self.ft or self.events
end
function M:get_augroup_name()
return "Sloth-plugin-" .. self.name
end
function M:shim()
return self.sm:shim()
end
function M:_shim()
if self.cmd then
for _, cmd in ipairs(self.cmd) do
vim.api.nvim_create_user_command(cmd, self:lazy_load_cmd(cmd), {
desc = "Sloth-flake placeholder for plugin " .. self.name,
nargs = '*',
bang = true,
})
end
end
if self.has_events then
local group_id = vim.api.nvim_create_augroup(self.augroup_name, {
clear = true,
})
if self.ft then
vim.api.nvim_create_autocmd('FileType', {
group = group_id,
pattern = self.ft,
callback = self:lazy_load_event('FileType')
})
end
if self.events then
for _, event in ipairs(self.events) do
vim.api.nvim_create_autocmd(event.name, {
group = group_id,
pattern = event.pattern,
callback = self:lazy_load_event(event.name)
})
end
end
end
if self.keymaps then
for _, keymap in ipairs(self.keymaps) do
for _, mapping in ipairs(keymap.mapping) do
vim.keymap.set(keymap.mode, mapping, self:lazy_load_mapping(mapping))
end
end
end
end
function M:_unshim()
if self.cmd then
for _, cmd in ipairs(self.cmd) do
vim.api.nvim_del_user_command(cmd)
end
end
if self.has_events then
vim.api.nvim_del_augroup_by_name(self.augroup_name)
end
if self.keymaps then
for _, keymap in ipairs(self.keymaps) do
for _, mapping in ipairs(keymap.mapping) do
vim.keymap.del(keymap.mode, mapping)
end
end
end
end
function M:init()
return self.sm:init()
end
function M:import()
self:init()
return self.sm:import()
end
function M:config()
self:init()
self:import()
return self.sm:config()
end
function M:load()
self:init()
self:import()
return self:config()
end
function M:lazy_load_cmd(cmd)
return function(param)
self:load()
local bang = param.bang and '!' or ''
vim.cmd(cmd .. bang .. ' ' .. param.args)
end
end
function M:lazy_load_event(name)
return function(param)
self:load()
vim.api.nvim_exec_autocmds(name, {
pattern = param.match,
})
end
end
function M:lazy_load_mapping(mapping)
return function(param)
self:load()
vim.cmd.normal(mapping)
end
end
local deps = {}
for k, v in pairs(raw_deps) do
deps[k] = M.new(v)
end
function M.get(name)
return deps[name]
end
function M.all()
return deps
end
return M

View File

@ -1,76 +0,0 @@
local Dep = require 'sloth-flake.dep'
local command = require 'sloth-flake.command'
local priv = {
setup_called = false,
}
local M = {}
function M.get(name)
return Dep.get(name)
end
function M.init_non_lazy()
for _, dep in ipairs(M.non_lazy_deps()) do
dep:init()
end
end
function M.import_non_lazy()
for _, dep in ipairs(M.non_lazy_deps()) do
dep:import()
end
end
function M.config_non_lazy()
for _, dep in ipairs(M.non_lazy_deps()) do
dep:config()
end
end
function M.dep_names()
return M.dep_names_by(function() return true end):totable()
end
function M.dep_names_by(fn)
return M.deps_iter_by(fn):map(function(v) return v:name() end)
end
function M.deps_iter_by(fn)
return vim.iter(Dep.all()):map(function(k, v) return v end):filter(fn)
end
function M.non_lazy_deps()
return M.deps_iter_by(function(dep)
return not dep.is_lazy
end):totable()
end
function M.lazy_deps()
return M.deps_iter_by(function(dep)
return dep.is_lazy
end):totable()
end
function M.setup(config)
if priv.setup_called then
return
end
priv.setup_called = true
local post_init = config and config.post_init or function() end
M.init_non_lazy()
post_init()
M.import_non_lazy()
M.config_non_lazy()
local lazy_deps = M.lazy_deps()
for _, dep in ipairs(lazy_deps) do
dep:shim()
end
command.register()
end
return M

View File

@ -1,104 +0,0 @@
local M = {}
local stateMeta
local function are_both_states(a, b)
return a.is_state and b.is_state
end
stateMeta = {
__tostring = function(v)
return v.name
end,
__le = function(self, other)
if not are_both_states(self, other) then
return false
end
return self.idx <= other.idx
end,
__lt = function(self, other)
if not are_both_states(self, other) then
return false
end
return self.idx < other.idx
end,
__index = function(self, name)
if name == 'is_state' then
return true
end
end
}
function M.build_states(defs)
local states = {}
for i, name in ipairs(defs) do
local state = setmetatable({ idx = i, name = name }, stateMeta)
states[i] = state
states[name] = state
end
return states
end
local SM = {}
local function empty_fn()
end
function SM:run_enter_state(state)
local enter_fn = self.defs.enter and self.defs.enter[state] or empty_fn
enter_fn(self.state)
end
function SM:run_exit_state(state)
local exit_fn = self.defs.exit and self.defs.exit[state] or empty_fn
exit_fn(self.state)
end
local function wrap_state_array(val)
return val.is_state and { val } or val
end
local function canonicalize_event(event)
return {
from = wrap_state_array(event.from),
to = event.to,
transition = event.transition or function() end,
}
end
function M.build(states, defs)
local machine = {
state = states[1],
defs = defs,
}
local prototype = {}
for name, infos in pairs(defs.events) do
local event = canonicalize_event(infos)
prototype[name] = function(self)
if not vim.list_contains(event.from, self.state) then
return false
end
local previous = self.state
self:run_exit_state(previous)
self.state = event.to
event.transition(previous, self.state)
self:run_enter_state(self.state)
return true
end
end
local ret = setmetatable(machine, { __index = vim.tbl_extend('error', SM, prototype)})
ret:run_enter_state(states[1])
return ret
end
local State = M.build_states {
'NotLoaded',
'Shimed',
'Inited',
'Imported',
'Loaded',
}
return M

View File

@ -1,11 +0,0 @@
local M = {}
function M.info(...)
print(string.format(...))
end
function M.error(...)
vim.api.nvim_err_writeln(string.format(...))
end
return M

View File

@ -1,14 +1,11 @@
{
formatter,
nil,
pkgs,
nil,
...
}:
with pkgs;
mkShell {
buildInputs = [
formatter
git-cliff
nil
sumneko-lua-language-server
];