@@ -16,6 +16,7 @@ library dartdoc.dartdoc_options;
1616
1717import 'dart:io' ;
1818
19+ import 'package:analyzer/dart/element/element.dart' ;
1920import 'package:args/args.dart' ;
2021import 'package:dartdoc/dartdoc.dart' ;
2122import 'package:path/path.dart' as pathLib;
@@ -32,6 +33,22 @@ const int _kIntVal = 0;
3233const double _kDoubleVal = 0.0 ;
3334const bool _kBoolVal = true ;
3435
36+ String _resolveTildePath (String originalPath) {
37+ if (originalPath == null || ! originalPath.startsWith ('~/' )) {
38+ return originalPath;
39+ }
40+
41+ String homeDir;
42+
43+ if (Platform .isWindows) {
44+ homeDir = pathLib.absolute (Platform .environment['USERPROFILE' ]);
45+ } else {
46+ homeDir = pathLib.absolute (Platform .environment['HOME' ]);
47+ }
48+
49+ return pathLib.join (homeDir, originalPath.substring (2 ));
50+ }
51+
3552class DartdocOptionError extends DartdocFailure {
3653 DartdocOptionError (String details) : super (details);
3754}
@@ -80,10 +97,12 @@ class _OptionValueWithContext<T> {
8097 /// if [T] isn't a [String] or [List<String>] .
8198 T get resolvedValue {
8299 if (value is List <String >) {
83- return (value as List <String >).map ((v) => pathContext.canonicalize (v))
84- as T ;
100+ return (value as List <String >)
101+ .map ((v) => pathContext.canonicalize (_resolveTildePath (v)))
102+ .cast <String >()
103+ .toList () as T ;
85104 } else if (value is String ) {
86- return pathContext.canonicalize (value as String ) as T ;
105+ return pathContext.canonicalize (_resolveTildePath ( value as String ) ) as T ;
87106 } else {
88107 throw new UnsupportedError ('Type $T is not supported for resolvedValue' );
89108 }
@@ -249,6 +268,13 @@ abstract class DartdocOption<T> {
249268 /// corresponding files or directories.
250269 T valueAt (Directory dir);
251270
271+ /// Calls [valueAt] with the current working directory.
272+ T valueAtCurrent () => valueAt (Directory .current);
273+
274+ /// Calls [valueAt] on the directory this element is defined in.
275+ T valueAtElement (Element element) => valueAt (new Directory (
276+ pathLib.canonicalize (pathLib.basename (element.source.fullName))));
277+
252278 /// Adds a DartdocOption to the children of this DartdocOption.
253279 void add (DartdocOption option) {
254280 if (_children.containsKey (option.name))
@@ -278,6 +304,38 @@ abstract class DartdocOption<T> {
278304 }
279305}
280306
307+ /// A synthetic option takes a closure at construction time that computes
308+ /// the value of the configuration option based on other configuration options.
309+ /// Does not protect against closures that self-reference. If [mustExist] and
310+ /// [isDir] or [isFile] is set, computed values will be resolved to canonical
311+ /// paths.
312+ class DartdocOptionSynthetic <T > extends DartdocOption <T > {
313+ T Function (DartdocOptionSynthetic , Directory ) _compute;
314+
315+ DartdocOptionSynthetic (String name, this ._compute,
316+ {bool mustExist = false ,
317+ String help = '' ,
318+ bool isDir = false ,
319+ bool isFile = false })
320+ : super ._(name, null , help, isDir, isFile, mustExist);
321+
322+ @override
323+ T valueAt (Directory dir) {
324+ _OptionValueWithContext context =
325+ new _OptionValueWithContext <T >(_compute (this , dir), dir.path);
326+ return _handlePathsInContext (context);
327+ }
328+
329+ @override
330+ void _onMissing (
331+ _OptionValueWithContext valueWithContext, String missingPath) {
332+ String description =
333+ 'Synthetic configuration option ${name } from <internal>' ;
334+ throw new DartdocFileMissing (
335+ '$description , computed as ${valueWithContext .value }, resolves to missing path: "${missingPath }"' );
336+ }
337+ }
338+
281339/// A [DartdocOption] that only contains other [DartdocOption] s and is not an option itself.
282340class DartdocOptionSet extends DartdocOption <Null > {
283341 DartdocOptionSet (String name)
@@ -292,9 +350,8 @@ class DartdocOptionSet extends DartdocOption<Null> {
292350 void _onMissing (
293351 _OptionValueWithContext valueWithContext, String missingFilename) {}
294352
295- @override
296-
297353 /// Traverse skips this node, because it doesn't represent a real configuration object.
354+ @override
298355 void traverse (void visitor (DartdocOption )) {
299356 _children.values.forEach ((d) => d.traverse (visitor));
300357 }
@@ -312,7 +369,7 @@ class DartdocOptionArgOnly<T> extends DartdocOption<T>
312369 DartdocOptionArgOnly (String name, T defaultsTo,
313370 {String abbr,
314371 bool mustExist = false ,
315- help: '' ,
372+ String help = '' ,
316373 bool hide,
317374 bool isDir = false ,
318375 bool isFile = false ,
@@ -708,6 +765,203 @@ abstract class _DartdocArgOption<T> implements DartdocOption<T> {
708765 }
709766}
710767
768+ /// An [DartdocOptionSet] wrapped in nice accessors specific to Dartdoc, which
769+ /// automatically passes in the right directory for a given context. Usually,
770+ /// a single [ModelElement] , [Package] , [Category] and so forth has a single context
771+ /// and so this can be made a member variable of those structures.
772+ class DartdocOptionContext {
773+ final DartdocOptionSet optionSet;
774+ Directory _context;
775+
776+ DartdocOptionContext (this .optionSet, FileSystemEntity entity) {
777+ _context = new Directory (pathLib
778+ .canonicalize (entity is File ? entity.parent.path : entity.path));
779+ }
780+
781+ /// Build a [DartdocOptionSet] and associate it with a [DartdocOptionContext]
782+ /// based on the input directory.
783+ factory DartdocOptionContext .fromArgv (List <String > argv) {
784+ DartdocOptionSet optionSet = _createDartdocOptions (argv);
785+ String inputDir = optionSet['input' ].valueAtCurrent ();
786+ return new DartdocOptionContext (optionSet, new Directory (inputDir));
787+ }
788+
789+ /// Build a DartdocOptionContext from an analyzer element (using its source
790+ /// location).
791+ factory DartdocOptionContext .fromElement (
792+ DartdocOptionSet optionSet, Element element) {
793+ return new DartdocOptionContext (
794+ optionSet, new File (element.source.fullName));
795+ }
796+
797+ /// Build a DartdocOptionContext from an existing [DartdocOptionContext] and a new analyzer [Element] .
798+ factory DartdocOptionContext .fromContextElement (
799+ DartdocOptionContext optionContext, Element element) {
800+ return new DartdocOptionContext .fromElement (
801+ optionContext.optionSet, element);
802+ }
803+
804+ /// Build a DartdocOptionContext from an existing [DartdocOptionContext] .
805+ factory DartdocOptionContext .fromContext (
806+ DartdocOptionContext optionContext, FileSystemEntity entity) {
807+ return new DartdocOptionContext (optionContext.optionSet, entity);
808+ }
809+
810+ // All values defined in createDartdocOptions should be exposed here.
811+
812+ bool get addCrossdart => optionSet['addCrossdart' ].valueAt (_context);
813+ double get ambiguousReexportScorerMinConfidence =>
814+ optionSet['ambiguousReexportScorerMinConfidence' ].valueAt (_context);
815+ bool get autoIncludeDependencies =>
816+ optionSet['autoIncludeDependencies' ].valueAt (_context);
817+ List <String > get categoryOrder =>
818+ optionSet['categoryOrder' ].valueAt (_context);
819+ String get examplePathPrefix =>
820+ optionSet['examplePathPrefix' ].valueAt (_context);
821+ List <String > get exclude => optionSet['exclude' ].valueAt (_context);
822+ List <String > get excludePackages =>
823+ optionSet['excludePackages' ].valueAt (_context);
824+ String get favicon => optionSet['favicon' ].valueAt (_context);
825+ List <String > get footer => optionSet['footer' ].valueAt (_context);
826+ List <String > get footerText => optionSet['footerText' ].valueAt (_context);
827+ List <String > get header => optionSet['header' ].valueAt (_context);
828+ bool get help => optionSet['help' ].valueAt (_context);
829+ bool get hideSdkText => optionSet['hideSdkText' ].valueAt (_context);
830+ String get hostedUrl => optionSet['hostedUrl' ].valueAt (_context);
831+ List <String > get include => optionSet['include' ].valueAt (_context);
832+ List <String > get includeExternal =>
833+ optionSet['includeExternal' ].valueAt (_context);
834+ bool get includeSource => optionSet['includeSource' ].valueAt (_context);
835+ String get input => optionSet['input' ].valueAt (_context);
836+ bool get json => optionSet['json' ].valueAt (_context);
837+ String get output => optionSet['output' ].valueAt (_context);
838+ List <String > get packageOrder => optionSet['packageOrder' ].valueAt (_context);
839+ bool get prettyIndexJson => optionSet['prettyIndexJson' ].valueAt (_context);
840+ String get relCanonicalPrefix =>
841+ optionSet['relCanonicalPrefix' ].valueAt (_context);
842+ bool get sdkDocs => optionSet['sdkDocs' ].valueAt (_context);
843+ String get sdkDir => optionSet['sdkDir' ].valueAt (_context);
844+ bool get showProgress => optionSet['showProgress' ].valueAt (_context);
845+ bool get showWarnings => optionSet['showWarnings' ].valueAt (_context);
846+ bool get useCategories => optionSet['useCategories' ].valueAt (_context);
847+ bool get validateLinks => optionSet['validateLinks' ].valueAt (_context);
848+ bool get verboseWarnings => optionSet['verboseWarnings' ].valueAt (_context);
849+ bool get version => optionSet['version' ].valueAt (_context);
850+ }
851+
852+ /// Instantiate dartdoc's configuration file and options parser with the
853+ /// given command line arguments.
854+ DartdocOptionSet _createDartdocOptions (List <String > argv) {
855+ // Sync with DartdocOptionContext.
856+ return new DartdocOptionSet ('dartdoc' )
857+ ..addAll ([
858+ new DartdocOptionArgOnly <bool >('addCrossdart' , false ,
859+ help: 'Add Crossdart links to the source code pieces.' ,
860+ negatable: false ),
861+ new DartdocOptionBoth <double >('ambiguousReexportScorerMinConfidence' , 0.1 ,
862+ help:
863+ 'Minimum scorer confidence to suppress warning on ambiguous reexport.' ),
864+ new DartdocOptionArgOnly <bool >('autoIncludeDependencies' , false ,
865+ help:
866+ 'Include all the used libraries into the docs, even the ones not in the current package or "include-external"' ,
867+ negatable: false ),
868+ new DartdocOptionBoth <List <String >>('categoryOrder' , [],
869+ help:
870+ "A list of categories (not package names) to place first when grouping symbols on dartdoc's sidebar. "
871+ 'Unmentioned categories are sorted after these.' ),
872+ new DartdocOptionBoth <String >('examplePathPrefix' , null ,
873+ isDir: true ,
874+ help: 'Prefix for @example paths.\n (defaults to the project root)' ,
875+ mustExist: true ),
876+ new DartdocOptionBoth <List <String >>('exclude' , [],
877+ help: 'Library names to ignore.' , splitCommas: true ),
878+ new DartdocOptionBoth <List <String >>('excludePackages' , [],
879+ help: 'Package names to ignore.' , splitCommas: true ),
880+ new DartdocOptionBoth <String >('favicon' , null ,
881+ isFile: true ,
882+ help: 'A path to a favicon for the generated docs.' ,
883+ mustExist: true ),
884+ new DartdocOptionBoth <List <String >>('footer' , [],
885+ isFile: true ,
886+ help: 'paths to footer files containing HTML text.' ,
887+ mustExist: true ,
888+ splitCommas: true ),
889+ new DartdocOptionBoth <List <String >>('footerText' , [],
890+ isFile: true ,
891+ help:
892+ 'paths to footer-text files (optional text next to the package name '
893+ 'and version).' ,
894+ mustExist: true ,
895+ splitCommas: true ),
896+ new DartdocOptionBoth <List <String >>('header' , [],
897+ isFile: true ,
898+ help: 'paths to header files containing HTML text.' ,
899+ splitCommas: true ),
900+ new DartdocOptionArgOnly <bool >('help' , false ,
901+ abbr: 'h' , help: 'Show command help.' , negatable: false ),
902+ new DartdocOptionArgOnly <bool >('hideSdkText' , false ,
903+ hide: true ,
904+ help:
905+ 'Drop all text for SDK components. Helpful for integration tests for dartdoc, probably not useful for anything else.' ,
906+ negatable: true ),
907+ new DartdocOptionArgOnly <String >('hostedUrl' , null ,
908+ help:
909+ 'URL where the docs will be hosted (used to generate the sitemap).' ),
910+ new DartdocOptionBoth <List <String >>('include' , null ,
911+ help: 'Library names to generate docs for.' , splitCommas: true ),
912+ new DartdocOptionBoth <List <String >>('includeExternal' , null ,
913+ isFile: true ,
914+ help:
915+ 'Additional (external) dart files to include; use "dir/fileName", '
916+ 'as in lib/material.dart.' ,
917+ mustExist: true ,
918+ splitCommas: true ),
919+ new DartdocOptionBoth <bool >('includeSource' , true ,
920+ help: 'Show source code blocks.' , negatable: true ),
921+ new DartdocOptionArgOnly <String >('input' , Directory .current.path,
922+ isDir: true , help: 'Path to source directory' , mustExist: true ),
923+ new DartdocOptionArgOnly <bool >('json' , false ,
924+ help: 'Prints out progress JSON maps. One entry per line.' ,
925+ negatable: true ),
926+ new DartdocOptionArgOnly <String >('output' , defaultOutDir,
927+ isDir: true , help: 'Path to output directory.' ),
928+ new DartdocOptionBoth <List <String >>('packageOrder' , [],
929+ help:
930+ 'A list of package names to place first when grouping libraries in packages. '
931+ 'Unmentioned categories are sorted after these.' ),
932+ new DartdocOptionArgOnly <bool >('prettyIndexJson' , false ,
933+ help:
934+ "Generates `index.json` with indentation and newlines. The file is larger, but it's also easier to diff." ,
935+ negatable: false ),
936+ new DartdocOptionArgOnly <String >('relCanonicalPrefix' , null ,
937+ help:
938+ 'If provided, add a rel="canonical" prefixed with provided value. '
939+ 'Consider using if\n building many versions of the docs for public '
940+ 'SEO; learn more at https://goo.gl/gktN6F.' ),
941+ new DartdocOptionArgOnly <bool >('sdk-docs' , false ,
942+ help: 'Generate ONLY the docs for the Dart SDK.' , negatable: false ),
943+ new DartdocOptionArgOnly <String >('sdk-dir' , defaultSdkDir.absolute.path,
944+ help: 'Path to the SDK directory' , isDir: true , mustExist: true ),
945+ new DartdocOptionArgOnly <bool >('show-progress' , false ,
946+ help: 'Display progress indications to console stdout' ,
947+ negatable: false ),
948+ new DartdocOptionArgOnly <bool >('show-warnings' , false ,
949+ help: 'Display all warnings.' , negatable: false ),
950+ new DartdocOptionArgOnly <bool >('use-categories' , true ,
951+ help: 'Display categories in the sidebar of packages' ,
952+ negatable: false ),
953+ new DartdocOptionArgOnly <bool >('validate-links' , true ,
954+ help:
955+ 'Runs the built-in link checker to display Dart context aware warnings for broken links (slow)' ,
956+ negatable: true ),
957+ new DartdocOptionArgOnly <bool >('verbose-warnings' , true ,
958+ help: 'Display extra debugging information and help with warnings.' ,
959+ negatable: true ),
960+ new DartdocOptionArgOnly <bool >('version' , false ,
961+ help: 'Display the version for $name .' , negatable: false ),
962+ ]);
963+ }
964+
711965final Map <String , DartdocOptions > _dartdocOptionsCache = {};
712966
713967/// Legacy dartdoc options class. TODO(jcollins-g): merge with [DartdocOption] .
0 commit comments