@@ -16,6 +16,7 @@ library dartdoc.dartdoc_options;
16
16
17
17
import 'dart:io' ;
18
18
19
+ import 'package:analyzer/dart/element/element.dart' ;
19
20
import 'package:args/args.dart' ;
20
21
import 'package:dartdoc/dartdoc.dart' ;
21
22
import 'package:path/path.dart' as pathLib;
@@ -32,6 +33,22 @@ const int _kIntVal = 0;
32
33
const double _kDoubleVal = 0.0 ;
33
34
const bool _kBoolVal = true ;
34
35
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
+
35
52
class DartdocOptionError extends DartdocFailure {
36
53
DartdocOptionError (String details) : super (details);
37
54
}
@@ -80,10 +97,12 @@ class _OptionValueWithContext<T> {
80
97
/// if [T] isn't a [String] or [List<String>] .
81
98
T get resolvedValue {
82
99
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 ;
85
104
} else if (value is String ) {
86
- return pathContext.canonicalize (value as String ) as T ;
105
+ return pathContext.canonicalize (_resolveTildePath ( value as String ) ) as T ;
87
106
} else {
88
107
throw new UnsupportedError ('Type $T is not supported for resolvedValue' );
89
108
}
@@ -249,6 +268,13 @@ abstract class DartdocOption<T> {
249
268
/// corresponding files or directories.
250
269
T valueAt (Directory dir);
251
270
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
+
252
278
/// Adds a DartdocOption to the children of this DartdocOption.
253
279
void add (DartdocOption option) {
254
280
if (_children.containsKey (option.name))
@@ -278,6 +304,38 @@ abstract class DartdocOption<T> {
278
304
}
279
305
}
280
306
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
+
281
339
/// A [DartdocOption] that only contains other [DartdocOption] s and is not an option itself.
282
340
class DartdocOptionSet extends DartdocOption <Null > {
283
341
DartdocOptionSet (String name)
@@ -292,9 +350,8 @@ class DartdocOptionSet extends DartdocOption<Null> {
292
350
void _onMissing (
293
351
_OptionValueWithContext valueWithContext, String missingFilename) {}
294
352
295
- @override
296
-
297
353
/// Traverse skips this node, because it doesn't represent a real configuration object.
354
+ @override
298
355
void traverse (void visitor (DartdocOption )) {
299
356
_children.values.forEach ((d) => d.traverse (visitor));
300
357
}
@@ -312,7 +369,7 @@ class DartdocOptionArgOnly<T> extends DartdocOption<T>
312
369
DartdocOptionArgOnly (String name, T defaultsTo,
313
370
{String abbr,
314
371
bool mustExist = false ,
315
- help: '' ,
372
+ String help = '' ,
316
373
bool hide,
317
374
bool isDir = false ,
318
375
bool isFile = false ,
@@ -708,6 +765,203 @@ abstract class _DartdocArgOption<T> implements DartdocOption<T> {
708
765
}
709
766
}
710
767
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
+
711
965
final Map <String , DartdocOptions > _dartdocOptionsCache = {};
712
966
713
967
/// Legacy dartdoc options class. TODO(jcollins-g): merge with [DartdocOption] .
0 commit comments