Skip to content

Commit ca81fc9

Browse files
committed
Work around dart-lang/sdk#45645
1 parent 65d5c8e commit ca81fc9

File tree

3 files changed

+47
-11
lines changed

3 files changed

+47
-11
lines changed

lib/src/executable/repl.dart

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import '../import_cache.dart';
1414
import '../importer/filesystem.dart';
1515
import '../logger/tracking.dart';
1616
import '../parse/parser.dart';
17+
import '../utils.dart';
1718
import '../visitor/evaluate.dart';
1819

1920
/// Runs an interactive SassScript shell according to [options].
@@ -24,12 +25,14 @@ Future<void> repl(ExecutableOptions options) async {
2425
importer: FilesystemImporter('.'),
2526
importCache: ImportCache(loadPaths: options.loadPaths, logger: logger),
2627
logger: logger);
27-
await for (String line in repl.runAsync()) {
28-
if (line.trim().isEmpty) continue;
28+
29+
// Use forEachCancelable to work around dart-lang/sdk#45645.
30+
await repl.runAsync().forEachCancelable((line) {
31+
if (line.trim().isEmpty) return;
2932
try {
3033
if (line.startsWith("@")) {
3134
evaluator.use(UseRule.parse(line, logger: logger));
32-
continue;
35+
return;
3336
}
3437

3538
if (Parser.isVariableDeclarationLike(line)) {
@@ -44,7 +47,7 @@ Future<void> repl(ExecutableOptions options) async {
4447
} on SassException catch (error, stackTrace) {
4548
_logError(error, stackTrace, line, repl, options, logger);
4649
}
47-
}
50+
}).value;
4851
}
4952

5053
/// Logs an error from the interactive shell.

lib/src/executable/watch.dart

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'dart:collection';
66

7+
import 'package:async/async.dart';
78
import 'package:path/path.dart' as p;
89
import 'package:stack_trace/stack_trace.dart';
910
import 'package:stream_transform/stream_transform.dart';
@@ -14,6 +15,7 @@ import '../importer/filesystem.dart';
1415
import '../io.dart';
1516
import '../stylesheet_graph.dart';
1617
import '../util/multi_dir_watcher.dart';
18+
import '../utils.dart';
1719
import 'compile_stylesheet.dart';
1820
import 'options.dart';
1921

@@ -123,30 +125,33 @@ class _Watcher {
123125
/// Listens to `watcher.events` and updates the filesystem accordingly.
124126
///
125127
/// Returns a future that will only complete if an unexpected error occurs.
126-
Future<void> watch(MultiDirWatcher watcher) async {
127-
await for (var event in _debounceEvents(watcher.events)) {
128+
Future<void> watch(MultiDirWatcher watcher) {
129+
late CancelableOperation<void> operation;
130+
operation =
131+
_debounceEvents(watcher.events).forEachCancelable((event) async {
128132
var extension = p.extension(event.path);
129133
if (extension != '.sass' && extension != '.scss' && extension != '.css') {
130-
continue;
134+
return;
131135
}
132136

133137
switch (event.type) {
134138
case ChangeType.MODIFY:
135139
var success = await _handleModify(event.path);
136-
if (!success && _options.stopOnError) return;
140+
if (!success && _options.stopOnError) operation.cancel();
137141
break;
138142

139143
case ChangeType.ADD:
140144
var success = await _handleAdd(event.path);
141-
if (!success && _options.stopOnError) return;
145+
if (!success && _options.stopOnError) operation.cancel();
142146
break;
143147

144148
case ChangeType.REMOVE:
145149
var success = await _handleRemove(event.path);
146-
if (!success && _options.stopOnError) return;
150+
if (!success && _options.stopOnError) operation.cancel();
147151
break;
148152
}
149-
}
153+
});
154+
return operation.valueOrCancellation();
150155
}
151156

152157
/// Handles a modify event for the stylesheet at [path].

lib/src/utils.dart

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
// MIT-style license that can be found in the LICENSE file or at
33
// https://opensource.org/licenses/MIT.
44

5+
import 'dart:async';
56
import 'dart:math' as math;
67

8+
import 'package:async/async.dart';
79
import 'package:charcode/charcode.dart';
810
import 'package:collection/collection.dart';
911
import 'package:source_span/source_span.dart';
@@ -403,3 +405,29 @@ extension SpanExtensions on FileSpan {
403405
: file.span(this.start.offset + start, this.start.offset + end + 1);
404406
}
405407
}
408+
409+
extension StreamExtension<T> on Stream<T> {
410+
/// Like [Stream.forEach], but returns a [CancelableOperation] that cancels
411+
/// the underlying stream subscription once it's cancelled.
412+
///
413+
/// This also fixed dart-lang/sdk#45645.
414+
CancelableOperation<void> forEachCancelable(
415+
FutureOr<void> callback(T element)) {
416+
var subscription = listen(null);
417+
var completer = CancelableCompleter<void>(onCancel: subscription.cancel);
418+
subscription.onData((event) async {
419+
subscription.pause();
420+
try {
421+
await callback(event);
422+
subscription.resume();
423+
} catch (error, stackTrace) {
424+
subscription.cancel();
425+
completer.completeError(error, stackTrace);
426+
}
427+
});
428+
subscription.onError(completer.completeError);
429+
subscription.onDone(completer.complete);
430+
431+
return completer.operation;
432+
}
433+
}

0 commit comments

Comments
 (0)