Merge branch 'main' of paperclover.dev:nix/config
This commit is contained in:
commit
d3c18bc88c
15 changed files with 722 additions and 113 deletions
593
files/autofmt.js
Executable file
593
files/autofmt.js
Executable file
|
@ -0,0 +1,593 @@
|
|||
#!/usr/bin/env node
|
||||
// autofmt v1 - https://paperclover.dev/nix/config/src/branch/main/files/autofmt.js
|
||||
//
|
||||
// Different codebases use different formatters. Autofmt looks for project
|
||||
// configuration to pick the correct formatter, allowing an editor to simply
|
||||
// point to this script and ambiguities resolved. This single file program
|
||||
// depends only on Node.js v22.
|
||||
//
|
||||
// When using this repository's Nix-based Neovim configuration, autofmt is
|
||||
// automatically configured as the default formatter. To configure manually,
|
||||
// set the editor's formatter to run `autofmt --stdio=<filename>`. The filename
|
||||
// is used to determine which formatter to invoke.
|
||||
//
|
||||
// With `conform.nvim`:
|
||||
// formatters = {
|
||||
// autofmt = { command = "autofmt" }
|
||||
// }
|
||||
//
|
||||
// With Zed:
|
||||
// "formatter": {
|
||||
// "external": {
|
||||
// "command": "autofmt",
|
||||
// "arguments": ["--stdio", "{buffer_path}"]
|
||||
// }
|
||||
// },
|
||||
//
|
||||
// @ts-nocheck
|
||||
|
||||
// -- definitions --
|
||||
const extensions = {
|
||||
c: [".c", ".h"],
|
||||
cpp: [".cpp", ".cc", ".cxx", ".hpp", ".hxx", ".hh"],
|
||||
css: [".css"],
|
||||
html: [".html"],
|
||||
javascript: [".js", ".ts", ".cjs", ".cts", ".mjs", ".mts", ".jsx", ".tsx"],
|
||||
json: [".json", ".jsonc"],
|
||||
markdown: [".md", ".markdown"],
|
||||
mdx: [".mdx"],
|
||||
nix: [".nix"],
|
||||
rust: [".rs"],
|
||||
toml: [".toml"],
|
||||
yaml: [".yml", ".yaml"],
|
||||
zig: [".zig"],
|
||||
};
|
||||
const formatters = {
|
||||
// this object is sorted by priority
|
||||
//
|
||||
// `languages: true` are multiplexers that apply to all listed
|
||||
// languages, as it is assumed a project will setup their multiplexer
|
||||
// correctly for all tracked files.
|
||||
//
|
||||
// if a formatter doesnt match a config file, the first one is picked
|
||||
// as a default, making things like `deno fmt file.ts` or `clang-format`
|
||||
// the default when there are no config files.
|
||||
dprint: {
|
||||
languages: true,
|
||||
files: ["dprint.json", "dprint.jsonc"],
|
||||
cmd: ["dprint", "fmt", "--", "$files"],
|
||||
stdin: (file) => ["dprint", "fmt", "--stdin", file],
|
||||
},
|
||||
treefmt: {
|
||||
languages: true,
|
||||
files: ["treefmt.toml", ".treefmt.toml"],
|
||||
cmd: ["treefmt", "--", "$files"],
|
||||
stdin: (file) => ["treefmt", "--stdin", file],
|
||||
},
|
||||
// -- web ecosystem --
|
||||
deno: {
|
||||
languages: ["javascript", "markdown", "json", "yaml", "html", "css"],
|
||||
files: ["dprint.json", "dprint.jsonc"],
|
||||
cmd: ["deno", "fmt", "--", "$files"],
|
||||
stdin: (file) => ["deno", "fmt", "--ext", path.extname(file).slice(1), "-"],
|
||||
},
|
||||
prettier: {
|
||||
languages: ["javascript", "markdown", "json", "yaml", "mdx", "html", "css"],
|
||||
files: ["node_modules/.bin/prettier"],
|
||||
cmd: ["node_modules/.bin/prettier", "--write", "--", "$files"],
|
||||
stdin: (file) => ["node_modules/.bin/prettier", "--stdin-filepath", file],
|
||||
},
|
||||
biome: {
|
||||
languages: ["javascript", "json", "css"],
|
||||
files: ["node_modules/.bin/biome"],
|
||||
cmd: ["node_modules/.bin/biome", "format", "--", "$files"],
|
||||
stdin: (file) => [
|
||||
"node_modules/bin/biome",
|
||||
"format",
|
||||
`--stdin-file-path=${file}`,
|
||||
],
|
||||
},
|
||||
"nixfmt-rfc-style": {
|
||||
languages: ["nix"],
|
||||
files: {
|
||||
"flake.nix": (contents) => contents.includes("nixfmt"),
|
||||
},
|
||||
cmd: ["nixfmt", "--", "$files"],
|
||||
stdin: (file) => ["nixfmt", `--filename=${file}`],
|
||||
},
|
||||
alejandra: {
|
||||
languages: ["nix"],
|
||||
files: {
|
||||
"flake.nix": (contents) => contents.includes("alejandra"),
|
||||
},
|
||||
cmd: ["alejandra", "--", "$files"],
|
||||
stdin: () => ["alejandra"],
|
||||
},
|
||||
clang: {
|
||||
languages: ["c", "cpp"],
|
||||
files: true,
|
||||
cmd: ["clang-format", "--", "$files"],
|
||||
stdin: (file) => ["clang-format", `--assume-filename=${file}`],
|
||||
},
|
||||
zig: {
|
||||
languages: ["zig"],
|
||||
files: true,
|
||||
cmd: ["zig", "fmt", "$files"],
|
||||
stdin: (file) => [
|
||||
"zig",
|
||||
"fmt",
|
||||
"--stdin",
|
||||
...file.endsWith(".zon") ? ["--zon"] : [],
|
||||
],
|
||||
},
|
||||
rustfmt: {
|
||||
languages: ["rust"],
|
||||
files: true,
|
||||
cmd: ["rustfmt", "--", "$files"],
|
||||
stdin: () => ["rustfmt"],
|
||||
},
|
||||
taplo: {
|
||||
languages: ["toml"],
|
||||
files: ["taplo.toml"],
|
||||
cmd: ["taplo", "format", "--", "$files"],
|
||||
cmd: () => ["taplo", "format", "-"],
|
||||
},
|
||||
};
|
||||
|
||||
// -- cli --
|
||||
if (!fs.globSync) {
|
||||
console.error(`error: autofmt must be run with Node.js v22 or newer`);
|
||||
process.exit(1);
|
||||
}
|
||||
const [, bin, ...argv] = process.argv;
|
||||
let inputs = [];
|
||||
let globalExclude = [];
|
||||
let gitignore = true;
|
||||
let dryRun = false;
|
||||
let stdio = null;
|
||||
let excludes = [".git"];
|
||||
while (argv.length > 0) {
|
||||
const arg = argv.shift();
|
||||
if (arg === "-h" || arg === "--help") usage();
|
||||
else if (arg === "--no-gitignore") gitignore = false;
|
||||
else if (arg === "--dry-run") dryRun = true;
|
||||
else if (arg.match(/^--stdio=./)) {
|
||||
if (stdio) {
|
||||
console.error("error: can only pass --stdio once");
|
||||
usage();
|
||||
}
|
||||
stdio = arg.slice("--stdio=".length);
|
||||
} else if (arg === "--stdio") {
|
||||
const value = argv.shift();
|
||||
if (!value) {
|
||||
console.error("error: missing value for --stdio");
|
||||
usage();
|
||||
}
|
||||
if (stdio) {
|
||||
console.error("error: can only pass --stdio once");
|
||||
usage();
|
||||
}
|
||||
stdio = value;
|
||||
} else if (arg.match(/^--exclude=./)) {
|
||||
excludes.push(arg.slice("--exclude=".length));
|
||||
} else if (arg === "--") {
|
||||
inputs.push(...argv);
|
||||
break;
|
||||
} else if (arg.startsWith("-")) {
|
||||
console.error("error: unknown option " + JSON.stringify(arg));
|
||||
usage();
|
||||
} else inputs.push(arg);
|
||||
}
|
||||
function usage() {
|
||||
const exe = path.basename(bin);
|
||||
console.error(`usage: ${exe} [...files or directories]`);
|
||||
console.error(``);
|
||||
console.error(`uses the right formatter for the job (by scanning config)`);
|
||||
console.error(`when autofmt reads dirs, it will respect gitignore`);
|
||||
console.error(``);
|
||||
console.error(`to format current directory recursively, run '${exe} .'`);
|
||||
console.error(``);
|
||||
console.error(`options:`);
|
||||
console.error(` --dry-run print commands instead of running them`);
|
||||
console.error(` --no-gitignore do not read '.gitignore'`);
|
||||
console.error(` --exclude=<glob> add an exclusion glob`);
|
||||
console.error(` --stdio=<filename> read/write contents via stdin/stdout`);
|
||||
console.error(``);
|
||||
process.exit(1);
|
||||
}
|
||||
if (inputs.length === 0 && !stdio) usage();
|
||||
if (stdio && inputs.length > 0) {
|
||||
console.error("error: stdio mode only operates on one file");
|
||||
process.exit(1);
|
||||
}
|
||||
const { sep } = path;
|
||||
|
||||
// -- disable warnings --
|
||||
const { emit: originalEmit } = process;
|
||||
const warnings = ["ExperimentalWarning"];
|
||||
process.emit = function (event, error) {
|
||||
return event === "warning" && warnings.includes(error.name)
|
||||
? false
|
||||
: originalEmit.apply(process, arguments);
|
||||
};
|
||||
|
||||
// -- vars --
|
||||
const extToLanguage = Object.fromEntries(
|
||||
Object.entries(extensions).flatMap(([lang, exts]) =>
|
||||
exts.map((ext) => [ext, lang])
|
||||
),
|
||||
);
|
||||
const multis = Object.keys(formatters).filter(
|
||||
(x) => formatters[x].languages === true,
|
||||
);
|
||||
const files = [];
|
||||
const gitignores = new Map();
|
||||
const dirs = new Map();
|
||||
const cached = new Map();
|
||||
|
||||
// -- stdin mode --
|
||||
if (stdio) {
|
||||
const fmtWithPath = pickFormatter(stdio);
|
||||
if (!fmtWithPath) {
|
||||
console.error(`No formatter configured for ${path.relative(".", stdio)}`);
|
||||
process.exit(1);
|
||||
}
|
||||
let [fmt, cwd] = fmtWithPath.split("\0");
|
||||
let cmd = formatters[fmt].stdin(stdio);
|
||||
cwd ??= process.cwd();
|
||||
if (dryRun) {
|
||||
console.info(cmd.join(" "));
|
||||
process.exit(0);
|
||||
}
|
||||
const proc = child_process.spawn(cmd[0], cmd.slice(1), {
|
||||
stdio: ["inherit", "inherit", "inherit"],
|
||||
});
|
||||
proc.on("error", (e) => {
|
||||
let message = "";
|
||||
if (e?.code === "ENOENT") {
|
||||
message = `${cmd[0]} is not installed`;
|
||||
} else {
|
||||
message = String(e?.message ?? e);
|
||||
}
|
||||
console.error(`error: ${message}`);
|
||||
process.exit(1);
|
||||
});
|
||||
const [code] = await events.once(proc, "exit");
|
||||
process.exit(code ?? 1);
|
||||
}
|
||||
|
||||
// -- decide what formatters to run
|
||||
inputs = inputs.map((x) => path.resolve(x));
|
||||
inputs.forEach(walk);
|
||||
const toRun = new Map();
|
||||
for (const file of new Set(files)) {
|
||||
const fmt = pickFormatter(file);
|
||||
if (!fmt) {
|
||||
if (inputs.includes(file)) {
|
||||
console.warn(`No formatter configured for ${path.relative(".", file)}`);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let list = toRun.get(fmt);
|
||||
list ?? toRun.set(fmt, list = []);
|
||||
list.push(file);
|
||||
}
|
||||
|
||||
// -- create a list of commands --
|
||||
const commands = [];
|
||||
let totalFiles = 0;
|
||||
for (const [fmtWithPath, files] of toRun) {
|
||||
let [fmt, cwd] = fmtWithPath.split("\0");
|
||||
let { cmd } = formatters[fmt];
|
||||
if (cwd) cmd = [path.join(cwd, cmd[0]), ...cmd.slice(1)];
|
||||
cwd ??= process.cwd();
|
||||
let i = 0;
|
||||
totalFiles += files.length;
|
||||
if ((i = cmd.indexOf("$files")) != -1) {
|
||||
const c = cmd.slice();
|
||||
c.splice(i, 1, ...files);
|
||||
commands.push({ cmd: c, cwd, files });
|
||||
} else if ((i = cmd.indexOf("$file")) != -1) {
|
||||
for (const file of files) {
|
||||
const c = cmd.slice();
|
||||
c.splice(i, 1, file);
|
||||
commands.push({ cmd: c, cwd, files: [file] });
|
||||
}
|
||||
} else {
|
||||
throw new Error(`Formatter ${fmt} has incorrectly configured command.`);
|
||||
}
|
||||
}
|
||||
if (commands.length === 0) {
|
||||
console.error("No formattable files");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// -- dry run mode --
|
||||
if (dryRun) {
|
||||
for (const { cmd } of commands) {
|
||||
console.info(cmd);
|
||||
}
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// -- user interface --
|
||||
let filesComplete = 0;
|
||||
let lastFile = commands[0].cmd;
|
||||
const syncStart = "\u001B[?2026h";
|
||||
const syncEnd = "\u001B[?2026l";
|
||||
let buffer = "";
|
||||
let statusVisible = false;
|
||||
const tty = process.stderr.isTTY;
|
||||
function writeStatus() {
|
||||
if (!tty) return;
|
||||
clearStatus();
|
||||
buffer ||= syncStart;
|
||||
buffer += `${filesComplete}/${totalFiles} - ${lastFile}`;
|
||||
statusVisible = true;
|
||||
}
|
||||
function clearStatus() {
|
||||
if (!tty) return;
|
||||
if (!statusVisible) return;
|
||||
buffer ||= syncStart;
|
||||
buffer += "\r\x1b[2K\r";
|
||||
statusVisible = false;
|
||||
}
|
||||
function flush() {
|
||||
if (!buffer) return;
|
||||
const width = Math.max(1, process.stderr.columns - 1);
|
||||
process.stderr.write(
|
||||
buffer.split("\n").map((x) => x.slice(0, width)).join("\n") + syncEnd,
|
||||
);
|
||||
buffer = "";
|
||||
}
|
||||
|
||||
// -- async process queue --
|
||||
let running = 0;
|
||||
/** @param cmd {{ cmd: string, cwd: string, files: string[] }} */
|
||||
function run({ cmd, cwd, files }) {
|
||||
running += 1;
|
||||
let c = child_process.spawn(cmd[0], cmd.slice(1), {
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
cwd,
|
||||
});
|
||||
const relatives = new Set(
|
||||
files.map((file) => file.startsWith(cwd) ? path.relative(cwd, file) : file),
|
||||
);
|
||||
function onLine(line) {
|
||||
for (const file of relatives) {
|
||||
if (line.includes(file)) {
|
||||
filesComplete += 1;
|
||||
relatives.delete(file);
|
||||
lastFile = path.relative(process.cwd(), path.resolve(cwd, file));
|
||||
if (tty) {
|
||||
writeStatus();
|
||||
flush();
|
||||
} else {
|
||||
console.info(lastFile);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
let errBuffer = "";
|
||||
let exited = false;
|
||||
c.on("error", (e) => {
|
||||
let message = "";
|
||||
if (e?.code === "ENOENT") {
|
||||
if (cmd[0].includes("/")) {
|
||||
running -= 1;
|
||||
run({ cmd: [path.basename(cmd[0]), ...cmd.slice(1)], cwd, files });
|
||||
return;
|
||||
}
|
||||
message = `${cmd[0]} is not installed`;
|
||||
} else {
|
||||
message = String(e?.message ?? e);
|
||||
}
|
||||
clearStatus();
|
||||
const filesConcise =
|
||||
path.relative(".", path.resolve(cwd, relatives.keys().next().value)) + (
|
||||
relatives.size > 1 ? ` and ${relatives.size - 1} more` : ""
|
||||
);
|
||||
buffer += errBuffer + `error: ${message}, cannot format ${filesConcise}.\n`;
|
||||
flush();
|
||||
exited = true;
|
||||
runNext();
|
||||
});
|
||||
readline.createInterface(c.stderr).addListener("line", (line) => {
|
||||
errBuffer += line + "\n";
|
||||
onLine(line);
|
||||
});
|
||||
readline.createInterface(c.stdout).addListener("line", onLine);
|
||||
c.on("exit", (code, signal) => {
|
||||
if (exited) return;
|
||||
exited = true;
|
||||
if (code !== 0) {
|
||||
clearStatus();
|
||||
const exitStatus = code != null ? `code ${code}` : `signal ${signal}`;
|
||||
buffer += errBuffer + `error: ${cmd[0]} exited with ${exitStatus}\n`;
|
||||
flush();
|
||||
} else {
|
||||
filesComplete += relatives.size;
|
||||
if (relatives.size) {
|
||||
lastFile = path.relative(
|
||||
".",
|
||||
path.resolve(cwd, relatives.keys().next().value),
|
||||
);
|
||||
}
|
||||
writeStatus();
|
||||
flush();
|
||||
}
|
||||
runNext();
|
||||
});
|
||||
function runNext() {
|
||||
running -= 1;
|
||||
const next = commands.pop();
|
||||
if (next) run(next);
|
||||
else if (running == 0) {
|
||||
clearStatus();
|
||||
flush();
|
||||
console.info(
|
||||
`Formatted ${filesComplete} file${filesComplete !== 1 ? "s" : ""}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < navigator.hardwareConcurrency; i++) {
|
||||
const cmd = commands.pop();
|
||||
if (cmd) run(cmd);
|
||||
else break;
|
||||
}
|
||||
|
||||
// -- library functions --
|
||||
|
||||
/** @param file {string} */
|
||||
function walk(file) {
|
||||
file = path.resolve(file);
|
||||
try {
|
||||
if (fs.statSync(file).isDirectory()) {
|
||||
const exclude = getGitIgnores(file);
|
||||
const read = fs
|
||||
.globSync(escapeGlob(file) + "/{**,.**}", {
|
||||
exclude,
|
||||
withFileTypes: true,
|
||||
})
|
||||
.filter((file) => !file.isDirectory())
|
||||
.map((file) => path.join(file.parentPath, file.name));
|
||||
files.push(...read);
|
||||
} else {
|
||||
files.push(file);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(
|
||||
`Failed to stat ${file}: ${err?.code ?? err?.message ?? err}`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/** @param dir {string} @returns {Array<string>} */
|
||||
function readDir(dir) {
|
||||
dir = path.resolve(dir);
|
||||
let contents = dirs.get(dir);
|
||||
if (!contents) {
|
||||
try {
|
||||
contents = fs.readdirSync(dir);
|
||||
} catch {
|
||||
contents = [];
|
||||
}
|
||||
dirs.set(dir, contents);
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
|
||||
/** @param dir {string} @returns {string} */
|
||||
function pickFormatter(file) {
|
||||
const lang = extToLanguage[path.extname(file)];
|
||||
const dir = path.dirname(file);
|
||||
let c = walkUp(dir, (x) => cached.get(x) ?? cached.get(`${x}:${lang}`));
|
||||
if (c) return c;
|
||||
|
||||
const possible = Object.keys(formatters).filter(
|
||||
(x) =>
|
||||
Array.isArray(formatters[x].languages) &&
|
||||
formatters[x].languages.includes(lang),
|
||||
);
|
||||
const order = [...multis, ...possible];
|
||||
return walkUp(dir, (x) => {
|
||||
const children = readDir(x);
|
||||
for (const fmt of order) {
|
||||
let matches = false;
|
||||
if (formatters[fmt].files === true) {
|
||||
matches = true;
|
||||
}
|
||||
if (!matches) {
|
||||
const filesToCheck = Array.isArray(formatters[fmt].files)
|
||||
? formatters[fmt].files.map((base) => ({ base, contents: true }))
|
||||
: Object.entries(formatters[fmt].files)
|
||||
.map(([base, contents]) => ({ base, contents }));
|
||||
for (const { base, contents } of filesToCheck) {
|
||||
if (base.includes("/")) {
|
||||
matches = readDir(path.join(x, path.dirname(base))) //
|
||||
.includes(path.basename(base));
|
||||
} else {
|
||||
matches = children.includes(base);
|
||||
}
|
||||
if (matches && typeof contents === "function") {
|
||||
matches = !!contents(fs.readFileSync(path.join(x, base), "utf-8"));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (matches) {
|
||||
const formatterId = `${fmt}\0${x}`;
|
||||
const k = multis.includes(fmt) ? x : `${x}:${lang}`;
|
||||
cached.set(k, formatterId);
|
||||
return formatterId;
|
||||
}
|
||||
}
|
||||
}) ?? possible[0];
|
||||
}
|
||||
|
||||
/** @param dir {string} @param find {(x: string) => any} */
|
||||
function walkUp(dir, find) {
|
||||
do {
|
||||
const found = find(dir);
|
||||
if (found != null) return found;
|
||||
const parent = path.dirname(dir);
|
||||
if (parent === dir) break;
|
||||
dir = parent;
|
||||
} while (true);
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @param dir {string} */
|
||||
function getGitIgnores(dir) {
|
||||
if (!gitignore) return [];
|
||||
if (dir.endsWith(sep)) dir = dir.slice(0, -1);
|
||||
const files = fs.globSync(`${dir}${sep}{**${sep}*${sep},}.gitignore`, {});
|
||||
const referenced = [];
|
||||
for (const abs of files) {
|
||||
const dir = path.dirname(abs);
|
||||
referenced.push(dir);
|
||||
if (!gitignores.has(dir)) gitignores.set(dir, readGitIgnore(dir));
|
||||
}
|
||||
do {
|
||||
const parent = path.dirname(dir);
|
||||
if (parent === dir || fs.existsSync(path.join(dir, ".git"))) break;
|
||||
referenced.push(parent);
|
||||
if (!gitignores.has(parent)) gitignores.set(parent, readGitIgnore(parent));
|
||||
dir = parent;
|
||||
} while (true);
|
||||
return referenced.flatMap((root) =>
|
||||
(gitignores.get(root) ?? [])
|
||||
.filter((x) => x[0] !== "!")
|
||||
.map(
|
||||
(rule) => `${root}${sep}${rule[0] === "/" ? "" : `**${sep}`}${rule}`,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/** @param dir {string} */
|
||||
function readGitIgnore(dir) {
|
||||
try {
|
||||
return fs
|
||||
.readFileSync(`${dir}${sep}.gitignore`, "utf-8")
|
||||
.split("\n")
|
||||
.map((line) => line.replace(/#.*$/, "").trim())
|
||||
.filter(Boolean);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function escapeGlob(str) {
|
||||
return str.replace(/[\\*,{}]/g, "\\$&");
|
||||
}
|
||||
|
||||
import * as child_process from "node:child_process";
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import * as readline from "node:readline";
|
||||
import * as events from "node:events";
|
||||
import process from "node:process";
|
||||
import assert from "node:assert";
|
69
flake.nix
69
flake.nix
|
@ -49,7 +49,7 @@
|
|||
}@inputs:
|
||||
let
|
||||
inherit (nixpkgs) lib;
|
||||
# TODO: apply these overlays sooner and remove uses of legacyPackages elsewhere.
|
||||
|
||||
overlays = [
|
||||
inputs.zig.overlays.default
|
||||
inputs.rust-overlay.overlays.default
|
||||
|
@ -66,55 +66,42 @@
|
|||
};
|
||||
});
|
||||
})
|
||||
|
||||
# custom packages
|
||||
(_: pkgs: {
|
||||
autofmt = pkgs.callPackage ./packages/autofmt.nix { };
|
||||
})
|
||||
];
|
||||
|
||||
# Users of this flake currently use x86_64 Linux and Apple Silicon
|
||||
systems = [
|
||||
"x86_64-linux"
|
||||
"aarch64-darwin"
|
||||
];
|
||||
# We only use x86_64 on Linux and Apple Silicon Macs
|
||||
# Overlays are applied here, as early as possible.
|
||||
getNixPkgs = system: import nixpkgs { inherit system overlays; };
|
||||
systems = {
|
||||
"x86_64-linux" = getNixPkgs "x86_64-linux";
|
||||
"aarch64-darwin" = getNixPkgs "aarch64-darwin";
|
||||
};
|
||||
forAllSystems =
|
||||
f:
|
||||
builtins.listToAttrs (
|
||||
builtins.map (system: {
|
||||
name = system;
|
||||
value = f (
|
||||
inputs
|
||||
// {
|
||||
inherit system;
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
}
|
||||
);
|
||||
}) systems
|
||||
);
|
||||
builtins.mapAttrs # #
|
||||
(system: pkgs: f (inputs // { inherit system pkgs; }))
|
||||
systems;
|
||||
|
||||
mkSystem = import ./lib/mkSystem.nix {
|
||||
inherit
|
||||
overlays
|
||||
nixpkgs
|
||||
inputs
|
||||
mkNeovim
|
||||
;
|
||||
};
|
||||
mkNeovim = import ./lib/mkNeovim.nix {
|
||||
inherit
|
||||
self
|
||||
overlays
|
||||
nixpkgs
|
||||
inputs
|
||||
;
|
||||
};
|
||||
# Library Functions
|
||||
mkNeovim = import ./lib/mkNeovim.nix { inherit self inputs; };
|
||||
mkSystem = import ./lib/mkSystem.nix { inherit inputs mkNeovim overlays; };
|
||||
in
|
||||
rec {
|
||||
inherit self;
|
||||
# "nix fmt"
|
||||
formatter = forAllSystems (inputs: inputs.pkgs.nixfmt-tree);
|
||||
formatter = forAllSystems (inputs: inputs.pkgs.autofmt);
|
||||
packages = forAllSystems (
|
||||
{ system, ... }:
|
||||
{ system, pkgs, ... }:
|
||||
{
|
||||
nvim-chloe = mkNeovim "chloe" system;
|
||||
nvim-natalie = mkNeovim "natalie" system;
|
||||
nvim-julia = mkNeovim "julia" system;
|
||||
nvim-chloe = mkNeovim "chloe" pkgs;
|
||||
nvim-natalie = mkNeovim "natalie" pkgs;
|
||||
nvim-julia = mkNeovim "julia" pkgs;
|
||||
|
||||
inherit (pkgs) autofmt;
|
||||
}
|
||||
// lib.optionalAttrs (system == "aarch64-darwin") {
|
||||
# "nix run .#darwin-rebuild"
|
||||
|
@ -127,16 +114,12 @@
|
|||
user = "natalie";
|
||||
host = "desktop";
|
||||
system = "x86_64-linux";
|
||||
extraModules = [
|
||||
];
|
||||
};
|
||||
# natalie's laptop
|
||||
darwinConfigurations."Natalies-MacBook-Air" = mkSystem "Natalies-MacBook-Air" {
|
||||
user = "natalie";
|
||||
host = "laptop";
|
||||
system = "aarch64-darwin";
|
||||
extraModules = [
|
||||
];
|
||||
};
|
||||
|
||||
# chloe's mac studio "sandwich"
|
||||
|
|
|
@ -1,13 +1,7 @@
|
|||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
# TODO: apply overlays here
|
||||
overlays,
|
||||
inputs,
|
||||
}:
|
||||
user: system:
|
||||
{ self, inputs }:
|
||||
user: pkgs:
|
||||
let
|
||||
darwin = nixpkgs.lib.strings.hasSuffix "-darwin" system;
|
||||
darwin = pkgs.lib.strings.hasSuffix "-darwin" pkgs.system;
|
||||
|
||||
host = {
|
||||
inherit darwin;
|
||||
|
@ -18,7 +12,7 @@ let
|
|||
userConfig = import (userDir + "/user.nix");
|
||||
in
|
||||
(inputs.nvf.lib.neovimConfiguration {
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
inherit pkgs;
|
||||
modules = builtins.filter (f: f != null) [
|
||||
(../users + ("/" + user + "/vim.nix"))
|
||||
../modules/neovim
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# This function creates a NixOS system based on our VM setup for a
|
||||
# particular architecture.
|
||||
{
|
||||
nixpkgs,
|
||||
overlays,
|
||||
inputs,
|
||||
mkNeovim,
|
||||
|
@ -14,11 +13,11 @@ name:
|
|||
extraModules ? [ ],
|
||||
}:
|
||||
let
|
||||
darwin = nixpkgs.lib.strings.hasSuffix "-darwin" system;
|
||||
darwin = inputs.nixpkgs.lib.strings.hasSuffix "-darwin" system;
|
||||
getInputModule = a: b: inputs.${a}.${if darwin then "darwinModules" else "nixosModules"}.${b};
|
||||
|
||||
# NixOS vs nix-darwin functions
|
||||
systemFunc = if darwin then inputs.darwin.lib.darwinSystem else nixpkgs.lib.nixosSystem;
|
||||
systemFunc = if darwin then inputs.darwin.lib.darwinSystem else inputs.nixpkgs.lib.nixosSystem;
|
||||
|
||||
userDir = ../users + "/${user}";
|
||||
userConfig = import (userDir + "/user.nix");
|
||||
|
@ -69,11 +68,12 @@ let
|
|||
mainHomeImports = builtins.filter (f: f != null) [
|
||||
(pathOrNull userHomePath)
|
||||
(pathOrNull hostHomePath)
|
||||
{
|
||||
home.packages = [
|
||||
(mkNeovim user system)
|
||||
];
|
||||
}
|
||||
(
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
home.packages = [ (mkNeovim user pkgs) ];
|
||||
}
|
||||
)
|
||||
inputs.nvf.homeManagerModules.default
|
||||
inputs.android-nixpkgs.hmModule
|
||||
];
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
...
|
||||
}:
|
||||
{
|
||||
imports = [ ./options.nix ];
|
||||
imports = [
|
||||
./options.nix
|
||||
./formatter.nix
|
||||
];
|
||||
# based on default options from upstream:
|
||||
# https://github.com/NotAShelf/nvf/blob/main/configuration.nix
|
||||
#
|
||||
|
@ -107,20 +110,6 @@
|
|||
format.type = "nixfmt"; # looks so much nicer
|
||||
};
|
||||
};
|
||||
formatter.conform-nvim = {
|
||||
enable = true;
|
||||
setupOpts = {
|
||||
formatters_by_ft = {
|
||||
typescript = [ "deno_fmt" ];
|
||||
typescriptreact = [ "deno_fmt" ];
|
||||
javascript = [ "deno_fmt" ];
|
||||
javascriptreact = [ "deno_fmt" ];
|
||||
};
|
||||
formatters.deno_fmt = {
|
||||
command = lib.meta.getExe pkgs.deno;
|
||||
};
|
||||
};
|
||||
};
|
||||
filetree = {
|
||||
neo-tree = {
|
||||
enable = false;
|
||||
|
@ -154,7 +143,6 @@
|
|||
enable = true;
|
||||
setupOpts = {
|
||||
bigfile.enable = true;
|
||||
explorer.replace_netrw = true;
|
||||
dashboard = {
|
||||
preset.keys = [
|
||||
{
|
||||
|
@ -209,7 +197,6 @@
|
|||
picker = {
|
||||
enable = true;
|
||||
sources = {
|
||||
explorer = { };
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
34
modules/neovim/formatter.nix
Normal file
34
modules/neovim/formatter.nix
Normal file
|
@ -0,0 +1,34 @@
|
|||
{ lib, pkgs, ... }:
|
||||
{
|
||||
vim.formatter.conform-nvim = {
|
||||
enable = true;
|
||||
setupOpts = {
|
||||
formatters_by_ft =
|
||||
let
|
||||
autofmt = lib.mkLuaInline ''{"autofmt",stop_after_first=true}'';
|
||||
in
|
||||
{
|
||||
css = autofmt;
|
||||
html = autofmt;
|
||||
javascript = autofmt;
|
||||
javascriptreact = autofmt;
|
||||
json = autofmt;
|
||||
jsonc = autofmt;
|
||||
markdown = autofmt;
|
||||
nix = autofmt;
|
||||
rust = autofmt;
|
||||
typescript = autofmt;
|
||||
typescriptreact = autofmt;
|
||||
yaml = autofmt;
|
||||
};
|
||||
formatters.autofmt = {
|
||||
"inherit" = false;
|
||||
args = [
|
||||
"--stdio"
|
||||
"$FILENAME"
|
||||
];
|
||||
command = "${pkgs.autofmt}/bin/autofmt";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -7,7 +7,10 @@ let
|
|||
nvidiaDriverChannel = config.boot.kernelPackages.nvidiaPackages.latest;
|
||||
in
|
||||
{
|
||||
services.xserver.videoDrivers = [ "nvidia" "amdgpu"];
|
||||
services.xserver.videoDrivers = [
|
||||
"nvidia"
|
||||
"amdgpu"
|
||||
];
|
||||
|
||||
nixpkgs.config = {
|
||||
nvidia.acceptLicense = true;
|
||||
|
@ -46,17 +49,17 @@ in
|
|||
finegrained = true; # More precise power consumption control
|
||||
};
|
||||
|
||||
prime = {
|
||||
offload = {
|
||||
enable = true;
|
||||
enableOffloadCmd = true;
|
||||
};
|
||||
|
||||
nvidiaBusId = "PCI:1:0:0";
|
||||
amdgpuBusId = "PCI:15:0:0";
|
||||
|
||||
prime = {
|
||||
offload = {
|
||||
enable = true;
|
||||
enableOffloadCmd = true;
|
||||
};
|
||||
|
||||
nvidiaBusId = "PCI:1:0:0";
|
||||
amdgpuBusId = "PCI:15:0:0";
|
||||
|
||||
};
|
||||
|
||||
# Use the NVidia open source kernel module (not to be confused with the
|
||||
# independent third-party "nouveau" open source driver).
|
||||
# Currently alpha-quality/buggy, so false is currently the recommended setting.
|
||||
|
|
|
@ -10,13 +10,12 @@
|
|||
# Set your time zone.
|
||||
time.timeZone = user.timeZone;
|
||||
|
||||
home-manager.users.${user.username}.home.sessionVariables =
|
||||
{
|
||||
EDITOR = user.editor;
|
||||
TERMINAL = user.term;
|
||||
}
|
||||
// lib.optionalAttrs (user ? "browser") {
|
||||
BROWSER = user.browser;
|
||||
};
|
||||
home-manager.users.${user.username}.home.sessionVariables = {
|
||||
EDITOR = user.editor;
|
||||
TERMINAL = user.term;
|
||||
}
|
||||
// lib.optionalAttrs (user ? "browser") {
|
||||
BROWSER = user.browser;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
14
packages/autofmt.nix
Normal file
14
packages/autofmt.nix
Normal file
|
@ -0,0 +1,14 @@
|
|||
{ pkgs, ... }:
|
||||
pkgs.writeShellApplication {
|
||||
name = "autofmt";
|
||||
runtimeInputs = with pkgs; [
|
||||
# include only a couple of formatters by default
|
||||
deno
|
||||
nixfmt-rfc-style
|
||||
dprint
|
||||
rustfmt
|
||||
zig
|
||||
clang-tools
|
||||
];
|
||||
text = ''exec deno run -A ${../files/autofmt.js} "$@"'';
|
||||
}
|
|
@ -78,4 +78,3 @@ configuration.
|
|||
nix run .#nvim-natalie # run by user
|
||||
./nvim # run based on your username
|
||||
```
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ in
|
|||
ripgrep
|
||||
uv
|
||||
nh
|
||||
pkgs.autofmt
|
||||
];
|
||||
# packages to install for desktop environments (non-server)
|
||||
desktop = [
|
||||
|
|
|
@ -2,13 +2,18 @@
|
|||
# your system. Help is available in the configuration.nix(5) man page, on
|
||||
# https://search.nixos.org/options and in the NixOS manual (`nixos-help`).
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
imports =
|
||||
[ # Include the results of the hardware scan.
|
||||
./hardware-configuration.nix
|
||||
];
|
||||
imports = [
|
||||
# Include the results of the hardware scan.
|
||||
./hardware-configuration.nix
|
||||
];
|
||||
|
||||
# Use the systemd-boot EFI boot loader.
|
||||
boot.loader.efi.canTouchEfiVariables = true;
|
||||
|
@ -39,7 +44,6 @@
|
|||
# Enable the GNOME Desktop Environment.
|
||||
services.xserver.displayManager.gdm.enable = true;
|
||||
services.xserver.desktopManager.gnome.enable = true;
|
||||
|
||||
|
||||
# Configure keymap in X11
|
||||
# services.xserver.xkb.layout = "us";
|
||||
|
@ -124,4 +128,3 @@
|
|||
# For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion .
|
||||
system.stateVersion = "25.05"; # Did you read the comment?
|
||||
}
|
||||
|
||||
|
|
|
@ -62,8 +62,8 @@ pages:
|
|||
location: London, United Kingdom
|
||||
units: metric # alternatively "imperial"
|
||||
hour-format: 12h # alternatively "24h"
|
||||
# Optionally hide the location from being displayed in the widget
|
||||
# hide-location: true
|
||||
# Optionally hide the location from being displayed in the widget
|
||||
# hide-location: true
|
||||
|
||||
- type: markets
|
||||
# The link to go to when clicking on the symbol in the UI,
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
withPython3 = true;
|
||||
python3Packages = [ "pynvim" ];
|
||||
|
||||
|
||||
autocmds = [
|
||||
#Autocommand to fall back to treesitter folding if LSP doesnt support it
|
||||
{
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
{ ... }:
|
||||
let
|
||||
mkKeymap = mode: key: action: desc: {
|
||||
inherit mode;
|
||||
inherit key action desc;
|
||||
};
|
||||
n = mkKeymap "n"; # normal mode
|
||||
let
|
||||
mkKeymap = mode: key: action: desc: {
|
||||
inherit mode;
|
||||
inherit key action desc;
|
||||
};
|
||||
n = mkKeymap "n"; # normal mode
|
||||
in
|
||||
{
|
||||
vim = {
|
||||
|
|
Loading…
Reference in a new issue