authorgravatar for nmarks413@gmail.comNatalie Marks <nmarks413@gmail.com> 2025-08-17 21:18:59-07:00
committergravatar for nmarks413@gmail.comNatalie Marks <nmarks413@gmail.com> 2025-08-17 21:18:59-07:00
logd3c18bc88cacbffe58f2d04e27cfcfb46d918c6e
tree2c236938c6c18a3f9bcd77ff67cde44a5d880ef5
parenta48259e526ef97bd2a78c0afab31b012ead79853
parentff5e23efc2d910484d7f44da0a67dee52035133f
signature Commit is signed but in an unrecognized format.

Merge branch 'main' of paperclover.dev:nix/config


15 files changed, 722 insertions(+), 113 deletions(-)

files/autofmt.js created+593
...@@ -0,0 +1,593 @@
1#!/usr/bin/env node
2// autofmt v1 - https://paperclover.dev/nix/config/src/branch/main/files/autofmt.js
3//
4// Different codebases use different formatters. Autofmt looks for project
5// configuration to pick the correct formatter, allowing an editor to simply
6// point to this script and ambiguities resolved. This single file program
7// depends only on Node.js v22.
8//
9// When using this repository's Nix-based Neovim configuration, autofmt is
10// automatically configured as the default formatter. To configure manually,
11// set the editor's formatter to run `autofmt --stdio=<filename>`. The filename
12// is used to determine which formatter to invoke.
13//
14// With `conform.nvim`:
15// formatters = {
16// autofmt = { command = "autofmt" }
17// }
18//
19// With Zed:
20// "formatter": {
21// "external": {
22// "command": "autofmt",
23// "arguments": ["--stdio", "{buffer_path}"]
24// }
25// },
26//
27// @ts-nocheck
28
29// -- definitions --
30const extensions = {
31 c: [".c", ".h"],
32 cpp: [".cpp", ".cc", ".cxx", ".hpp", ".hxx", ".hh"],
33 css: [".css"],
34 html: [".html"],
35 javascript: [".js", ".ts", ".cjs", ".cts", ".mjs", ".mts", ".jsx", ".tsx"],
36 json: [".json", ".jsonc"],
37 markdown: [".md", ".markdown"],
38 mdx: [".mdx"],
39 nix: [".nix"],
40 rust: [".rs"],
41 toml: [".toml"],
42 yaml: [".yml", ".yaml"],
43 zig: [".zig"],
44};
45const formatters = {
46 // this object is sorted by priority
47 //
48 // `languages: true` are multiplexers that apply to all listed
49 // languages, as it is assumed a project will setup their multiplexer
50 // correctly for all tracked files.
51 //
52 // if a formatter doesnt match a config file, the first one is picked
53 // as a default, making things like `deno fmt file.ts` or `clang-format`
54 // the default when there are no config files.
55 dprint: {
56 languages: true,
57 files: ["dprint.json", "dprint.jsonc"],
58 cmd: ["dprint", "fmt", "--", "$files"],
59 stdin: (file) => ["dprint", "fmt", "--stdin", file],
60 },
61 treefmt: {
62 languages: true,
63 files: ["treefmt.toml", ".treefmt.toml"],
64 cmd: ["treefmt", "--", "$files"],
65 stdin: (file) => ["treefmt", "--stdin", file],
66 },
67 // -- web ecosystem --
68 deno: {
69 languages: ["javascript", "markdown", "json", "yaml", "html", "css"],
70 files: ["dprint.json", "dprint.jsonc"],
71 cmd: ["deno", "fmt", "--", "$files"],
72 stdin: (file) => ["deno", "fmt", "--ext", path.extname(file).slice(1), "-"],
73 },
74 prettier: {
75 languages: ["javascript", "markdown", "json", "yaml", "mdx", "html", "css"],
76 files: ["node_modules/.bin/prettier"],
77 cmd: ["node_modules/.bin/prettier", "--write", "--", "$files"],
78 stdin: (file) => ["node_modules/.bin/prettier", "--stdin-filepath", file],
79 },
80 biome: {
81 languages: ["javascript", "json", "css"],
82 files: ["node_modules/.bin/biome"],
83 cmd: ["node_modules/.bin/biome", "format", "--", "$files"],
84 stdin: (file) => [
85 "node_modules/bin/biome",
86 "format",
87 `--stdin-file-path=${file}`,
88 ],
89 },
90 "nixfmt-rfc-style": {
91 languages: ["nix"],
92 files: {
93 "flake.nix": (contents) => contents.includes("nixfmt"),
94 },
95 cmd: ["nixfmt", "--", "$files"],
96 stdin: (file) => ["nixfmt", `--filename=${file}`],
97 },
98 alejandra: {
99 languages: ["nix"],
100 files: {
101 "flake.nix": (contents) => contents.includes("alejandra"),
102 },
103 cmd: ["alejandra", "--", "$files"],
104 stdin: () => ["alejandra"],
105 },
106 clang: {
107 languages: ["c", "cpp"],
108 files: true,
109 cmd: ["clang-format", "--", "$files"],
110 stdin: (file) => ["clang-format", `--assume-filename=${file}`],
111 },
112 zig: {
113 languages: ["zig"],
114 files: true,
115 cmd: ["zig", "fmt", "$files"],
116 stdin: (file) => [
117 "zig",
118 "fmt",
119 "--stdin",
120 ...file.endsWith(".zon") ? ["--zon"] : [],
121 ],
122 },
123 rustfmt: {
124 languages: ["rust"],
125 files: true,
126 cmd: ["rustfmt", "--", "$files"],
127 stdin: () => ["rustfmt"],
128 },
129 taplo: {
130 languages: ["toml"],
131 files: ["taplo.toml"],
132 cmd: ["taplo", "format", "--", "$files"],
133 cmd: () => ["taplo", "format", "-"],
134 },
135};
136
137// -- cli --
138if (!fs.globSync) {
139 console.error(`error: autofmt must be run with Node.js v22 or newer`);
140 process.exit(1);
141}
142const [, bin, ...argv] = process.argv;
143let inputs = [];
144let globalExclude = [];
145let gitignore = true;
146let dryRun = false;
147let stdio = null;
148let excludes = [".git"];
149while (argv.length > 0) {
150 const arg = argv.shift();
151 if (arg === "-h" || arg === "--help") usage();
152 else if (arg === "--no-gitignore") gitignore = false;
153 else if (arg === "--dry-run") dryRun = true;
154 else if (arg.match(/^--stdio=./)) {
155 if (stdio) {
156 console.error("error: can only pass --stdio once");
157 usage();
158 }
159 stdio = arg.slice("--stdio=".length);
160 } else if (arg === "--stdio") {
161 const value = argv.shift();
162 if (!value) {
163 console.error("error: missing value for --stdio");
164 usage();
165 }
166 if (stdio) {
167 console.error("error: can only pass --stdio once");
168 usage();
169 }
170 stdio = value;
171 } else if (arg.match(/^--exclude=./)) {
172 excludes.push(arg.slice("--exclude=".length));
173 } else if (arg === "--") {
174 inputs.push(...argv);
175 break;
176 } else if (arg.startsWith("-")) {
177 console.error("error: unknown option " + JSON.stringify(arg));
178 usage();
179 } else inputs.push(arg);
180}
181function usage() {
182 const exe = path.basename(bin);
183 console.error(`usage: ${exe} [...files or directories]`);
184 console.error(``);
185 console.error(`uses the right formatter for the job (by scanning config)`);
186 console.error(`when autofmt reads dirs, it will respect gitignore`);
187 console.error(``);
188 console.error(`to format current directory recursively, run '${exe} .'`);
189 console.error(``);
190 console.error(`options:`);
191 console.error(` --dry-run print commands instead of running them`);
192 console.error(` --no-gitignore do not read '.gitignore'`);
193 console.error(` --exclude=<glob> add an exclusion glob`);
194 console.error(` --stdio=<filename> read/write contents via stdin/stdout`);
195 console.error(``);
196 process.exit(1);
197}
198if (inputs.length === 0 && !stdio) usage();
199if (stdio && inputs.length > 0) {
200 console.error("error: stdio mode only operates on one file");
201 process.exit(1);
202}
203const { sep } = path;
204
205// -- disable warnings --
206const { emit: originalEmit } = process;
207const warnings = ["ExperimentalWarning"];
208process.emit = function (event, error) {
209 return event === "warning" && warnings.includes(error.name)
210 ? false
211 : originalEmit.apply(process, arguments);
212};
213
214// -- vars --
215const extToLanguage = Object.fromEntries(
216 Object.entries(extensions).flatMap(([lang, exts]) =>
217 exts.map((ext) => [ext, lang])
218 ),
219);
220const multis = Object.keys(formatters).filter(
221 (x) => formatters[x].languages === true,
222);
223const files = [];
224const gitignores = new Map();
225const dirs = new Map();
226const cached = new Map();
227
228// -- stdin mode --
229if (stdio) {
230 const fmtWithPath = pickFormatter(stdio);
231 if (!fmtWithPath) {
232 console.error(`No formatter configured for ${path.relative(".", stdio)}`);
233 process.exit(1);
234 }
235 let [fmt, cwd] = fmtWithPath.split("\0");
236 let cmd = formatters[fmt].stdin(stdio);
237 cwd ??= process.cwd();
238 if (dryRun) {
239 console.info(cmd.join(" "));
240 process.exit(0);
241 }
242 const proc = child_process.spawn(cmd[0], cmd.slice(1), {
243 stdio: ["inherit", "inherit", "inherit"],
244 });
245 proc.on("error", (e) => {
246 let message = "";
247 if (e?.code === "ENOENT") {
248 message = `${cmd[0]} is not installed`;
249 } else {
250 message = String(e?.message ?? e);
251 }
252 console.error(`error: ${message}`);
253 process.exit(1);
254 });
255 const [code] = await events.once(proc, "exit");
256 process.exit(code ?? 1);
257}
258
259// -- decide what formatters to run
260inputs = inputs.map((x) => path.resolve(x));
261inputs.forEach(walk);
262const toRun = new Map();
263for (const file of new Set(files)) {
264 const fmt = pickFormatter(file);
265 if (!fmt) {
266 if (inputs.includes(file)) {
267 console.warn(`No formatter configured for ${path.relative(".", file)}`);
268 }
269 continue;
270 }
271 let list = toRun.get(fmt);
272 list ?? toRun.set(fmt, list = []);
273 list.push(file);
274}
275
276// -- create a list of commands --
277const commands = [];
278let totalFiles = 0;
279for (const [fmtWithPath, files] of toRun) {
280 let [fmt, cwd] = fmtWithPath.split("\0");
281 let { cmd } = formatters[fmt];
282 if (cwd) cmd = [path.join(cwd, cmd[0]), ...cmd.slice(1)];
283 cwd ??= process.cwd();
284 let i = 0;
285 totalFiles += files.length;
286 if ((i = cmd.indexOf("$files")) != -1) {
287 const c = cmd.slice();
288 c.splice(i, 1, ...files);
289 commands.push({ cmd: c, cwd, files });
290 } else if ((i = cmd.indexOf("$file")) != -1) {
291 for (const file of files) {
292 const c = cmd.slice();
293 c.splice(i, 1, file);
294 commands.push({ cmd: c, cwd, files: [file] });
295 }
296 } else {
297 throw new Error(`Formatter ${fmt} has incorrectly configured command.`);
298 }
299}
300if (commands.length === 0) {
301 console.error("No formattable files");
302 process.exit(0);
303}
304
305// -- dry run mode --
306if (dryRun) {
307 for (const { cmd } of commands) {
308 console.info(cmd);
309 }
310 process.exit(0);
311}
312
313// -- user interface --
314let filesComplete = 0;
315let lastFile = commands[0].cmd;
316const syncStart = "\u001B[?2026h";
317const syncEnd = "\u001B[?2026l";
318let buffer = "";
319let statusVisible = false;
320const tty = process.stderr.isTTY;
321function writeStatus() {
322 if (!tty) return;
323 clearStatus();
324 buffer ||= syncStart;
325 buffer += `${filesComplete}/${totalFiles} - ${lastFile}`;
326 statusVisible = true;
327}
328function clearStatus() {
329 if (!tty) return;
330 if (!statusVisible) return;
331 buffer ||= syncStart;
332 buffer += "\r\x1b[2K\r";
333 statusVisible = false;
334}
335function flush() {
336 if (!buffer) return;
337 const width = Math.max(1, process.stderr.columns - 1);
338 process.stderr.write(
339 buffer.split("\n").map((x) => x.slice(0, width)).join("\n") + syncEnd,
340 );
341 buffer = "";
342}
343
344// -- async process queue --
345let running = 0;
346/** @param cmd {{ cmd: string, cwd: string, files: string[] }} */
347function run({ cmd, cwd, files }) {
348 running += 1;
349 let c = child_process.spawn(cmd[0], cmd.slice(1), {
350 stdio: ["ignore", "pipe", "pipe"],
351 cwd,
352 });
353 const relatives = new Set(
354 files.map((file) => file.startsWith(cwd) ? path.relative(cwd, file) : file),
355 );
356 function onLine(line) {
357 for (const file of relatives) {
358 if (line.includes(file)) {
359 filesComplete += 1;
360 relatives.delete(file);
361 lastFile = path.relative(process.cwd(), path.resolve(cwd, file));
362 if (tty) {
363 writeStatus();
364 flush();
365 } else {
366 console.info(lastFile);
367 }
368 return;
369 }
370 }
371 }
372 let errBuffer = "";
373 let exited = false;
374 c.on("error", (e) => {
375 let message = "";
376 if (e?.code === "ENOENT") {
377 if (cmd[0].includes("/")) {
378 running -= 1;
379 run({ cmd: [path.basename(cmd[0]), ...cmd.slice(1)], cwd, files });
380 return;
381 }
382 message = `${cmd[0]} is not installed`;
383 } else {
384 message = String(e?.message ?? e);
385 }
386 clearStatus();
387 const filesConcise =
388 path.relative(".", path.resolve(cwd, relatives.keys().next().value)) + (
389 relatives.size > 1 ? ` and ${relatives.size - 1} more` : ""
390 );
391 buffer += errBuffer + `error: ${message}, cannot format ${filesConcise}.\n`;
392 flush();
393 exited = true;
394 runNext();
395 });
396 readline.createInterface(c.stderr).addListener("line", (line) => {
397 errBuffer += line + "\n";
398 onLine(line);
399 });
400 readline.createInterface(c.stdout).addListener("line", onLine);
401 c.on("exit", (code, signal) => {
402 if (exited) return;
403 exited = true;
404 if (code !== 0) {
405 clearStatus();
406 const exitStatus = code != null ? `code ${code}` : `signal ${signal}`;
407 buffer += errBuffer + `error: ${cmd[0]} exited with ${exitStatus}\n`;
408 flush();
409 } else {
410 filesComplete += relatives.size;
411 if (relatives.size) {
412 lastFile = path.relative(
413 ".",
414 path.resolve(cwd, relatives.keys().next().value),
415 );
416 }
417 writeStatus();
418 flush();
419 }
420 runNext();
421 });
422 function runNext() {
423 running -= 1;
424 const next = commands.pop();
425 if (next) run(next);
426 else if (running == 0) {
427 clearStatus();
428 flush();
429 console.info(
430 `Formatted ${filesComplete} file${filesComplete !== 1 ? "s" : ""}`,
431 );
432 }
433 }
434}
435
436for (let i = 0; i < navigator.hardwareConcurrency; i++) {
437 const cmd = commands.pop();
438 if (cmd) run(cmd);
439 else break;
440}
441
442// -- library functions --
443
444/** @param file {string} */
445function walk(file) {
446 file = path.resolve(file);
447 try {
448 if (fs.statSync(file).isDirectory()) {
449 const exclude = getGitIgnores(file);
450 const read = fs
451 .globSync(escapeGlob(file) + "/{**,.**}", {
452 exclude,
453 withFileTypes: true,
454 })
455 .filter((file) => !file.isDirectory())
456 .map((file) => path.join(file.parentPath, file.name));
457 files.push(...read);
458 } else {
459 files.push(file);
460 }
461 } catch (err) {
462 console.error(
463 `Failed to stat ${file}: ${err?.code ?? err?.message ?? err}`,
464 );
465 process.exit(1);
466 }
467}
468
469/** @param dir {string} @returns {Array<string>} */
470function readDir(dir) {
471 dir = path.resolve(dir);
472 let contents = dirs.get(dir);
473 if (!contents) {
474 try {
475 contents = fs.readdirSync(dir);
476 } catch {
477 contents = [];
478 }
479 dirs.set(dir, contents);
480 }
481 return contents;
482}
483
484/** @param dir {string} @returns {string} */
485function pickFormatter(file) {
486 const lang = extToLanguage[path.extname(file)];
487 const dir = path.dirname(file);
488 let c = walkUp(dir, (x) => cached.get(x) ?? cached.get(`${x}:${lang}`));
489 if (c) return c;
490
491 const possible = Object.keys(formatters).filter(
492 (x) =>
493 Array.isArray(formatters[x].languages) &&
494 formatters[x].languages.includes(lang),
495 );
496 const order = [...multis, ...possible];
497 return walkUp(dir, (x) => {
498 const children = readDir(x);
499 for (const fmt of order) {
500 let matches = false;
501 if (formatters[fmt].files === true) {
502 matches = true;
503 }
504 if (!matches) {
505 const filesToCheck = Array.isArray(formatters[fmt].files)
506 ? formatters[fmt].files.map((base) => ({ base, contents: true }))
507 : Object.entries(formatters[fmt].files)
508 .map(([base, contents]) => ({ base, contents }));
509 for (const { base, contents } of filesToCheck) {
510 if (base.includes("/")) {
511 matches = readDir(path.join(x, path.dirname(base))) //
512 .includes(path.basename(base));
513 } else {
514 matches = children.includes(base);
515 }
516 if (matches && typeof contents === "function") {
517 matches = !!contents(fs.readFileSync(path.join(x, base), "utf-8"));
518 }
519 }
520 }
521 if (matches) {
522 const formatterId = `${fmt}\0${x}`;
523 const k = multis.includes(fmt) ? x : `${x}:${lang}`;
524 cached.set(k, formatterId);
525 return formatterId;
526 }
527 }
528 }) ?? possible[0];
529}
530
531/** @param dir {string} @param find {(x: string) => any} */
532function walkUp(dir, find) {
533 do {
534 const found = find(dir);
535 if (found != null) return found;
536 const parent = path.dirname(dir);
537 if (parent === dir) break;
538 dir = parent;
539 } while (true);
540 return null;
541}
542
543/** @param dir {string} */
544function getGitIgnores(dir) {
545 if (!gitignore) return [];
546 if (dir.endsWith(sep)) dir = dir.slice(0, -1);
547 const files = fs.globSync(`${dir}${sep}{**${sep}*${sep},}.gitignore`, {});
548 const referenced = [];
549 for (const abs of files) {
550 const dir = path.dirname(abs);
551 referenced.push(dir);
552 if (!gitignores.has(dir)) gitignores.set(dir, readGitIgnore(dir));
553 }
554 do {
555 const parent = path.dirname(dir);
556 if (parent === dir || fs.existsSync(path.join(dir, ".git"))) break;
557 referenced.push(parent);
558 if (!gitignores.has(parent)) gitignores.set(parent, readGitIgnore(parent));
559 dir = parent;
560 } while (true);
561 return referenced.flatMap((root) =>
562 (gitignores.get(root) ?? [])
563 .filter((x) => x[0] !== "!")
564 .map(
565 (rule) => `${root}${sep}${rule[0] === "/" ? "" : `**${sep}`}${rule}`,
566 )
567 );
568}
569
570/** @param dir {string} */
571function readGitIgnore(dir) {
572 try {
573 return fs
574 .readFileSync(`${dir}${sep}.gitignore`, "utf-8")
575 .split("\n")
576 .map((line) => line.replace(/#.*$/, "").trim())
577 .filter(Boolean);
578 } catch {
579 return [];
580 }
581}
582
583function escapeGlob(str) {
584 return str.replace(/[\\*,{}]/g, "\\$&");
585}
586
587import * as child_process from "node:child_process";
588import * as fs from "node:fs";
589import * as path from "node:path";
590import * as readline from "node:readline";
591import * as events from "node:events";
592import process from "node:process";
593import assert from "node:assert";
flake.nix+27-44
...@@ -49,7 +49,7 @@...@@ -49,7 +49,7 @@
49 }@inputs:49 }@inputs:
50 let50 let
51 inherit (nixpkgs) lib;51 inherit (nixpkgs) lib;
52 # TODO: apply these overlays sooner and remove uses of legacyPackages elsewhere.52
53 overlays = [53 overlays = [
54 inputs.zig.overlays.default54 inputs.zig.overlays.default
55 inputs.rust-overlay.overlays.default55 inputs.rust-overlay.overlays.default
...@@ -66,55 +66,42 @@...@@ -66,55 +66,42 @@
66 };66 };
67 });67 });
68 })68 })
69 ];
7069
71 # Users of this flake currently use x86_64 Linux and Apple Silicon70 # custom packages
72 systems = [71 (_: pkgs: {
73 "x86_64-linux"72 autofmt = pkgs.callPackage ./packages/autofmt.nix { };
74 "aarch64-darwin"73 })
75 ];74 ];
75
76 # We only use x86_64 on Linux and Apple Silicon Macs
77 # Overlays are applied here, as early as possible.
78 getNixPkgs = system: import nixpkgs { inherit system overlays; };
79 systems = {
80 "x86_64-linux" = getNixPkgs "x86_64-linux";
81 "aarch64-darwin" = getNixPkgs "aarch64-darwin";
82 };
76 forAllSystems =83 forAllSystems =
77 f:84 f:
78 builtins.listToAttrs (85 builtins.mapAttrs # #
79 builtins.map (system: {86 (system: pkgs: f (inputs // { inherit system pkgs; }))
80 name = system;87 systems;
81 value = f (88
82 inputs89 # Library Functions
83 // {90 mkNeovim = import ./lib/mkNeovim.nix { inherit self inputs; };
84 inherit system;91 mkSystem = import ./lib/mkSystem.nix { inherit inputs mkNeovim overlays; };
85 pkgs = nixpkgs.legacyPackages.${system};
86 }
87 );
88 }) systems
89 );
90
91 mkSystem = import ./lib/mkSystem.nix {
92 inherit
93 overlays
94 nixpkgs
95 inputs
96 mkNeovim
97 ;
98 };
99 mkNeovim = import ./lib/mkNeovim.nix {
100 inherit
101 self
102 overlays
103 nixpkgs
104 inputs
105 ;
106 };
107 in92 in
108 rec {93 rec {
109 inherit self;94 inherit self;
110 # "nix fmt"95 # "nix fmt"
111 formatter = forAllSystems (inputs: inputs.pkgs.nixfmt-tree);96 formatter = forAllSystems (inputs: inputs.pkgs.autofmt);
112 packages = forAllSystems (97 packages = forAllSystems (
113 { system, ... }:98 { system, pkgs, ... }:
114 {99 {
115 nvim-chloe = mkNeovim "chloe" system;100 nvim-chloe = mkNeovim "chloe" pkgs;
116 nvim-natalie = mkNeovim "natalie" system;101 nvim-natalie = mkNeovim "natalie" pkgs;
117 nvim-julia = mkNeovim "julia" system;102 nvim-julia = mkNeovim "julia" pkgs;
103
104 inherit (pkgs) autofmt;
118 }105 }
119 // lib.optionalAttrs (system == "aarch64-darwin") {106 // lib.optionalAttrs (system == "aarch64-darwin") {
120 # "nix run .#darwin-rebuild"107 # "nix run .#darwin-rebuild"
...@@ -127,16 +114,12 @@...@@ -127,16 +114,12 @@
127 user = "natalie";114 user = "natalie";
128 host = "desktop";115 host = "desktop";
129 system = "x86_64-linux";116 system = "x86_64-linux";
130 extraModules = [
131 ];
132 };117 };
133 # natalie's laptop118 # natalie's laptop
134 darwinConfigurations."Natalies-MacBook-Air" = mkSystem "Natalies-MacBook-Air" {119 darwinConfigurations."Natalies-MacBook-Air" = mkSystem "Natalies-MacBook-Air" {
135 user = "natalie";120 user = "natalie";
136 host = "laptop";121 host = "laptop";
137 system = "aarch64-darwin";122 system = "aarch64-darwin";
138 extraModules = [
139 ];
140 };123 };
141124
142 # chloe's mac studio "sandwich"125 # chloe's mac studio "sandwich"
lib/mkNeovim.nix+4-10
...@@ -1,13 +1,7 @@...@@ -1,13 +1,7 @@
1{1{ self, inputs }:
2 self,2user: pkgs:
3 nixpkgs,
4 # TODO: apply overlays here
5 overlays,
6 inputs,
7}:
8user: system:
9let3let
10 darwin = nixpkgs.lib.strings.hasSuffix "-darwin" system;4 darwin = pkgs.lib.strings.hasSuffix "-darwin" pkgs.system;
115
12 host = {6 host = {
13 inherit darwin;7 inherit darwin;
...@@ -18,7 +12,7 @@ let...@@ -18,7 +12,7 @@ let
18 userConfig = import (userDir + "/user.nix");12 userConfig = import (userDir + "/user.nix");
19in13in
20(inputs.nvf.lib.neovimConfiguration {14(inputs.nvf.lib.neovimConfiguration {
21 pkgs = nixpkgs.legacyPackages.${system};15 inherit pkgs;
22 modules = builtins.filter (f: f != null) [16 modules = builtins.filter (f: f != null) [
23 (../users + ("/" + user + "/vim.nix"))17 (../users + ("/" + user + "/vim.nix"))
24 ../modules/neovim18 ../modules/neovim
lib/mkSystem.nix+8-8
...@@ -1,7 +1,6 @@...@@ -1,7 +1,6 @@
1# This function creates a NixOS system based on our VM setup for a1# This function creates a NixOS system based on our VM setup for a
2# particular architecture.2# particular architecture.
3{3{
4 nixpkgs,
5 overlays,4 overlays,
6 inputs,5 inputs,
7 mkNeovim,6 mkNeovim,
...@@ -14,11 +13,11 @@ name:...@@ -14,11 +13,11 @@ name:
14 extraModules ? [ ],13 extraModules ? [ ],
15}:14}:
16let15let
17 darwin = nixpkgs.lib.strings.hasSuffix "-darwin" system;16 darwin = inputs.nixpkgs.lib.strings.hasSuffix "-darwin" system;
18 getInputModule = a: b: inputs.${a}.${if darwin then "darwinModules" else "nixosModules"}.${b};17 getInputModule = a: b: inputs.${a}.${if darwin then "darwinModules" else "nixosModules"}.${b};
1918
20 # NixOS vs nix-darwin functions19 # NixOS vs nix-darwin functions
21 systemFunc = if darwin then inputs.darwin.lib.darwinSystem else nixpkgs.lib.nixosSystem;20 systemFunc = if darwin then inputs.darwin.lib.darwinSystem else inputs.nixpkgs.lib.nixosSystem;
2221
23 userDir = ../users + "/${user}";22 userDir = ../users + "/${user}";
24 userConfig = import (userDir + "/user.nix");23 userConfig = import (userDir + "/user.nix");
...@@ -69,11 +68,12 @@ let...@@ -69,11 +68,12 @@ let
69 mainHomeImports = builtins.filter (f: f != null) [68 mainHomeImports = builtins.filter (f: f != null) [
70 (pathOrNull userHomePath)69 (pathOrNull userHomePath)
71 (pathOrNull hostHomePath)70 (pathOrNull hostHomePath)
72 {71 (
73 home.packages = [72 { pkgs, ... }:
74 (mkNeovim user system)73 {
75 ];74 home.packages = [ (mkNeovim user pkgs) ];
76 }75 }
76 )
77 inputs.nvf.homeManagerModules.default77 inputs.nvf.homeManagerModules.default
78 inputs.android-nixpkgs.hmModule78 inputs.android-nixpkgs.hmModule
79 ];79 ];
modules/neovim/default.nix+4-17
...@@ -5,7 +5,10 @@...@@ -5,7 +5,10 @@
5 ...5 ...
6}:6}:
7{7{
8 imports = [ ./options.nix ];8 imports = [
9 ./options.nix
10 ./formatter.nix
11 ];
9 # based on default options from upstream:12 # based on default options from upstream:
10 # https://github.com/NotAShelf/nvf/blob/main/configuration.nix13 # https://github.com/NotAShelf/nvf/blob/main/configuration.nix
11 #14 #
...@@ -107,20 +110,6 @@...@@ -107,20 +110,6 @@
107 format.type = "nixfmt"; # looks so much nicer110 format.type = "nixfmt"; # looks so much nicer
108 };111 };
109 };112 };
110 formatter.conform-nvim = {
111 enable = true;
112 setupOpts = {
113 formatters_by_ft = {
114 typescript = [ "deno_fmt" ];
115 typescriptreact = [ "deno_fmt" ];
116 javascript = [ "deno_fmt" ];
117 javascriptreact = [ "deno_fmt" ];
118 };
119 formatters.deno_fmt = {
120 command = lib.meta.getExe pkgs.deno;
121 };
122 };
123 };
124 filetree = {113 filetree = {
125 neo-tree = {114 neo-tree = {
126 enable = false;115 enable = false;
...@@ -154,7 +143,6 @@...@@ -154,7 +143,6 @@
154 enable = true;143 enable = true;
155 setupOpts = {144 setupOpts = {
156 bigfile.enable = true;145 bigfile.enable = true;
157 explorer.replace_netrw = true;
158 dashboard = {146 dashboard = {
159 preset.keys = [147 preset.keys = [
160 {148 {
...@@ -209,7 +197,6 @@...@@ -209,7 +197,6 @@
209 picker = {197 picker = {
210 enable = true;198 enable = true;
211 sources = {199 sources = {
212 explorer = { };
213 };200 };
214 };201 };
215 };202 };
modules/neovim/formatter.nix created+34
...@@ -0,0 +1,34 @@
1{ lib, pkgs, ... }:
2{
3 vim.formatter.conform-nvim = {
4 enable = true;
5 setupOpts = {
6 formatters_by_ft =
7 let
8 autofmt = lib.mkLuaInline ''{"autofmt",stop_after_first=true}'';
9 in
10 {
11 css = autofmt;
12 html = autofmt;
13 javascript = autofmt;
14 javascriptreact = autofmt;
15 json = autofmt;
16 jsonc = autofmt;
17 markdown = autofmt;
18 nix = autofmt;
19 rust = autofmt;
20 typescript = autofmt;
21 typescriptreact = autofmt;
22 yaml = autofmt;
23 };
24 formatters.autofmt = {
25 "inherit" = false;
26 args = [
27 "--stdio"
28 "$FILENAME"
29 ];
30 command = "${pkgs.autofmt}/bin/autofmt";
31 };
32 };
33 };
34}
modules/nixos/nvidia.nix+12-9
...@@ -7,7 +7,10 @@ let...@@ -7,7 +7,10 @@ let
7 nvidiaDriverChannel = config.boot.kernelPackages.nvidiaPackages.latest;7 nvidiaDriverChannel = config.boot.kernelPackages.nvidiaPackages.latest;
8in8in
9{9{
10 services.xserver.videoDrivers = [ "nvidia" "amdgpu"];10 services.xserver.videoDrivers = [
11 "nvidia"
12 "amdgpu"
13 ];
1114
12 nixpkgs.config = {15 nixpkgs.config = {
13 nvidia.acceptLicense = true;16 nvidia.acceptLicense = true;
...@@ -46,16 +49,16 @@ in...@@ -46,16 +49,16 @@ in
46 finegrained = true; # More precise power consumption control49 finegrained = true; # More precise power consumption control
47 };50 };
4851
49 prime = {52 prime = {
50 offload = {53 offload = {
51 enable = true;54 enable = true;
52 enableOffloadCmd = true;55 enableOffloadCmd = true;
53 };56 };
5457
55 nvidiaBusId = "PCI:1:0:0";58 nvidiaBusId = "PCI:1:0:0";
56 amdgpuBusId = "PCI:15:0:0";59 amdgpuBusId = "PCI:15:0:0";
5760
58 };61 };
5962
60 # Use the NVidia open source kernel module (not to be confused with the63 # Use the NVidia open source kernel module (not to be confused with the
61 # independent third-party "nouveau" open source driver).64 # independent third-party "nouveau" open source driver).
modules/shared/user-system-settings.nix+7-8
...@@ -10,13 +10,12 @@...@@ -10,13 +10,12 @@
10 # Set your time zone.10 # Set your time zone.
11 time.timeZone = user.timeZone;11 time.timeZone = user.timeZone;
1212
13 home-manager.users.${user.username}.home.sessionVariables =13 home-manager.users.${user.username}.home.sessionVariables = {
14 {14 EDITOR = user.editor;
15 EDITOR = user.editor;15 TERMINAL = user.term;
16 TERMINAL = user.term;16 }
17 }17 // lib.optionalAttrs (user ? "browser") {
18 // lib.optionalAttrs (user ? "browser") {18 BROWSER = user.browser;
19 BROWSER = user.browser;19 };
20 };
2120
22}21}
packages/autofmt.nix created+14
...@@ -0,0 +1,14 @@
1{ pkgs, ... }:
2pkgs.writeShellApplication {
3 name = "autofmt";
4 runtimeInputs = with pkgs; [
5 # include only a couple of formatters by default
6 deno
7 nixfmt-rfc-style
8 dprint
9 rustfmt
10 zig
11 clang-tools
12 ];
13 text = ''exec deno run -A ${../files/autofmt.js} "$@"'';
14}
readme.md-1
...@@ -78,4 +78,3 @@ configuration....@@ -78,4 +78,3 @@ configuration.
78nix run .#nvim-natalie # run by user78nix run .#nvim-natalie # run by user
79./nvim # run based on your username79./nvim # run based on your username
80```80```
81
users/chloe/home.nix+1
...@@ -16,6 +16,7 @@ in...@@ -16,6 +16,7 @@ in
16 ripgrep16 ripgrep
17 uv17 uv
18 nh18 nh
19 pkgs.autofmt
19 ];20 ];
20 # packages to install for desktop environments (non-server)21 # packages to install for desktop environments (non-server)
21 desktop = [22 desktop = [
users/julia/cattop/configuration.nix+10-7
...@@ -2,13 +2,18 @@...@@ -2,13 +2,18 @@
2# your system. Help is available in the configuration.nix(5) man page, on2# your system. Help is available in the configuration.nix(5) man page, on
3# https://search.nixos.org/options and in the NixOS manual (`nixos-help`).3# https://search.nixos.org/options and in the NixOS manual (`nixos-help`).
44
5{ config, lib, pkgs, ... }:5{
6 config,
7 lib,
8 pkgs,
9 ...
10}:
611
7{12{
8 imports =13 imports = [
9 [ # Include the results of the hardware scan.14 # Include the results of the hardware scan.
10 ./hardware-configuration.nix15 ./hardware-configuration.nix
11 ];16 ];
1217
13 # Use the systemd-boot EFI boot loader.18 # Use the systemd-boot EFI boot loader.
14 boot.loader.efi.canTouchEfiVariables = true;19 boot.loader.efi.canTouchEfiVariables = true;
...@@ -39,7 +44,6 @@...@@ -39,7 +44,6 @@
39 # Enable the GNOME Desktop Environment.44 # Enable the GNOME Desktop Environment.
40 services.xserver.displayManager.gdm.enable = true;45 services.xserver.displayManager.gdm.enable = true;
41 services.xserver.desktopManager.gnome.enable = true;46 services.xserver.desktopManager.gnome.enable = true;
42
4347
44 # Configure keymap in X1148 # Configure keymap in X11
45 # services.xserver.xkb.layout = "us";49 # services.xserver.xkb.layout = "us";
...@@ -124,4 +128,3 @@...@@ -124,4 +128,3 @@
124 # For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion .128 # For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion .
125 system.stateVersion = "25.05"; # Did you read the comment?129 system.stateVersion = "25.05"; # Did you read the comment?
126}130}
127
users/natalie/glance.yml+2-2
...@@ -62,8 +62,8 @@ pages:...@@ -62,8 +62,8 @@ pages:
62 location: London, United Kingdom62 location: London, United Kingdom
63 units: metric # alternatively "imperial"63 units: metric # alternatively "imperial"
64 hour-format: 12h # alternatively "24h"64 hour-format: 12h # alternatively "24h"
65 # Optionally hide the location from being displayed in the widget65 # Optionally hide the location from being displayed in the widget
66 # hide-location: true66 # hide-location: true
6767
68 - type: markets68 - type: markets
69 # The link to go to when clicking on the symbol in the UI,69 # The link to go to when clicking on the symbol in the UI,
users/natalie/vim.nix-1
...@@ -9,7 +9,6 @@...@@ -9,7 +9,6 @@
9 withPython3 = true;9 withPython3 = true;
10 python3Packages = [ "pynvim" ];10 python3Packages = [ "pynvim" ];
1111
12
13 autocmds = [12 autocmds = [
14 #Autocommand to fall back to treesitter folding if LSP doesnt support it13 #Autocommand to fall back to treesitter folding if LSP doesnt support it
15 {14 {
users/natalie/vim/keybinds.nix+6-6
...@@ -1,10 +1,10 @@...@@ -1,10 +1,10 @@
1{ ... }:1{ ... }:
2 let2let
3 mkKeymap = mode: key: action: desc: {3 mkKeymap = mode: key: action: desc: {
4 inherit mode;4 inherit mode;
5 inherit key action desc;5 inherit key action desc;
6 };6 };
7 n = mkKeymap "n"; # normal mode7 n = mkKeymap "n"; # normal mode
8in8in
9{9{
10 vim = {10 vim = {