diff --git a/files/autofmt.js b/files/autofmt.js new file mode 100755 index 0000000..4e2ff72 --- /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 cfd82ab..8f40bd9 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 @@ }; }); }) + + # 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" diff --git a/lib/mkNeovim.nix b/lib/mkNeovim.nix index 72e6d9c..c0e0c70 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 e393869..9e3a13c 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 54e7b0f..80bd3a1 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 0000000..aa3e4dd --- /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 fb64da3..88f2252 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 bbd03c9..dd67a48 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 0000000..16ade37 --- /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 aca64be..7c7416d 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 1899685..b46fbf4 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 4b0e96f..35b2d32 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 92b4de1..1f149d6 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 ec5865f..9104cec 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 cc6d5d4..6caf7ac 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 = {