├── .github ├── dependabot.yml └── workflows │ └── ci.yaml ├── .gitignore ├── README.md ├── fix-input.el ├── flake.lock └── flake.nix /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | types: 8 | - opened 9 | - synchronize 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: cachix/install-nix-action@v31 16 | - run: nix build 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *-autoloads.el 2 | *.elc 3 | *~ 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fix Input 2 | 3 | [![License GPL 3](https://img.shields.io/badge/license-GPL_3-green.svg)](http://www.gnu.org/licenses/gpl-3.0.txt) 4 | [![MELPA](https://melpa.org/packages/fix-input-badge.svg)](https://melpa.org/#/fix-input) 5 | [![CI](https://github.com/mrkkrp/fix-input/actions/workflows/ci.yaml/badge.svg)](https://github.com/mrkkrp/fix-input/actions/workflows/ci.yaml) 6 | 7 | Let's suppose you have switched to an alternative keyboard layout. Chances 8 | are you're going to use that layout everywhere, not only in Emacs, so you 9 | set it up on the OS level or maybe you even get a special keyboard that uses 10 | that layout. Now suppose that you need to use an input method in Emacs. The 11 | nightmare begins: the input methods in Emacs translate Latin characters 12 | assuming the traditional QWERTY layout. With an alternative keyboard layout, 13 | the input methods do not work anymore. 14 | 15 | One solution is to define a new custom input method and call it for example 16 | `dvorak-russian`. But that is not a general solution to the problem—we want 17 | to be able to make any existing input method work with any Latin layout on 18 | the OS level. This package generates input methods knowing the input method 19 | that corresponds to the layout on the OS level and the input method you want 20 | to fix. 21 | 22 | ## Installation 23 | 24 | The package is available via MELPA, so you can just type `M-x 25 | package-install RET fix-input RET`. 26 | 27 | If you would like to install the package manually, download or clone it and 28 | put on Emacs' `load-path`. Then you can require it in your init file like 29 | this: 30 | 31 | ```emacs-lisp 32 | (require 'fix-input) 33 | ``` 34 | 35 | ## Usage 36 | 37 | In your configuration you need to generate a new “corrected” input method, 38 | for example: 39 | 40 | ```emacs-lisp 41 | (fix-input "english-dvorak" ;; matches alternative layout 42 | "russian-computer" ;; works with QWERTY 43 | "dvorak-russian") ;; name of new input method that preserves 44 | ;; the same layout with Dvorak 45 | ``` 46 | 47 | Here the new input method is named `"dvorak-russian"`. With Dvorak layout, 48 | this will let users to use the familiar layout when they wish to type 49 | Russian. 50 | 51 | Once generated, the layout can be used as usual. 52 | 53 | ## License 54 | 55 | Copyright © 2016–present Mark Karpov 56 | 57 | Distributed under GNU GPL, version 3. 58 | -------------------------------------------------------------------------------- /fix-input.el: -------------------------------------------------------------------------------- 1 | ;;; fix-input.el --- Make input methods play nicely with alternative layouts -*- lexical-binding: t; -*- 2 | ;; 3 | ;; Copyright © 2016–present Mark Karpov 4 | ;; 5 | ;; Author: Mark Karpov 6 | ;; URL: https://github.com/mrkkrp/fix-input 7 | ;; Version: 0.1.1 8 | ;; Package-Requires: ((emacs "24.4")) 9 | ;; Keywords: convenience input 10 | ;; 11 | ;; This file is not part of GNU Emacs. 12 | ;; 13 | ;; This program is free software: you can redistribute it and/or modify it 14 | ;; under the terms of the GNU General Public License as published by the 15 | ;; Free Software Foundation, either version 3 of the License, or (at your 16 | ;; option) any later version. 17 | ;; 18 | ;; This program is distributed in the hope that it will be useful, but 19 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of 20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 21 | ;; Public License for more details. 22 | ;; 23 | ;; You should have received a copy of the GNU General Public License along 24 | ;; with this program. If not, see . 25 | 26 | ;;; Commentary: 27 | 28 | ;; Let's suppose you have switched to an alternative keyboard layout. 29 | ;; Chances are you're going to use that layout everywhere, not only in 30 | ;; Emacs, so you set it up on the OS level or maybe you even get a special 31 | ;; keyboard that uses that layout. Now suppose that you need to use an 32 | ;; input method in Emacs. The nightmare begins: the input methods in Emacs 33 | ;; translate Latin characters assuming the traditional QWERTY layout. With 34 | ;; an alternative keyboard layout, the input methods do not work anymore. 35 | ;; 36 | ;; One solution is to define a new custom input method and call it for 37 | ;; example `dvorak-russian'. But that is not a general solution to the 38 | ;; problem—we want to be able to make any existing input method work with 39 | ;; any Latin layout on the OS level. This package generates input methods 40 | ;; knowing the input method that corresponds to the layout on the OS level 41 | ;; and the input method you want to fix. 42 | 43 | ;;; Code: 44 | 45 | (require 'cl-lib) 46 | (require 'quail) 47 | 48 | ;;;###autoload 49 | (defun fix-input (base-method old-method new-method) 50 | "Adjust an input method to an alternative layout on the OS level. 51 | 52 | In fact, an entirely new input method is generated. The 53 | BASE-METHOD describes the new alternative layout on OS level. 54 | The OLD-METHOD will be copied as NEW-METHOD so the layout in 55 | which the keys are laid on the keyboard when the OLD-METHOD is 56 | used with QWERTY will be the same when the NEW-METHOD is used 57 | with that new alternative layout. 58 | 59 | BASE-METHOD, OLD-METHOD, and NEW-METHOD are strings—names of 60 | input methods, they all must be different. 61 | 62 | This function uses Quail (and assumes that all input methods are 63 | defined with it), but it does not select the new package." 64 | (when (or (string= base-method old-method) 65 | (string= base-method new-method) 66 | (string= new-method old-method)) 67 | (error "All input methods must be different")) 68 | (fix-input--load-libs base-method) 69 | (fix-input--load-libs old-method) 70 | (let* ((base-map (nth 2 (quail-package base-method))) 71 | (old (quail-package old-method)) 72 | (old-title (nth 1 old)) 73 | (old-map (nth 2 old)) 74 | (old-stuff (nthcdr 3 old)) 75 | (new-map 76 | (mapcar 77 | (lambda (item) 78 | (when item 79 | (cl-destructuring-bind (ch . val) item 80 | ;; NOTE The approach may be brittle, since it does not take 81 | ;; into account all possible formats of the translation map 82 | ;; (described in the docs for `quile-map-p'), only for the 83 | ;; format I have encountered in practice with input methods 84 | ;; that are of interest for me. If this does not work for 85 | ;; you, open an issue on GitHub issue tracker of the 86 | ;; project (or better yet open a PR if you can fix it 87 | ;; yourself). 88 | (cons 89 | (or (elt (elt (cadr (assoc ch base-map)) 0) 0) ch) 90 | (cl-copy-list val))))) 91 | old-map)) 92 | (oldi (assoc old-method input-method-alist))) 93 | (quail-add-package 94 | (append 95 | (list new-method old-title new-map) 96 | (cl-copy-list old-stuff))) 97 | (let ((slot (assoc new-method input-method-alist)) 98 | (val (cl-copy-list (cdr oldi)))) 99 | (if slot 100 | (setcdr slot val) 101 | (push (cons new-method val) 102 | input-method-alist)))) 103 | nil) 104 | 105 | (defun fix-input--load-libs (input-method) 106 | "Load the libraries for the specified INPUT-METHOD, but do not activate it. 107 | 108 | If the INPUT-METHOD is not defined, signal an error. Return the 109 | list of libraries loaded." 110 | (let ((slot (assoc input-method input-method-alist))) 111 | (unless slot 112 | (error "No such input method: ‘%s’" input-method)) 113 | (mapc 114 | (lambda (library) 115 | (unless (load library t) 116 | (error "Having trouble loading ‘%s’" library))) 117 | (nthcdr 5 slot)))) 118 | 119 | (provide 'fix-input) 120 | 121 | ;;; fix-input.el ends here 122 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "emacs-package-flake": { 4 | "inputs": { 5 | "flake-utils": "flake-utils", 6 | "nixpkgs": "nixpkgs" 7 | }, 8 | "locked": { 9 | "lastModified": 1686064298, 10 | "narHash": "sha256-AmhZ5UPCdEyUuYCKuNuga59YDNJmyyjImYc/J5wo4vE=", 11 | "owner": "mrkkrp", 12 | "repo": "emacs-package-flake", 13 | "rev": "ffeea4f1aa7d32eb09e53772e183f73ebda72cfd", 14 | "type": "github" 15 | }, 16 | "original": { 17 | "owner": "mrkkrp", 18 | "repo": "emacs-package-flake", 19 | "type": "github" 20 | } 21 | }, 22 | "flake-utils": { 23 | "inputs": { 24 | "systems": "systems" 25 | }, 26 | "locked": { 27 | "lastModified": 1685518550, 28 | "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=", 29 | "owner": "numtide", 30 | "repo": "flake-utils", 31 | "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef", 32 | "type": "github" 33 | }, 34 | "original": { 35 | "owner": "numtide", 36 | "repo": "flake-utils", 37 | "type": "github" 38 | } 39 | }, 40 | "nixpkgs": { 41 | "locked": { 42 | "lastModified": 1685931219, 43 | "narHash": "sha256-8EWeOZ6LKQfgAjB/USffUSELPRjw88A+xTcXnOUvO5M=", 44 | "owner": "NixOS", 45 | "repo": "nixpkgs", 46 | "rev": "7409480d5c8584a1a83c422530419efe4afb0d19", 47 | "type": "github" 48 | }, 49 | "original": { 50 | "id": "nixpkgs", 51 | "ref": "nixos-unstable", 52 | "type": "indirect" 53 | } 54 | }, 55 | "root": { 56 | "inputs": { 57 | "emacs-package-flake": "emacs-package-flake" 58 | } 59 | }, 60 | "systems": { 61 | "locked": { 62 | "lastModified": 1681028828, 63 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 64 | "owner": "nix-systems", 65 | "repo": "default", 66 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 67 | "type": "github" 68 | }, 69 | "original": { 70 | "owner": "nix-systems", 71 | "repo": "default", 72 | "type": "github" 73 | } 74 | } 75 | }, 76 | "root": "root", 77 | "version": 7 78 | } 79 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | emacs-package-flake.url = "github:mrkkrp/emacs-package-flake"; 4 | }; 5 | outputs = { self, emacs-package-flake }: 6 | emacs-package-flake.lib.mkOutputs { 7 | name = "fix-input"; 8 | srcDir = ./.; 9 | }; 10 | } 11 | --------------------------------------------------------------------------------