Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement keyboard layout remapping #5046

Closed

Conversation

sinefabula
Copy link

@sinefabula sinefabula commented Dec 7, 2022

This patch adds a config option to remap keys from a different keyboard layout into English so that keybindings work even with the keyboard switched into a non-English layout.

The corresponding config option is editor.layout-remap which is a list of dictionaries with two mandatory keys, from and into, which specify the translation mapping, e.g.:

[[editor.layout-remap]]
from = 'йцукенгшщзхъЙЦУКЕНГШЩЗХЪ'
into = 'qwertyuiop[]QWERTYUIOP{}'

These can be repeated multiple times to facilitate specifying mappings containing different types of quotes and other special characters which may require escaping in TOML config. Naming (from and into) is chosen to be of the same length so that the letters are aligned when using a monospace font, and the config is easier to write.

These translation mappings are applied in every mode except for (obviously) the Insert mode.

This circumvents #133 in a way that while Helix still does not recognise keypresses by their scan-codes but yet it allows for the non-English layout users to operate the editor without switching the layout which can be especially useful for writing documentation in their native language with Helix.

One caveat with this patch is that you can't remap keys already present in the English layout because Helix is not aware of the current keyboard layout and will apply translation every time, thus messing with the original mapping. This patch prevents such behaviour by disabling corresponding bindings and reporting these cases to the Helix log file. The other option would be to fail but I thought it would be safe to just filter them and log it.

A complete configuration example for macOS ISO keyboard layout looks like this. I've been using this for over a month now and it works great. Of course it is not the best solution of #133 but it does like 80% of the job, covering most used cases.

[[editor.layout-remap]]
from = '<!"№%:,.;()_+'
into = '±!@#$%^&*()_+'

[[editor.layout-remap]]
from = 'йцукенгшщзхъЙЦУКЕНГШЩЗХЪ'
into = 'qwertyuiop[]QWERTYUIOP{}'

[[editor.layout-remap]]
from = 'фывапролджёФЫВАПРОЛДЖЭЁ'
into = 'asdfghjkl;\ASDFGHJKL:"|'

[[editor.layout-remap]]
from = 'э'
into = "'"

[[editor.layout-remap]]
from = ']ячсмитьбю/[ЯЧСМИТЬБЮ?'
into = '`zxcvbnm,./~ZXCVBNM<>?'

Adds a config option to remap keys in a different keyboard layout into
English so that keybindings work even when switched into a non-English
keyboard layout.

The corresponding config option is `editor.layout-remap` which is a
list of dictionaries with two mandatory keys, `from` and `into`, which
specify the translation mapping, e.g.:

```toml
[[editor.layout-remap]]
from = 'йцукенгшщзхъЙЦУКЕНГШЩЗХЪ'
into = 'qwertyuiop[]QWERTYUIOP{}'
```

These can be repeated multiple times to facilitate specifying mappings
containing different types of quotes and other special characters
which may require escaping in TOML config.

This circumvents helix-editor#133 in a way that Helix still does not recognise
keypresses by their scan-codes but still allows for the non-English
layout users to operate Helix without switching the layout which can
be especially useful for writing documentation in their native language
with Helix.
@pascalkuthe pascalkuthe added C-enhancement Category: Improvements A-keymap Area: Keymap and keybindings S-waiting-on-review Status: Awaiting review from a maintainer. labels Dec 7, 2022
@SoraTenshi
Copy link
Contributor

My 2 cents:
I don't really think the way this is configured is optimal, i would love to have a clear distinction between which row is being remapped.

Something like this:

[[editor.layout-remap-r1]]
from = '<!"№%:,.;()_+'
into = '±!@#$%^&*()_+'

[[editor.layout-remap-r2]]
from = 'йцукенгшщзхъЙЦУКЕНГШЩЗХЪ'
into = 'qwertyuiop[]QWERTYUIOP{}'

etc.

@idoric
Copy link

idoric commented Dec 15, 2022

I'm using the bépo layout (some kind of french dvorak), and where you find the y on a qwerty layout there is a dead key (which doesn't generate a character by itself) on the bépo. Am I mistaken in thinking that this PR would not solve this scenario?

Also, I don't understand what the caveat is.

@AmitDIRTYC0W
Copy link

Will this be merged any time soon? I'm waiting to switch to Helix and Workman support is the dealbreaker

Copy link

@BreakingLead BreakingLead left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested, it works great.
So will this be merged?

@soloturn
Copy link

soloturn commented Mar 15, 2023

to be honest i would prefer for the command mode to use the scancode or keyid instead of remappings. swiss german or swiss french keyboards have a dead key on the english ] position, also described a little in #133.

something in the lines of this tiny library.
https://crates.io/crates/keyboard_query

@sinefabula
Copy link
Author

@SoraTenshi I'm afraid I don't get your point on explicit row mapping. Why would you want that? I just allowed the user to split the mapping into sections for easier reading and writing; these sections do not have to be rows. I'm coming from vim here where you can't do this and you end up with a really long langmap line which is very hard to fix if there's a mistake.

@idoric I guess you're right, dead key won't generate a keystroke therefore I don't think you'd be able to remap it this way. This whole remapping thing, it's still a hack, a good one (in my opinion at least), but a hack nevertheless!

@BreakingLead glad it worked for you too! I'm using it for several moths straight and have zero problems. It's quite a simple piece of code actually.

@soloturn yeah you're right, I'd very much prefer that too. I just didn't see that coming in any near future so I felt this workaround was essential for me (and perhaps many others who write in a language other than English). I'm not sure at the moment how hard it would be to implement these scancodes. I certainly would like to try it so thanks for pointing to the library. But I guess someone may have already tried this?

@SoraTenshi
Copy link
Contributor

@SoraTenshi I'm afraid I don't get your point on explicit row mapping. Why would you want that? I just allowed the user to split the mapping into sections for easier reading and writing; these sections do not have to be rows. I'm coming from vim here where you can't do this and you end up with a really long langmap line which is very hard to fix if there's a mistake.

My idea here was to map it based around the actual, physical keyboard 'rows', so that there is not a real ambiguity.
However, this is just an idea I had.

@soloturn
Copy link

@sinefabula often only one dead key looks a challenge, though i think the PR should be merged.

for dead keys, i found another file with keylayouts and wonder if this would help specify a dead key in the from mapping? lets pick out one example, the diaresis dead key, on swiss german keyboard at the location of english right brace, ].

#define ASCII_5D	KEY_RIGHT_BRACE				// 93 ]

#ifdef LAYOUT_GERMAN_SWISS

#define DIAERESIS_BITS		0x0300
#define DEADKEY_DIAERESIS	KEY_RIGHT_BRACE
#define ISO_8859_1_EB	DIAERESIS_BITS + KEY_E			// 235 ë     e DIAERESIS  TODO: check this

links:

@soloturn
Copy link

@pascalkuthe anything missing to merge this?

@pascalkuthe
Copy link
Member

I don't have merge permissions so mostly a review from @archseer. From my side tKS We have a lot on our plate right now as we are heading towards the next release but I am sure he will get to your PR eventually.

the-mikedavis and others added 4 commits March 31, 2023 15:21
* changelog: Checkpoint 2023-01-10

commit: 927fa11

* changelog: Checkpoint 2023-02-05

commit: 9c98043

* changelog: Checkpoint 2023-03-17

commit: bd47392

* changelog: Checkpoint 2023-03-27

commit: 5323020

* Set a tentative release date for 2023-03-31

* Update CHANGELOG.md

* Mention virtual text PR in inlayed type hints feature links

* Fix description for 5097

* Rebalance features, usability improvements and fixes

* Reorganize some out-of-place changes to the proper sections

* Eliminate the LSP configurations section

This has a lot of overlap with the 'new languages' section with newly
supported LSP configurations. Smaller changes to LSP configurations
are not so common so I folded those into the 'updated languages and
queries' section.
Copy link
Member

@the-mikedavis the-mikedavis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left some notes on the changes themselves but IMO this shouldn't be merged. It's something I imagine would be really trivial to do once we can script config: I'm imagining mapping over the keymap data structure and switching the characters using a hashmap - that's similar to how you'd do this in an editor like Kakoune. That would also be more flexible so that you could choose to apply these in any mode, for example only in insert mode. Also see #5520 (comment).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like there is a problem with the merge commit - all of these changes are from the master branch and shouldn't be duplicated

/// `KeyCode::Char(c)` are translated. This method should be used
/// whenever a translation for the characters is desired, e.g. when
/// remapping a keyboard layout from non-Latin to Latin.
pub fn translate<F>(&self, f: F) -> KeyEvent
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unused and can be removed

@@ -300,6 +300,32 @@ impl Keymap {
res
}

pub fn traverse_map<F>(&self, f: F)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also unused, can be removed

Comment on lines +96 to +107
pairs
.iter()
.filter_map(|(_, to)| pairs.contains_key(to).then(|| *to))
.collect::<Vec<_>>()
.into_iter()
.for_each(|c| {
warn!(
"Key '{}' removed from layout-remap as it would overwrite Latin layout binding",
c
);
pairs.remove(&c);
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this part actually necessary? If you wanted to remap between keyboard layouts for example:

[[editor.layout-remap]]
# qwerty
from = "qwertyuiop[]asdfghjkl;'zxcvbnm,./"
# dvorak
into = "',.pyfgcrl/=aoeuidhtns-;qjkxbmwvz"

you would only rebind [ and ] since those are the only characters not duplicated between from and into. You only translate the incoming key event once so having duplication between from and into shouldn't matter - it won't cause some recursive loop of remapping.

Comment on lines +80 to +83
struct LayoutMap {
from: String,
into: String,
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm very opposed to this representation. It's a lot of indirection to have a Vec<LayoutMap> turn into a HashMap<char, char> when we could just use a TOML table. This is less verbose than a TOML table but we don't really need to optimize for concise config - I would rather have it be explicit.

@the-mikedavis the-mikedavis added S-needs-discussion Status: Needs discussion or design. and removed S-waiting-on-review Status: Awaiting review from a maintainer. labels Jan 19, 2024
@pascalkuthe
Copy link
Member

pascalkuthe commented Apr 13, 2024

closing this as this pr has gone stale. As Mike already said I don't see us going forward with something like this in core. This should instead be covered by #10389 in the future.

Thank you for contributing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-keymap Area: Keymap and keybindings C-enhancement Category: Improvements S-needs-discussion Status: Needs discussion or design.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants