A post-modern development environment.
1{
2 stdenv,
3 lib,
4 runCommand,
5 includeGrammarIf ? _: true,
6 grammarOverlays ? [],
7 ...
8}: let
9 languagesConfig =
10 builtins.fromTOML (builtins.readFile ./languages.toml);
11 isGitGrammar = grammar:
12 builtins.hasAttr "source" grammar
13 && builtins.hasAttr "git" grammar.source
14 && builtins.hasAttr "rev" grammar.source;
15 isGitHubGrammar = grammar: lib.hasPrefix "https://github.com" grammar.source.git;
16 toGitHubFetcher = url: let
17 match = builtins.match "https://github\\.com/([^/]*)/([^/]*)/?" url;
18 in {
19 owner = builtins.elemAt match 0;
20 repo = builtins.elemAt match 1;
21 };
22 # If `use-grammars.only` is set, use only those grammars.
23 # If `use-grammars.except` is set, use all other grammars.
24 # Otherwise use all grammars.
25 useGrammar = grammar:
26 if languagesConfig ? use-grammars.only
27 then builtins.elem grammar.name languagesConfig.use-grammars.only
28 else if languagesConfig ? use-grammars.except
29 then !(builtins.elem grammar.name languagesConfig.use-grammars.except)
30 else true;
31 grammarsToUse = builtins.filter useGrammar languagesConfig.grammar;
32 gitGrammars = builtins.filter isGitGrammar grammarsToUse;
33 buildGrammar = grammar: let
34 gh = toGitHubFetcher grammar.source.git;
35 sourceGit = builtins.fetchTree {
36 type = "git";
37 url = grammar.source.git;
38 rev = grammar.source.rev;
39 ref = grammar.source.ref or "HEAD";
40 shallow = true;
41 };
42 sourceGitHub = builtins.fetchTree {
43 type = "github";
44 owner = gh.owner;
45 repo = gh.repo;
46 inherit (grammar.source) rev;
47 };
48 source =
49 if isGitHubGrammar grammar
50 then sourceGitHub
51 else sourceGit;
52 in
53 stdenv.mkDerivation {
54 # see https://github.com/NixOS/nixpkgs/blob/fbdd1a7c0bc29af5325e0d7dd70e804a972eb465/pkgs/development/tools/parsing/tree-sitter/grammar.nix
55
56 pname = "helix-tree-sitter-${grammar.name}";
57 version = grammar.source.rev;
58
59 src = source;
60 sourceRoot =
61 if builtins.hasAttr "subpath" grammar.source
62 then "source/${grammar.source.subpath}"
63 else "source";
64
65 dontConfigure = true;
66
67 FLAGS = [
68 "-Isrc"
69 "-g"
70 "-O3"
71 "-fPIC"
72 "-fno-exceptions"
73 "-Wl,-z,relro,-z,now"
74 ];
75
76 SHARED_LIB = grammar.name + stdenv.hostPlatform.extensions.sharedLibrary;
77
78 buildPhase = ''
79 runHook preBuild
80
81 if [[ -e src/scanner.cc ]]; then
82 $CXX -c src/scanner.cc -o scanner.o $FLAGS
83 elif [[ -e src/scanner.c ]]; then
84 $CC -c src/scanner.c -o scanner.o $FLAGS
85 fi
86
87 $CC -c src/parser.c -o parser.o $FLAGS
88 $CXX -shared -o $SHARED_LIB *.o
89
90 runHook postBuild
91 '';
92
93 installPhase = ''
94 runHook preInstall
95 mkdir $out
96 mv $SHARED_LIB $out/
97 runHook postInstall
98 '';
99
100 # Strip failed on darwin: strip: error: symbols referenced by indirect symbol table entries that can't be stripped
101 fixupPhase = lib.optionalString stdenv.isLinux ''
102 runHook preFixup
103 $STRIP $out/$SHARED_LIB
104 runHook postFixup
105 '';
106 };
107 grammarsToBuild = builtins.filter includeGrammarIf gitGrammars;
108 builtGrammars =
109 builtins.map (grammar: {
110 inherit (grammar) name;
111 value = buildGrammar grammar;
112 })
113 grammarsToBuild;
114 extensibleGrammars =
115 lib.makeExtensible (self: builtins.listToAttrs builtGrammars);
116 overlaidGrammars =
117 lib.pipe extensibleGrammars
118 (builtins.map (overlay: grammar: grammar.extend overlay) grammarOverlays);
119 sharedLibExtension = stdenv.hostPlatform.extensions.sharedLibrary;
120 grammarLinks =
121 lib.mapAttrsToList
122 (name: artifact: "ln -s ${artifact}/${name}${sharedLibExtension} $out/${name}${sharedLibExtension}")
123 (lib.filterAttrs (n: v: lib.isDerivation v) overlaidGrammars);
124in
125 runCommand "consolidated-helix-grammars" {} ''
126 mkdir -p $out
127 ${builtins.concatStringsSep "\n" grammarLinks}
128 ''