From f3d020afd0a5b84d4370374ba10a5c2e7a098b6c Mon Sep 17 00:00:00 2001 From: "jun.zhou" Date: Fri, 10 Feb 2023 15:50:33 +0800 Subject: [PATCH 1/6] word separators --- lib/src/core/buffer/buffer.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/core/buffer/buffer.dart b/lib/src/core/buffer/buffer.dart index c88548e..6a3d0b8 100644 --- a/lib/src/core/buffer/buffer.dart +++ b/lib/src/core/buffer/buffer.dart @@ -469,7 +469,7 @@ class Buffer { r' '.codeUnitAt(0), r'.'.codeUnitAt(0), r':'.codeUnitAt(0), - r'-'.codeUnitAt(0), + // r'-'.codeUnitAt(0), r'\'.codeUnitAt(0), r'"'.codeUnitAt(0), r'*'.codeUnitAt(0), From 0095b47bdffb58f9109d86acaf0883e0e54d681d Mon Sep 17 00:00:00 2001 From: "jun.zhou" Date: Fri, 10 Feb 2023 16:10:34 +0800 Subject: [PATCH 2/6] customize selection word separators --- lib/src/core/buffer/buffer.dart | 12 ++++++++---- lib/src/terminal.dart | 19 ++++++++++++++++--- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/src/core/buffer/buffer.dart b/lib/src/core/buffer/buffer.dart index 6a3d0b8..34afc13 100644 --- a/lib/src/core/buffer/buffer.dart +++ b/lib/src/core/buffer/buffer.dart @@ -18,10 +18,13 @@ class Buffer { final bool isAltBuffer; + Set? wordSeparators; + Buffer( this.terminal, { required this.maxLines, required this.isAltBuffer, + this.wordSeparators, }) { for (int i = 0; i < terminal.viewHeight; i++) { lines.push(_newEmptyLine()); @@ -464,12 +467,12 @@ class Buffer { return line; } - static final _kWordSeparators = { + static final _defaultWordSeparators = { 0, r' '.codeUnitAt(0), r'.'.codeUnitAt(0), r':'.codeUnitAt(0), - // r'-'.codeUnitAt(0), + r'-'.codeUnitAt(0), r'\'.codeUnitAt(0), r'"'.codeUnitAt(0), r'*'.codeUnitAt(0), @@ -479,6 +482,7 @@ class Buffer { }; BufferRangeLine? getWordBoundary(CellOffset position) { + var separators = wordSeparators ?? _defaultWordSeparators; if (position.y >= lines.length) { return null; } @@ -492,7 +496,7 @@ class Buffer { break; } final char = line.getCodePoint(start - 1); - if (_kWordSeparators.contains(char)) { + if (separators.contains(char)) { break; } start--; @@ -503,7 +507,7 @@ class Buffer { break; } final char = line.getCodePoint(end); - if (_kWordSeparators.contains(char)) { + if (separators.contains(char)) { break; } end++; diff --git a/lib/src/terminal.dart b/lib/src/terminal.dart index e3654da..5d88ef0 100644 --- a/lib/src/terminal.dart +++ b/lib/src/terminal.dart @@ -62,6 +62,8 @@ class Terminal with Observable implements TerminalState, EscapeHandler { /// Flag to toggle os specific behaviors. final TerminalTargetPlatform platform; + Set? wordSeparators; + Terminal({ this.maxLines = 1000, this.onBell, @@ -73,6 +75,7 @@ class Terminal with Observable implements TerminalState, EscapeHandler { this.inputHandler = defaultInputHandler, this.mouseHandler = defaultMouseHandler, this.reflowEnabled = true, + this.wordSeparators, }); late final _parser = EscapeParser(this); @@ -81,9 +84,19 @@ class Terminal with Observable implements TerminalState, EscapeHandler { late var _buffer = _mainBuffer; - late final _mainBuffer = Buffer(this, maxLines: maxLines, isAltBuffer: false); - - late final _altBuffer = Buffer(this, maxLines: maxLines, isAltBuffer: true); + late final _mainBuffer = Buffer( + this, + maxLines: maxLines, + isAltBuffer: false, + wordSeparators: wordSeparators, + ); + + late final _altBuffer = Buffer( + this, + maxLines: maxLines, + isAltBuffer: true, + wordSeparators: wordSeparators, + ); final _tabStops = TabStops(); From 1d504d3542b8daf74f8c79bc0858a0e03de83f4a Mon Sep 17 00:00:00 2001 From: "jun.zhou" Date: Sun, 12 Feb 2023 15:55:52 +0800 Subject: [PATCH 3/6] fix tab --- lib/src/core/tabs.dart | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/src/core/tabs.dart b/lib/src/core/tabs.dart index 226f5be..4045fca 100644 --- a/lib/src/core/tabs.dart +++ b/lib/src/core/tabs.dart @@ -5,12 +5,23 @@ const _kMaxColumns = 1024; class TabStops { final _stops = List.filled(_kMaxColumns, false); + TabStops() { + setUpTabs(); + } + + void setUpTabs() { + const interval = 8; + for (var i = 0; i < _kMaxColumns; i += interval) { + _stops[i] = true; + } + } + int? find(int start, int end) { if (start >= end) { return null; } end = min(end, _stops.length); - for (var i = start; i < end; i++) { + for (var i = start + 1; i < end; i++) { if (_stops[i]) { return i; } @@ -38,9 +49,6 @@ class TabStops { void reset() { clearAll(); - const interval = 8; - for (var i = 0; i < _kMaxColumns; i += interval) { - _stops[i] = true; - } + setUpTabs(); } } From a3cca7fc4c8c34ac19693eed670fa1b943c004c2 Mon Sep 17 00:00:00 2001 From: xuty Date: Thu, 16 Feb 2023 20:33:14 +0800 Subject: [PATCH 4/6] Add more comments --- lib/src/core/tabs.dart | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/src/core/tabs.dart b/lib/src/core/tabs.dart index 4045fca..803ceb8 100644 --- a/lib/src/core/tabs.dart +++ b/lib/src/core/tabs.dart @@ -2,20 +2,23 @@ import 'dart:math' show min; const _kMaxColumns = 1024; +/// Manages the tab stop state for a terminal. class TabStops { final _stops = List.filled(_kMaxColumns, false); TabStops() { - setUpTabs(); + _initialize(); } - void setUpTabs() { + /// Initializes the tab stops to the default 8 column intervals. + void _initialize() { const interval = 8; for (var i = 0; i < _kMaxColumns; i += interval) { _stops[i] = true; } } + /// Finds the next tab stop between [start] and [end]. int? find(int start, int end) { if (start >= end) { return null; @@ -29,26 +32,37 @@ class TabStops { return null; } + /// Sets the tab stop at [index]. If there is already a tab stop at [index], + /// this method does nothing. + /// + /// See also: + /// * [clearAt] which does the opposite. void setAt(int index) { assert(index >= 0 && index < _kMaxColumns); _stops[index] = true; } + /// Clears the tab stop at [index]. If there is no tab stop at [index], this + /// method does nothing. void clearAt(int index) { assert(index >= 0 && index < _kMaxColumns); _stops[index] = false; } + /// Clears all tab stops without resetting them to the default 8 column + /// intervals. void clearAll() { _stops.fillRange(0, _kMaxColumns, false); } + /// Returns true if there is a tab stop at [index]. bool isSetAt(int index) { return _stops[index]; } + /// Resets the tab stops to the default 8 column intervals. void reset() { clearAll(); - setUpTabs(); + _initialize(); } } From 41b44ba93c9a8c63c0aa4945ac1ae9ef55be9c39 Mon Sep 17 00:00:00 2001 From: xuty Date: Thu, 16 Feb 2023 20:34:23 +0800 Subject: [PATCH 5/6] Move `+1` to appropriate location --- lib/src/core/tabs.dart | 4 ++-- lib/src/terminal.dart | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/core/tabs.dart b/lib/src/core/tabs.dart index 803ceb8..385f949 100644 --- a/lib/src/core/tabs.dart +++ b/lib/src/core/tabs.dart @@ -18,13 +18,13 @@ class TabStops { } } - /// Finds the next tab stop between [start] and [end]. + /// Finds the next tab stop index, which satisfies [start] <= index < [end]. int? find(int start, int end) { if (start >= end) { return null; } end = min(end, _stops.length); - for (var i = start + 1; i < end; i++) { + for (var i = start; i < end; i++) { if (_stops[i]) { return i; } diff --git a/lib/src/terminal.dart b/lib/src/terminal.dart index 5d88ef0..dda2563 100644 --- a/lib/src/terminal.dart +++ b/lib/src/terminal.dart @@ -396,7 +396,7 @@ class Terminal with Observable implements TerminalState, EscapeHandler { @override void tab() { - final nextStop = _tabStops.find(_buffer.cursorX, _viewWidth); + final nextStop = _tabStops.find(_buffer.cursorX + 1, _viewWidth); if (nextStop != null) { _buffer.setCursorX(nextStop); From f68802f08e0bd8cd3f31fbcf7b86c826e0721865 Mon Sep 17 00:00:00 2001 From: xuty Date: Thu, 16 Feb 2023 20:37:59 +0800 Subject: [PATCH 6/6] Add test for TabStops --- test/src/core/tabs_test.dart | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 test/src/core/tabs_test.dart diff --git a/test/src/core/tabs_test.dart b/test/src/core/tabs_test.dart new file mode 100644 index 0000000..8c17e42 --- /dev/null +++ b/test/src/core/tabs_test.dart @@ -0,0 +1,31 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:xterm/src/core/tabs.dart'; + +void main() { + group('TabStops', () { + test('has default tab stops after created', () { + final tabStops = TabStops(); + + expect(tabStops.isSetAt(0), true); + expect(tabStops.isSetAt(1), false); + expect(tabStops.isSetAt(7), false); + expect(tabStops.isSetAt(8), true); + expect(tabStops.isSetAt(9), false); + expect(tabStops.isSetAt(15), false); + expect(tabStops.isSetAt(16), true); + }); + }); + + group('TabStops.find()', () { + test('includes start', () { + final tabStops = TabStops(); + expect(tabStops.find(0, 10), 0); + }); + + test('excludes end', () { + final tabStops = TabStops(); + expect(tabStops.find(0, 8), 0); + expect(tabStops.find(1, 9), 8); + }); + }); +}