Skip to content

Commit 7930d89

Browse files
authored
Extend dartdoc options to begin to handle experiment flags (#1884)
* Add basic support for experiment flags * dartfmt * Add test for throw * Synchronize analyzer and dartdoc experiment implementations * dartfmt
1 parent 27ae7bf commit 7930d89

6 files changed

+144
-15
lines changed

lib/src/dartdoc_options.dart

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import 'dart:io';
2020
import 'package:analyzer/dart/element/element.dart';
2121
import 'package:args/args.dart';
2222
import 'package:dartdoc/dartdoc.dart';
23+
import 'package:dartdoc/src/experiment_options.dart';
2324
import 'package:dartdoc/src/io_utils.dart';
2425
import 'package:dartdoc/src/tool_runner.dart';
2526
import 'package:dartdoc/src/tuple.dart';
@@ -489,8 +490,8 @@ abstract class DartdocOption<T> {
489490
/// and requires that one of [isDir] or [isFile] is set.
490491
final bool mustExist;
491492

492-
DartdocOption._(this.name, this.defaultsTo, this.help, this.isDir,
493-
this.isFile, this.mustExist, this._convertYamlToType) {
493+
DartdocOption(this.name, this.defaultsTo, this.help, this.isDir, this.isFile,
494+
this.mustExist, this._convertYamlToType) {
494495
assert(!(isDir && isFile));
495496
if (isDir || isFile) assert(_isString || _isListString || _isMapString);
496497
if (mustExist) {
@@ -674,7 +675,7 @@ class DartdocOptionFileSynth<T> extends DartdocOption<T>
674675
bool isFile = false,
675676
bool parentDirOverridesChild,
676677
T Function(YamlMap, pathLib.Context) convertYamlToType})
677-
: super._(name, null, help, isDir, isFile, mustExist, convertYamlToType) {
678+
: super(name, null, help, isDir, isFile, mustExist, convertYamlToType) {
678679
_parentDirOverridesChild = parentDirOverridesChild;
679680
}
680681

@@ -721,7 +722,7 @@ class DartdocOptionArgSynth<T> extends DartdocOption<T>
721722
bool isFile = false,
722723
bool negatable = false,
723724
bool splitCommas})
724-
: super._(name, null, help, isDir, isFile, mustExist, null) {
725+
: super(name, null, help, isDir, isFile, mustExist, null) {
725726
_hide = hide;
726727
_negatable = negatable;
727728
_splitCommas = splitCommas;
@@ -767,7 +768,7 @@ class DartdocOptionSyntheticOnly<T> extends DartdocOption<T>
767768
String help = '',
768769
bool isDir = false,
769770
bool isFile = false})
770-
: super._(name, null, help, isDir, isFile, mustExist, null);
771+
: super(name, null, help, isDir, isFile, mustExist, null);
771772
}
772773

773774
abstract class DartdocSyntheticOption<T> implements DartdocOption<T> {
@@ -801,7 +802,7 @@ typedef Future<List<DartdocOption>> OptionGenerator();
801802
/// A [DartdocOption] that only contains other [DartdocOption]s and is not an option itself.
802803
class DartdocOptionSet extends DartdocOption<Null> {
803804
DartdocOptionSet(String name)
804-
: super._(name, null, null, false, false, false, null);
805+
: super(name, null, null, false, false, false, null);
805806

806807
/// Asynchronous factory that is the main entry point to initialize Dartdoc
807808
/// options for use.
@@ -852,7 +853,7 @@ class DartdocOptionArgOnly<T> extends DartdocOption<T>
852853
bool isFile = false,
853854
bool negatable = false,
854855
bool splitCommas})
855-
: super._(name, defaultsTo, help, isDir, isFile, mustExist, null) {
856+
: super(name, defaultsTo, help, isDir, isFile, mustExist, null) {
856857
_hide = hide;
857858
_negatable = negatable;
858859
_splitCommas = splitCommas;
@@ -888,7 +889,7 @@ class DartdocOptionArgFile<T> extends DartdocOption<T>
888889
bool negatable = false,
889890
bool parentDirOverridesChild: false,
890891
bool splitCommas})
891-
: super._(name, defaultsTo, help, isDir, isFile, mustExist, null) {
892+
: super(name, defaultsTo, help, isDir, isFile, mustExist, null) {
892893
_abbr = abbr;
893894
_hide = hide;
894895
_negatable = negatable;
@@ -938,7 +939,7 @@ class DartdocOptionFileOnly<T> extends DartdocOption<T>
938939
bool isFile = false,
939940
bool parentDirOverridesChild: false,
940941
T Function(YamlMap, pathLib.Context) convertYamlToType})
941-
: super._(name, defaultsTo, help, isDir, isFile, mustExist,
942+
: super(name, defaultsTo, help, isDir, isFile, mustExist,
942943
convertYamlToType) {
943944
_parentDirOverridesChild = parentDirOverridesChild;
944945
}
@@ -1258,12 +1259,22 @@ abstract class _DartdocArgOption<T> implements DartdocOption<T> {
12581259
}
12591260
}
12601261

1262+
/// All DartdocOptionContext mixins should implement this, as well as any other
1263+
/// DartdocOptionContext mixins they use for calculating synthetic options.
1264+
abstract class DartdocOptionContextBase {
1265+
DartdocOptionSet get optionSet;
1266+
Directory get context;
1267+
}
1268+
12611269
/// An [DartdocOptionSet] wrapped in nice accessors specific to Dartdoc, which
12621270
/// automatically passes in the right directory for a given context. Usually,
12631271
/// a single [ModelElement], [Package], [Category] and so forth has a single context
12641272
/// and so this can be made a member variable of those structures.
1265-
class DartdocOptionContext {
1273+
class DartdocOptionContext extends DartdocOptionContextBase
1274+
with DartdocExperimentOptionContext {
1275+
@override
12661276
final DartdocOptionSet optionSet;
1277+
@override
12671278
Directory context;
12681279

12691280
// TODO(jcollins-g): Allow passing in structured data to initialize a
@@ -1561,5 +1572,7 @@ Future<List<DartdocOption>> createDartdocOptions() async {
15611572
'exist. Executables for different platforms are specified by '
15621573
'giving the platform name as a key, and a list of strings as the '
15631574
'command.'),
1564-
];
1575+
// TODO(jcollins-g): refactor so there is a single static "create" for
1576+
// each DartdocOptionContext that traverses the inheritance tree itself.
1577+
]..addAll(await createExperimentOptions());
15651578
}

lib/src/experiment_options.dart

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
///
6+
/// Implementation of Dart language experiment option handling for dartdoc.
7+
/// See https://github.com/dart-lang/sdk/blob/master/docs/process/experimental-flags.md.
8+
///
9+
library dartdoc.experiment_options;
10+
11+
import 'package:analyzer/src/dart/analysis/experiments.dart';
12+
import 'package:dartdoc/src/dartdoc_options.dart';
13+
14+
abstract class DartdocExperimentOptionContext
15+
implements DartdocOptionContextBase {
16+
List<String> get enableExperiment =>
17+
optionSet['enable-experiment'].valueAt(context);
18+
ExperimentStatus get experimentStatus =>
19+
optionSet['experimentStatus'].valueAt(context);
20+
}
21+
22+
// TODO(jcollins-g): Implement YAML parsing for these flags and generation
23+
// of [DartdocExperimentOptionContext], once a YAML file is available.
24+
Future<List<DartdocOption>> createExperimentOptions() async {
25+
return <DartdocOption>[
26+
// TODO(jcollins-g): Consider loading experiment values from dartdoc_options.yaml?
27+
new DartdocOptionArgOnly<List<String>>('enable-experiment', [],
28+
help: 'Enable or disable listed experiments.\n' +
29+
ExperimentStatus.knownFeatures.values
30+
.where((e) => e.documentation != null)
31+
.map((e) =>
32+
' [no-]${e.enableString}: ${e.documentation} (default: ${e.isEnabledByDefault})')
33+
.join('\n')),
34+
new DartdocOptionSyntheticOnly<ExperimentStatus>(
35+
'experimentStatus',
36+
(option, dir) => new ExperimentStatus.fromStrings(
37+
option.parent['enable-experiment'].valueAt(dir))),
38+
];
39+
}

lib/src/logging.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ void startLogging(LoggingContext config) {
111111
}
112112
}
113113

114-
abstract class LoggingContext implements DartdocOptionContext {
114+
abstract class LoggingContext implements DartdocOptionContextBase {
115115
bool get json => optionSet['json'].valueAt(context);
116116
bool get showProgress => optionSet['showProgress'].valueAt(context);
117117
}

lib/src/model.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6458,6 +6458,9 @@ class PackageBuilder {
64586458
AnalysisDriverScheduler scheduler = new AnalysisDriverScheduler(log);
64596459
AnalysisOptionsImpl options = new AnalysisOptionsImpl();
64606460

6461+
// TODO(jcollins-g): pass in an ExperimentStatus instead?
6462+
options.enabledExperiments = config.enableExperiment;
6463+
64616464
// TODO(jcollins-g): Make use of currently not existing API for managing
64626465
// many AnalysisDrivers
64636466
// TODO(jcollins-g): make use of DartProject isApi()

test/dartdoc_test.dart

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,15 @@ void main() {
154154

155155
test('help prints command line args', () async {
156156
List<String> outputLines = [];
157-
await subprocessLauncher.runStreamed(Platform.resolvedExecutable, [dartdocPath, '--help'], perLine: outputLines.add);
158-
expect(outputLines, contains('Generate HTML documentation for Dart libraries.'));
159-
expect(outputLines.join('\n'), contains(new RegExp('^-h, --help[ ]+Show command help.', multiLine: true))) ;
157+
await subprocessLauncher.runStreamed(
158+
Platform.resolvedExecutable, [dartdocPath, '--help'],
159+
perLine: outputLines.add);
160+
expect(outputLines,
161+
contains('Generate HTML documentation for Dart libraries.'));
162+
expect(
163+
outputLines.join('\n'),
164+
contains(new RegExp('^-h, --help[ ]+Show command help.',
165+
multiLine: true)));
160166
});
161167

162168
test('Validate missing FLUTTER_ROOT exception is clean', () async {

test/experiment_options_test.dart

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
/// Unit tests for lib/src/experiment_options.dart.
6+
library dartdoc.experiment_options_test;
7+
8+
import 'dart:io';
9+
10+
import 'package:dartdoc/src/dartdoc_options.dart';
11+
import 'package:dartdoc/src/experiment_options.dart';
12+
import 'package:path/path.dart' as pathLib;
13+
import 'package:test/test.dart';
14+
15+
class DartdocExperimentOptionContextTester extends DartdocOptionContext {
16+
DartdocExperimentOptionContextTester(
17+
DartdocOptionSet optionSet, FileSystemEntity entity)
18+
: super(optionSet, entity);
19+
}
20+
21+
void main() {
22+
DartdocOptionSet experimentOptions;
23+
Directory tempDir;
24+
File optionsFile;
25+
26+
setUp(() async {
27+
experimentOptions = await DartdocOptionSet.fromOptionGenerators(
28+
'dartdoc', [createExperimentOptions]);
29+
});
30+
31+
setUpAll(() {
32+
tempDir = Directory.systemTemp.createTempSync('experiment_options_test');
33+
optionsFile = new File(pathLib.join(tempDir.path, 'dartdoc_options.yaml'))
34+
..createSync();
35+
optionsFile.writeAsStringSync('''
36+
dartdoc:
37+
enable-experiment:
38+
- constant-update-2018
39+
- fake-experiment
40+
- no-fake-experiment-on
41+
''');
42+
});
43+
44+
tearDownAll(() {
45+
tempDir.deleteSync(recursive: true);
46+
});
47+
48+
group('Experimental options test', () {
49+
test('Defaults work for all options', () {
50+
experimentOptions.parseArguments([]);
51+
DartdocExperimentOptionContextTester tester =
52+
new DartdocExperimentOptionContextTester(
53+
experimentOptions, Directory.current);
54+
expect(tester.experimentStatus.constant_update_2018, isFalse);
55+
expect(tester.experimentStatus.set_literals, isFalse);
56+
});
57+
58+
test('Overriding defaults works via args', () {
59+
experimentOptions.parseArguments(
60+
['--enable-experiment', 'constant-update-2018,set-literals']);
61+
DartdocExperimentOptionContextTester tester =
62+
new DartdocExperimentOptionContextTester(
63+
experimentOptions, Directory.current);
64+
expect(tester.experimentStatus.constant_update_2018, isTrue);
65+
expect(tester.experimentStatus.set_literals, isTrue);
66+
});
67+
});
68+
}

0 commit comments

Comments
 (0)