天天看点

Mac版程序员喜欢的键位 -- macos Sierra 版本 v1前言:正文

前言:

以前写过一篇《Mac版程序员喜欢的键位(使用 IDEA)》文章,写了关于在macos Sierra 版本之前,用Karabiner进行键盘映射的方法。但在macos Sierra之后,Karabiner就无法使用了,代替品Karabiner-elements功能也不是很强大。

没有办法,只能使用Hammerspoon来做键盘映射。使用Hammerspoon时,用一些简单的方法,做不到我们想要的结果。因为Hammerspoon在模拟按键时,hs.hotkey.bind方法不支持用 fn 来做修饰键的(原来的文章就是用 fn 做修饰键的。有的方法使用 ctrl + cmd + alt +shift 做 hyper键,但这样就无法使用 hyper + shift/alt/cmd 这样的组合键了)。

最后在网上找了一个脚本,在修改后之后,终于达到我们想的结果了,哈哈哈。

正文

1,使用MacBook自带键盘的设置

1,安装

Karabiner-Elements

。下载地址:Karabiner-Elements

2,修改

Karabiner-Elements

的Json文件,路径如下:

~/.config/karabiner/karabiner.json

如果你没有修改过的话,可以使用下面的脚本,把

capslock

键,修改成

fn

键。

{
    "global": {
        "check_for_updates_on_startup": true,
        "show_in_menu_bar": true,
        "show_profile_name_in_menu_bar": false
    },
    "profiles": [
        {
            "complex_modifications": {
                "rules": [
                    {
                        "manipulators": [
                            {
                                "description": "Change caps_lock to command+control+option+shift. Escape if no other key used.",
                                "from": {
                                    "key_code": "caps_lock",
                                    "modifiers": {
                                        "optional": [
                                            "any"
                                        ]
                                    }
                                },
                                "to": [
                                    {
                                        "key_code": "fn"
                                    }
                                ],
                                "to_if_alone": [
                                    {
                                        "key_code": "caps_lock",
                                        "modifiers": {
                                            "optional": [
                                                "any"
                                            ]
                                        }
                                    }
                                ],
                                "type": "basic"
                            }
                        ]
                    }
                ]
            },
            "devices": [],
            "fn_function_keys": {
                "f1": "display_brightness_decrement",
                "f10": "mute",
                "f11": "volume_decrement",
                "f12": "volume_increment",
                "f2": "display_brightness_increment",
                "f3": "mission_control",
                "f4": "launchpad",
                "f5": "illumination_decrement",
                "f6": "illumination_increment",
                "f7": "rewind",
                "f8": "play_or_pause",
                "f9": "fastforward"
            },
            "name": "Default profile",
            "selected": true,
            "simple_modifications": {},
            "virtual_hid_keyboard": {
                "caps_lock_delay_milliseconds": 0,
                "keyboard_type": "ansi"
            }
        },
        {
            "devices": [],
            "fn_function_keys": {
                "f1": "f1",
                "f10": "mute",
                "f11": "volume_decrement",
                "f12": "volume_increment",
                "f2": "display_brightness_increment",
                "f3": "mission_control",
                "f4": "launchpad",
                "f5": "illumination_decrement",
                "f6": "illumination_increment",
                "f7": "rewind",
                "f8": "play_or_pause",
                "f9": "fastforward"
            },
            "name": "New profile",
            "selected": false,
            "simple_modifications": {},
            "virtual_hid_keyboard": {
                "caps_lock_delay_milliseconds": 0,
                "keyboard_type": "ansi"
            }
        }
    ]
}
           

3,首先要安装

Hammerspoon

。下载地址:Hammerspoon

4,安装完后,在屏幕上方,有一个小锤子图标,点击图标,选择

open config

。点完后,就出现一个

init.lua

编辑画面。

5,把下面的脚本粘贴进去,再点击小锤子图标,点击

Reload Config

,脚本就生效了。

脚本如下:(脚本在原来脚本基础之上,修改了3个地方,在下面都有注释)

原来脚本地址:viKeys.lua

local module = {}

module.debugging = false -- whether to print status updates

local eventtap  = require "hs.eventtap"
local event     = eventtap.event
local inspect   = require "hs.inspect"
local watchable = require "hs.watchable"


local keyHandler = function(e)

    --+++++++++++++++ 重要:这里是写 “按下的键” 和 “要映射的键” ++++++++++++++++
    local watchFor = { j = "left", k = "down", i = "up", l = "right",
                       u = "alt_left", o = "alt_right",
                       y = "pageup", h = "pagedown"
                     }
   --+++++++++++++++ 重要:这里也是写 “按下的键” 和 “要映射的键” ++++++++++++++++
   --+++++++++++++++ 因为符号(例如 ; )无法像上面那么写,所以用下面的方式设置 ++++++++++++++++
   watchFor["p"] = "gohome"
   watchFor[":"] = "goend"
   watchFor[";"] = "go_and_select_end" -- 在按 fn + shift + ; 时,就变成 fn + :

   watchFor["m"] = "delete"
   watchFor[","] = "forwarddelete"
   watchFor["."] = "return"

   watchFor["c"] = "copy"
   watchFor["v"] = "paste"
   watchFor["x"] = "cut"
   watchFor["z"] = "undo"
   watchFor["b"] = "gotoDef" -- idea setting
   watchFor["d"] = "delLine" -- idea setting



    local actualKey = e:getCharacters(true)
    local actlKyNoCs = actualKey:lower()
    local replacement = watchFor[actualKey:lower()]
    --hs.alert(replacement)
    if replacement then
        -- hs.alert("okokokok")
        local isDown = e:getType() == event.types.keyDown
        local flags  = {}
        -- add start
        local isShiftPressed = nil
        local isAltPressed = nil
        local isCmdPressed = nil
        local isCtrlPressed = nil
        -- add end
        for k, v in pairs(e:getFlags()) do
            if v and k ~= "fn" then -- fn will be down because that's our "wrapper", so ignore it
                table.insert(flags, k)
            end
            -- add start
            if k == "shift" then
               isShiftPressed = true
            end
            if k == "alt" then
               isAltPressed = true
            end
            if k == "cmd" then
               isCmdPressed = true
            end
            if k == "ctrl" then
               isCtrlPressed = true
            end
            -- add end
        end

        --[[
            重要:下面是定义了,什么样的键组合可以进行映射,之外的键组合,都不进行映射,使用原来的键组合
        ]]
        local isPass = false
        -- fn + 单个按键的组合
        if (not isShiftPressed) and (not isCtrlPressed) and (not isCmdPressed) and (not isAltPressed)  
           and 
           (actlKyNoCs == 'i' or actlKyNoCs == 'j' or actlKyNoCs == 'k' or actlKyNoCs == 'l' 
            or actlKyNoCs == 'u' or actlKyNoCs == 'o' or actlKyNoCs == 'p' or actlKyNoCs == ';'
            or actlKyNoCs == 'y' or actlKyNoCs == 'h' 
            or actlKyNoCs == 'm' or actlKyNoCs == ',' or actlKyNoCs == '.'
            or actlKyNoCs == 'v' or actlKyNoCs == 'c' or actlKyNoCs == 'x' or actlKyNoCs == 'z'
            or actlKyNoCs == 'b' or actlKyNoCs == 'd') then
           --hs.alert("if 1")
           isPass = true
        -- fn + shift + 单个按键的组合
        elseif (isShiftPressed) and (not isCtrlPressed) and (not isCmdPressed) and (not isAltPressed)  
           and 
           (actlKyNoCs == 'i' or actlKyNoCs == 'j' or actlKyNoCs == 'k' or actlKyNoCs == 'l'
            or actlKyNoCs == 'u' or actlKyNoCs == 'o' or actlKyNoCs == 'p' or actlKyNoCs == ':') then
           --hs.alert("if 2")
           isPass = true 
        -- fn + cmd + 单个按键的组合
        elseif (not isShiftPressed) and (not isCtrlPressed) and (isCmdPressed) and (not isAltPressed)  
           and 
           (actlKyNoCs == 'u' or actlKyNoCs == 'o' or actlKyNoCs == 'i' or actlKyNoCs == 'k') then
           --hs.alert("if 2")
           isPass = true
        -- fn + ctrl + shift + 单个按键的组合 (Idea中,屏幕上下移动)
        elseif (not isShiftPressed) and (not isCtrlPressed) and (isCmdPressed) and (isAltPressed)  
           and 
           (actlKyNoCs == 'i' or actlKyNoCs == 'j' or actlKyNoCs == 'k' or actlKyNoCs == 'l') then
           --hs.alert("if 2")
           isPass = true
        -- fn + ctrl + alt + 单个按键的组合 (Idea中,复制行或块)
        elseif (not isShiftPressed) and (isCtrlPressed) and (not isCmdPressed) and (isAltPressed)  
           and 
           (actlKyNoCs == 'i' or actlKyNoCs == 'j' or actlKyNoCs == 'k' or actlKyNoCs == 'l') then
           --hs.alert("if 2")
           isPass = true
        -- fn + ctrl + cmd + 单个按键的组合 (Idea中,行或块上下移动)
        elseif (not isShiftPressed) and (isCtrlPressed) and (isCmdPressed) and (not isAltPressed)  
           and 
           (actlKyNoCs == 'i' or actlKyNoCs == 'j' or actlKyNoCs == 'k' or actlKyNoCs == 'l') then
           --hs.alert("if 2")
           isPass = true

        end


        if not isPass then
            return false
        end

        -- add end


        if module.debugging then print("viKeys: " .. replacement, inspect(flags), isDown) end




        --+++++++++++++++ 重要 ++++++++++++++++
        --[[
            上面定义了“按下的键” 和 “要映射的键”,但无法定义组合键(例如:alt + j 想要映射成 alt + righ),
            所以在上面定义了一些符号,在这里进行解析
        ]]
        if replacement == 'alt_left' then
            table.insert(flags, 'alt')
            replacement = 'left'
        elseif  replacement == 'alt_right' then
            table.insert(flags, 'alt')
            replacement = 'right'
        elseif  replacement == 'gohome' then
            table.insert(flags, 'cmd')
            replacement = 'left'
        elseif  replacement == 'goend' then
            table.insert(flags, 'cmd')
            replacement = 'right'
        elseif  replacement == 'go_and_select_end' then
            table.insert(flags, 'cmd')
            replacement = 'right'
        elseif  replacement == 'copy' then
            table.insert(flags, 'cmd')
            replacement = 'c'
        elseif  replacement == 'paste' then
            table.insert(flags, 'cmd')
            replacement = 'v'
        elseif  replacement == 'cut' then
            table.insert(flags, 'cmd')
            replacement = 'x'
        elseif  replacement == 'undo' then
            table.insert(flags, 'cmd')
            replacement = 'z'
        elseif  replacement == 'gotoDef' then
            table.insert(flags, 'cmd')
            replacement = 'b'
        elseif  replacement == 'delLine' then
            table.insert(flags, 'cmd')
            replacement = 'd'

        end
        





        local replacementEvent = event.newKeyEvent(flags, replacement, isDown)
        if isDown then
            -- allow for auto-repeat
            replacementEvent:setProperty(event.properties.keyboardEventAutorepeat, e:getProperty(event.properties.keyboardEventAutorepeat))
        end
        return true, { replacementEvent }
    else
        return false -- do nothing to the event, just pass it along
    end
end

local modifierHandler = function(e)
    local flags = e:getFlags()
    local onlyFNPressed = false
    -- add start
    local isShiftPressed = false
    local isAltPressed = false
    local isCmdPressed = false
    local isCtrlPressed = false
    local modifierCount = 0
    -- add end

    for k, v in pairs(flags) do

        --+++++++++++++++++  重要  +++++++++++++++++++++--
        --[[ 原来是下面被注释掉的语句,把 k == "fn" 变成了 k ~= "shift"。
             原因是,这个语句是判断只有 fn 被按下后,才被处理。如果有其它修饰键,同时被按下的话,不一定成功。
             最开始把这个语句删除了,就好用了,但出现另外一个问题,按住 shfit + 字母键后,无法打出大写字母,
             于是把这个变成了 k ~= "shift",也就是说,这个事件不会响应 修饰键只是Shift的按键组合 ]]
        --onlyFNPressed = v and k == "fn"
        onlyFNPressed = v

        --[[ 原代码修改后有个问题:如果按Shift + key的话,就无法打出大写字符。
             修改方法:在循环里统计修饰键的个数,如果 修饰键 = 1 并且 修饰键= 单个Shift/cmd/ctrl/alt 的话,
                      就使用原来的按键组合,不进行映射。
        ]]
        -- add start
        if v and k == "shift" then
            isShiftPressed = true
        elseif v and k == "alt" then
            isAltPressed = true
        elseif v and k == "cmd" then
            isCmdPressed = true
        elseif v and k == "ctrl" then
            isCtrlPressed = true
        end


        modifierCount = modifierCount + 1
        -- add end

        if not onlyFNPressed then break end
    end

     --[[ 
          如果 修饰键 = 1 并且 修饰键=Shift 的话,在下面就跳事件。
     ]]
     -- add start

     if (isShiftPressed or isAltPressed  or isCmdPressed or isCtrlPressed) 
        and modifierCount == 1 then

        if isAltPressed then
            -- hs.alert("alt is pressed")
            --hs.hotkey.bind({ 'alt' }, 'b', function() smartLaunchOrFocus({ 'Safari' }) end) 
        end
         return
     end

     -- add end

    -- you must tap and hold fn by itself to turn this on
    if onlyFNPressed and not module.keyListener then
        if module.debugging then print("viKeys: keyhandler on") end
        module.keyListener = eventtap.new({ event.types.keyDown, event.types.keyUp }, keyHandler):start()
    -- however, adding additional modifiers afterwards is ok... its only when fn isn't down that we switch back off
    elseif not flags.fn and module.keyListener then
        if module.debugging then print("viKeys: keyhandler off") end
        module.keyListener:stop()
        module.keyListener = nil
    end
    return false
end

module.watchables = watchable.new("viKeys", true)

module.modifierListener = eventtap.new({ event.types.flagsChanged }, modifierHandler)

module.start = function()
    module.watchables.enabled = true
    module.modifierListener:start()
end

module.stop = function()
    if module.keyListener then
        module.keyListener:stop()
        module.keyListener = nil
    end
    module.modifierListener:stop()
    module.watchables.enabled = false
end

module.toggle = function()
    if module.watchable.enabled then
        module.stop()
    else
        module.start()
    end
end

module.watchExternalToggle = watchable.watch("viKeys.enabled", function(w, p, k, o, n)
    if not o and n then
        module.start()
    elseif o and not n then
        module.stop()
    end
end)

module.start() -- autostart

return module
           

2,使用外接键盘

1,打开

Karabiner-Elements

Preferences...

,然后选择

Profiles

选项卡,再点击下面的

Add Profile

新建一个

Profile

,我们命名为

PC-87

Mac版程序员喜欢的键位 -- macos Sierra 版本 v1前言:正文

2,修改

Command

Option

键盘的映射。选择新建的

PC-87

Profile

,然后选择第一个选项卡

Simple Modifications

,然后分别映射左右的

Command

Option

键。修改方式如下:

Mac版程序员喜欢的键位 -- macos Sierra 版本 v1前言:正文

3,修改

karabiner.json

文件(路径看上面),修改后内容如下:

(下面的修改内容是包括了上面的

Default Profile

的内容。再有,如果新的

Profile

命名不一样的话,注意修改一下。修改方法是,找到下面文件中的

PC-87

,然后修改。)

{
    "global": {
        "check_for_updates_on_startup": true,
        "show_in_menu_bar": true,
        "show_profile_name_in_menu_bar": false
    },
    "profiles": [
        {
            "complex_modifications": {
                "rules": [
                    {
                        "manipulators": [
                            {
                                "description": "Change caps_lock to command+control+option+shift. Escape if no other key used.",
                                "from": {
                                    "key_code": "caps_lock",
                                    "modifiers": {
                                        "optional": [
                                            "any"
                                        ]
                                    }
                                },
                                "to": [
                                    {
                                        "key_code": "fn"
                                    }
                                ],
                                "to_if_alone": [
                                    {
                                        "key_code": "caps_lock",
                                        "modifiers": {
                                            "optional": [
                                                "any"
                                            ]
                                        }
                                    }
                                ],
                                "type": "basic"
                            }
                        ]
                    }
                ]
            },
            "devices": [],
            "fn_function_keys": {
                "f1": "display_brightness_decrement",
                "f10": "mute",
                "f11": "volume_decrement",
                "f12": "volume_increment",
                "f2": "display_brightness_increment",
                "f3": "mission_control",
                "f4": "launchpad",
                "f5": "illumination_decrement",
                "f6": "illumination_increment",
                "f7": "rewind",
                "f8": "play_or_pause",
                "f9": "fastforward"
            },
            "name": "Default profile",
            "selected": false,
            "simple_modifications": {},
            "virtual_hid_keyboard": {
                "caps_lock_delay_milliseconds": 0,
                "keyboard_type": "ansi"
            }
        },
        {
            "complex_modifications": {
                "rules": [
                    {
                        "manipulators": [
                            {
                                "description": "Change caps_lock to command+control+option+shift. Escape if no other key used.",
                                "from": {
                                    "key_code": "caps_lock",
                                    "modifiers": {
                                        "optional": [
                                            "any"
                                        ]
                                    }
                                },
                                "to": [
                                    {
                                        "key_code": "fn"
                                    }
                                ],
                                "to_if_alone": [
                                    {
                                        "key_code": "caps_lock",
                                        "modifiers": {
                                            "optional": [
                                                "any"
                                            ]
                                        }
                                    }
                                ],
                                "type": "basic"
                            }
                        ]
                    }
                ]
            },
            "devices": [],
            "fn_function_keys": {
                "f1": "f1",
                "f10": "mute",
                "f11": "volume_decrement",
                "f12": "volume_increment",
                "f2": "display_brightness_increment",
                "f3": "mission_control",
                "f4": "launchpad",
                "f5": "illumination_decrement",
                "f6": "illumination_increment",
                "f7": "rewind",
                "f8": "play_or_pause",
                "f9": "fastforward"
            },
            "name": "PC-87",
            "selected": true,
            "simple_modifications": {
                "left_command": "left_option",
                "left_option": "left_command",
                "right_command": "right_option",
                "right_option": "right_command"
            },
            "virtual_hid_keyboard": {
                "caps_lock_delay_milliseconds": 0,
                "keyboard_type": "ansi"
            }
        }
    ]
}
           

##问题点

1,在使用内置键盘时,使用 shift + F1~F12 的组合键时,不起作用。但使用外接键盘没有问题。

参考资料:

  • 简单的把 hjkl 变成 上下左右的方法:这个方法是用 ctrl 键做修饰键,但如果改成 Fn 键的话,就不行了。Fn 键的修改比较复杂。
  • 把FN变成Hyper键的方法:可以看看,使用Karabiner-element
  • Hammerspoon的一些例子

继续阅读