Merge options having the submodule type.

Now we should be able to have multiple declaration of the same option as
long as all declarations have the same type.  If the type has a sub module,
then it is merged with the submodules of other declarations, as done with
option sets.

In addition, the file of the option declaration is passed into the
submodule, such as the documentation can display it correctly.
This commit is contained in:
Nicolas Pierron 2014-08-29 16:42:44 +02:00
parent bb944b4dc8
commit b5f0cc3cda
2 changed files with 35 additions and 7 deletions

View File

@ -144,7 +144,8 @@ rec {
opt.options ? example && res ? example || opt.options ? example && res ? example ||
opt.options ? description && res ? description || opt.options ? description && res ? description ||
opt.options ? apply && res ? apply || opt.options ? apply && res ? apply ||
opt.options ? type && res ? type # Accept to merge options which have identical types.
opt.options ? type && res ? type && opt.options.type.name != res.type.name
then then
throw "The option `${showOption loc}' in `${opt.file}' is already declared in ${showFiles res.declarations}." throw "The option `${showOption loc}' in `${opt.file}' is already declared in ${showFiles res.declarations}."
else else
@ -155,12 +156,16 @@ rec {
current option declaration as the file use for the submodule. If the current option declaration as the file use for the submodule. If the
submodule defines any filename, then we ignore the enclosing option file. */ submodule defines any filename, then we ignore the enclosing option file. */
options' = toList opt.options.options; options' = toList opt.options.options;
addModuleFile = m:
if isFunction m then args: { _file = opt.file; } // (m args)
else { _file = opt.file; } // m;
coerceOption = file: opt: coerceOption = file: opt:
if isFunction opt then args: { _file = file; } // (opt args) if isFunction opt then args: { _file = file; } // (opt args)
else args: { _file = file; options = opt; }; else { _file = file; options = opt; };
getSubModules = opt.options.type.getSubModules or null;
submodules = submodules =
if opt.options ? options if getSubModules != null then map addModuleFile getSubModules ++ res.options
then map (coerceOption opt.file) options' ++ res.options else if opt.options ? options then map (coerceOption opt.file) options' ++ res.options
else res.options; else res.options;
in opt.options // res // in opt.options // res //
{ declarations = [opt.file] ++ res.declarations; { declarations = [opt.file] ++ res.declarations;
@ -292,7 +297,8 @@ rec {
in sort compare defs'; in sort compare defs';
/* Hack for backward compatibility: convert options of type /* Hack for backward compatibility: convert options of type
optionSet to configOf. FIXME: remove eventually. */ optionSet to options of type submodule. FIXME: remove
eventually. */
fixupOptionType = loc: opt: fixupOptionType = loc: opt:
let let
options = opt.options or options = opt.options or
@ -305,7 +311,10 @@ rec {
else if tp.name == "list of option sets" then types.listOf (types.submodule options) else if tp.name == "list of option sets" then types.listOf (types.submodule options)
else if tp.name == "null or option set" then types.nullOr (types.submodule options) else if tp.name == "null or option set" then types.nullOr (types.submodule options)
else tp; else tp;
in opt // { type = f (opt.type or types.unspecified); }; in
if opt.type.getSubModules or null == null
then opt // { type = f (opt.type or types.unspecified); }
else opt // { type = opt.type.substSubModules opt.options; options = []; };
/* Properties. */ /* Properties. */

View File

@ -33,9 +33,14 @@ rec {
, # Return a flat list of sub-options. Used to generate , # Return a flat list of sub-options. Used to generate
# documentation. # documentation.
getSubOptions ? prefix: {} getSubOptions ? prefix: {}
, # List of modules if any, or null if none.
getSubModules ? null
, # Function for building the same option type with a different list of
# modules.
substSubModules ? m: null
}: }:
{ _type = "option-type"; { _type = "option-type";
inherit name check merge getSubOptions; inherit name check merge getSubOptions getSubModules substSubModules;
}; };
@ -110,6 +115,8 @@ rec {
elemType.merge (loc ++ ["[${toString n}-${toString m}]"]) elemType.merge (loc ++ ["[${toString n}-${toString m}]"])
[{ inherit (def) file; value = def'; }]) def.value) defs); [{ inherit (def) file; value = def'; }]) def.value) defs);
getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["*"]); getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["*"]);
getSubModules = elemType.getSubModules;
substSubModules = m: listOf (elemType.substSubModules m);
}; };
attrsOf = elemType: mkOptionType { attrsOf = elemType: mkOptionType {
@ -121,6 +128,8 @@ rec {
(map (def: listToAttrs (mapAttrsToList (n: def': (map (def: listToAttrs (mapAttrsToList (n: def':
{ name = n; value = { inherit (def) file; value = def'; }; }) def.value)) defs); { name = n; value = { inherit (def) file; value = def'; }; }) def.value)) defs);
getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name>"]); getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name>"]);
getSubModules = elemType.getSubModules;
substSubModules = m: attrsOf (elemType.substSubModules m);
}; };
# List or attribute set of ... # List or attribute set of ...
@ -147,12 +156,16 @@ rec {
else false; else false;
merge = loc: defs: attrOnly.merge loc (imap convertIfList defs); merge = loc: defs: attrOnly.merge loc (imap convertIfList defs);
getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name?>"]); getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name?>"]);
getSubModules = elemType.getSubModules;
substSubModules = m: loaOf (elemType.substSubModules m);
}; };
uniq = elemType: mkOptionType { uniq = elemType: mkOptionType {
inherit (elemType) name check; inherit (elemType) name check;
merge = mergeOneOption; merge = mergeOneOption;
getSubOptions = elemType.getSubOptions; getSubOptions = elemType.getSubOptions;
getSubModules = elemType.getSubModules;
substSubModules = m: uniq (elemType.substSubModules m);
}; };
nullOr = elemType: mkOptionType { nullOr = elemType: mkOptionType {
@ -165,6 +178,8 @@ rec {
throw "The option `${showOption loc}' is defined both null and not null, in ${showFiles (getFiles defs)}." throw "The option `${showOption loc}' is defined both null and not null, in ${showFiles (getFiles defs)}."
else elemType.merge loc defs; else elemType.merge loc defs;
getSubOptions = elemType.getSubOptions; getSubOptions = elemType.getSubOptions;
getSubModules = elemType.getSubModules;
substSubModules = m: nullOr (elemType.substSubModules m);
}; };
functionTo = elemType: mkOptionType { functionTo = elemType: mkOptionType {
@ -173,6 +188,8 @@ rec {
merge = loc: defs: merge = loc: defs:
fnArgs: elemType.merge loc (map (fn: { inherit (fn) file; value = fn.value fnArgs; }) defs); fnArgs: elemType.merge loc (map (fn: { inherit (fn) file; value = fn.value fnArgs; }) defs);
getSubOptions = elemType.getSubOptions; getSubOptions = elemType.getSubOptions;
getSubModules = elemType.getSubModules;
substSubModules = m: functionTo (elemType.substSubModules m);
}; };
submodule = opts: submodule = opts:
@ -192,6 +209,8 @@ rec {
{ modules = opts'; inherit prefix; { modules = opts'; inherit prefix;
# FIXME: hack to get shit to evaluate. # FIXME: hack to get shit to evaluate.
args = { name = ""; }; }).options; args = { name = ""; }; }).options;
getSubModules = opts';
substSubModules = m: submodule m;
}; };
enum = values: mkOptionType { enum = values: mkOptionType {