diff --git a/tool/doc.dart b/tool/doc.dart index 87acdbb21..563ae90e1 100644 --- a/tool/doc.dart +++ b/tool/doc.dart @@ -12,6 +12,7 @@ import 'package:linter/src/analyzer.dart'; import 'package:linter/src/rules.dart'; import 'package:markdown/markdown.dart'; +import 'machine.dart'; import 'since.dart'; /// Generates lint rule docs for publishing to https://dart-lang.github.io/ @@ -61,14 +62,26 @@ These rules are under active development. Feedback is const ruleLeadMatter = 'Rules are organized into familiar rule groups.'; +final effectiveDartRules = []; final flutterRules = []; final pedanticRules = []; -final effectiveDartRules = []; /// Sorted list of contributed lint rules. final List rules = List.from(Registry.ruleRegistry, growable: false)..sort(); +Map sinceInfo; + +Future get effectiveDartLatestVersion async { + var url = + 'https://raw.githubusercontent.com/tenhobi/effective_dart/master/lib/analysis_options.yaml'; + var client = http.Client(); + print('loading $url...'); + var req = await client.get(url); + var parts = req.body.split('package:effective_dart/analysis_options.'); + return parts[1].split('.yaml')[0]; +} + String get enumerateErrorRules => rules .where((r) => r.group == Group.errors) .map((r) => '${toDescription(r)}') @@ -99,15 +112,8 @@ Future get pedanticLatestVersion async { return parts[1].split('.yaml')[0]; } -Future get effectiveDartLatestVersion async { - var url = - 'https://raw.githubusercontent.com/tenhobi/effective_dart/master/lib/analysis_options.yaml'; - var client = http.Client(); - print('loading $url...'); - var req = await client.get(url); - var parts = req.body.split('package:effective_dart/analysis_options.'); - return parts[1].split('.yaml')[0]; -} +String describeMaturity(LintRule r) => + r.maturity == Maturity.stable ? '' : ' (${r.maturity.name})'; Future fetchBadgeInfo() async { var latestPedantic = await pedanticLatestVersion; @@ -132,10 +138,6 @@ Future fetchBadgeInfo() async { } } -Future fetchSinceInfo() async { - sinceInfo = await sinceMap; -} - Future fetchConfig(String url) async { var client = http.Client(); print('loading $url...'); @@ -143,7 +145,9 @@ Future fetchConfig(String url) async { return processAnalysisOptionsFile(req.body); } -Map sinceInfo; +Future fetchSinceInfo() async { + sinceInfo = await sinceMap; +} Future generateDocs(String dir) async { var outDir = dir; @@ -184,6 +188,9 @@ Future generateDocs(String dir) async { // Generate options samples. OptionsSample(rules).generate(outDir); + + // Generate a machine-readable summary of rules. + MachineSummaryGenerator(Registry.ruleRegistry).generate(outDir); } String getBadges(String rule) { @@ -220,9 +227,6 @@ ${parser.usage} String qualify(LintRule r) => r.name.toString() + describeMaturity(r); -String describeMaturity(LintRule r) => - r.maturity == Maturity.stable ? '' : ' (${r.maturity.name})'; - String toDescription(LintRule r) => '${qualify(r)}
${getBadges(r.name)} ${markdownToHtml(r.description)}'; @@ -242,134 +246,15 @@ class CountBadger { } } -class RuleMarkdownGenerator { - final LintRule rule; - - RuleMarkdownGenerator(this.rule); - - String get name => rule.name; - - String get group => rule.group.name; - - String get maturity => rule.maturity.name; - - String get details => rule.details ?? ''; - - String get since { - var info = sinceInfo[name]; - var version = info.sinceDartSdk != null - ? '>= ${info.sinceDartSdk}' - : '**unreleased**'; - return 'Dart SDK: $version • (Linter v${info.sinceLinter})'; - } - - void generate({String filePath}) { - final buffer = StringBuffer(); - - buffer.writeln('# Rule $name'); - buffer.writeln(); - buffer.writeln('**Group**: $group\\'); - buffer.writeln('**Maturity**: $maturity\\'); - buffer.writeln('**Since**: $since\\'); - buffer.writeln(); - - // badges - if (flutterRules.contains(name)) { - buffer.writeln('[![flutter](style-flutter.svg)]' - '(https://github.com/flutter/flutter/blob/master/packages/' - 'flutter/lib/analysis_options_user.yaml)'); - } - if (pedanticRules.contains(name)) { - buffer.writeln('[![pedantic](style-pedantic.svg)]' - '(https://github.com/dart-lang/pedantic/#enabled-lints)'); - } - if (effectiveDartRules.contains(name)) { - buffer.writeln('[![effective dart](style-effective_dart.svg)]' - '(https://github.com/tenhobi/effective_dart)'); - } - - buffer.writeln(); - - buffer.writeln('## Description'); - buffer.writeln(); - buffer.writeln('${details.trim()}'); - - // incompatible rules - final incompatibleRules = rule.incompatibleRules; - if (incompatibleRules.isNotEmpty) { - buffer.writeln('## Incompatible With'); - buffer.writeln(); - for (var rule in incompatibleRules) { - buffer.writeln('- [$rule]($rule.md)'); - } - buffer.writeln(); - } - - if (filePath == null) { - print(buffer.toString()); - } else { - File('$filePath/$name.md').writeAsStringSync(buffer.toString()); - } - } -} - -class RuleHtmlGenerator { - final LintRule rule; - - RuleHtmlGenerator(this.rule); - - String get details => rule.details ?? ''; - - String get group => rule.group.name; - - String get humanReadableName => rule.name; - - String get maturity => rule.maturity.name; - - String get maturityString { - switch (rule.maturity) { - case Maturity.deprecated: - return '$maturity'; - case Maturity.experimental: - return '$maturity'; - default: - return maturity; - } - } - - String get name => rule.name; - - String get since { - var info = sinceInfo[name]; - var version = info.sinceDartSdk != null - ? '>= ${info.sinceDartSdk}' - : 'unreleased'; - return 'Dart SDK: $version • (Linter v${info.sinceLinter})'; - } +class HtmlIndexer { + final Iterable rules; - String get incompatibleRuleDetails { - final sb = StringBuffer(); - var incompatibleRules = rule.incompatibleRules; - if (incompatibleRules.isNotEmpty) { - sb.writeln('

'); - sb.write('Incompatible with: '); - var rule = incompatibleRules.first; - sb.write( - '$rule'); - for (var i = 1; i < incompatibleRules.length; ++i) { - rule = incompatibleRules[i]; - sb.write(', $rule'); - } - sb.writeln('.'); - sb.writeln('

'); - } - return sb.toString(); - } + HtmlIndexer(this.rules); - void generate([String filePath]) { + void generate(String filePath) { var generated = _generate(); if (filePath != null) { - var outPath = '$filePath/$name.html'; + var outPath = '$filePath/index.html'; print('Writing to $outPath'); File(outPath).writeAsStringSync(generated); } else { @@ -386,31 +271,45 @@ class RuleHtmlGenerator { - $name + Linter for Dart
-

$humanReadableName

-

Group: $group

-

Maturity: $maturityString

-
-

$since

- Since info is static, may be stale -
- ${getBadges(name)} + +

Linter for Dart

+
+

Lint Rules

-

View all Lint Rules

Using the Linter

- ${markdownToHtml(details)} - $incompatibleRuleDetails +

Supported Lint Rules

+

+ This list is auto-generated from our sources. +

+ ${markdownToHtml(ruleLeadMatter)} +
    + $enumerateGroups +
+ ${markdownToHtml(ruleFootMatter)} + +

Error Rules

+ + $enumerateErrorRules + +

Style Rules

+ + $enumerateStyleRules + +

Pub Rules

+ + $enumeratePubRules +