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
diff --git a/lib/src/dartdoc_options.dart b/lib/src/dartdoc_options.dart
index 15d7a47..bb95737 100644
--- a/lib/src/dartdoc_options.dart
+++ b/lib/src/dartdoc_options.dart
@@ -20,6 +20,7 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:args/args.dart';
import 'package:dartdoc/dartdoc.dart';
+import 'package:dartdoc/src/experiment_options.dart';
import 'package:dartdoc/src/io_utils.dart';
import 'package:dartdoc/src/tool_runner.dart';
import 'package:dartdoc/src/tuple.dart';
@@ -489,8 +490,8 @@
/// and requires that one of [isDir] or [isFile] is set.
final bool mustExist;
- DartdocOption._(this.name, this.defaultsTo, this.help, this.isDir,
- this.isFile, this.mustExist, this._convertYamlToType) {
+ DartdocOption(this.name, this.defaultsTo, this.help, this.isDir, this.isFile,
+ this.mustExist, this._convertYamlToType) {
assert(!(isDir && isFile));
if (isDir || isFile) assert(_isString || _isListString || _isMapString);
if (mustExist) {
@@ -674,7 +675,7 @@
bool isFile = false,
bool parentDirOverridesChild,
T Function(YamlMap, pathLib.Context) convertYamlToType})
- : super._(name, null, help, isDir, isFile, mustExist, convertYamlToType) {
+ : super(name, null, help, isDir, isFile, mustExist, convertYamlToType) {
_parentDirOverridesChild = parentDirOverridesChild;
}
@@ -721,7 +722,7 @@
bool isFile = false,
bool negatable = false,
bool splitCommas})
- : super._(name, null, help, isDir, isFile, mustExist, null) {
+ : super(name, null, help, isDir, isFile, mustExist, null) {
_hide = hide;
_negatable = negatable;
_splitCommas = splitCommas;
@@ -767,7 +768,7 @@
String help = '',
bool isDir = false,
bool isFile = false})
- : super._(name, null, help, isDir, isFile, mustExist, null);
+ : super(name, null, help, isDir, isFile, mustExist, null);
}
abstract class DartdocSyntheticOption<T> implements DartdocOption<T> {
@@ -801,7 +802,7 @@
/// A [DartdocOption] that only contains other [DartdocOption]s and is not an option itself.
class DartdocOptionSet extends DartdocOption<Null> {
DartdocOptionSet(String name)
- : super._(name, null, null, false, false, false, null);
+ : super(name, null, null, false, false, false, null);
/// Asynchronous factory that is the main entry point to initialize Dartdoc
/// options for use.
@@ -852,7 +853,7 @@
bool isFile = false,
bool negatable = false,
bool splitCommas})
- : super._(name, defaultsTo, help, isDir, isFile, mustExist, null) {
+ : super(name, defaultsTo, help, isDir, isFile, mustExist, null) {
_hide = hide;
_negatable = negatable;
_splitCommas = splitCommas;
@@ -888,7 +889,7 @@
bool negatable = false,
bool parentDirOverridesChild: false,
bool splitCommas})
- : super._(name, defaultsTo, help, isDir, isFile, mustExist, null) {
+ : super(name, defaultsTo, help, isDir, isFile, mustExist, null) {
_abbr = abbr;
_hide = hide;
_negatable = negatable;
@@ -938,7 +939,7 @@
bool isFile = false,
bool parentDirOverridesChild: false,
T Function(YamlMap, pathLib.Context) convertYamlToType})
- : super._(name, defaultsTo, help, isDir, isFile, mustExist,
+ : super(name, defaultsTo, help, isDir, isFile, mustExist,
convertYamlToType) {
_parentDirOverridesChild = parentDirOverridesChild;
}
@@ -1258,12 +1259,22 @@
}
}
+/// All DartdocOptionContext mixins should implement this, as well as any other
+/// DartdocOptionContext mixins they use for calculating synthetic options.
+abstract class DartdocOptionContextBase {
+ DartdocOptionSet get optionSet;
+ Directory get context;
+}
+
/// An [DartdocOptionSet] wrapped in nice accessors specific to Dartdoc, which
/// automatically passes in the right directory for a given context. Usually,
/// a single [ModelElement], [Package], [Category] and so forth has a single context
/// and so this can be made a member variable of those structures.
-class DartdocOptionContext {
+class DartdocOptionContext extends DartdocOptionContextBase
+ with DartdocExperimentOptionContext {
+ @override
final DartdocOptionSet optionSet;
+ @override
Directory context;
// TODO(jcollins-g): Allow passing in structured data to initialize a
@@ -1561,5 +1572,7 @@
'exist. Executables for different platforms are specified by '
'giving the platform name as a key, and a list of strings as the '
'command.'),
- ];
+ // TODO(jcollins-g): refactor so there is a single static "create" for
+ // each DartdocOptionContext that traverses the inheritance tree itself.
+ ]..addAll(await createExperimentOptions());
}
diff --git a/lib/src/experiment_options.dart b/lib/src/experiment_options.dart
new file mode 100644
index 0000000..0d9fa2f
--- /dev/null
+++ b/lib/src/experiment_options.dart
@@ -0,0 +1,39 @@
+// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+///
+/// Implementation of Dart language experiment option handling for dartdoc.
+/// See https://github.com/dart-lang/sdk/blob/master/docs/process/experimental-flags.md.
+///
+library dartdoc.experiment_options;
+
+import 'package:analyzer/src/dart/analysis/experiments.dart';
+import 'package:dartdoc/src/dartdoc_options.dart';
+
+abstract class DartdocExperimentOptionContext
+ implements DartdocOptionContextBase {
+ List<String> get enableExperiment =>
+ optionSet['enable-experiment'].valueAt(context);
+ ExperimentStatus get experimentStatus =>
+ optionSet['experimentStatus'].valueAt(context);
+}
+
+// TODO(jcollins-g): Implement YAML parsing for these flags and generation
+// of [DartdocExperimentOptionContext], once a YAML file is available.
+Future<List<DartdocOption>> createExperimentOptions() async {
+ return <DartdocOption>[
+ // TODO(jcollins-g): Consider loading experiment values from dartdoc_options.yaml?
+ new DartdocOptionArgOnly<List<String>>('enable-experiment', [],
+ help: 'Enable or disable listed experiments.\n' +
+ ExperimentStatus.knownFeatures.values
+ .where((e) => e.documentation != null)
+ .map((e) =>
+ ' [no-]${e.enableString}: ${e.documentation} (default: ${e.isEnabledByDefault})')
+ .join('\n')),
+ new DartdocOptionSyntheticOnly<ExperimentStatus>(
+ 'experimentStatus',
+ (option, dir) => new ExperimentStatus.fromStrings(
+ option.parent['enable-experiment'].valueAt(dir))),
+ ];
+}
diff --git a/lib/src/logging.dart b/lib/src/logging.dart
index 103be7e..c52b05f 100644
--- a/lib/src/logging.dart
+++ b/lib/src/logging.dart
@@ -111,7 +111,7 @@
}
}
-abstract class LoggingContext implements DartdocOptionContext {
+abstract class LoggingContext implements DartdocOptionContextBase {
bool get json => optionSet['json'].valueAt(context);
bool get showProgress => optionSet['showProgress'].valueAt(context);
}
diff --git a/lib/src/model.dart b/lib/src/model.dart
index 13bace5..b72ee5b 100644
--- a/lib/src/model.dart
+++ b/lib/src/model.dart
@@ -6458,6 +6458,9 @@
AnalysisDriverScheduler scheduler = new AnalysisDriverScheduler(log);
AnalysisOptionsImpl options = new AnalysisOptionsImpl();
+ // TODO(jcollins-g): pass in an ExperimentStatus instead?
+ options.enabledExperiments = config.enableExperiment;
+
// TODO(jcollins-g): Make use of currently not existing API for managing
// many AnalysisDrivers
// TODO(jcollins-g): make use of DartProject isApi()
diff --git a/test/dartdoc_test.dart b/test/dartdoc_test.dart
index 0e405e8..ab02d48 100644
--- a/test/dartdoc_test.dart
+++ b/test/dartdoc_test.dart
@@ -154,9 +154,15 @@
test('help prints command line args', () async {
List<String> outputLines = [];
- await subprocessLauncher.runStreamed(Platform.resolvedExecutable, [dartdocPath, '--help'], perLine: outputLines.add);
- expect(outputLines, contains('Generate HTML documentation for Dart libraries.'));
- expect(outputLines.join('\n'), contains(new RegExp('^-h, --help[ ]+Show command help.', multiLine: true))) ;
+ await subprocessLauncher.runStreamed(
+ Platform.resolvedExecutable, [dartdocPath, '--help'],
+ perLine: outputLines.add);
+ expect(outputLines,
+ contains('Generate HTML documentation for Dart libraries.'));
+ expect(
+ outputLines.join('\n'),
+ contains(new RegExp('^-h, --help[ ]+Show command help.',
+ multiLine: true)));
});
test('Validate missing FLUTTER_ROOT exception is clean', () async {
diff --git a/test/experiment_options_test.dart b/test/experiment_options_test.dart
new file mode 100644
index 0000000..6772bfd
--- /dev/null
+++ b/test/experiment_options_test.dart
@@ -0,0 +1,68 @@
+// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// Unit tests for lib/src/experiment_options.dart.
+library dartdoc.experiment_options_test;
+
+import 'dart:io';
+
+import 'package:dartdoc/src/dartdoc_options.dart';
+import 'package:dartdoc/src/experiment_options.dart';
+import 'package:path/path.dart' as pathLib;
+import 'package:test/test.dart';
+
+class DartdocExperimentOptionContextTester extends DartdocOptionContext {
+ DartdocExperimentOptionContextTester(
+ DartdocOptionSet optionSet, FileSystemEntity entity)
+ : super(optionSet, entity);
+}
+
+void main() {
+ DartdocOptionSet experimentOptions;
+ Directory tempDir;
+ File optionsFile;
+
+ setUp(() async {
+ experimentOptions = await DartdocOptionSet.fromOptionGenerators(
+ 'dartdoc', [createExperimentOptions]);
+ });
+
+ setUpAll(() {
+ tempDir = Directory.systemTemp.createTempSync('experiment_options_test');
+ optionsFile = new File(pathLib.join(tempDir.path, 'dartdoc_options.yaml'))
+ ..createSync();
+ optionsFile.writeAsStringSync('''
+dartdoc:
+ enable-experiment:
+ - constant-update-2018
+ - fake-experiment
+ - no-fake-experiment-on
+''');
+ });
+
+ tearDownAll(() {
+ tempDir.deleteSync(recursive: true);
+ });
+
+ group('Experimental options test', () {
+ test('Defaults work for all options', () {
+ experimentOptions.parseArguments([]);
+ DartdocExperimentOptionContextTester tester =
+ new DartdocExperimentOptionContextTester(
+ experimentOptions, Directory.current);
+ expect(tester.experimentStatus.constant_update_2018, isFalse);
+ expect(tester.experimentStatus.set_literals, isFalse);
+ });
+
+ test('Overriding defaults works via args', () {
+ experimentOptions.parseArguments(
+ ['--enable-experiment', 'constant-update-2018,set-literals']);
+ DartdocExperimentOptionContextTester tester =
+ new DartdocExperimentOptionContextTester(
+ experimentOptions, Directory.current);
+ expect(tester.experimentStatus.constant_update_2018, isTrue);
+ expect(tester.experimentStatus.set_literals, isTrue);
+ });
+ });
+}