Async progress bar for longer running tasks

This commit is contained in:
Sarah Jamie Lewis 2022-10-10 11:11:45 -07:00
parent 521aa85cbe
commit fccc925740
4 changed files with 91 additions and 22 deletions

View File

@ -1,6 +1,7 @@
import 'dart:collection';
import 'dart:io';
import 'dart:async';
import 'dart:isolate';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@ -27,7 +28,34 @@ class LineFile extends ChangeNotifier {
var showSuggestions = false;
final SpellChecker spellchecker = SpellChecker.load("dicts/english.txt");
SpellChecker? spellchecker;
buildDictionary() async {
ReceivePort receivePort = ReceivePort();
receivePort.listen((dynamic message) {
updateStatus(message as double);
});
status = "loading dictionary";
notifyListeners();
compute(SpellChecker.load, DictionaryLoadRequest("dicts/english.txt", receivePort.sendPort)).then(
(value) {
spellchecker = value;
status = "dictionary loaded";
notifyListeners();
},
);
}
void updateStatus(double update) {
statusProgress = update;
print("New Progress Status...$statusProgress");
notifyListeners();
}
double statusProgress = 0.0;
void cancelSuggestions() {
suggestionIndex = 0;
@ -196,8 +224,12 @@ class LineFile extends ChangeNotifier {
showSuggestions = false;
notifyListeners();
} else if (event.logicalKey == LogicalKeyboardKey.f4) {
status = "spellchecking document";
notifyListeners();
updateStatus(0.0);
spellCheckAll().then((value) {
status = "spellcheck complete";
notifyListeners();
});
} else {
if (event.isControlPressed) {
@ -387,6 +419,10 @@ class LineFile extends ChangeNotifier {
var line = backingLines[i];
line.annotations.removeWhere((element) => element.tool == "spellcheck");
if (spellchecker == null) {
return;
}
List<LineAnnotation> annotations = List.empty(growable: true);
var wordStart = 0;
var wordEnd = 0;
@ -443,20 +479,20 @@ class LineFile extends ChangeNotifier {
continue;
}
HashSet<String>? candidates = spellchecker.candidates(word);
HashSet<String>? candidates = spellchecker!.candidates(word);
if (candidates == null) {
await compute(mutations2, word).then((results) {
spellchecker.dictionary.putIfAbsent(word, () => HashSet());
spellchecker!.dictionary.putIfAbsent(word, () => HashSet());
results.forEach((element) {
if (spellchecker.known.contains(element)) {
spellchecker.dictionary[word]!.add(element);
if (spellchecker!.known.contains(element)) {
spellchecker!.dictionary[word]!.add(element);
}
});
});
}
// Candidates should now not be null...
candidates = spellchecker.candidates(word);
candidates = spellchecker!.candidates(word);
if (candidates!.contains(word) == false) {
annotations.add(LineAnnotation(SourceLink("", lineStart: 0, colStart: annotationStart, colEnd: wordEnd + 1, lineEnd: 0), "spellcheck", AnnotationType.highlight, "spellcheck",
description: "${candidates.join(":")}:$word"));
@ -644,12 +680,13 @@ class LineFile extends ChangeNotifier {
Future<bool> spellCheckAll() async {
for (int i = 0; i < backingLines.length; i++) {
await spellcheck(i);
updateStatus(i.toDouble() / backingLines.length.toDouble());
}
return Future.value(true);
}
customDic(BuildContext context, Suggestion suggestion) {
spellchecker.addToCustomDictionary(suggestion.name);
spellchecker!.addToCustomDictionary(suggestion.name);
int line = suggestion.link.lineStart! - 1;
spellcheck(line);
}

View File

@ -8,6 +8,7 @@ import 'outline.dart';
void main(List<String> args) {
var docfile = LineFile();
docfile.buildDictionary();
if (args.length > 0) {
docfile.openFile(args[0], 0);
}
@ -72,17 +73,30 @@ class _SarahDownApp extends State<SarahDownApp> {
child: Padding(
padding: EdgeInsets.symmetric(vertical: 2.0, horizontal: 5.0),
child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Text(
"${Provider.of<LineFile>(context).status}",
textAlign: TextAlign.left,
style: TextStyle(
fontFamily: 'Iosevka',
fontSize: 10,
fontWeight: FontWeight.bold,
color: foreground,
backgroundColor: Colors.transparent,
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
SizedBox(
width: 200,
child: LinearProgressIndicator(
color: comment,
backgroundColor: sidebar,
value: Provider.of<LineFile>(context).statusProgress,
minHeight: 5.0,
)),
SizedBox(
width: 10,
),
),
Text(
"${Provider.of<LineFile>(context).status}",
textAlign: TextAlign.left,
style: TextStyle(
fontFamily: 'Iosevka',
fontSize: 8,
fontWeight: FontWeight.bold,
color: foreground,
backgroundColor: Colors.transparent,
),
),
]),
Text(
"Word Count: ${Provider.of<LineFile>(context).wordCount()}",
textAlign: TextAlign.right,

View File

@ -1,8 +1,15 @@
import 'dart:collection';
import 'dart:io';
import 'dart:isolate';
import 'package:flutter/foundation.dart';
class DictionaryLoadRequest {
String dictPath;
SendPort callback;
DictionaryLoadRequest(this.dictPath, this.callback);
}
class SpellChecker {
HashMap<String, HashSet<String>> dictionary;
HashSet<String> known;
@ -10,13 +17,17 @@ class SpellChecker {
SpellChecker(this.dictionary, this.known, this.custom);
static SpellChecker load(String dictPath) {
static SpellChecker load(DictionaryLoadRequest request) {
HashMap<String, HashSet<String>> dictionary = HashMap();
HashSet<String> known = HashSet();
HashSet<String> custom = HashSet();
File dict = File(dictPath);
File dict = File(request.dictPath);
var lines = dict.readAsLinesSync();
print("building dictionary...");
dict.readAsLinesSync().forEach((word) {
for (int i = 0; i < lines.length; i++) {
var word = lines[i];
var lower = word.toLowerCase().trim();
dictionary.putIfAbsent(lower, () => HashSet.from([lower]));
known.add(lower);
@ -25,7 +36,11 @@ class SpellChecker {
dictionary.putIfAbsent(element, () => HashSet());
dictionary[element]!.add(lower);
});
});
if (i % 1000 == 0) {
request.callback.send(i.toDouble() / lines.length);
}
}
File customdict = File("subwrite.custom.dictionary.txt");
print("building custom dictionary...");
@ -43,6 +58,8 @@ class SpellChecker {
});
}
// finished!
request.callback.send(1.0);
print("built dictionary...${dictionary.length}");
return SpellChecker(dictionary, known, custom);
}

View File

@ -3,11 +3,12 @@ decrypted
adversary
replying
plaintext
weirdly
bypassed
weirdly
leaked
fuzzing
decrypt
stash
encrypt
ciphertext
decrypting