Skip to content

Commit dee092f

Browse files
committed
feat: Add logUpdate
1 parent f8604bb commit dee092f

File tree

6 files changed

+127
-16
lines changed

6 files changed

+127
-16
lines changed

lib/lint_staged.dart

+3-2
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,14 @@ Future<bool> lintStaged({
2323
String? workingDirectory,
2424
int maxArgLength = 0,
2525
}) async {
26-
final logger = Logger('lint_staged');
26+
final logger = Logger();
2727
try {
2828
final ctx = await runAll(
2929
allowEmpty: allowEmpty,
3030
diff: diff,
3131
diffFilter: diffFilter,
3232
stash: stash,
33+
logger: logger,
3334
maxArgLength: maxArgLength,
3435
workingDirectory: workingDirectory);
3536
_printTaskOutput(ctx, logger);
@@ -60,7 +61,7 @@ Future<bool> lintStaged({
6061

6162
void _printTaskOutput(LintStagedContext ctx, Logger logger) {
6263
if (ctx.output.isEmpty) return;
63-
final log = ctx.errors.isNotEmpty ? logger.stderr : logger.stdout;
64+
final log = ctx.errors.isNotEmpty ? logger.error : logger.success;
6465
for (var line in ctx.output) {
6566
log(line);
6667
}

lib/src/figures.dart

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
class Figures {
2+
static const success = '✔';
3+
static const error = '✖';
4+
}

lib/src/log_update.dart

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import 'dart:io';
2+
3+
import 'package:ansi_escapes/ansi_escapes.dart';
4+
import 'package:ansi_slice/ansi_slice.dart';
5+
import 'package:ansi_strip/ansi_strip.dart';
6+
import 'package:ansi_wrap/ansi_wrap.dart';
7+
8+
int getWidth(IOSink stream) {
9+
return stdout.terminalColumns;
10+
}
11+
12+
String fitToTerminalHeight(IOSink stream, String text) {
13+
final terminalHeight = stdout.terminalLines;
14+
final lines = text.split('\n');
15+
16+
final toRemove = lines.length - terminalHeight;
17+
if (toRemove <= 0) {
18+
return text;
19+
}
20+
21+
return sliceAnsi(
22+
text,
23+
start: stripAnsi(lines.sublist(0, toRemove).join('\n')).length + 1,
24+
);
25+
}
26+
27+
Render createLogUpdate(IOSink stream, {bool showCursor = true}) {
28+
return Render(stream, showCursor);
29+
}
30+
31+
final logUpdate = createLogUpdate(stdout);
32+
final logUpdateStderr = createLogUpdate(stderr);
33+
34+
class Render {
35+
int previousLineCount = 0;
36+
int previousWidth;
37+
String previousOutput = '';
38+
final IOSink stream;
39+
final bool showCursor;
40+
41+
Render(this.stream, this.showCursor) : previousWidth = getWidth(stream);
42+
43+
void call(String output) {
44+
if (!showCursor) {
45+
stream.write(ansiEscapes.cursorHide);
46+
}
47+
output = fitToTerminalHeight(stream, output);
48+
final width = getWidth(stream);
49+
if (output == previousOutput && previousWidth == width) {
50+
return;
51+
}
52+
53+
previousOutput = output;
54+
previousWidth = width;
55+
output = wrapAnsi(output, width, trim: false, hard: true, wordWrap: false);
56+
stream.write(ansiEscapes.eraseLines(previousLineCount) + output);
57+
previousLineCount = output.split('\n').length;
58+
}
59+
60+
void clear() {
61+
stream.write(ansiEscapes.eraseLines(previousLineCount));
62+
previousOutput = '';
63+
previousWidth = getWidth(stream);
64+
previousLineCount = 0;
65+
}
66+
67+
void done() {
68+
previousOutput = '';
69+
previousWidth = getWidth(stream);
70+
previousLineCount = 0;
71+
if (!showCursor) {
72+
stream.write(ansiEscapes.cursorShow);
73+
}
74+
}
75+
}

lib/src/logger.dart

+25-6
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,35 @@
1+
import 'dart:async';
12
import 'dart:io' as io;
23

34
import 'package:ansi/ansi.dart';
5+
import 'package:lint_staged/src/figures.dart';
6+
7+
import 'log_update.dart';
8+
9+
const _kFrames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
410

511
class Logger {
6-
final String name;
7-
Logger(this.name);
12+
int _index = 0;
13+
Timer? _timer;
14+
Logger();
15+
16+
void error(String message) {
17+
io.stderr.writeln(ansi.red('${Figures.error} $message'));
18+
}
819

9-
void stderr(String message) {
10-
io.stderr.writeln(ansi.red('$name $message'));
20+
void progress(String message) {
21+
_index = 0;
22+
_timer?.cancel();
23+
logUpdate.clear();
24+
_timer = Timer.periodic(const Duration(milliseconds: 80), (timer) {
25+
final frame = _kFrames[_index = ++_index % _kFrames.length];
26+
logUpdate('${ansi.yellow(frame)} $message\n');
27+
});
1128
}
1229

13-
void stdout(String message) {
14-
io.stdout.writeln('$name $message');
30+
void success(String message) {
31+
logUpdate.clear();
32+
_timer?.cancel();
33+
io.stdout.writeln('${ansi.green(Figures.success)} $message');
1534
}
1635
}

lib/src/run.dart

+15-8
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import 'message.dart';
1414
import 'context.dart';
1515
import 'symbols.dart';
1616

17-
final logger = Logger('lint_staged:run');
1817
final verbose = Verbose('lint_staged:run');
1918

2019
Future<LintStagedContext> runAll({
@@ -24,6 +23,7 @@ Future<LintStagedContext> runAll({
2423
bool stash = true,
2524
String? workingDirectory,
2625
int maxArgLength = 0,
26+
required Logger logger,
2727
}) async {
2828
final ctx = getInitialContext();
2929
if (!FileSystemEntity.isDirectorySync('.git') &&
@@ -105,29 +105,36 @@ Future<LintStagedContext> runAll({
105105
matchedFileChunks: matchedFileChunks,
106106
workingDirectory: workingDirectory,
107107
);
108-
logger.stdout('Preparing lint_staged...');
108+
logger.progress('Preparing lint_staged...');
109109
await git.prepare(ctx);
110+
logger.success('Preparing lint_staged...');
110111
if (ctx.hasPartiallyStagedFiles) {
111-
logger.stdout('Hiding unstaged changes to partially staged files...');
112+
logger.progress('Hiding unstaged changes to partially staged files...');
112113
await git.hideUnstagedChanges(ctx);
114+
logger.success('Hiding unstaged changes to partially staged files...');
113115
}
114-
logger.stdout('Running tasks for staged files...');
116+
logger.progress('Running tasks for staged files...');
115117
await Future.wait(tasks.map((task) => task()));
118+
logger.success('Running tasks for staged files...');
116119
if (!applyModifationsSkipped(ctx)) {
117-
logger.stdout('Applying modifications from tasks...');
120+
logger.progress('Applying modifications from tasks...');
118121
await git.applyModifications(ctx);
122+
logger.success('Applying modifications from tasks...');
119123
}
120124
if (ctx.hasPartiallyStagedFiles && !restoreUnstagedChangesSkipped(ctx)) {
121-
logger.stdout('Restoring unstaged changes to partially staged files...');
125+
logger.progress('Restoring unstaged changes to partially staged files...');
122126
await git.resotreUnstagedChanges(ctx);
127+
logger.success('Restoring unstaged changes to partially staged files...');
123128
}
124129
if (restoreOriginalStateEnabled(ctx) && !restoreOriginalStateSkipped(ctx)) {
125-
logger.stdout('Reverting to original state because of errors...');
130+
logger.progress('Reverting to original state because of errors...');
126131
await git.restoreOriginState(ctx);
132+
logger.success('Reverting to original state because of errors...');
127133
}
128134
if (cleanupEnabled(ctx) && !cleanupSkipped(ctx)) {
129-
logger.stdout('Cleaning up temporary files...');
135+
logger.progress('Cleaning up temporary files...');
130136
await git.cleanup(ctx);
137+
logger.success('Cleaning up temporary files...');
131138
}
132139
if (ctx.errors.isNotEmpty) {
133140
throw createError(ctx);

pubspec.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,13 @@ environment:
1010

1111
dependencies:
1212
ansi: ^0.2.2
13+
ansi_escapes: ^1.1.0
14+
ansi_slice: ^0.1.1
15+
ansi_strip: ^0.1.1+1
16+
ansi_wrap: ^0.1.2+1
1317
args: ^2.3.1
1418
glob: ^2.0.1
19+
indent: ^2.0.0
1520
path: ^1.8.0
1621
verbose: ^0.1.0
1722
yaml: ^3.1.1

0 commit comments

Comments
 (0)