2023-08-28 17:28:12 +02:00
|
|
|
lib: pkgs:
|
|
|
|
# Future improvements:
|
|
|
|
# - extra documentation from anyOf types, they can have `enumDescriptions` that are valuable
|
|
|
|
with lib;
|
|
|
|
let
|
|
|
|
packageJson = "${pkgs.rust-analyzer.src}/editors/code/package.json";
|
|
|
|
|
|
|
|
options = (trivial.importJSON packageJson).contributes.configuration.properties;
|
|
|
|
|
|
|
|
packageJsonTxt = strings.splitString "\n" (builtins.readFile packageJson);
|
|
|
|
vscodeStart = lists.findFirstIndex (
|
|
|
|
x: x == " \"configuration\": {"
|
|
|
|
) (throw "no configuration") packageJsonTxt;
|
|
|
|
vscodeEnd =
|
|
|
|
lists.findFirstIndex (strings.hasInfix "generated-start") (throw "no generated start")
|
|
|
|
packageJsonTxt;
|
|
|
|
vscodeOptions = lists.sublist vscodeStart (vscodeEnd - vscodeStart) packageJsonTxt;
|
|
|
|
|
|
|
|
vscodeOptionNames = builtins.map (
|
|
|
|
x: strings.removeSuffix "\": {" (strings.removePrefix " \"" x)
|
|
|
|
) (lists.filter (strings.hasPrefix " \"rust-analyzer.") vscodeOptions);
|
|
|
|
|
|
|
|
rustAnalyzerOptions = builtins.removeAttrs options (
|
|
|
|
vscodeOptionNames
|
|
|
|
++ [
|
|
|
|
"\$generated-start"
|
|
|
|
"\$generated-end"
|
2024-05-05 19:39:35 +02:00
|
|
|
]
|
2023-08-28 17:28:12 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
nullType = mkOptionType {
|
|
|
|
name = "nullType";
|
|
|
|
description = "null";
|
|
|
|
descriptionClass = "noun";
|
|
|
|
merge = mergeEqualOption;
|
|
|
|
check = e: e == null;
|
|
|
|
};
|
|
|
|
|
|
|
|
mkRustAnalyzerType =
|
|
|
|
opt:
|
|
|
|
{
|
|
|
|
type,
|
|
|
|
enum ? null,
|
|
|
|
minimum ? null,
|
|
|
|
maximum ? null,
|
|
|
|
items ? null,
|
|
|
|
anyOf ? null,
|
|
|
|
uniqueItems ? null,
|
|
|
|
# unused, but for exhaustivness
|
|
|
|
enumDescriptions ? null,
|
|
|
|
}@def:
|
|
|
|
if enum != null then
|
|
|
|
types.enum enum
|
|
|
|
else if anyOf != null then
|
|
|
|
types.oneOf (builtins.map (mkRustAnalyzerType opt) anyOf)
|
|
|
|
else if builtins.isList type then
|
|
|
|
if builtins.length type == 2 && builtins.head type == "null" then
|
|
|
|
types.nullOr (mkRustAnalyzerType opt (def // { type = builtins.elemAt type 1; }))
|
|
|
|
else
|
|
|
|
types.oneOf (builtins.map (t: mkRustAnalyzerType opt (def // { type = t; })) type)
|
|
|
|
else if type == "boolean" then
|
|
|
|
types.bool
|
|
|
|
else if type == "object" then
|
|
|
|
types.attrsOf types.anything
|
|
|
|
else if type == "string" then
|
|
|
|
types.str
|
|
|
|
else if type == "null" then
|
|
|
|
nullType
|
|
|
|
else if type == "array" then
|
|
|
|
types.listOf (
|
|
|
|
mkRustAnalyzerType "${opt}-items" (
|
|
|
|
if items == null then throw "null items with array type (in ${opt})" else items
|
|
|
|
)
|
|
|
|
)
|
|
|
|
else if type == "number" then
|
|
|
|
if minimum != null && maximum != null then
|
|
|
|
types.numbers.between minimum maximum
|
|
|
|
else if minimum != null then
|
|
|
|
types.addCheck types.number (x: x >= minimum)
|
|
|
|
else if maximum != null then
|
|
|
|
types.addCheck types.number (x: x <= maximum)
|
|
|
|
else
|
|
|
|
types.number
|
|
|
|
else if type == "integer" then
|
|
|
|
if minimum != null && maximum != null then
|
|
|
|
types.ints.between minimum maximum
|
|
|
|
else if minimum != null then
|
|
|
|
types.addCheck types.int (x: x >= minimum)
|
|
|
|
else if maximum != null then
|
|
|
|
types.addCheck types.int (x: x <= maximum)
|
|
|
|
else
|
|
|
|
types.int
|
|
|
|
else
|
|
|
|
throw "unhandled type `${builtins.toJSON type}` (in ${opt})";
|
|
|
|
|
|
|
|
mkRustAnalyzerOption =
|
|
|
|
opt:
|
|
|
|
{
|
|
|
|
default,
|
|
|
|
markdownDescription,
|
|
|
|
type ? null,
|
|
|
|
anyOf ? null,
|
|
|
|
# Enum types
|
|
|
|
enum ? null,
|
|
|
|
enumDescriptions ? null,
|
|
|
|
# Int types
|
|
|
|
minimum ? null,
|
|
|
|
maximum ? null,
|
|
|
|
# List types
|
|
|
|
items ? null,
|
|
|
|
uniqueItems ? false,
|
|
|
|
}:
|
|
|
|
mkOption {
|
|
|
|
type = types.nullOr (
|
|
|
|
mkRustAnalyzerType opt {
|
|
|
|
inherit
|
|
|
|
type
|
|
|
|
enum
|
|
|
|
minimum
|
|
|
|
maximum
|
|
|
|
items
|
|
|
|
anyOf
|
|
|
|
uniqueItems
|
|
|
|
;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
default = null;
|
|
|
|
description =
|
|
|
|
if enum != null then
|
|
|
|
let
|
|
|
|
valueDesc = builtins.map ({ fst, snd }: ''- ${fst}: ${snd}'') (
|
|
|
|
lists.zipLists enum enumDescriptions
|
|
|
|
);
|
|
|
|
in
|
|
|
|
''
|
|
|
|
${markdownDescription}
|
|
|
|
|
|
|
|
Values:
|
|
|
|
${builtins.concatStringsSep "\n" valueDesc}
|
|
|
|
|
|
|
|
```nix
|
|
|
|
${generators.toPretty { } default}
|
|
|
|
```
|
|
|
|
''
|
|
|
|
else
|
|
|
|
''
|
|
|
|
${markdownDescription}
|
|
|
|
|
|
|
|
default:
|
|
|
|
```nix
|
|
|
|
${generators.toPretty { } default}
|
|
|
|
```
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
nixOptions = builtins.mapAttrs mkRustAnalyzerOption rustAnalyzerOptions;
|
|
|
|
|
|
|
|
nestOpt =
|
|
|
|
opt: value:
|
|
|
|
let
|
|
|
|
parts = strings.splitString "." opt;
|
|
|
|
in
|
|
|
|
builtins.foldl' (current: segment: { ${segment} = current; }) value (lists.reverseList parts);
|
|
|
|
|
|
|
|
nestedNixOptions = attrsets.mapAttrsToList nestOpt nixOptions;
|
|
|
|
in
|
|
|
|
(builtins.foldl' attrsets.recursiveUpdate { } nestedNixOptions).rust-analyzer
|