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 @@
4949 }@inputs:
5050 let
5151 inherit (nixpkgs) lib;
52 # TODO: apply these overlays sooner and remove uses of legacyPackages elsewhere.
52
5353 overlays = [
5454 inputs.zig.overlays.default
5555 inputs.rust-overlay.overlays.default
......@@ -66,55 +66,42 @@
6666 };
6767 });
6868 })
69 ];
7069
71 # Users of this flake currently use x86_64 Linux and Apple Silicon
72 systems = [
73 "x86_64-linux"
74 "aarch64-darwin"
70 # custom packages
71 (_: pkgs: {
72 autofmt = pkgs.callPackage ./packages/autofmt.nix { };
73 })
7574 ];
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 };
7683 forAllSystems =
7784 f:
78 builtins.listToAttrs (
79 builtins.map (system: {
80 name = system;
81 value = f (
82 inputs
83 // {
84 inherit system;
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 };
85 builtins.mapAttrs # #
86 (system: pkgs: f (inputs // { inherit system pkgs; }))
87 systems;
88
89 # Library Functions
90 mkNeovim = import ./lib/mkNeovim.nix { inherit self inputs; };
91 mkSystem = import ./lib/mkSystem.nix { inherit inputs mkNeovim overlays; };
10792 in
10893 rec {
10994 inherit self;
11095 # "nix fmt"
111 formatter = forAllSystems (inputs: inputs.pkgs.nixfmt-tree);
96 formatter = forAllSystems (inputs: inputs.pkgs.autofmt);
11297 packages = forAllSystems (
113 { system, ... }:
98 { system, pkgs, ... }:
11499 {
115 nvim-chloe = mkNeovim "chloe" system;
116 nvim-natalie = mkNeovim "natalie" system;
117 nvim-julia = mkNeovim "julia" system;
100 nvim-chloe = mkNeovim "chloe" pkgs;
101 nvim-natalie = mkNeovim "natalie" pkgs;
102 nvim-julia = mkNeovim "julia" pkgs;
103
104 inherit (pkgs) autofmt;
118105 }
119106 // lib.optionalAttrs (system == "aarch64-darwin") {
120107 # "nix run .#darwin-rebuild"
......@@ -127,16 +114,12 @@
127114 user = "natalie";
128115 host = "desktop";
129116 system = "x86_64-linux";
130 extraModules = [
131 ];
132117 };
133118 # natalie's laptop
134119 darwinConfigurations."Natalies-MacBook-Air" = mkSystem "Natalies-MacBook-Air" {
135120 user = "natalie";
136121 host = "laptop";
137122 system = "aarch64-darwin";
138 extraModules = [
139 ];
140123 };
141124
142125 # chloe's mac studio "sandwich"
lib/mkNeovim.nix+4-10
......@@ -1,13 +1,7 @@
1{
2 self,
3 nixpkgs,
4 # TODO: apply overlays here
5 overlays,
6 inputs,
7}:
8user: system:
1{ self, inputs }:
2user: pkgs:
93let
10 darwin = nixpkgs.lib.strings.hasSuffix "-darwin" system;
4 darwin = pkgs.lib.strings.hasSuffix "-darwin" pkgs.system;
115
126 host = {
137 inherit darwin;
......@@ -18,7 +12,7 @@ let
1812 userConfig = import (userDir + "/user.nix");
1913in
2014(inputs.nvf.lib.neovimConfiguration {
21 pkgs = nixpkgs.legacyPackages.${system};
15 inherit pkgs;
2216 modules = builtins.filter (f: f != null) [
2317 (../users + ("/" + user + "/vim.nix"))
2418 ../modules/neovim
lib/mkSystem.nix+8-8
......@@ -1,7 +1,6 @@
11# This function creates a NixOS system based on our VM setup for a
22# particular architecture.
33{
4 nixpkgs,
54 overlays,
65 inputs,
76 mkNeovim,
......@@ -14,11 +13,11 @@ name:
1413 extraModules ? [ ],
1514}:
1615let
17 darwin = nixpkgs.lib.strings.hasSuffix "-darwin" system;
16 darwin = inputs.nixpkgs.lib.strings.hasSuffix "-darwin" system;
1817 getInputModule = a: b: inputs.${a}.${if darwin then "darwinModules" else "nixosModules"}.${b};
1918
2019 # 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
2322 userDir = ../users + "/${user}";
2423 userConfig = import (userDir + "/user.nix");
......@@ -69,11 +68,12 @@ let
6968 mainHomeImports = builtins.filter (f: f != null) [
7069 (pathOrNull userHomePath)
7170 (pathOrNull hostHomePath)
72 {
73 home.packages = [
74 (mkNeovim user system)
75 ];
76 }
71 (
72 { pkgs, ... }:
73 {
74 home.packages = [ (mkNeovim user pkgs) ];
75 }
76 )
7777 inputs.nvf.homeManagerModules.default
7878 inputs.android-nixpkgs.hmModule
7979 ];
modules/neovim/default.nix+4-17
......@@ -5,7 +5,10 @@
55 ...
66}:
77{
8 imports = [ ./options.nix ];
8 imports = [
9 ./options.nix
10 ./formatter.nix
11 ];
912 # based on default options from upstream:
1013 # https://github.com/NotAShelf/nvf/blob/main/configuration.nix
1114 #
......@@ -107,20 +110,6 @@
107110 format.type = "nixfmt"; # looks so much nicer
108111 };
109112 };
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 };
124113 filetree = {
125114 neo-tree = {
126115 enable = false;
......@@ -154,7 +143,6 @@
154143 enable = true;
155144 setupOpts = {
156145 bigfile.enable = true;
157 explorer.replace_netrw = true;
158146 dashboard = {
159147 preset.keys = [
160148 {
......@@ -209,7 +197,6 @@
209197 picker = {
210198 enable = true;
211199 sources = {
212 explorer = { };
213200 };
214201 };
215202 };
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
77 nvidiaDriverChannel = config.boot.kernelPackages.nvidiaPackages.latest;
88in
99{
10 services.xserver.videoDrivers = [ "nvidia" "amdgpu"];
10 services.xserver.videoDrivers = [
11 "nvidia"
12 "amdgpu"
13 ];
1114
1215 nixpkgs.config = {
1316 nvidia.acceptLicense = true;
......@@ -46,16 +49,16 @@ in
4649 finegrained = true; # More precise power consumption control
4750 };
4851
49 prime = {
50 offload = {
51 enable = true;
52 enableOffloadCmd = true;
53 };
52 prime = {
53 offload = {
54 enable = true;
55 enableOffloadCmd = true;
56 };
5457
55 nvidiaBusId = "PCI:1:0:0";
56 amdgpuBusId = "PCI:15:0:0";
58 nvidiaBusId = "PCI:1:0:0";
59 amdgpuBusId = "PCI:15:0:0";
5760
58 };
61 };
5962
6063 # Use the NVidia open source kernel module (not to be confused with the
6164 # independent third-party "nouveau" open source driver).
modules/shared/user-system-settings.nix+7-8
......@@ -10,13 +10,12 @@
1010 # Set your time zone.
1111 time.timeZone = user.timeZone;
1212
13 home-manager.users.${user.username}.home.sessionVariables =
14 {
15 EDITOR = user.editor;
16 TERMINAL = user.term;
17 }
18 // lib.optionalAttrs (user ? "browser") {
19 BROWSER = user.browser;
20 };
13 home-manager.users.${user.username}.home.sessionVariables = {
14 EDITOR = user.editor;
15 TERMINAL = user.term;
16 }
17 // lib.optionalAttrs (user ? "browser") {
18 BROWSER = user.browser;
19 };
2120
2221}
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.
7878nix run .#nvim-natalie # run by user
7979./nvim # run based on your username
8080```
81
users/chloe/home.nix+1
......@@ -16,6 +16,7 @@ in
1616 ripgrep
1717 uv
1818 nh
19 pkgs.autofmt
1920 ];
2021 # packages to install for desktop environments (non-server)
2122 desktop = [
users/julia/cattop/configuration.nix+10-7
......@@ -2,13 +2,18 @@
22# your system. Help is available in the configuration.nix(5) man page, on
33# 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
712{
8 imports =
9 [ # Include the results of the hardware scan.
10 ./hardware-configuration.nix
11 ];
13 imports = [
14 # Include the results of the hardware scan.
15 ./hardware-configuration.nix
16 ];
1217
1318 # Use the systemd-boot EFI boot loader.
1419 boot.loader.efi.canTouchEfiVariables = true;
......@@ -39,7 +44,6 @@
3944 # Enable the GNOME Desktop Environment.
4045 services.xserver.displayManager.gdm.enable = true;
4146 services.xserver.desktopManager.gnome.enable = true;
42
4347
4448 # Configure keymap in X11
4549 # services.xserver.xkb.layout = "us";
......@@ -124,4 +128,3 @@
124128 # For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion .
125129 system.stateVersion = "25.05"; # Did you read the comment?
126130}
127
users/natalie/glance.yml+2-2
......@@ -62,8 +62,8 @@ pages:
6262 location: London, United Kingdom
6363 units: metric # alternatively "imperial"
6464 hour-format: 12h # alternatively "24h"
65 # Optionally hide the location from being displayed in the widget
66 # hide-location: true
65 # Optionally hide the location from being displayed in the widget
66 # hide-location: true
6767
6868 - type: markets
6969 # The link to go to when clicking on the symbol in the UI,
users/natalie/vim.nix-1
......@@ -9,7 +9,6 @@
99 withPython3 = true;
1010 python3Packages = [ "pynvim" ];
1111
12
1312 autocmds = [
1413 #Autocommand to fall back to treesitter folding if LSP doesnt support it
1514 {
users/natalie/vim/keybinds.nix+6-6
......@@ -1,10 +1,10 @@
11{ ... }:
2 let
3 mkKeymap = mode: key: action: desc: {
4 inherit mode;
5 inherit key action desc;
6 };
7 n = mkKeymap "n"; # normal mode
2let
3 mkKeymap = mode: key: action: desc: {
4 inherit mode;
5 inherit key action desc;
6 };
7 n = mkKeymap "n"; # normal mode
88in
99{
1010 vim = {