Skip to content

Extend dartdoc options to begin to handle experiment flags #1884

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jan 7, 2019
35 changes: 24 additions & 11 deletions lib/src/dartdoc_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import 'dart:io';
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';
Expand Down Expand Up @@ -489,8 +490,8 @@ abstract class DartdocOption<T> {
/// 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) {
Expand Down Expand Up @@ -674,7 +675,7 @@ class DartdocOptionFileSynth<T> extends DartdocOption<T>
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;
}

Expand Down Expand Up @@ -721,7 +722,7 @@ class DartdocOptionArgSynth<T> extends DartdocOption<T>
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;
Expand Down Expand Up @@ -767,7 +768,7 @@ class DartdocOptionSyntheticOnly<T> extends DartdocOption<T>
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> {
Expand Down Expand Up @@ -801,7 +802,7 @@ typedef Future<List<DartdocOption>> OptionGenerator();
/// 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.
Expand Down Expand Up @@ -852,7 +853,7 @@ class DartdocOptionArgOnly<T> extends DartdocOption<T>
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;
Expand Down Expand Up @@ -888,7 +889,7 @@ class DartdocOptionArgFile<T> extends DartdocOption<T>
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;
Expand Down Expand Up @@ -938,7 +939,7 @@ class DartdocOptionFileOnly<T> extends DartdocOption<T>
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;
}
Expand Down Expand Up @@ -1258,12 +1259,22 @@ abstract class _DartdocArgOption<T> implements DartdocOption<T> {
}
}

/// 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
Expand Down Expand Up @@ -1561,5 +1572,7 @@ Future<List<DartdocOption>> createDartdocOptions() async {
'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());
}
39 changes: 39 additions & 0 deletions lib/src/experiment_options.dart
Original file line number Diff line number Diff line change
@@ -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))),
];
}
2 changes: 1 addition & 1 deletion lib/src/logging.dart
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ void startLogging(LoggingContext config) {
}
}

abstract class LoggingContext implements DartdocOptionContext {
abstract class LoggingContext implements DartdocOptionContextBase {
bool get json => optionSet['json'].valueAt(context);
bool get showProgress => optionSet['showProgress'].valueAt(context);
}
Expand Down
3 changes: 3 additions & 0 deletions lib/src/model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6458,6 +6458,9 @@ class PackageBuilder {
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()
Expand Down
12 changes: 9 additions & 3 deletions test/dartdoc_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,15 @@ void main() {

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 {
Expand Down
68 changes: 68 additions & 0 deletions test/experiment_options_test.dart
Original file line number Diff line number Diff line change
@@ -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);
});
});
}