Ccmmutty logo
Commutty IT
8 min read

Neovim IMEの状態をカーソルの色に反映させる

https://cdn.magicode.io/media/notebox/eae75632-512a-47a3-a776-090266fc5d87.jpeg

コード

local toggle = true -- トグル式のキーバインドを使うか否か
local external_ime_control = true -- <esc>押下の際にimeをoffにするやつがkarabinerとかで設定されているか

if vim.fn.has("mac") == 0 then
  return
end

local _ime_on = function()
  vim.cmd("highlight iCursor guibg=#cc6666")
  vim.cmd("set guicursor+=i:var25-iCursor")
  vim.g.ime_on = true
end

local _ime_off = function()
  vim.cmd("highlight iCursor guibg=#5FAFFF")
  vim.cmd("set guicursor+=i:var25-iCursor")
  vim.g.ime_on = false
end

-- 日本語
local ime_on = function()
  if not vim.g.ime_on then
    local script = [[osascript -e "tell application \"System Events\" to key code 104"]]
    vim.fn.system(script)
    _ime_on()
  end
end

-- 英語
local ime_off = function()
  if vim.g.ime_on then
    local script = [[osascript -e "tell application \"System Events\" to key code 102"]]
    vim.fn.system(script)
    _ime_off()
  end
end

local toggle_ime = function()
  if vim.g.ime_on then
    local script = [[osascript -e "tell application \"System Events\" to key code 102"]]
    vim.fn.system(script)
    _ime_off()
  else
    local script = [[osascript -e "tell application \"System Events\" to key code 104"]]
    vim.fn.system(script)
    _ime_on()
  end
end

if toggle then
  vim.api.nvim_set_keymap(
    "i",
    "<c-j>",
    "",
    { nowait = true, callback = toggle_ime, noremap = true, silent = true, desc = "toggle IME" }
    )
else
  vim.api.nvim_set_keymap(
    "i",
    "<c-k>",
    "",
    { nowait = true, callback = ime_on, noremap = true, silent = true, desc = "IME on" }
    )
  vim.api.nvim_set_keymap(
    "i",
    "<c-j>",
    "",
    { nowait = true, callback = ime_off, noremap = true, silent = true, desc = "IME off" }
    )
end

-- lmap
vim.api.nvim_set_keymap("i", "<c-^>", "", {
    nowait = true,
    callback = function()
        if vim.g.ime_on then
            ime_off()
        end
        if vim.g.lmap_on then
          _ime_off()
        else
          _lmap_on()
        end
        return vim.api.nvim_replace_termcodes("<C-^>", true, false, true)
    end,
    expr = true,
    noremap = true,
    silent = true,
    desc = "<C-^>",
})

local g = vim.api.nvim_create_augroup("my_ime", { clear = true })

-- モード切り替え時の制御
if external_ime_control then -- karabiner等で制御されている場合
  vim.api.nvim_create_autocmd("InsertLeave", {
      group = g,
      callback = function()
        _ime_off()
      end,
    })
else
  vim.api.nvim_create_autocmd("InsertLeave", {
      group = g,
      callback = function()
        ime_off()
      end,
    })
end

_ime_off()

スクリプトの高速化

AppleScriptではIMEをonにする際にラグがあります。 AppleScriptからSwiftに変更することで高速化しました。IME on 直後の押下が反映されない問題を解決しました。

変更点

@ .config/nvim/script/ime.swift
import Cocoa
import InputMethodKit

class InputSource {
    fileprivate static var inputSources: [TISInputSource] {
        let inputSourceNSArray = TISCreateInputSourceList(nil, false).takeRetainedValue() as NSArray
        return inputSourceNSArray as! [TISInputSource]
    }

    fileprivate static var selectCapableInputSources: [TISInputSource] {
        return inputSources.filter({ $0.isSelectCapable })
    }

    static func change(id: String) {
        guard let inputSource = selectCapableInputSources.filter({ $0.id == id }).first else { return }
        TISSelectInputSource(inputSource)
    }

    // 確認用
    static func print() {
        for source in inputSources {
            Swift.print("id:[\(source.id)]")
            Swift.print("localizedName:[\(source.localizedName)]")
            Swift.print("isSelectCapable:[\(source.isSelectCapable)]")
            Swift.print("isSelected:[\(source.isSelected)]")
            Swift.print("sourceLanguages:[\(source.sourceLanguages)]")
            Swift.print("--------------------")
        }
    }
}

extension TISInputSource {
    func getProperty(_ key: CFString) -> AnyObject? {
        guard let cfType = TISGetInputSourceProperty(self, key) else { return nil }
        return Unmanaged<AnyObject>.fromOpaque(cfType).takeUnretainedValue()
    }

    var id: String {
        return getProperty(kTISPropertyInputSourceID) as! String
    }

    var localizedName: String {
        return getProperty(kTISPropertyLocalizedName) as! String
    }

    var isSelectCapable: Bool {
        return getProperty(kTISPropertyInputSourceIsSelectCapable) as! Bool
    }

    var isSelected: Bool {
        return getProperty(kTISPropertyInputSourceIsSelected) as! Bool
    }

    var sourceLanguages: [String] {
        return getProperty(kTISPropertyInputSourceLanguages) as! [String]
    }
}

//  ╭──────────────────────────────────────────────────────────────────────────────╮
//  │                                  Main Loop                                   │
//  ╰──────────────────────────────────────────────────────────────────────────────╯

let arg = CommandLine.arguments
if arg.count == 2 {
    InputSource.change(id: arg[1])
} else {
    InputSource.print()
}

//  ╭──────────────────────────────────────────────────────────────────────────────╮
//  │                                  Change IME                                  │
//  ╰──────────────────────────────────────────────────────────────────────────────╯
// Ref: https://qiita.com/SolaRayLino/items/8d01eebb550d871c35cd
// Run: `swift ime.swift`
// Compile: `swiftc -emit-executable ime.swift`
// Execute: `./ime`
これをコンパイルします。
$ swiftc -emit-executable ime.swift
toggle_ime を書き換えます。
local toggle_ime = function()
    local exe = vim.fn.expand("$HOME/.config/nvim/") .. "script/ime"
    if vim.g.ime_on then
        vim.fn.system(exe .. " com.google.inputmethod.Japanese.Roman")
        _ime_off()
    else
        vim.fn.system(exe .. " com.google.inputmethod.Japanese.base")
        _ime_on()
    end
end

Discussion

コメントにはログインが必要です。