Add support for the shift modifier.
Because keyboard layouts vary, the shift-modifier `<s-…>` is only supported for special keys (like `<up>` and `<home>`) and for ASCII lowercase where we assume the shift-modifier just produces the matching uppercase character. Even that's not universally true, since in Turkish `i` and `I` are not an uppercase/lowercase pair, but Kakoune's default keyboard mappings already assume en-US mappings for mnemonic purposes. Mappings of the form `<s-x>` are normalized to `<X>` when `x` is an ASCII character. `<backtab>` is removed, since we can now say `<s-tab>`.
This commit is contained in:
parent
d846400279
commit
50e422659b
|
@ -6,8 +6,8 @@ Usual keys are written using their ascii character, including capital
|
||||||
keys. Non printable keys use an alternate name, written between *<*
|
keys. Non printable keys use an alternate name, written between *<*
|
||||||
and *>*, such as *<esc>* or *<del>*. Modified keys are written between
|
and *>*, such as *<esc>* or *<del>*. Modified keys are written between
|
||||||
*<* and *>* as well, with the modifier specified as either *c* for
|
*<* and *>* as well, with the modifier specified as either *c* for
|
||||||
Control, or *a* for Alt, followed by a *-* and the key (either its
|
Control, *a* for Alt, or *s* for Shift, followed by a *-* and the key (either
|
||||||
name or ascii character), for example *<c-x>*, *<a-space>*, *<c-a-w>*.
|
its name or ascii character), for example *<c-x>*, *<a-space>*, *<c-a-w>*.
|
||||||
|
|
||||||
In order to bind some keys to arbitrary ones, refer to <<mapping#,`:doc mapping`>>
|
In order to bind some keys to arbitrary ones, refer to <<mapping#,`:doc mapping`>>
|
||||||
|
|
||||||
|
@ -95,18 +95,20 @@ it when pasting text.
|
||||||
== Movement
|
== Movement
|
||||||
|
|
||||||
'word' is a sequence of alphanumeric characters or underscore, and 'WORD'
|
'word' is a sequence of alphanumeric characters or underscore, and 'WORD'
|
||||||
is a sequence of non whitespace characters
|
is a sequence of non whitespace characters. Generally, a movement on it own
|
||||||
|
will move the selection to cover the text moved over, while holding down
|
||||||
|
the Shift modifier and moving will extend the selection instead.
|
||||||
|
|
||||||
*h*::
|
*h*, *<left>*::
|
||||||
select the character on the left of selection end
|
select the character on the left of selection end
|
||||||
|
|
||||||
*j*::
|
*j*, *<down>*::
|
||||||
select the character below the selection end
|
select the character below the selection end
|
||||||
|
|
||||||
*k*::
|
*k*, *<up>*::
|
||||||
select the character above the selection end
|
select the character above the selection end
|
||||||
|
|
||||||
*l*::
|
*l*, *<right>*::
|
||||||
select the character on the right of selection end
|
select the character on the right of selection end
|
||||||
|
|
||||||
*w*::
|
*w*::
|
||||||
|
@ -134,16 +136,10 @@ is a sequence of non whitespace characters
|
||||||
select to matching character, see the `matching_pairs` option
|
select to matching character, see the `matching_pairs` option
|
||||||
in <<options#,`:doc options`>>
|
in <<options#,`:doc options`>>
|
||||||
|
|
||||||
*M*::
|
|
||||||
extend selection to matching character
|
|
||||||
|
|
||||||
*x*::
|
*x*::
|
||||||
select line on which selection end lies (or next line when end lies
|
select line on which selection end lies (or next line when end lies
|
||||||
on an end-of-line)
|
on an end-of-line)
|
||||||
|
|
||||||
*X*::
|
|
||||||
similar to *x*, except the current selection is extended
|
|
||||||
|
|
||||||
*<a-x>*::
|
*<a-x>*::
|
||||||
expand selections to contain full lines (including end-of-lines)
|
expand selections to contain full lines (including end-of-lines)
|
||||||
|
|
||||||
|
@ -154,10 +150,10 @@ is a sequence of non whitespace characters
|
||||||
*%*::
|
*%*::
|
||||||
select whole buffer
|
select whole buffer
|
||||||
|
|
||||||
*<a-h>*::
|
*<a-h>*, *<home>*::
|
||||||
select to line begin
|
select to line begin
|
||||||
|
|
||||||
*<a-l>*::
|
*<a-l>*, *<end>*::
|
||||||
select to line end
|
select to line end
|
||||||
|
|
||||||
*/*::
|
*/*::
|
||||||
|
@ -696,7 +692,7 @@ The following keys are recognized by this mode to help edition.
|
||||||
*<tab>*::
|
*<tab>*::
|
||||||
select next completion candidate
|
select next completion candidate
|
||||||
|
|
||||||
*<backtab>*::
|
*<s-tab>*::
|
||||||
select previous completion candidate
|
select previous completion candidate
|
||||||
|
|
||||||
*<c-r>*::
|
*<c-r>*::
|
||||||
|
|
|
@ -65,15 +65,20 @@ be used:
|
||||||
Keys can also be wrapped in angle-brackets for consistency
|
Keys can also be wrapped in angle-brackets for consistency
|
||||||
with the non-alphabetic keys below.
|
with the non-alphabetic keys below.
|
||||||
|
|
||||||
*X*, *<X>*::
|
|
||||||
Holding down Shift while pressing the *x* key.
|
|
||||||
|
|
||||||
*<c-x>*::
|
*<c-x>*::
|
||||||
Holding down Control while pressing the *x* key.
|
Holding down Control while pressing the *x* key.
|
||||||
|
|
||||||
*<a-x>*::
|
*<a-x>*::
|
||||||
Holding down Alt while pressing the *x* key.
|
Holding down Alt while pressing the *x* key.
|
||||||
|
|
||||||
|
*<s-x>*, *X*, *<X>*, *<s-X>*::
|
||||||
|
Holding down Shift while pressing the *x* key.
|
||||||
|
*<s-x>*, *<s-X>* and *<X>* are treated as the same key. The *s-* modifier
|
||||||
|
only works with ASCII letters and cannot be used with other printable keys
|
||||||
|
(non-ASCII letters, digits, punctuation) because their shift behaviour
|
||||||
|
depends on your keyboard layout. The *s-* modifier _can_ be used with
|
||||||
|
special keys like *<up>* and *<tab>*.
|
||||||
|
|
||||||
*<c-a-x>*::
|
*<c-a-x>*::
|
||||||
Holding down Control and Alt while pressing the *x* key.
|
Holding down Control and Alt while pressing the *x* key.
|
||||||
|
|
||||||
|
@ -92,9 +97,6 @@ be used:
|
||||||
*<tab>*::
|
*<tab>*::
|
||||||
The Tab key.
|
The Tab key.
|
||||||
|
|
||||||
*<backtab>*::
|
|
||||||
The reverse-tab key. This is Shift-Tab on most keyboards.
|
|
||||||
|
|
||||||
*<backspace>*::
|
*<backspace>*::
|
||||||
The Backspace (delete to the left) key.
|
The Backspace (delete to the left) key.
|
||||||
|
|
||||||
|
@ -110,3 +112,8 @@ be used:
|
||||||
|
|
||||||
*<f1>*, *<f2>*, ...*<f12>*::
|
*<f1>*, *<f2>*, ...*<f12>*::
|
||||||
Function keys.
|
Function keys.
|
||||||
|
|
||||||
|
NOTE: Although Kakoune allows many key combinations to be mapped, not every
|
||||||
|
possible combination can be triggered. For example, due to limitations in
|
||||||
|
the way terminals handle control characters, mappings like *<c-s-a>* are
|
||||||
|
unlikely to work in Kakoune's terminal UI.
|
||||||
|
|
|
@ -22,8 +22,16 @@ void on_assert_failed(const char* message);
|
||||||
on_assert_failed("assert failed \"" #__VA_ARGS__ \
|
on_assert_failed("assert failed \"" #__VA_ARGS__ \
|
||||||
"\" at " __FILE__ ":" TOSTRING(__LINE__)); \
|
"\" at " __FILE__ ":" TOSTRING(__LINE__)); \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
|
#define kak_expect_throw(exception_type, ...) try {\
|
||||||
|
__VA_ARGS__; \
|
||||||
|
on_assert_failed("expression \"" #__VA_ARGS__ \
|
||||||
|
"\" did not throw \"" #exception_type \
|
||||||
|
"\" at " __FILE__ ":" TOSTRING(__LINE__)); \
|
||||||
|
} catch (exception_type &err) {}
|
||||||
#else
|
#else
|
||||||
#define kak_assert(...) do { (void)sizeof(__VA_ARGS__); } while(false)
|
#define kak_assert(...) do { (void)sizeof(__VA_ARGS__); } while(false)
|
||||||
|
#define kak_expect_throw(_, ...) do { (void)sizeof(__VA_ARGS__); } while(false)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -461,15 +461,15 @@ public:
|
||||||
}
|
}
|
||||||
else if (key == ctrl('w'))
|
else if (key == ctrl('w'))
|
||||||
to_next_word_begin<Word>(m_cursor_pos, m_line);
|
to_next_word_begin<Word>(m_cursor_pos, m_line);
|
||||||
else if (key == ctrlalt('w'))
|
else if (key == ctrl(alt('w')))
|
||||||
to_next_word_begin<WORD>(m_cursor_pos, m_line);
|
to_next_word_begin<WORD>(m_cursor_pos, m_line);
|
||||||
else if (key == ctrl('b'))
|
else if (key == ctrl('b'))
|
||||||
to_prev_word_begin<Word>(m_cursor_pos, m_line);
|
to_prev_word_begin<Word>(m_cursor_pos, m_line);
|
||||||
else if (key == ctrlalt('b'))
|
else if (key == ctrl(alt('b')))
|
||||||
to_prev_word_begin<WORD>(m_cursor_pos, m_line);
|
to_prev_word_begin<WORD>(m_cursor_pos, m_line);
|
||||||
else if (key == ctrl('e'))
|
else if (key == ctrl('e'))
|
||||||
to_next_word_end<Word>(m_cursor_pos, m_line);
|
to_next_word_end<Word>(m_cursor_pos, m_line);
|
||||||
else if (key == ctrlalt('e'))
|
else if (key == ctrl(alt('e')))
|
||||||
to_next_word_end<WORD>(m_cursor_pos, m_line);
|
to_next_word_end<WORD>(m_cursor_pos, m_line);
|
||||||
else if (key == ctrl('k'))
|
else if (key == ctrl('k'))
|
||||||
m_line = m_line.substr(0_char, m_cursor_pos).str();
|
m_line = m_line.substr(0_char, m_cursor_pos).str();
|
||||||
|
@ -623,7 +623,7 @@ public:
|
||||||
it = std::find_if(m_choices.begin(), m_selected, match_filter);
|
it = std::find_if(m_choices.begin(), m_selected, match_filter);
|
||||||
select(it);
|
select(it);
|
||||||
}
|
}
|
||||||
else if (key == Key::Up or key == Key::BackTab or
|
else if (key == Key::Up or key == shift(Key::Tab) or
|
||||||
key == ctrl('p') or (not m_edit_filter and key == 'k'))
|
key == ctrl('p') or (not m_edit_filter and key == 'k'))
|
||||||
{
|
{
|
||||||
ChoiceList::const_reverse_iterator selected(m_selected+1);
|
ChoiceList::const_reverse_iterator selected(m_selected+1);
|
||||||
|
@ -837,9 +837,9 @@ public:
|
||||||
m_refresh_completion_pending = true;
|
m_refresh_completion_pending = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (key == Key::Tab or key == Key::BackTab) // tab completion
|
else if (key == Key::Tab or key == shift(Key::Tab)) // tab completion
|
||||||
{
|
{
|
||||||
const bool reverse = (key == Key::BackTab);
|
const bool reverse = (key == shift(Key::Tab));
|
||||||
CandidateList& candidates = m_completions.candidates;
|
CandidateList& candidates = m_completions.candidates;
|
||||||
// first try, we need to ask our completer for completions
|
// first try, we need to ask our completer for completions
|
||||||
if (candidates.empty())
|
if (candidates.empty())
|
||||||
|
@ -1568,8 +1568,10 @@ InputHandler::ScopedForceNormal::~ScopedForceNormal()
|
||||||
|
|
||||||
static bool is_valid(Key key)
|
static bool is_valid(Key key)
|
||||||
{
|
{
|
||||||
|
constexpr Key::Modifiers valid_mods = (Key::Modifiers::Control | Key::Modifiers::Alt | Key::Modifiers::Shift);
|
||||||
|
|
||||||
return key != Key::Invalid and
|
return key != Key::Invalid and
|
||||||
((key.modifiers & ~Key::Modifiers::ControlAlt) or key.key <= 0x10FFFF);
|
((key.modifiers & ~valid_mods) or key.key <= 0x10FFFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputHandler::handle_key(Key key)
|
void InputHandler::handle_key(Key key)
|
||||||
|
|
73
src/keys.cc
73
src/keys.cc
|
@ -11,6 +11,11 @@
|
||||||
namespace Kakoune
|
namespace Kakoune
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct key_parse_error : runtime_error
|
||||||
|
{
|
||||||
|
using runtime_error::runtime_error;
|
||||||
|
};
|
||||||
|
|
||||||
static Key canonicalize_ifn(Key key)
|
static Key canonicalize_ifn(Key key)
|
||||||
{
|
{
|
||||||
if (key.key > 0 and key.key < 27)
|
if (key.key > 0 and key.key < 27)
|
||||||
|
@ -19,6 +24,22 @@ static Key canonicalize_ifn(Key key)
|
||||||
key.modifiers = Key::Modifiers::Control;
|
key.modifiers = Key::Modifiers::Control;
|
||||||
key.key = key.key - 1 + 'a';
|
key.key = key.key - 1 + 'a';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (key.modifiers & Key::Modifiers::Shift)
|
||||||
|
{
|
||||||
|
if (is_basic_alpha(key.key))
|
||||||
|
{
|
||||||
|
// Shift + ASCII letters is just the uppercase letter.
|
||||||
|
key.modifiers &= ~Key::Modifiers::Shift;
|
||||||
|
key.key = to_upper(key.key);
|
||||||
|
}
|
||||||
|
else if (key.key < 0xD800 || key.key > 0xDFFF)
|
||||||
|
{
|
||||||
|
// Shift + any other printable character is not allowed.
|
||||||
|
throw key_parse_error(format("Shift modifier only works on special keys and lowercase ASCII, not '{}'", key.key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +74,6 @@ static constexpr KeyAndName keynamemap[] = {
|
||||||
{ "pagedown", Key::PageDown },
|
{ "pagedown", Key::PageDown },
|
||||||
{ "home", Key::Home },
|
{ "home", Key::Home },
|
||||||
{ "end", Key::End },
|
{ "end", Key::End },
|
||||||
{ "backtab", Key::BackTab },
|
|
||||||
{ "del", Key::Delete },
|
{ "del", Key::Delete },
|
||||||
{ "plus", '+' },
|
{ "plus", '+' },
|
||||||
{ "minus", '-' },
|
{ "minus", '-' },
|
||||||
|
@ -85,15 +105,16 @@ KeyList parse_keys(StringView str)
|
||||||
for (auto dash = find(desc, '-'); dash != desc.end(); dash = find(desc, '-'))
|
for (auto dash = find(desc, '-'); dash != desc.end(); dash = find(desc, '-'))
|
||||||
{
|
{
|
||||||
if (dash != desc.begin() + 1)
|
if (dash != desc.begin() + 1)
|
||||||
throw runtime_error(format("unable to parse modifier in '{}'",
|
throw key_parse_error(format("unable to parse modifier in '{}'",
|
||||||
full_desc));
|
full_desc));
|
||||||
|
|
||||||
switch(to_lower(desc[0_byte]))
|
switch(to_lower(desc[0_byte]))
|
||||||
{
|
{
|
||||||
case 'c': modifier |= Key::Modifiers::Control; break;
|
case 'c': modifier |= Key::Modifiers::Control; break;
|
||||||
case 'a': modifier |= Key::Modifiers::Alt; break;
|
case 'a': modifier |= Key::Modifiers::Alt; break;
|
||||||
|
case 's': modifier |= Key::Modifiers::Shift; break;
|
||||||
default:
|
default:
|
||||||
throw runtime_error(format("unable to parse modifier in '{}'",
|
throw key_parse_error(format("unable to parse modifier in '{}'",
|
||||||
full_desc));
|
full_desc));
|
||||||
}
|
}
|
||||||
desc = StringView{dash+1, desc.end()};
|
desc = StringView{dash+1, desc.end()};
|
||||||
|
@ -104,17 +125,17 @@ KeyList parse_keys(StringView str)
|
||||||
if (name_it != std::end(keynamemap))
|
if (name_it != std::end(keynamemap))
|
||||||
result.push_back(canonicalize_ifn({ modifier, name_it->key }));
|
result.push_back(canonicalize_ifn({ modifier, name_it->key }));
|
||||||
else if (desc.char_length() == 1)
|
else if (desc.char_length() == 1)
|
||||||
result.emplace_back(modifier, desc[0_char]);
|
result.push_back(canonicalize_ifn({ modifier, desc[0_char] }));
|
||||||
else if (to_lower(desc[0_byte]) == 'f' and desc.length() <= 3)
|
else if (to_lower(desc[0_byte]) == 'f' and desc.length() <= 3)
|
||||||
{
|
{
|
||||||
int val = str_to_int(desc.substr(1_byte));
|
int val = str_to_int(desc.substr(1_byte));
|
||||||
if (val >= 1 and val <= 12)
|
if (val >= 1 and val <= 12)
|
||||||
result.emplace_back(modifier, Key::F1 + (val - 1));
|
result.emplace_back(modifier, Key::F1 + (val - 1));
|
||||||
else
|
else
|
||||||
throw runtime_error("only F1 through F12 are supported");
|
throw key_parse_error(format("only F1 through F12 are supported, not '{}'", desc));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw runtime_error("unable to parse " +
|
throw key_parse_error("unable to parse " +
|
||||||
StringView{it.base(), end_it.base()+1});
|
StringView{it.base(), end_it.base()+1});
|
||||||
|
|
||||||
it = end_it;
|
it = end_it;
|
||||||
|
@ -165,13 +186,10 @@ String key_to_str(Key key)
|
||||||
else
|
else
|
||||||
res = String{key.key};
|
res = String{key.key};
|
||||||
|
|
||||||
switch (key.modifiers)
|
if (key.modifiers & Key::Modifiers::Shift) { res = "s-" + res; named = true; }
|
||||||
{
|
if (key.modifiers & Key::Modifiers::Alt) { res = "a-" + res; named = true; }
|
||||||
case Key::Modifiers::Control: res = "c-" + res; named = true; break;
|
if (key.modifiers & Key::Modifiers::Control) { res = "c-" + res; named = true; }
|
||||||
case Key::Modifiers::Alt: res = "a-" + res; named = true; break;
|
|
||||||
case Key::Modifiers::ControlAlt: res = "c-a-" + res; named = true; break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
if (named)
|
if (named)
|
||||||
res = StringView{'<'} + res + StringView{'>'};
|
res = StringView{'<'} + res + StringView{'>'};
|
||||||
return res;
|
return res;
|
||||||
|
@ -182,8 +200,10 @@ UnitTest test_keys{[]()
|
||||||
KeyList keys{
|
KeyList keys{
|
||||||
{ ' ' },
|
{ ' ' },
|
||||||
{ 'c' },
|
{ 'c' },
|
||||||
{ Key::Modifiers::Alt, 'j' },
|
{ Key::Up },
|
||||||
{ Key::Modifiers::Control, 'r' }
|
alt('j'),
|
||||||
|
ctrl('r'),
|
||||||
|
shift(Key::Up),
|
||||||
};
|
};
|
||||||
String keys_as_str;
|
String keys_as_str;
|
||||||
for (auto& key : keys)
|
for (auto& key : keys)
|
||||||
|
@ -191,7 +211,28 @@ UnitTest test_keys{[]()
|
||||||
auto parsed_keys = parse_keys(keys_as_str);
|
auto parsed_keys = parse_keys(keys_as_str);
|
||||||
kak_assert(keys == parsed_keys);
|
kak_assert(keys == parsed_keys);
|
||||||
kak_assert(ConstArrayView<Key>{parse_keys("a<c-a-b>c")} ==
|
kak_assert(ConstArrayView<Key>{parse_keys("a<c-a-b>c")} ==
|
||||||
ConstArrayView<Key>{'a', {Key::Modifiers::ControlAlt, 'b'}, 'c'});
|
ConstArrayView<Key>{'a', ctrl(alt({'b'})), 'c'});
|
||||||
|
|
||||||
|
kak_assert(parse_keys("x") == KeyList{ {'x'} });
|
||||||
|
kak_assert(parse_keys("<x>") == KeyList{ {'x'} });
|
||||||
|
kak_assert(parse_keys("<s-x>") == KeyList{ {'X'} });
|
||||||
|
kak_assert(parse_keys("<s-X>") == KeyList{ {'X'} });
|
||||||
|
kak_assert(parse_keys("<X>") == KeyList{ {'X'} });
|
||||||
|
kak_assert(parse_keys("X") == KeyList{ {'X'} });
|
||||||
|
kak_assert(parse_keys("<s-up>") == KeyList{ shift({Key::Up}) });
|
||||||
|
kak_assert(parse_keys("<s-tab>") == KeyList{ shift({Key::Tab}) });
|
||||||
|
|
||||||
|
kak_assert(key_to_str(shift({Key::Tab})) == "<s-tab>");
|
||||||
|
|
||||||
|
kak_expect_throw(key_parse_error, parse_keys("<-x>"));
|
||||||
|
kak_expect_throw(key_parse_error, parse_keys("<xy-z>"));
|
||||||
|
kak_expect_throw(key_parse_error, parse_keys("<x-y>"));
|
||||||
|
kak_expect_throw(key_parse_error, parse_keys("<s-/>"));
|
||||||
|
kak_expect_throw(key_parse_error, parse_keys("<s-ë>"));
|
||||||
|
kak_expect_throw(key_parse_error, parse_keys("<s-lt>"));
|
||||||
|
kak_expect_throw(key_parse_error, parse_keys("<f99>"));
|
||||||
|
kak_expect_throw(key_parse_error, parse_keys("<backtab>"));
|
||||||
|
kak_expect_throw(key_parse_error, parse_keys("<invalidkey>"));
|
||||||
}};
|
}};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
23
src/keys.hh
23
src/keys.hh
|
@ -19,17 +19,17 @@ struct Key
|
||||||
None = 0,
|
None = 0,
|
||||||
Control = 1 << 0,
|
Control = 1 << 0,
|
||||||
Alt = 1 << 1,
|
Alt = 1 << 1,
|
||||||
ControlAlt = Control | Alt,
|
Shift = 1 << 2,
|
||||||
|
|
||||||
MousePress = 1 << 2,
|
MousePress = 1 << 3,
|
||||||
MouseRelease = 1 << 3,
|
MouseRelease = 1 << 4,
|
||||||
MousePos = 1 << 4,
|
MousePos = 1 << 5,
|
||||||
MouseWheelDown = 1 << 5,
|
MouseWheelDown = 1 << 6,
|
||||||
MouseWheelUp = 1 << 6,
|
MouseWheelUp = 1 << 7,
|
||||||
MouseEvent = MousePress | MouseRelease | MousePos |
|
MouseEvent = MousePress | MouseRelease | MousePos |
|
||||||
MouseWheelDown | MouseWheelUp,
|
MouseWheelDown | MouseWheelUp,
|
||||||
|
|
||||||
Resize = 1 << 7,
|
Resize = 1 << 8,
|
||||||
};
|
};
|
||||||
enum NamedKey : Codepoint
|
enum NamedKey : Codepoint
|
||||||
{
|
{
|
||||||
|
@ -47,7 +47,6 @@ struct Key
|
||||||
Home,
|
Home,
|
||||||
End,
|
End,
|
||||||
Tab,
|
Tab,
|
||||||
BackTab,
|
|
||||||
F1,
|
F1,
|
||||||
F2,
|
F2,
|
||||||
F3,
|
F3,
|
||||||
|
@ -97,6 +96,10 @@ class StringView;
|
||||||
KeyList parse_keys(StringView str);
|
KeyList parse_keys(StringView str);
|
||||||
String key_to_str(Key key);
|
String key_to_str(Key key);
|
||||||
|
|
||||||
|
constexpr Key shift(Key key)
|
||||||
|
{
|
||||||
|
return { key.modifiers | Key::Modifiers::Shift, key.key };
|
||||||
|
}
|
||||||
constexpr Key alt(Key key)
|
constexpr Key alt(Key key)
|
||||||
{
|
{
|
||||||
return { key.modifiers | Key::Modifiers::Alt, key.key };
|
return { key.modifiers | Key::Modifiers::Alt, key.key };
|
||||||
|
@ -105,10 +108,6 @@ constexpr Key ctrl(Key key)
|
||||||
{
|
{
|
||||||
return { key.modifiers | Key::Modifiers::Control, key.key };
|
return { key.modifiers | Key::Modifiers::Control, key.key };
|
||||||
}
|
}
|
||||||
constexpr Key ctrlalt(Key key)
|
|
||||||
{
|
|
||||||
return { key.modifiers | Key::Modifiers::ControlAlt, key.key };
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr Codepoint encode_coord(DisplayCoord coord) { return (Codepoint)(((int)coord.line << 16) | ((int)coord.column & 0x0000FFFF)); }
|
constexpr Codepoint encode_coord(DisplayCoord coord) { return (Codepoint)(((int)coord.line << 16) | ((int)coord.column & 0x0000FFFF)); }
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,8 @@ static const char* startup_info =
|
||||||
" * 'x' will only jump to next line if full line is already selected\n"
|
" * 'x' will only jump to next line if full line is already selected\n"
|
||||||
" * WORD text object moved to <a-w> instead of W for consistency\n"
|
" * WORD text object moved to <a-w> instead of W for consistency\n"
|
||||||
" * rotate main selection moved to ), rotate content to <a-)>, ( for backward\n"
|
" * rotate main selection moved to ), rotate content to <a-)>, ( for backward\n"
|
||||||
" * faces are now scoped, set-face command takes an additional scope parameter\n";
|
" * faces are now scoped, set-face command takes an additional scope parameter\n"
|
||||||
|
" * <backtab> key is gone, use <s-tab> instead\n";
|
||||||
|
|
||||||
struct startup_error : runtime_error
|
struct startup_error : runtime_error
|
||||||
{
|
{
|
||||||
|
|
|
@ -570,15 +570,24 @@ Optional<Key> NCursesUI::get_next_key()
|
||||||
{
|
{
|
||||||
case KEY_BACKSPACE: case 127: return {Key::Backspace};
|
case KEY_BACKSPACE: case 127: return {Key::Backspace};
|
||||||
case KEY_DC: return {Key::Delete};
|
case KEY_DC: return {Key::Delete};
|
||||||
|
case KEY_SDC: return shift(Key::Delete);
|
||||||
case KEY_UP: return {Key::Up};
|
case KEY_UP: return {Key::Up};
|
||||||
|
case KEY_SR: return shift(Key::Up);
|
||||||
case KEY_DOWN: return {Key::Down};
|
case KEY_DOWN: return {Key::Down};
|
||||||
|
case KEY_SF: return shift(Key::Down);
|
||||||
case KEY_LEFT: return {Key::Left};
|
case KEY_LEFT: return {Key::Left};
|
||||||
|
case KEY_SLEFT: return shift(Key::Left);
|
||||||
case KEY_RIGHT: return {Key::Right};
|
case KEY_RIGHT: return {Key::Right};
|
||||||
|
case KEY_SRIGHT: return shift(Key::Right);
|
||||||
case KEY_PPAGE: return {Key::PageUp};
|
case KEY_PPAGE: return {Key::PageUp};
|
||||||
|
case KEY_SPREVIOUS: return shift(Key::PageUp);
|
||||||
case KEY_NPAGE: return {Key::PageDown};
|
case KEY_NPAGE: return {Key::PageDown};
|
||||||
|
case KEY_SNEXT: return shift(Key::PageDown);
|
||||||
case KEY_HOME: return {Key::Home};
|
case KEY_HOME: return {Key::Home};
|
||||||
|
case KEY_SHOME: return shift(Key::Home);
|
||||||
case KEY_END: return {Key::End};
|
case KEY_END: return {Key::End};
|
||||||
case KEY_BTAB: return {Key::BackTab};
|
case KEY_SEND: return shift(Key::End);
|
||||||
|
case KEY_BTAB: return shift(Key::Tab);
|
||||||
case KEY_RESIZE: return resize(m_dimensions);
|
case KEY_RESIZE: return resize(m_dimensions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2062,6 +2062,11 @@ static const HashMap<Key, NormalCmd, MemoryDomain::Undefined, KeymapBackend> key
|
||||||
{ {'K'}, {"extend up", move<LineCount, Backward, SelectMode::Extend>} },
|
{ {'K'}, {"extend up", move<LineCount, Backward, SelectMode::Extend>} },
|
||||||
{ {'L'}, {"extend right", move<CharCount, Forward, SelectMode::Extend>} },
|
{ {'L'}, {"extend right", move<CharCount, Forward, SelectMode::Extend>} },
|
||||||
|
|
||||||
|
{ shift(Key::Left), {"extend left", move<CharCount, Backward, SelectMode::Extend>} },
|
||||||
|
{ shift(Key::Down), {"extend down", move<LineCount, Forward, SelectMode::Extend>} },
|
||||||
|
{ shift(Key::Up), {"extend up", move<LineCount, Backward, SelectMode::Extend>} },
|
||||||
|
{ shift(Key::Right), {"extend right", move<CharCount, Forward, SelectMode::Extend>} },
|
||||||
|
|
||||||
{ {'t'}, {"select to next character", select_to_next_char<SelectFlags::None>} },
|
{ {'t'}, {"select to next character", select_to_next_char<SelectFlags::None>} },
|
||||||
{ {'f'}, {"select to next character included", select_to_next_char<SelectFlags::Inclusive>} },
|
{ {'f'}, {"select to next character included", select_to_next_char<SelectFlags::Inclusive>} },
|
||||||
{ {'T'}, {"extend to next character", select_to_next_char<SelectFlags::Extend>} },
|
{ {'T'}, {"extend to next character", select_to_next_char<SelectFlags::Extend>} },
|
||||||
|
@ -2140,9 +2145,11 @@ static const HashMap<Key, NormalCmd, MemoryDomain::Undefined, KeymapBackend> key
|
||||||
{ {alt('l')}, {"select to line end", repeated<select<SelectMode::Replace, select_to_line_end<false>>>} },
|
{ {alt('l')}, {"select to line end", repeated<select<SelectMode::Replace, select_to_line_end<false>>>} },
|
||||||
{ {Key::End}, {"select to line end", repeated<select<SelectMode::Replace, select_to_line_end<false>>>} },
|
{ {Key::End}, {"select to line end", repeated<select<SelectMode::Replace, select_to_line_end<false>>>} },
|
||||||
{ {alt('L')}, {"extend to line end", repeated<select<SelectMode::Extend, select_to_line_end<false>>>} },
|
{ {alt('L')}, {"extend to line end", repeated<select<SelectMode::Extend, select_to_line_end<false>>>} },
|
||||||
|
{ shift(Key::End), {"extend to line end", repeated<select<SelectMode::Extend, select_to_line_end<false>>>} },
|
||||||
{ {alt('h')}, {"select to line begin", repeated<select<SelectMode::Replace, select_to_line_begin<false>>>} },
|
{ {alt('h')}, {"select to line begin", repeated<select<SelectMode::Replace, select_to_line_begin<false>>>} },
|
||||||
{ {Key::Home}, {"select to line begin", repeated<select<SelectMode::Replace, select_to_line_begin<false>>>} },
|
{ {Key::Home}, {"select to line begin", repeated<select<SelectMode::Replace, select_to_line_begin<false>>>} },
|
||||||
{ {alt('H')}, {"extend to line begin", repeated<select<SelectMode::Extend, select_to_line_begin<false>>>} },
|
{ {alt('H')}, {"extend to line begin", repeated<select<SelectMode::Extend, select_to_line_begin<false>>>} },
|
||||||
|
{ shift(Key::Home), {"extend to line begin", repeated<select<SelectMode::Extend, select_to_line_begin<false>>>} },
|
||||||
|
|
||||||
{ {'x'}, {"select line", repeated<select<SelectMode::Replace, select_line>>} },
|
{ {'x'}, {"select line", repeated<select<SelectMode::Replace, select_line>>} },
|
||||||
{ {'X'}, {"extend line", repeated<select<SelectMode::Extend, select_line>>} },
|
{ {'X'}, {"extend line", repeated<select<SelectMode::Extend, select_line>>} },
|
||||||
|
|
Loading…
Reference in New Issue
Block a user