From e5627836779e42e28ae46741abce815c3794f57b Mon Sep 17 00:00:00 2001 From: Daniel Lukic Date: Wed, 24 Apr 2024 17:06:03 +0200 Subject: [PATCH 1/3] Add quirks to fix behavior of , and ZelliJ --- packages/termansi/lib/src/term.dart | 16 +++++-- packages/termlib/lib/src/extensions/term.dart | 6 ++- packages/termlib/lib/src/termlib_base.dart | 4 ++ packages/termparser/lib/src/engine.dart | 9 +++- packages/termparser/lib/src/parsers.dart | 46 ++++++++++++++----- packages/termparser/test/parser_test.dart | 40 ++++++++++++++++ 6 files changed, 102 insertions(+), 19 deletions(-) diff --git a/packages/termansi/lib/src/term.dart b/packages/termansi/lib/src/term.dart index d4f42a0..bbcabff 100644 --- a/packages/termansi/lib/src/term.dart +++ b/packages/termansi/lib/src/term.dart @@ -79,6 +79,12 @@ abstract class Term { /// Stop receiving mouse events static String get disableMouseEvents => '$CSI?1000;1003;1006l'; + /// Start receiving mouse events inside default configuration zellij multiplexer + static String get enableZellijMouseEvents => _mouseModeCode(true); + + /// Stop receiving mouse events inside default configuration zellij multiplexer + static String get disableZellijMouseEvents => _mouseModeCode(false); + /// Start receiving mouse events as pixels static String get enableMousePixelEvents => '$CSI?1000;1003;1016h'; @@ -111,8 +117,10 @@ abstract class Term { /// Query Primary Device Attributes static String get queryPrimaryDeviceAttributes => '${CSI}c'; - // soft terminal reset - // CSI ! p - // read window size in pixels - // CSI 14 t +// soft terminal reset +// CSI ! p +// read window size in pixels +// CSI 14 t } + +String _mouseModeCode(bool value) => '$CSI?1000;1002;1003;1006;1015${value ? 'h' : 'l'}'; diff --git a/packages/termlib/lib/src/extensions/term.dart b/packages/termlib/lib/src/extensions/term.dart index 69bba07..6a641b9 100644 --- a/packages/termlib/lib/src/extensions/term.dart +++ b/packages/termlib/lib/src/extensions/term.dart @@ -20,10 +20,12 @@ extension TermUtils on TermLib { void setTerminalTitle(String title) => write(ansi.Term.setTerminalTitle(title)); /// Start receiving mouse events - void enableMouseEvents() => write(ansi.Term.enableMouseEvents); + void enableMouseEvents() => + write(zellijMouseMotionQuirk ? ansi.Term.enableZellijMouseEvents : ansi.Term.enableMouseEvents); /// Stop receiving mouse events - void disableMouseEvents() => write(ansi.Term.disableMouseEvents); + void disableMouseEvents() => + write(zellijMouseMotionQuirk ? ansi.Term.disableZellijMouseEvents : ansi.Term.disableMouseEvents); /// Start receiving focus events void startFocusTracking() => write(ansi.Term.enableFocusTracking); diff --git a/packages/termlib/lib/src/termlib_base.dart b/packages/termlib/lib/src/termlib_base.dart index df15d99..e51a859 100644 --- a/packages/termlib/lib/src/termlib_base.dart +++ b/packages/termlib/lib/src/termlib_base.dart @@ -12,6 +12,10 @@ import './ffi/termos.dart'; import './readline.dart'; import './style.dart'; +/// Send additional control codes to have mouse motion codes in default zellij +/// configuration. +bool zellijMouseMotionQuirk = false; + /// Type similar to Platform.environment, used for dependency injection typedef EnvironmentData = Map; diff --git a/packages/termparser/lib/src/engine.dart b/packages/termparser/lib/src/engine.dart index b86dccb..af7c48d 100644 --- a/packages/termparser/lib/src/engine.dart +++ b/packages/termparser/lib/src/engine.dart @@ -73,10 +73,17 @@ enum State { /// Possible UTF-8 sequence and we're collecting UTF-8 code points. utf8, - oscBlock, } +// we want as . +bool ctrlQuestionMarkQuirk = false; + +// in this case we want "return", not "enter". and instead of +// mapped to "enter", we want to have / for vim +// style navigation. +bool rawModeReturnQuirk = false; + /// class Engine { final parameters = List.filled(_maxParameters, _defaultParameterValue); diff --git a/packages/termparser/lib/src/parsers.dart b/packages/termparser/lib/src/parsers.dart index 202b013..c0e77f5 100644 --- a/packages/termparser/lib/src/parsers.dart +++ b/packages/termparser/lib/src/parsers.dart @@ -1,5 +1,7 @@ import 'dart:convert'; +import 'package:termparser/src/engine.dart'; + import 'events.dart'; import 'events_types.dart'; import 'extensions/string_extension.dart'; @@ -16,6 +18,18 @@ Event? parseChar(String char, {bool escO = false}) { _ => const KeyEvent(KeyCode()) // none }; } + + // in this case we want "return", not "enter". and instead of + // mapped to "enter", we want to have / for vim + // style navigation. + if (rawModeReturnQuirk) { + switch (char) { + case '\r': + return const KeyEvent(KeyCode(name: KeyCodeName.enter)); + case '\n': + return _ctrlOrKey(char); + } + } return switch (char) { '\r' || '\n' => const KeyEvent(KeyCode(name: KeyCodeName.enter)), '\t' => const KeyEvent(KeyCode(name: KeyCodeName.tab)), @@ -29,6 +43,10 @@ Event? parseChar(String char, {bool escO = false}) { KeyEvent _ctrlOrKey(String char) { final code = char.codeUnitAt(0); return switch (code) { + 0x1F when ctrlQuestionMarkQuirk => const KeyEvent( + KeyCode(char: '?'), + modifiers: KeyModifiers(KeyModifiers.ctrl), + ), >= 0x01 && <= 0x1A => KeyEvent( KeyCode(char: String.fromCharCode(code - 0x01 + 0x61)), modifiers: const KeyModifiers(KeyModifiers.ctrl), @@ -144,18 +162,22 @@ Event _parseKeyboardEnhancedMode(List parameters, String char) { final c = StringExtension.tryFromCharCode(codePoint); if (c == null) return const NoneEvent(); - keyCode = switch (codePoint) { - 0x1b => const KeyCode(name: KeyCodeName.escape), - 0xd => const KeyCode(name: KeyCodeName.enter), - // if the terminal is in raw mode, the enter key sends \r - // we need to handle this case. How to receive the raw mode status? - 0xa => const KeyCode(name: KeyCodeName.enter), - 0x9 => modifiers.has(KeyModifiers.shift) - ? const KeyCode(name: KeyCodeName.backTab) - : const KeyCode(name: KeyCodeName.tab), - 0x7f => const KeyCode(name: KeyCodeName.backSpace), - _ => KeyCode(char: String.fromCharCode(codePoint)) - }; + if (rawModeReturnQuirk && codePoint == 0xa) { + keyCode = KeyCode(char: String.fromCharCode(codePoint)); + } else { + keyCode = switch (codePoint) { + 0x1b => const KeyCode(name: KeyCodeName.escape), + 0xd => const KeyCode(name: KeyCodeName.enter), + // if the terminal is in raw mode, the enter key sends \r + // we need to handle this case. How to receive the raw mode status? + 0xa => const KeyCode(name: KeyCodeName.enter), + 0x9 => modifiers.has(KeyModifiers.shift) + ? const KeyCode(name: KeyCodeName.backTab) + : const KeyCode(name: KeyCodeName.tab), + 0x7f => const KeyCode(name: KeyCodeName.backSpace), + _ => KeyCode(char: String.fromCharCode(codePoint)) + }; + } stateFromKeyCode = KeyEventState.none(); } diff --git a/packages/termparser/test/parser_test.dart b/packages/termparser/test/parser_test.dart index f6ab19e..fcaa14f 100644 --- a/packages/termparser/test/parser_test.dart +++ b/packages/termparser/test/parser_test.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:termparser/src/engine.dart'; import 'package:termparser/termparser.dart'; import 'package:termparser/termparser_events.dart'; import 'package:test/test.dart'; @@ -646,4 +647,43 @@ void main() { expect(parser.current, equals(const NameAndVersionEvent('term v1-234'))); }); }); + + group('special cases?', () { + test(' is mapped to ', () { + final parser = Parser()..advance([0x0a]); + expect(parser.moveNext(), true); + expect(parser.current, const KeyEvent(KeyCode(name: KeyCodeName.enter))); + }); + + test(' is mapped to ', () { + rawModeReturnQuirk = true; + final parser = Parser()..advance([0x0a]); + expect(parser.moveNext(), true); + expect(parser.current, const KeyEvent(KeyCode(char: 'j'), modifiers: KeyModifiers(KeyModifiers.ctrl))); + }); + + test(' is mapped to by default (why?)', () { + final parser = Parser()..advance([0x1f]); + expect(parser.moveNext(), true); + expect( + parser.current, + const KeyEvent(KeyCode(char: '7'), modifiers: KeyModifiers(KeyModifiers.ctrl)), + ); + }); + + test(' is mapped properly with quirk active', () { + ctrlQuestionMarkQuirk = true; + final parser = Parser()..advance([0x1f]); + expect(parser.moveNext(), true); + expect( + parser.current, + const KeyEvent(KeyCode(char: '?'), modifiers: KeyModifiers(KeyModifiers.ctrl)), + ); + }); + + tearDown(() { + rawModeReturnQuirk = false; + ctrlQuestionMarkQuirk = false; + }); + }); } From 76959e23036acaa7f048519d615be7de9472d40c Mon Sep 17 00:00:00 2001 From: Daniel Lukic Date: Thu, 25 Apr 2024 08:57:21 +0200 Subject: [PATCH 2/3] FIXUP --- packages/termparser/lib/src/engine.dart | 8 -------- packages/termparser/lib/src/parser.dart | 8 ++++++++ packages/termparser/lib/src/parsers.dart | 2 +- packages/termparser/lib/termparser.dart | 2 +- packages/termparser/test/parser_test.dart | 3 +-- 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/termparser/lib/src/engine.dart b/packages/termparser/lib/src/engine.dart index af7c48d..ff05185 100644 --- a/packages/termparser/lib/src/engine.dart +++ b/packages/termparser/lib/src/engine.dart @@ -76,14 +76,6 @@ enum State { oscBlock, } -// we want as . -bool ctrlQuestionMarkQuirk = false; - -// in this case we want "return", not "enter". and instead of -// mapped to "enter", we want to have / for vim -// style navigation. -bool rawModeReturnQuirk = false; - /// class Engine { final parameters = List.filled(_maxParameters, _defaultParameterValue); diff --git a/packages/termparser/lib/src/parser.dart b/packages/termparser/lib/src/parser.dart index 53470a8..adbe23e 100644 --- a/packages/termparser/lib/src/parser.dart +++ b/packages/termparser/lib/src/parser.dart @@ -3,6 +3,14 @@ import 'events.dart'; import 'parsers.dart' as parsers; import 'provider.dart'; +/// we want as . +bool ctrlQuestionMarkQuirk = false; + +/// in this case we want "return", not "enter". and instead of +/// mapped to "enter", we want to have / for vim +/// style navigation. +bool rawModeReturnQuirk = false; + /// The ANSI escape sequence parser /// /// This class implements the ANSI escape sequence parser allowing to parse diff --git a/packages/termparser/lib/src/parsers.dart b/packages/termparser/lib/src/parsers.dart index c0e77f5..0324331 100644 --- a/packages/termparser/lib/src/parsers.dart +++ b/packages/termparser/lib/src/parsers.dart @@ -1,6 +1,6 @@ import 'dart:convert'; -import 'package:termparser/src/engine.dart'; +import 'package:termparser/src/parser.dart'; import 'events.dart'; import 'events_types.dart'; diff --git a/packages/termparser/lib/termparser.dart b/packages/termparser/lib/termparser.dart index 0fd957d..5e3feec 100644 --- a/packages/termparser/lib/termparser.dart +++ b/packages/termparser/lib/termparser.dart @@ -10,4 +10,4 @@ /// consistent state even if the input data is not complete. library; -export 'src/parser.dart' show Parser; +export 'src/parser.dart' show Parser, ctrlQuestionMarkQuirk, rawModeReturnQuirk; diff --git a/packages/termparser/test/parser_test.dart b/packages/termparser/test/parser_test.dart index fcaa14f..1d1a300 100644 --- a/packages/termparser/test/parser_test.dart +++ b/packages/termparser/test/parser_test.dart @@ -1,7 +1,6 @@ import 'dart:convert'; -import 'package:termparser/src/engine.dart'; -import 'package:termparser/termparser.dart'; +import 'package:termparser/src/parser.dart'; import 'package:termparser/termparser_events.dart'; import 'package:test/test.dart'; From 7f0ff479445c8fbc883b49f889a20864a2b5857f Mon Sep 17 00:00:00 2001 From: Daniel Lukic Date: Thu, 25 Apr 2024 09:37:08 +0200 Subject: [PATCH 3/3] FIXUP --- packages/termansi/lib/src/term.dart | 8 ++++---- packages/termparser/lib/src/engine.dart | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/termansi/lib/src/term.dart b/packages/termansi/lib/src/term.dart index bbcabff..c676527 100644 --- a/packages/termansi/lib/src/term.dart +++ b/packages/termansi/lib/src/term.dart @@ -117,10 +117,10 @@ abstract class Term { /// Query Primary Device Attributes static String get queryPrimaryDeviceAttributes => '${CSI}c'; -// soft terminal reset -// CSI ! p -// read window size in pixels -// CSI 14 t + // soft terminal reset + // CSI ! p + // read window size in pixels + // CSI 14 t } String _mouseModeCode(bool value) => '$CSI?1000;1002;1003;1006;1015${value ? 'h' : 'l'}'; diff --git a/packages/termparser/lib/src/engine.dart b/packages/termparser/lib/src/engine.dart index ff05185..b86dccb 100644 --- a/packages/termparser/lib/src/engine.dart +++ b/packages/termparser/lib/src/engine.dart @@ -73,6 +73,7 @@ enum State { /// Possible UTF-8 sequence and we're collecting UTF-8 code points. utf8, + oscBlock, }