Skip to content

Commit

Permalink
#87 Detect terminal size
Browse files Browse the repository at this point in the history
  • Loading branch information
nikosportolos committed Jun 2, 2023
1 parent c098421 commit d32d4ca
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 22 deletions.
4 changes: 4 additions & 0 deletions lib/src/ansix.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'dart:convert';
import 'package:ansix/ansix.dart';
import 'package:ansix/src/controller.dart';
import 'package:ansix/src/formatter/formatter.dart';
import 'package:ansix/src/system/terminal.dart';

abstract class AnsiX {
static final AnsiXController _controller = AnsiXController();
Expand All @@ -22,6 +23,9 @@ abstract class AnsiX {
/// Returns the active text formatter.
static TextFormatter get formatter => _controller.formatter;

/// Returns the size of the attached terminal.
static TerminalSize get size => _controller.terminalSize;

/// Enables ANSI formatting (if supported by the system).
static void enable() {
_controller.enable();
Expand Down
6 changes: 5 additions & 1 deletion lib/src/controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@ import 'package:ansix/src/formatter/formatters.dart';
import 'package:ansix/src/system/process_manager.dart';
import 'package:ansix/src/system/terminal.dart';

/// **AnsiXController**
class AnsiXController {
AnsiXController({
final ProcessManager? processManager,
final AnsiTerminal? terminal,
}) : _processManager = processManager ?? const ProcessManager(),
_terminal = terminal ?? AnsiTerminal();
_terminal = terminal ?? AnsiTerminal() {
terminalSize = _terminal.size;
}

final ProcessManager _processManager;
final AnsiTerminal _terminal;
late final TerminalSize terminalSize;

/// Returns true if ANSI formatting is supported and enabled.
bool get isEnabled => _isEnabled;
Expand Down
4 changes: 4 additions & 0 deletions lib/src/system/process_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import 'package:ansix/src/core/exceptions/exceptions.dart';
import 'package:ansix/src/system/system.dart';
import 'package:meta/meta.dart';

/// **ProcessManager**
///
/// A library that provides tools and info using bash commands.
class ProcessManager {
const ProcessManager({
final Shell? shell,
Expand Down Expand Up @@ -80,6 +83,7 @@ class ProcessManager {
}
}

/// Returns the [TerminalType] of the attached terminal.
TerminalType determineTerminalType() {
return _checkIfRunningInBash() ?? _checkIfRunningInPowershell() ?? TerminalType.unknown;
}
Expand Down
7 changes: 7 additions & 0 deletions lib/src/system/shell.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import 'dart:io';

/// **Shell**
///
/// A library that wraps Dart's [Process] features.
class Shell {
const Shell();

Expand All @@ -16,6 +19,10 @@ class Shell {
}
}

/// **ShellCommand**
///
/// A class that contains all required information for a
/// shell command to be executed in the attached terminal.
class ShellCommand {
const ShellCommand(
this.command, {
Expand Down
39 changes: 39 additions & 0 deletions lib/src/system/terminal.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,29 @@
import 'dart:io';

/// **TerminalType**
enum TerminalType { unknown, bash, cmd, powershell }

/// **TerminalSize**
///
/// Provides information about the size of the attached terminal.
class TerminalSize {
const TerminalSize({
required this.columns,
required this.lines,
});

final int columns;
final int lines;

static const TerminalSize $default = TerminalSize(columns: 80, lines: 24);

@override
String toString() => '($columns, $lines)';
}

/// **AnsiTerminal**
///
/// A library that provides information on the attached terminal.
class AnsiTerminal {
AnsiTerminal({
final Stdout? out,
Expand All @@ -20,4 +42,21 @@ class AnsiTerminal {
return stdioType(_stdout) == StdioType.terminal || //
stdioType(_stdout) == StdioType.pipe;
}

/// Returns the [TerminalSize] of the attached terminal.
///
/// If no terminal is attached,
/// the default terminal size (80, 24) will be returned instead.
TerminalSize get size {
try {
if (_stdout.hasTerminal) {
return TerminalSize(
columns: _stdout.terminalColumns,
lines: _stdout.terminalLines,
);
}
} catch (_) {}

return TerminalSize.$default;
}
}
3 changes: 3 additions & 0 deletions test/ansix/mocks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ class MockAnsiTerminal extends Mock implements AnsiTerminal {

@override
bool get runsOnWindows;

@override
TerminalSize get size => TerminalSize.$default;
}

class MockStdOut extends Mock implements Stdout {
Expand Down
71 changes: 50 additions & 21 deletions test/system/terminal_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,66 @@ import 'package:test/test.dart';
class MockStdOut extends Mock implements Stdout {
@override
bool get supportsAnsiEscapes;

@override
bool get hasTerminal;

@override
int get terminalColumns;

@override
int get terminalLines;
}

void main() {
final MockStdOut stdOut = MockStdOut();

setUp(() => resetMocktailState());

IOOverrides.runZoned(
() {
final AnsiTerminal terminal = AnsiTerminal(out: stdOut);
final AnsiTerminal terminal = AnsiTerminal(out: stdOut);

group('AnsiTerminal', () {
test('runsOnWindows', () {
expect(terminal.runsOnWindows, Platform.isWindows);
});
group('AnsiTerminal', () {
test('runsOnWindows', () {
expect(terminal.runsOnWindows, Platform.isWindows);
});

test('supportsAnsiEscapes=false', () {
when(() => stdOut.supportsAnsiEscapes).thenReturn(false);
expect(terminal.supportsAnsi, false);
});
group('supportsAnsiEscapes', () {
test('false', () {
when(() => stdOut.supportsAnsiEscapes).thenReturn(false);
expect(terminal.supportsAnsi, false);
});

test('true', () {
when(() => stdOut.supportsAnsiEscapes).thenReturn(true);
expect(terminal.supportsAnsi, true);
});
});

test('supportsAnsiEscapes=true', () {
when(() => stdOut.supportsAnsiEscapes).thenReturn(true);
expect(terminal.supportsAnsi, true);
});
test('attachedToValidStream', () {
expect(terminal.attachedToValidStream, false); // TODO
});

test('attachedToValidStream', () {
expect(terminal.attachedToValidStream, false); // TODO
});
group('TerminalSize', () {
test('hasTerminal=false (returns default)', () {
when(() => stdOut.hasTerminal).thenReturn(false);
expect(terminal.size, TerminalSize.$default);
expect(
terminal.size.toString(),
'(${TerminalSize.$default.columns}, ${TerminalSize.$default.lines})',
);
});
test('hasTerminal=true (returns default)', () {
when(() => stdOut.hasTerminal).thenReturn(true);
expect(terminal.size, TerminalSize.$default);
});
test('hasTerminal=true', () {
const TerminalSize size = TerminalSize(columns: 120, lines: 100);
when(() => stdOut.hasTerminal).thenReturn(true);
when(() => stdOut.terminalColumns).thenReturn(size.columns);
when(() => stdOut.terminalLines).thenReturn(size.lines);
expect(terminal.size.columns, size.columns);
expect(terminal.size.lines, size.lines);
});
},
stdout: () => stdOut,
);
});
});
}

0 comments on commit d32d4ca

Please sign in to comment.