[CFE] Interative spell check mode for messages
Change-Id: I61a71250159ec88cee1aafa82f3749dc3ac13c06
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/163862
Commit-Queue: Jens Johansen <jensj@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/front_end/test/comments_on_certain_arguments_tool.dart b/pkg/front_end/test/comments_on_certain_arguments_tool.dart
index 5920035..c0ff2cb 100644
--- a/pkg/front_end/test/comments_on_certain_arguments_tool.dart
+++ b/pkg/front_end/test/comments_on_certain_arguments_tool.dart
@@ -4,16 +4,7 @@
import 'dart:convert' show utf8;
import 'dart:io'
- show
- Directory,
- File,
- FileSystemEntity,
- Platform,
- Process,
- ProcessResult,
- exitCode,
- stdin,
- stdout;
+ show Directory, File, FileSystemEntity, exitCode, stdin, stdout;
import 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity;
import 'package:_fe_analyzer_shared/src/scanner/token.dart'
@@ -41,16 +32,9 @@
import 'package:kernel/target/targets.dart' show TargetFlags;
import "package:vm/target/vm.dart" show VmTarget;
-final Uri repoDir = _computeRepoDir();
+import "utils/io_utils.dart";
-Uri _computeRepoDir() {
- ProcessResult result = Process.runSync(
- 'git', ['rev-parse', '--show-toplevel'],
- runInShell: true,
- workingDirectory: new File.fromUri(Platform.script).parent.path);
- String dirPath = (result.stdout as String).trim();
- return new Directory(dirPath).uri;
-}
+final Uri repoDir = computeRepoDirUri();
Set<Uri> libUris = {};
diff --git a/pkg/front_end/test/explicit_creation_test.dart b/pkg/front_end/test/explicit_creation_test.dart
index 215f824..7e8246a 100644
--- a/pkg/front_end/test/explicit_creation_test.dart
+++ b/pkg/front_end/test/explicit_creation_test.dart
@@ -40,17 +40,9 @@
import "package:vm/target/vm.dart" show VmTarget;
import 'testing_utils.dart' show getGitFiles;
+import "utils/io_utils.dart";
-final Uri repoDir = _computeRepoDir();
-
-Uri _computeRepoDir() {
- ProcessResult result = Process.runSync(
- 'git', ['rev-parse', '--show-toplevel'],
- runInShell: true,
- workingDirectory: new File.fromUri(Platform.script).parent.path);
- String dirPath = (result.stdout as String).trim();
- return new Directory(dirPath).uri;
-}
+final Uri repoDir = computeRepoDirUri();
Set<Uri> libUris = {};
diff --git a/pkg/front_end/test/fasta/messages_suite.dart b/pkg/front_end/test/fasta/messages_suite.dart
index cb0f930..3a3dcd2 100644
--- a/pkg/front_end/test/fasta/messages_suite.dart
+++ b/pkg/front_end/test/fasta/messages_suite.dart
@@ -6,7 +6,7 @@
import "dart:convert" show utf8;
-import "dart:io" show File;
+import 'dart:io' show File, Platform;
import "dart:typed_data" show Uint8List;
@@ -97,8 +97,31 @@
final BatchCompiler compiler;
final bool fastOnly;
+ final bool interactive;
- MessageTestSuite(this.fastOnly)
+ final Set<String> reportedWords = {};
+ final Set<String> reportedWordsDenylisted = {};
+
+ @override
+ Future<void> postRun() {
+ String dartPath = Platform.resolvedExecutable;
+ Uri suiteUri =
+ spell.repoDir.resolve("pkg/front_end/test/fasta/messages_suite.dart");
+ File suiteFile = new File.fromUri(suiteUri).absolute;
+ if (!suiteFile.existsSync()) {
+ throw "Specified suite path is invalid.";
+ }
+ String suitePath = suiteFile.path;
+ spell.spellSummarizeAndInteractiveMode(
+ reportedWords,
+ reportedWordsDenylisted,
+ [spell.Dictionaries.cfeMessages],
+ interactive,
+ '"$dartPath" "$suitePath" -DfastOnly=true -Dinteractive=true');
+ return null;
+ }
+
+ MessageTestSuite(this.fastOnly, this.interactive)
: fileSystem = new MemoryFileSystem(Uri.parse("org-dartlang-fasta:///")),
compiler = new BatchCompiler(null);
@@ -143,8 +166,8 @@
Configuration configuration;
Source source;
- List<String> formatSpellingMistakes(
- spell.SpellingResult spellResult, int offset, String message) {
+ List<String> formatSpellingMistakes(spell.SpellingResult spellResult,
+ int offset, String message, String messageForDenyListed) {
if (source == null) {
List<int> bytes = file.readAsBytesSync();
List<int> lineStarts = new List<int>();
@@ -160,12 +183,20 @@
for (int i = 0; i < spellResult.misspelledWords.length; i++) {
Location location = source.getLocation(
uri, offset + spellResult.misspelledWordsOffset[i]);
+ bool denylisted = spellResult.misspelledWordsDenylisted[i];
+ String messageToUse = message;
+ if (denylisted) {
+ messageToUse = messageForDenyListed;
+ reportedWordsDenylisted.add(spellResult.misspelledWords[i]);
+ } else {
+ reportedWords.add(spellResult.misspelledWords[i]);
+ }
result.add(command_line_reporting.formatErrorMessage(
source.getTextLine(location.line),
location,
spellResult.misspelledWords[i].length,
relativize(uri),
- "$message: '${spellResult.misspelledWords[i]}'."));
+ "$messageToUse: '${spellResult.misspelledWords[i]}'."));
}
return result;
}
@@ -190,7 +221,10 @@
spellingMessages.addAll(formatSpellingMistakes(
spellingResult,
node.span.start.offset,
- "Template likely has the following spelling mistake"));
+ "Template has the following word that is "
+ "not in our dictionary",
+ "Template has the following word that is "
+ "on our deny-list"));
}
break;
@@ -206,7 +240,10 @@
spellingMessages.addAll(formatSpellingMistakes(
spellingResult,
node.span.start.offset,
- "Tip likely has the following spelling mistake"));
+ "Tip has the following word that is "
+ "not in our dictionary",
+ "Tip has the following word that is "
+ "on our deny-list"));
}
break;
@@ -745,7 +782,8 @@
Future<MessageTestSuite> createContext(
Chain suite, Map<String, String> environment) async {
final bool fastOnly = environment["fastOnly"] == "true";
- return new MessageTestSuite(fastOnly);
+ final bool interactive = environment["interactive"] == "true";
+ return new MessageTestSuite(fastOnly, interactive);
}
String relativize(Uri uri) {
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index 519403a..5aa5116 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -351,6 +351,7 @@
ko
koo
la
+launch
launching
le
legs
diff --git a/pkg/front_end/test/spell_checking_utils.dart b/pkg/front_end/test/spell_checking_utils.dart
index e29b233..948ea88 100644
--- a/pkg/front_end/test/spell_checking_utils.dart
+++ b/pkg/front_end/test/spell_checking_utils.dart
@@ -2,7 +2,11 @@
// 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.
-import 'dart:io';
+import 'dart:io' show File, stdin, stdout;
+
+import "utils/io_utils.dart";
+
+final Uri repoDir = computeRepoDirUri();
enum Dictionaries {
common,
@@ -162,19 +166,18 @@
Uri dictionaryToUri(Dictionaries dictionaryType) {
switch (dictionaryType) {
case Dictionaries.common:
- return Uri.base
+ return repoDir
.resolve("pkg/front_end/test/spell_checking_list_common.txt");
case Dictionaries.cfeMessages:
- return Uri.base
+ return repoDir
.resolve("pkg/front_end/test/spell_checking_list_messages.txt");
case Dictionaries.cfeCode:
- return Uri.base
- .resolve("pkg/front_end/test/spell_checking_list_code.txt");
+ return repoDir.resolve("pkg/front_end/test/spell_checking_list_code.txt");
case Dictionaries.cfeTests:
- return Uri.base
+ return repoDir
.resolve("pkg/front_end/test/spell_checking_list_tests.txt");
case Dictionaries.denylist:
- return Uri.base
+ return repoDir
.resolve("pkg/front_end/test/spell_checking_list_denylist.txt");
}
throw "Unknown Dictionary";
@@ -342,3 +345,121 @@
}
return result;
}
+
+void spellSummarizeAndInteractiveMode(
+ Set<String> reportedWords,
+ Set<String> reportedWordsDenylisted,
+ List<Dictionaries> dictionaries,
+ bool interactive,
+ String interactiveLaunchExample) {
+ if (reportedWordsDenylisted.isNotEmpty) {
+ print("\n\n\n");
+ print("================");
+ print("The following words was reported as used and denylisted:");
+ print("----------------");
+ for (String s in reportedWordsDenylisted) {
+ print("$s");
+ }
+ print("================");
+ }
+ if (reportedWords.isNotEmpty) {
+ print("\n\n\n");
+ print("================");
+ print("The following word(s) were reported as unknown:");
+ print("----------------");
+
+ Dictionaries dictionaryToUse;
+ if (dictionaries.contains(Dictionaries.cfeTests)) {
+ dictionaryToUse = Dictionaries.cfeTests;
+ } else if (dictionaries.contains(Dictionaries.cfeMessages)) {
+ dictionaryToUse = Dictionaries.cfeMessages;
+ } else if (dictionaries.contains(Dictionaries.cfeCode)) {
+ dictionaryToUse = Dictionaries.cfeCode;
+ } else {
+ for (Dictionaries dictionary in dictionaries) {
+ if (dictionaryToUse == null ||
+ dictionary.index < dictionaryToUse.index) {
+ dictionaryToUse = dictionary;
+ }
+ }
+ }
+
+ if (interactive && dictionaryToUse != null) {
+ List<String> addedWords = new List<String>();
+ for (String s in reportedWords) {
+ print("- $s");
+ String answer;
+ bool add;
+ while (true) {
+ stdout.write("Do you want to add the word to the dictionary "
+ "$dictionaryToUse (y/n)? ");
+ answer = stdin.readLineSync().trim().toLowerCase();
+ switch (answer) {
+ case "y":
+ case "yes":
+ case "true":
+ add = true;
+ break;
+ case "n":
+ case "no":
+ case "false":
+ add = false;
+ break;
+ default:
+ add = null;
+ print("'$answer' is not a valid answer. Please try again.");
+ break;
+ }
+ if (add != null) break;
+ }
+ if (add) {
+ addedWords.add(s);
+ }
+ }
+ if (addedWords.isNotEmpty) {
+ File dictionaryFile =
+ new File.fromUri(dictionaryToUri(dictionaryToUse));
+ List<String> lines = dictionaryFile.readAsLinesSync();
+ List<String> header = new List<String>();
+ List<String> sortThis = new List<String>();
+ for (String line in lines) {
+ if (line.startsWith("#")) {
+ header.add(line);
+ } else if (line.trim().isEmpty && sortThis.isEmpty) {
+ header.add(line);
+ } else if (line.trim().isNotEmpty) {
+ sortThis.add(line);
+ }
+ }
+ sortThis.addAll(addedWords);
+ sortThis.sort();
+ lines = new List<String>();
+ lines.addAll(header);
+ if (header.isEmpty || header.last.isNotEmpty) {
+ lines.add("");
+ }
+ lines.addAll(sortThis);
+ lines.add("");
+ dictionaryFile.writeAsStringSync(lines.join("\n"));
+ }
+ } else {
+ for (String s in reportedWords) {
+ print("$s");
+ }
+ if (dictionaries.isNotEmpty) {
+ print("----------------");
+ print("If the word(s) are correctly spelled please add it to one of "
+ "these files:");
+ for (Dictionaries dictionary in dictionaries) {
+ print(" - ${dictionaryToUri(dictionary)}");
+ }
+
+ print("");
+ print("To add words easily, try to run this script in interactive "
+ "mode via the command");
+ print(interactiveLaunchExample);
+ }
+ }
+ print("================");
+ }
+}
diff --git a/pkg/front_end/test/spelling_test_base.dart b/pkg/front_end/test/spelling_test_base.dart
index 9879c8c..9bafa38 100644
--- a/pkg/front_end/test/spelling_test_base.dart
+++ b/pkg/front_end/test/spelling_test_base.dart
@@ -4,7 +4,7 @@
import 'dart:async' show Future;
-import 'dart:io' show File, Platform, stdin, stdout;
+import 'dart:io' show File, Platform;
import 'dart:typed_data' show Uint8List;
@@ -51,6 +51,8 @@
bool get onlyDenylisted;
+ String get repoRelativeSuitePath;
+
Set<String> reportedWords = {};
Set<String> reportedWordsDenylisted = {};
@@ -61,111 +63,19 @@
@override
Future<void> postRun() {
- if (reportedWordsDenylisted.isNotEmpty) {
- print("\n\n\n");
- print("================");
- print("The following words was reported as used and denylisted:");
- print("----------------");
- for (String s in reportedWordsDenylisted) {
- print("$s");
- }
- print("================");
+ String dartPath = Platform.resolvedExecutable;
+ Uri suiteUri = spell.repoDir.resolve(repoRelativeSuitePath);
+ File suiteFile = new File.fromUri(suiteUri).absolute;
+ if (!suiteFile.existsSync()) {
+ throw "Specified suite path is invalid.";
}
- if (reportedWords.isNotEmpty) {
- print("\n\n\n");
- print("================");
- print("The following word(s) were reported as unknown:");
- print("----------------");
-
- spell.Dictionaries dictionaryToUse;
- if (dictionaries.contains(spell.Dictionaries.cfeTests)) {
- dictionaryToUse = spell.Dictionaries.cfeTests;
- } else if (dictionaries.contains(spell.Dictionaries.cfeMessages)) {
- dictionaryToUse = spell.Dictionaries.cfeMessages;
- } else if (dictionaries.contains(spell.Dictionaries.cfeCode)) {
- dictionaryToUse = spell.Dictionaries.cfeCode;
- } else {
- for (spell.Dictionaries dictionary in dictionaries) {
- if (dictionaryToUse == null ||
- dictionary.index < dictionaryToUse.index) {
- dictionaryToUse = dictionary;
- }
- }
- }
-
- if (interactive && dictionaryToUse != null) {
- List<String> addedWords = new List<String>();
- for (String s in reportedWords) {
- print("- $s");
- stdout.write("Do you want to add the word to the dictionary "
- "$dictionaryToUse (y/n)? ");
- String answer = stdin.readLineSync().trim().toLowerCase();
- bool add;
- switch (answer) {
- case "y":
- case "yes":
- case "true":
- add = true;
- break;
- case "n":
- case "no":
- case "false":
- add = false;
- break;
- default:
- throw "Didn't understand '$answer'";
- }
- if (add) {
- addedWords.add(s);
- }
- }
- if (addedWords.isNotEmpty) {
- File dictionaryFile =
- new File.fromUri(spell.dictionaryToUri(dictionaryToUse));
- List<String> lines = dictionaryFile.readAsLinesSync();
- List<String> header = new List<String>();
- List<String> sortThis = new List<String>();
- for (String line in lines) {
- if (line.startsWith("#")) {
- header.add(line);
- } else if (line.trim().isEmpty && sortThis.isEmpty) {
- header.add(line);
- } else if (line.trim().isNotEmpty) {
- sortThis.add(line);
- }
- }
- sortThis.addAll(addedWords);
- sortThis.sort();
- lines = new List<String>();
- lines.addAll(header);
- if (header.isEmpty || header.last.isNotEmpty) {
- lines.add("");
- }
- lines.addAll(sortThis);
- lines.add("");
- dictionaryFile.writeAsStringSync(lines.join("\n"));
- }
- } else {
- for (String s in reportedWords) {
- print("$s");
- }
- if (dictionaries.isNotEmpty) {
- print("----------------");
- print("If the word(s) are correctly spelled please add it to one of "
- "these files:");
- for (spell.Dictionaries dictionary in dictionaries) {
- print(" - ${spell.dictionaryToUri(dictionary)}");
- }
-
- print("");
- print("To add words easily, try to run this script in interactive "
- "mode via the command");
- print("dart ${Platform.script.toFilePath()} "
- "-DonlyInGit=$onlyInGit -Dinteractive=true");
- }
- }
- print("================");
- }
+ String suitePath = suiteFile.path;
+ spell.spellSummarizeAndInteractiveMode(
+ reportedWords,
+ reportedWordsDenylisted,
+ dictionaries,
+ interactive,
+ '"$dartPath" "$suitePath" -DonlyInGit=$onlyInGit -Dinteractive=true');
return null;
}
}
diff --git a/pkg/front_end/test/spelling_test_external_targets.dart b/pkg/front_end/test/spelling_test_external_targets.dart
index f339a46..0330b22 100644
--- a/pkg/front_end/test/spelling_test_external_targets.dart
+++ b/pkg/front_end/test/spelling_test_external_targets.dart
@@ -39,6 +39,10 @@
@override
bool get onlyDenylisted => true;
+ @override
+ String get repoRelativeSuitePath =>
+ "pkg/front_end/test/spelling_test_external_targets.dart";
+
Stream<TestDescription> list(Chain suite) async* {
for (String subdir in const ["pkg/", "sdk/"]) {
Directory testRoot = new Directory.fromUri(suite.uri.resolve(subdir));
diff --git a/pkg/front_end/test/spelling_test_not_src_suite.dart b/pkg/front_end/test/spelling_test_not_src_suite.dart
index 28d5262..3259c88 100644
--- a/pkg/front_end/test/spelling_test_not_src_suite.dart
+++ b/pkg/front_end/test/spelling_test_not_src_suite.dart
@@ -38,4 +38,8 @@
@override
bool get onlyDenylisted => false;
+
+ @override
+ String get repoRelativeSuitePath =>
+ "pkg/front_end/test/spelling_test_not_src_suite.dart";
}
diff --git a/pkg/front_end/test/spelling_test_src_suite.dart b/pkg/front_end/test/spelling_test_src_suite.dart
index 4fe9568..afea1e0 100644
--- a/pkg/front_end/test/spelling_test_src_suite.dart
+++ b/pkg/front_end/test/spelling_test_src_suite.dart
@@ -37,4 +37,8 @@
@override
bool get onlyDenylisted => false;
+
+ @override
+ String get repoRelativeSuitePath =>
+ "pkg/front_end/test/spelling_test_src_suite.dart";
}