From ff5e23efc2d910484d7f44da0a67dee52035133f Mon Sep 17 00:00:00 2001 From: chloe caruso Date: Sun, 17 Aug 2025 20:40:58 -0700 Subject: [PATCH] feat: "autofmt" code formatter different codebases use different formatters. autofmt looks for project configuration to pick the correct formatter, allowing an editor to simply point to the script and have all ambiguities resolved. this commit overhauls the conform configuration to set up autofmt correctly, as well installing it with 'pkgs.autofmt' closes #6 --- files/autofmt.js | 593 ++++++++++++++++++++++++ flake.nix | 69 ++- lib/mkNeovim.nix | 14 +- lib/mkSystem.nix | 16 +- modules/neovim/default.nix | 21 +- modules/neovim/formatter.nix | 34 ++ modules/nixos/nvidia.nix | 23 +- modules/shared/user-system-settings.nix | 15 +- packages/autofmt.nix | 14 + readme.md | 1 - users/chloe/home.nix | 1 + users/julia/cattop/configuration.nix | 17 +- users/natalie/glance.yml | 4 +- users/natalie/vim.nix | 1 - users/natalie/vim/keybinds.nix | 12 +- 15 files changed, 722 insertions(+), 113 deletions(-) create mode 100755 files/autofmt.js create mode 100644 modules/neovim/formatter.nix create mode 100644 packages/autofmt.nix diff --git a/files/autofmt.js b/files/autofmt.js new file mode 100755 index 0000000000000000000000000000000000000000..4e2ff72211661546099d762e33526cb0781cf8a9 --- /dev/null +++ b/files/autofmt.js @@ -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=`. 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= add an exclusion glob`); + console.error(` --stdio= 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} */ +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"; diff --git a/flake.nix b/flake.nix index cfd82ab61f92376702e2a691e16b0869d64b79c9..8f40bd9ed49461870591c375968181f6fa8e5448 100644 --- a/flake.nix +++ b/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 @@ }; }); }) - ]; - # Users of this flake currently use x86_64 Linux and Apple Silicon - systems = [ - "x86_64-linux" - "aarch64-darwin" + # custom packages + (_: pkgs: { + autofmt = pkgs.callPackage ./packages/autofmt.nix { }; + }) ]; + + # 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" diff --git a/lib/mkNeovim.nix b/lib/mkNeovim.nix index 72e6d9c2a014d4705a3de7a69f1085f915e9656a..c0e0c70e832de6bd998898eb5db7a71f3b2a495c 100644 --- a/lib/mkNeovim.nix +++ b/lib/mkNeovim.nix @@ -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 diff --git a/lib/mkSystem.nix b/lib/mkSystem.nix index e39386909a33f178cba8429f1e6fd8ed67a2f562..9e3a13c459d7382e0c7d888d249f7ccb23a00fff 100644 --- a/lib/mkSystem.nix +++ b/lib/mkSystem.nix @@ -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 ]; diff --git a/modules/neovim/default.nix b/modules/neovim/default.nix index 54e7b0fff678ccaa105f05c82989dea8e3d6101f..80bd3a11d2307dbb0ea0f5be5c87d650ffb252ab 100644 --- a/modules/neovim/default.nix +++ b/modules/neovim/default.nix @@ -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 = { }; }; }; }; diff --git a/modules/neovim/formatter.nix b/modules/neovim/formatter.nix new file mode 100644 index 0000000000000000000000000000000000000000..aa3e4ddf9f75c3c584ebd1f639c831d35452a3e7 --- /dev/null +++ b/modules/neovim/formatter.nix @@ -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"; + }; + }; + }; +} diff --git a/modules/nixos/nvidia.nix b/modules/nixos/nvidia.nix index fb64da3c7f2981a432a7b4e841abf092569a32fb..88f2252341a33631e504018b8a531b7c1b32fec9 100644 --- a/modules/nixos/nvidia.nix +++ b/modules/nixos/nvidia.nix @@ -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. diff --git a/modules/shared/user-system-settings.nix b/modules/shared/user-system-settings.nix index bbd03c9d3b0d1e577c07449c81b4d543f242d225..dd67a48fc3603ed283d47990939351c70d3659e4 100644 --- a/modules/shared/user-system-settings.nix +++ b/modules/shared/user-system-settings.nix @@ -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; + }; } diff --git a/packages/autofmt.nix b/packages/autofmt.nix new file mode 100644 index 0000000000000000000000000000000000000000..16ade370450c4f4eb38f940ec30def0cec0241ee --- /dev/null +++ b/packages/autofmt.nix @@ -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} "$@"''; +} diff --git a/readme.md b/readme.md index aca64be4ffd64914dd492aa7fedeb32078914e00..7c7416d44e70df0ed578b1f085592798c93561a1 100644 --- a/readme.md +++ b/readme.md @@ -78,4 +78,3 @@ configuration. nix run .#nvim-natalie # run by user ./nvim # run based on your username ``` - diff --git a/users/chloe/home.nix b/users/chloe/home.nix index 1899685125539742a4a9f18182a1ebe36202c039..b46fbf4522864d7a6ccf6977e30fd2bcce7a1f59 100644 --- a/users/chloe/home.nix +++ b/users/chloe/home.nix @@ -16,6 +16,7 @@ in ripgrep uv nh + pkgs.autofmt ]; # packages to install for desktop environments (non-server) desktop = [ diff --git a/users/julia/cattop/configuration.nix b/users/julia/cattop/configuration.nix index 4b0e96ffc7b75cb55a02d7c75d3b456632c77ca7..35b2d32f4ccbc4592fdedf71585d0c63f8d18361 100644 --- a/users/julia/cattop/configuration.nix +++ b/users/julia/cattop/configuration.nix @@ -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? } - diff --git a/users/natalie/glance.yml b/users/natalie/glance.yml index 92b4de1c4871cde24a2f8d192f6a74189dbd8986..1f149d68508ffc5daf5c457153eb27a33f440fc2 100644 --- a/users/natalie/glance.yml +++ b/users/natalie/glance.yml @@ -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, diff --git a/users/natalie/vim.nix b/users/natalie/vim.nix index ec5865f327fb042603b7cd20c5ad71365be77f49..9104cec4f818859d19d728dd01182ea540e9b9c7 100644 --- a/users/natalie/vim.nix +++ b/users/natalie/vim.nix @@ -9,7 +9,6 @@ withPython3 = true; python3Packages = [ "pynvim" ]; - autocmds = [ #Autocommand to fall back to treesitter folding if LSP doesnt support it { diff --git a/users/natalie/vim/keybinds.nix b/users/natalie/vim/keybinds.nix index cc6d5d4eb0a0491cda941e483cb8300e3a94cde5..6caf7ac8721a35ba329f7e7771290dceff01d7e4 100644 --- a/users/natalie/vim/keybinds.nix +++ b/users/natalie/vim/keybinds.nix @@ -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 = { -- 2.54.0