/macim.el

Primary LanguageEmacs LispGNU General Public License v3.0GPL-3.0

macim.el

Introduction

This package is similar to emacs-smart-input-source, but it uses dynamic module to switch input source.

Installation

Requirements

  • macOS 10.15 or later
  • Emacs 29.1 or later, built with dynamic module support (use --with-modules during compilation)

Build dynamic module

Pre-built (recommendation)

If you enable macim-mode and the module cannot be found, it will prompt whether to automatically download it from GitHub. Or you can manually retrieve the pre-built module from the releases section and place the dylib file in the emacs-macos-tokenizer-lib-path (by default, it is located at modules/libMacIM.dylib within your personal configuration folder, normally ~/.emacs.d/modules/libMacIM.dylib).

Current version of the dynamic module is v0.0.1, make sure you have updated to latest module.

Manually build

  • Install Xcode.
  • Build the module using macim-compile-module, which compiles and copies the module to macim-lib-path.

If you enconter the folloing error:

No such module “PackageDescription”

run the following command and try again:

sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer

Install package

Install with straight and use-package:

(use-package macim
  :straight (:host github :repo "roife/emt"
                   :files ("*.el" "module/*" "module"))
  :hook (after-init . emt-mode))

Configurations

For simplicity, Macim does not come with most customizations preset. You can use the configurations in this section as presets.

If you are using use-package, you can put the following configurations into :config.

context-mode related

Moew

Select ascii input source when exiting meow-insert-mode.

(add-hook 'meow-insert-exit-hook #'macim-select-ascii)

Detect by predicates when entering meow-insert-mode.

(add-hook 'meow-insert-enter-hook #'macim-context-switch)

Detect input source after selecting window

(advice-add 'select-window :after #'(lambda (&rest _) (macim-context-switch)))

Disable predicates in certain modes for better performance

Here takes pdf-view-mode as an example.

(defun +macim-context-ignore-modes ()
  (when (derived-mode-p 'pdf-view-mode)
    'ascii))

(add-to-list 'macim-context-early-predicates #'+macim-context-ignore-modes)

inline-mode related

Trim excess spaces on both sides on deactivation

(setq +macim-chinese-punc-chars (mapcar #'string-to-char macim--chinese-punc-list))

(defun +macim-remove-head-space-after-cc-punc (_)
  (when (or (memq (char-before) +macim-chinese-punc-chars)
            (bolp))
    (delete-char 1)))
(setq macim-inline-head-handler #'+macim-remove-head-space-after-cc-punc)

(defun +macim-remove-tail-space-before-cc-punc (tighten-back-to)
  (when (> (point) tighten-back-to)
    (backward-delete-char (1- (- (point) tighten-back-to))))
  (when (and (eq (char-before) ? )
             (memq (char-after) +macim-chinese-punc-chars))
    (backward-delete-char 1)))
(setq macim-inline-tail-handler #'+macim-remove-tail-space-before-cc-punc)

Before inserting Chinese punctuation, delete extra spaces introduced by inline mode

(defun +macim-line-set-last-space-pos ()
  (when (eq (char-before) ?\s)
    (setq +macim-inline-english-last-space-pos (point))))
(add-hook 'macim-inline-deactivated-hook #'+macim-line-set-last-space-pos)

(defun +macim-inline-remove-redundant-space ()
  (when (eq +macim-inline-english-last-space-pos (1- (point)))
    (when (and (memq (char-before) +macim-chinese-punc-chars)
               (eq (char-before (1- (point))) ?\s))
      (save-excursion
        (backward-char 2)
        (delete-char 1)
        (setq-local +macim-inline-english-last-space-pos nil)))
    (remove-hook 'post-self-insert-hook #'+macim-inline-remove-redundant-space t))
  )

(defun +macim-inline-add-post-self-insert-hook ()
  (add-hook 'post-self-insert-hook #'+macim-inline-remove-redundant-space nil t))

(add-hook 'macim-inline-deactivated-hook #'+macim-inline-add-post-self-insert-hook)

Customizations

Face

  • macim-inline-face: Face of the overlay for inline-mode region

Variables

  • macim-lib-path: The path to the directory of dynamic library for macim.
  • macim-ascii: The ASCII input source (i.e. English input source). Default: com.apple.keylayout.ABC.
  • macim-other: The other input source (e.g. Chinese input source). Default: com.apple.inputmethod.SCIM.Shuangpin

context-mode related

Each predicate for context-mode should return on the of following values:

  • nil: left the determination to later predicates
  • ascii: switch to ASCII input source
  • other: switch to other input source

Once any of the predicates returns a non-nil results, the rest of predicates are not evaluated.

  • macim-context-early-predicates: Predicate to detect the context. It is called before computations of macim--back-detect-chars and macim--fore-detect-chars. So predicates that do not need context can be put here.
  • macim-context-predicates: Predicate to detect the context. Each predicate have two arguments: back-detect and fore-detect.

The back-detect is a struct that contains the following fields:

  • to: The position of the first non-blank char before the current position in current line
  • char: The first non-blank char before the current position in current line
  • cross-line-to: Same as `to’, but cross lines
  • cross-line-char: Same as `char’, but cross lines

Similarly, the fore-detect is a struct that contains four fields which are the same as back-detect, but for the positions and chars after the current position.

inline-mode related

  • macim-inline-head-handler: Function to delete head spaces. The cursor will be moved to the beginning of the inline region, and the function will be called with the end position of the leading whitespaces in region.
  • macim-inline-tail-handler: Function to delete tail spaces. The cursor will be moved to the end of the inline region, and the function will be called with the start position of the trailing whitespaces in region.
  • macim-inline-activated-hook, macim-inline-deactivated-hook: Hook run when inline mode is activated/deactivated.

Acknowledgements

This package is inspired by emacs-smart-input-source which is a awesome package for input method switching.

The dynamic module uses emacs-swift-module, which provides an interface for writing Emacs dynamic modules in Swift.