nix all the things
1{
2 lib,
3 pkgs,
4 inputs',
5 self',
6 config,
7 ...
8}:
9let
10 jj-patch = pkgs.fetchurl {
11 url = "https://patch-diff.githubusercontent.com/raw/helix-editor/helix/pull/14519.patch";
12 hash = "sha256-L+dcrYNApgNdMH5iWDhgGnmmtmKG2s/DnxOa7Ug/dsw=";
13 };
14 helix = inputs'.helix.packages.default.overrideAttrs (_: {
15 patches = jj-patch;
16 });
17 global-tools = with pkgs; [
18 nixfmt
19 biome
20 golangci-lint
21 gotools
22 gopls
23 sql-formatter
24 nodePackages.prettier
25 ];
26in
27lib.mkIf config.dev.editor.enable {
28 home.packages = global-tools;
29 programs.helix = {
30 enable = true;
31 defaultEditor = true;
32 package = helix;
33 extraPackages =
34 with pkgs;
35 [
36 golangci-lint-langserver
37 nixd
38 marksman
39 nodePackages.typescript-language-server
40 vscode-langservers-extracted
41 yaml-language-server
42 typos-lsp
43 nil
44 ]
45 ++ global-tools;
46
47 ignores = [
48 ".zig-cache"
49 "node_modules"
50 ".direnv"
51 "!/notes"
52 ];
53
54 settings = {
55 keys =
56 let
57 plusMenu = {
58 g = ''
59 :sh ${pkgs.nushell}/bin/nu -c '
60 let line = ("%{selection_line_start}" | default "%{cursor_line}")
61 let line_end = (if ("%{selection_line_end}" | is-not-empty) {$"-L%{selection_line_end}"} else "")
62 let root = (${pkgs.jujutsu}/bin/jj workspace root | str trim)
63 let rel_path = ("%{file_path_absolute}" | path relative-to $root)
64 let ref = (${pkgs.jujutsu}/bin/jj log -r "heads(::@ & bookmarks())" -n 1 --no-graph -T "commit_id")
65 let remote_url = (${pkgs.jujutsu}/bin/jj git remote list | parse "{remote} {url}" | where remote == origin | get url.0 | if ($in | str contains '://') {$in} else $"https://($in | str replace ':' '/')" | url parse)
66 let url = $"https://($remote_url.host)($remote_url.path | str replace ".git" "")/blob/($ref)/($rel_path)#L($line)($line_end)"
67 $url | ${pkgs.wl-clipboard}/bin/wl-copy
68 '
69 '';
70 b = ":echo %sh{git blame -L %{cursor_line},+1 %{buffer_name}}";
71 p = ":sh echo %{buffer_name} | ${pkgs.wl-clipboard}/bin/wl-copy";
72 };
73 goMenu = {
74 "8" = [
75 "move_prev_word_start"
76 "move_next_word_end"
77 ];
78 "c" = caseMenu;
79 };
80 caseMenu = {
81 p = ":pipe ${lib.getExe pkgs.sttr} pascal";
82 c = ":pipe ${lib.getExe pkgs.sttr} camel";
83 k = ":pipe ${lib.getExe pkgs.sttr} kebab";
84 K = ":pipe ${lib.getExe pkgs.sttr} kebab | ${lib.getExe pkgs.sttr} upper";
85 s = ":pipe ${lib.getExe pkgs.sttr} snake";
86 S = ":pipe ${lib.getExe pkgs.sttr} snake | ${lib.getExe pkgs.sttr} upper";
87 t = ":pipe ${lib.getExe pkgs.sttr} title";
88 };
89 runMenu = {
90 f = [
91 ":sh golangci-lint run --issues-exit-code=0 --fix %{buffer_name}"
92 ":reload"
93 ];
94 };
95 scrollFast = {
96 "C-j" = lib.replicate 5 "move_visual_line_down";
97 "C-k" = lib.replicate 5 "move_visual_line_up";
98 };
99 in
100 {
101 normal = {
102 "+" = plusMenu;
103 "-" = runMenu;
104 "g" = goMenu;
105 }
106 // scrollFast;
107 select = {
108 "+" = plusMenu;
109 "-" = runMenu;
110 "." = goMenu;
111 }
112 // scrollFast;
113 };
114
115 editor = {
116 scrolloff = 10;
117 text-width = 120;
118 rulers = [ 120 ];
119 bufferline = "multiple";
120 color-modes = true;
121 auto-format = true;
122 auto-save = true;
123 lsp = {
124 snippets = true;
125 display-color-swatches = true;
126 display-messages = true;
127 };
128 soft-wrap = {
129 enable = true;
130 };
131 statusline = {
132 left = [
133 "mode"
134 "spacer"
135 "diagnostics"
136 "read-only-indicator"
137 "file-modification-indicator"
138 "spinner"
139 ];
140 center = [
141 "version-control"
142 "spacer"
143 "file-name"
144 ];
145 right = [
146 "file-encoding"
147 "file-type"
148 "selections"
149 "position"
150 ];
151 };
152 gutters = [
153 "line-numbers"
154 "diagnostics"
155 "diff"
156 ];
157 end-of-line-diagnostics = "hint";
158 inline-diagnostics = {
159 cursor-line = "warning"; # show warnings and errors on the cursorline inline
160 };
161 cursor-shape = {
162 insert = "bar";
163 normal = "block";
164 select = "underline";
165 };
166 whitespace.render.tab = "all";
167 indent-guides = {
168 render = true;
169 character = "┊";
170 skip-levels = 1;
171 };
172 };
173 };
174
175 languages = {
176 language-server = {
177 biome = {
178 command = "biome";
179 args = [ "lsp-proxy" ];
180 };
181 nu-lsp = {
182 command = "nu";
183 args = [
184 "--lsp"
185 "--no-config-file"
186 ];
187 };
188 typos = {
189 command = "typos-lsp";
190 config = {
191 diagnosticSeverity = "Warning";
192 };
193 };
194 yaml-language-server = {
195 config = {
196 enabled = true;
197 enabledForFilesGlob = "*.{yaml,yml}";
198 diagnosticsLimit = 50;
199 showDiagnosticsDirectly = false;
200 config = {
201 schemas = {
202 kubernetes = "templates/**";
203 };
204 completion = true;
205 hover = true;
206 };
207 };
208 };
209 golangci-lint-lsp = {
210 command = "golangci-lint-langserver";
211 config = {
212 command = [
213 "nu"
214 "-c"
215 ''
216 let args = [
217 --output.json.path=stdout
218 --path-mode=abs
219 --issues-exit-code=1
220 --show-stats=false
221 ]
222
223 if ($env.GOLANGCI_LINT_CONFIG? | is-not-empty) {
224 golangci-lint run --config $env.GOLANGCI_LINT_CONFIG ...$args
225 } else {
226 golangci-lint run ...$args
227 }
228 ''
229 ];
230 };
231 };
232 rubocop = {
233 command = "rubocop";
234 args = [ "--lsp" ];
235 };
236 ruby-lsp = {
237 command = "ruby-lsp";
238 config = {
239 diagnostics = true;
240 formatting = true;
241 config = {
242 initializationOptions = {
243 enabledFeatures = {
244 codeActions = true;
245 codeLens = true;
246 completion = true;
247 definition = true;
248 diagnostics = true;
249 documentHighlights = true;
250 documentLink = true;
251 documentSymbols = true;
252 foldingRanges = true;
253 formatting = true;
254 hover = true;
255 inlayHint = true;
256 onTypeFormatting = true;
257 selectionRanges = true;
258 semanticHighlighting = true;
259 signatureHelp = true;
260 typeHierarchy = true;
261 workspaceSymbol = true;
262 };
263 featuresConfiguration = {
264 inlayHint = {
265 implicitHashValue = true;
266 implicitRescue = true;
267 };
268 };
269 };
270 };
271 };
272 };
273 thriftls = {
274 command = "thriftls";
275 except-features = [ "format" ];
276 };
277 };
278
279 language =
280 let
281 defaults = [ "typos" ];
282 in
283 map
284 (lang: lang // { language-servers = if lang ? language-servers then lang.language-servers ++ defaults else defaults; })
285 (
286 [
287 {
288 name = "nix";
289 language-servers = [
290 "nixd"
291 "nil"
292 ];
293 formatter = {
294 command = "nixfmt";
295 args = [
296 "-s"
297 "-w"
298 "120"
299 ];
300 };
301 auto-format = true;
302 }
303 {
304 name = "go";
305 language-servers = [
306 "gopls"
307 "golangci-lint-lsp"
308 ];
309 formatter = {
310 command = "goimports";
311 };
312 auto-format = true;
313 }
314 {
315 name = "ruby";
316 language-servers = [
317 "ruby-lsp"
318 "rubocop"
319 ];
320 auto-format = true;
321 }
322 {
323 name = "html";
324 language-servers = [ "vscode-html-language-server" ];
325 formatter = {
326 command = "prettier";
327 args = [
328 "--stdin-filepath"
329 "file.html"
330 ];
331 };
332 auto-format = true;
333 }
334 {
335 name = "javascript";
336 language-servers = [
337 {
338 name = "typescript-language-server";
339 except-features = [ "format" ];
340 }
341 "biome"
342 ];
343 auto-format = true;
344 }
345 {
346 name = "json";
347 language-servers = [
348 {
349 name = "vscode-json-language-server";
350 except-features = [ "format" ];
351 }
352 "biome"
353 ];
354 formatter = {
355 command = "biome";
356 args = [
357 "format"
358 "--stdin-file-path"
359 "file.json"
360 ];
361 };
362 auto-format = true;
363 }
364 {
365 name = "graphql";
366 formatter = {
367 command = "biome";
368 args = [
369 "format"
370 "--stdin-file-path"
371 "file.gql"
372 ];
373 };
374 auto-format = true;
375 }
376 {
377 name = "jsonc";
378 language-servers = [
379 {
380 name = "vscode-json-language-server";
381 except-features = [ "format" ];
382 }
383 "biome"
384 ];
385 formatter = {
386 command = "biome";
387 args = [
388 "format"
389 "--stdin-file-path"
390 "file.jsonc"
391 ];
392 };
393 file-types = [
394 "jsonc"
395 "hujson"
396 ];
397 auto-format = true;
398 }
399 {
400 name = "jsx";
401 language-servers = [
402 {
403 name = "typescript-language-server";
404 except-features = [ "format" ];
405 }
406 "biome"
407 ];
408 formatter = {
409 command = "biome";
410 args = [
411 "format"
412 "--stdin-file-path"
413 "file.jsx"
414 ];
415 };
416 auto-format = true;
417 }
418 {
419 name = "tsx";
420 language-servers = [
421 {
422 name = "typescript-language-server";
423 except-features = [ "format" ];
424 }
425 "biome"
426 ];
427 formatter = {
428 command = "biome";
429 args = [
430 "format"
431 "--stdin-file-path"
432 "file.tsx"
433 ];
434 };
435 auto-format = true;
436 }
437 {
438 name = "typescript";
439 language-servers = [
440 {
441 name = "typescript-language-server";
442 except-features = [ "format" ];
443 }
444 "biome"
445 ];
446 formatter = {
447 command = "biome";
448 args = [
449 "format"
450 "--stdin-file-path"
451 "file.ts"
452 ];
453 };
454 auto-format = true;
455 }
456 {
457 name = "yaml";
458 language-servers = [ "yaml-language-server" ];
459 formatter = {
460 command = "prettier";
461 args = [
462 "--stdin-filepath"
463 "file.yaml"
464 ];
465 };
466 auto-format = true;
467 }
468 {
469 name = "helm";
470 language-servers = [ "helm_ls" ];
471 }
472 {
473 name = "typst";
474 language-servers = [ "tinymist" ];
475 }
476 {
477 name = "markdown";
478 language-servers = [
479 "marksman"
480 # "vale-ls"
481 ];
482 text-width = 100;
483 rulers = [ 100 ];
484 soft-wrap = {
485 enable = true;
486 wrap-at-text-width = true;
487 };
488 formatter = {
489 command = "prettier";
490 args = [
491 "--stdin-filepath"
492 "file.md"
493 ];
494 };
495 auto-format = true;
496 }
497 {
498 name = "sql";
499 formatter = {
500 command = "sql-formatter";
501 args = [
502 "-c"
503 (builtins.toJSON {
504 keywordCase = "upper";
505 functionCase = "upper";
506 dataTypeCase = "upper";
507 identifierCase = "lower";
508 language = "postgresql";
509 expressionWidth = 80;
510 tabWidth = 2;
511 })
512 ];
513 };
514 auto-format = false;
515 }
516 {
517 name = "nu";
518 language-servers = [ "nu-lsp" ];
519 formatter = {
520 command = "${lib.getExe self'.packages.topiary-nu}";
521 args = [
522 "format"
523 "--language"
524 "nu"
525 ];
526 };
527 auto-format = true;
528 }
529 {
530 name = "thrift";
531 language-servers = [ "thriftls" ];
532 formatter = {
533 command = "thriftls";
534 args = [
535 "format"
536 "-indent"
537 "2space"
538 ];
539 };
540 auto-format = true;
541 }
542 ]
543 ++ map (lang: { name = lang; }) [
544 "git-attributes"
545 "git-commit"
546 "git-config"
547 "git-ignore"
548 "git-rebase"
549 ]
550 );
551 };
552 };
553}