macos 神器之 hammerspoon 篇

About Hammerspoon

Hammerspoon 是 Mac OS 上强大自动化管理工具,Hammerspoon 的核心只是操作系统和 Lua 脚本引擎之间的桥梁。Hammerspoon 强大之处在于提供了一组扩展,这些扩展向用户提供了特定的系统功能,有了这些扩展,我们可以编写 Lua 脚本来控制 Mac OS X 系统的许多行为。

安装

 brew cask install hammerspoon

第一次打开需要授予系统辅助功能权限 Enable Accessibility,如下 2 图。

配置文件和 Spoon 插件结构

配置文件为 ~/.hammerspoon/init.lua,插件安装目录 ~/.hammerspoon/Spoons

功能配置

自定义 hyper 键

这个如果配合Karabiner Element.app来定义是最好的,也可以直接用Hammerspoon自定义Hyper键如下,即同时按下Ctrl + Option + Command三个键:

local hyper = {'ctrl', 'alt', 'cmd'}

窗口管理

对窗口大小的位置进行配置管理,达到Spectacle.app软件类似的效果,快捷键说明:

  1. Hyper + 1: 当前窗口显示到屏幕左上方 1/4 位置;
  2. Hyper + 2: 当前窗口显示到屏幕右上方 1/4 位置;
  3. Hyper + 3: 当前窗口显示到屏幕左下方 1/4 位置;
  4. Hyper + 4: 当前窗口显示到屏幕右下方 1/4 位置;
  5. Hyper + left: 当前窗口显示到屏幕左方 1/2 位置;
  6. Hyper + right: 当前窗口显示到屏幕右方 1/2 位置;
  7. Hyper + up: 当前窗口显示到屏幕上方 1/2 位置;
  8. Hyper + down: 当前窗口显示到屏幕下方 1/2 位置;
  9. Hyper + 5: 当前窗口最大化(不是全屏)。
local function baseMove(x, y, w, h)    return function()        local win = hs.window.focusedWindow()        local f = win:frame()        local screen = win:screen()        local max = screen:frame()        f.x = max.w * x + max.x        f.y = max.h * y        f.w = max.w * w        f.h = max.h * h        win:setFrame(f, 0)    endendhs.hotkey.bind(hyper, 'Left', baseMove(0, 0, 0.5, 1))hs.hotkey.bind(hyper, 'Right', baseMove(0.5, 0, 0.5, 1))hs.hotkey.bind(hyper, 'Down', baseMove(0, 0.5, 1, 0.5))hs.hotkey.bind(hyper, 'Up', baseMove(0, 0, 1, 0.5))hs.hotkey.bind(hyper, '1', baseMove(0, 0, 0.5, 0.5))hs.hotkey.bind(hyper, '2', baseMove(0.5, 0, 0.5, 0.5))hs.hotkey.bind(hyper, '3', baseMove(0, 0.5, 0.5, 0.5))hs.hotkey.bind(hyper, '4', baseMove(0.5, 0.5, 0.5, 0.5))hs.hotkey.bind(hyper, '5', hs.grid.maximizeWindow)

应用快速切换

使用快捷键一步直达想要操作的应用,避免使用Command + Tab切换:

  1. Alt + a: 打开Launchpad
  2. Alt + s: 打开System Preferemces
  3. Alt + d: 打开Intellij IDEA CE
  4. Alt + e: 打开Evernote
  5. Alt + f: 打开finder
  6. Alt + g: 打开Google Chrome
  7. Alt + w: 打开Mission Control
  8. Alt + x: 打开iTerm
hs.hotkey.bind({'alt'}, "a", function() hs.application.launchOrFocus('Launchpad') end)hs.hotkey.bind({'alt'}, "s", function() hs.application.launchOrFocus('System Preferences') end)hs.hotkey.bind({'alt'}, "d", function() hs.application.launchOrFocus('Intellij IDEA CE') end)hs.hotkey.bind({'alt'}, "e", function() hs.application.launchOrFocus('Evernote') end)hs.hotkey.bind({'alt'}, "f", function() hs.application.launchOrFocus('finder') end)hs.hotkey.bind({'alt'}, "g", function() hs.application.launchOrFocus('Google Chrome') end)hs.hotkey.bind({'alt'}, "w", function() hs.application.launchOrFocus('Mission Control') end)hs.hotkey.bind({'alt'}, "x", function() hs.application.launchOrFocus('iTerm') end)

屏幕锁定

可以安装这 2 款好看的屏保:

 brew cask install fliqlo clocksaver xattr -rc "$HOME/Library/Screen Savers"
hs.hotkey.bind(hyper, 'L', function()    hs.caffeinate.startScreensaver()end)

方向键映射

映射Alt + JK,只映射了最常用到的上下方向键,在命令行和 vim 中与其他快捷键冲突较少:

  1. Alt-J 映射为向下方向键 down
  2. Alt-K 映射为向上方向键 up
local function pressFn(mods, key)    if key == nil then        key = mods        mods = {}    end    return function() hs.eventtap.keyStroke(mods, key, 80) endendlocal function remap(mods, key, pressFn)    hs.hotkey.bind(mods, key, pressFn, nil, pressFn)endremap({'alt'}, 'j', pressFn('down'))remap({'alt'}, 'k', pressFn('up'))

网上有配置是映射Ctrl + HJKL,如 https://github.com/kkamdooong/hammerspoon-control-hjkl-to-arrow,这篇文章里对方向键做了更多的映射,可以参考,不过这些与我在命令行和 vim 中的有些快捷键冲突了,所以并不合适我。

remap({'ctrl'}, 'h', pressFn('left'))remap({'ctrl'}, 'j', pressFn('down'))remap({'ctrl'}, 'k', pressFn('up'))remap({'ctrl'}, 'l', pressFn('right'))remap({'ctrl', 'shift'}, 'h', pressFn({'shift'}, 'left'))remap({'ctrl', 'shift'}, 'j', pressFn({'shift'}, 'down'))remap({'ctrl', 'shift'}, 'k', pressFn({'shift'}, 'up'))remap({'ctrl', 'shift'}, 'l', pressFn({'shift'}, 'right'))remap({'ctrl', 'cmd'}, 'h', pressFn({'cmd'}, 'left'))remap({'ctrl', 'cmd'}, 'j', pressFn({'cmd'}, 'down'))remap({'ctrl', 'cmd'}, 'k', pressFn({'cmd'}, 'up'))remap({'ctrl', 'cmd'}, 'l', pressFn({'cmd'}, 'right'))remap({'ctrl', 'alt'}, 'h', pressFn({'alt'}, 'left'))remap({'ctrl', 'alt'}, 'j', pressFn({'alt'}, 'down'))remap({'ctrl', 'alt'}, 'k', pressFn({'alt'}, 'up'))remap({'ctrl', 'alt'}, 'l', pressFn({'alt'}, 'right'))remap({'ctrl', 'shift', 'cmd'}, 'h', pressFn({'shift', 'cmd'}, 'left'))remap({'ctrl', 'shift', 'cmd'}, 'j', pressFn({'shift', 'cmd'}, 'down'))remap({'ctrl', 'shift', 'cmd'}, 'k', pressFn({'shift', 'cmd'}, 'up'))remap({'ctrl', 'shift', 'cmd'}, 'l', pressFn({'shift', 'cmd'}, 'right'))remap({'ctrl', 'shift', 'alt'}, 'h', pressFn({'shift', 'alt'}, 'left'))remap({'ctrl', 'shift', 'alt'}, 'j', pressFn({'shift', 'alt'}, 'down'))remap({'ctrl', 'shift', 'alt'}, 'k', pressFn({'shift', 'alt'}, 'up'))remap({'ctrl', 'shift', 'alt'}, 'l', pressFn({'shift', 'alt'}, 'right'))remap({'ctrl', 'cmd', 'alt'}, 'h', pressFn({'cmd', 'alt'}, 'left'))remap({'ctrl', 'cmd', 'alt'}, 'j', pressFn({'cmd', 'alt'}, 'down'))remap({'ctrl', 'cmd', 'alt'}, 'k', pressFn({'cmd', 'alt'}, 'up'))remap({'ctrl', 'cmd', 'alt'}, 'l', pressFn({'cmd', 'alt'}, 'right'))remap({'ctrl', 'cmd', 'alt', 'shift'}, 'h', pressFn({'cmd', 'alt', 'shift'}, 'left'))remap({'ctrl', 'cmd', 'alt', 'shift'}, 'j', pressFn({'cmd', 'alt', 'shift'}, 'down'))remap({'ctrl', 'cmd', 'alt', 'shift'}, 'k', pressFn({'cmd', 'alt', 'shift'}, 'up'))remap({'ctrl', 'cmd', 'alt', 'shift'}, 'l', pressFn({'cmd', 'alt', 'shift'}, 'right'))

历史剪切板

TextClipboardHistory,剪贴板历史,仅支持文本内容,Download 后解压出来的文件夹加到~/.hammerspoon/Spoons目录下,然后加入以下配置到~/.hammerspoon/init.lua文件中,重启应用即可:

hs.loadSpoon('TextClipboardHistory')spoon.TextClipboardHistory.show_in_menubar = falsespoon.TextClipboardHistory.paste_on_select = truespoon.TextClipboardHistory.honor_ignoredidentifiers = truespoon.TextClipboardHistory:start()hs.hotkey.bind(hyper, "V", function()    spoon.TextClipboardHistory:toggleClipboard()    mode:exit()end)

不过 Mac OS 上其他一些剪贴板管理工具更加强大好用,比如收费的 ipastepaste 都很不错,开源免费的 clipyflycut 也是极好用。

菜单栏实时显示网速

SpeedMenu 在 menu bar 上显示网速:

hs.loadSpoon('SpeedMenu')spoon.SpeedMenu:start()

Hammerspoon reload 配置和显示 Hammerspoon console

修改完配置文件,按Ctrl + Option + Command + R重新加载配置,这个功能有一个官方的ReloadConfiguration 插件

hs.hotkey.bind(hyper, "R", function()    hs.reload()    hs.notify.new({title="Hammerspoon config reloaded", informativeText="Manually via keyboard shortcut"}):send()end)hs.hotkey.bind(hyper, "C", function()    hs.application.get("Hammerspoon"):selectMenuItem("Console...")    hs.application.launchOrFocus("Hammerspoon")end)

如上配置之后,启动Hammerspoon后,可以使用快捷键Ctrl + Option + Command + C调出控制台,如下所示:

init.lua 完整配置

个人使用的完整配置~/.hammerspoon/init.lua,没有包括历史剪贴板和显示网速等插件功能,更多插件可以查看:http://www.hammerspoon.org/Spoons/

local hyper = {'ctrl', 'alt', 'cmd'}hs.hotkey.bind(hyper, 'L', function()    hs.caffeinate.startScreensaver()end)hs.hotkey.bind(hyper, "R", function()    hs.reload()    hs.notify.new({title="Hammerspoon config reloaded", informativeText="Manually via keyboard shortcut"}):send()end)hs.hotkey.bind(hyper, "C", function()    hs.application.get("Hammerspoon"):selectMenuItem("Console...")    hs.application.launchOrFocus("Hammerspoon")end)------------------------------------------------------------------------------------------------------------ Hotkey app management -------------------------------------------------------------------------------------------------------------hs.hotkey.bind({'alt'}, "[", function() hs.execute('~/.hammerspoon/bin/google', true) end)hs.hotkey.bind({'alt'}, "]", function() hs.execute('~/.hammerspoon/bin/translate', true) end)hs.hotkey.bind({'alt'}, "a", function() hs.application.launchOrFocus('Launchpad') end)hs.hotkey.bind({'alt'}, "s", function() hs.application.launchOrFocus('System Preferences') end)hs.hotkey.bind({'alt'}, "d", function() hs.application.launchOrFocus('Intellij IDEA CE') end)hs.hotkey.bind({'alt'}, "e", function() hs.application.launchOrFocus('Evernote') end)hs.hotkey.bind({'alt'}, "f", function() hs.application.launchOrFocus('finder') end)hs.hotkey.bind({'alt'}, "g", function() hs.application.launchOrFocus('Google Chrome') end)hs.hotkey.bind({'alt'}, "w", function() hs.application.launchOrFocus('Mission Control') end)hs.hotkey.bind({'alt'}, "x", function() hs.application.launchOrFocus('iTerm') end)-------------------------------------------------------------------------------------------------------------- Window management ---------------------------------------------------------------------------------------------------------------local function baseMove(x, y, w, h)    return function()        local win = hs.window.focusedWindow()        local f = win:frame()        local screen = win:screen()        local max = screen:frame()        f.x = max.w * x + max.x        f.y = max.h * y        f.w = max.w * w        f.h = max.h * h        win:setFrame(f, 0)    endendhs.hotkey.bind(hyper, 'Left', baseMove(0, 0, 0.5, 1))hs.hotkey.bind(hyper, 'Right', baseMove(0.5, 0, 0.5, 1))hs.hotkey.bind(hyper, 'Down', baseMove(0, 0.5, 1, 0.5))hs.hotkey.bind(hyper, 'Up', baseMove(0, 0, 1, 0.5))hs.hotkey.bind(hyper, '1', baseMove(0, 0, 0.5, 0.5))hs.hotkey.bind(hyper, '2', baseMove(0.5, 0, 0.5, 0.5))hs.hotkey.bind(hyper, '3', baseMove(0, 0.5, 0.5, 0.5))hs.hotkey.bind(hyper, '4', baseMove(0.5, 0.5, 0.5, 0.5))hs.hotkey.bind(hyper, '5', hs.grid.maximizeWindow)--------------------------------------------------------------------------------------------------------------- remapping alt+jk ---------------------------------------------------------------------------------------------------------------local function pressFn(mods, key)    if key == nil then        key = mods        mods = {}    end    return function() hs.eventtap.keyStroke(mods, key, 80) endendlocal function remap(mods, key, pressFn)    hs.hotkey.bind(mods, key, pressFn, nil, pressFn)endremap({'alt'}, 'j', pressFn('down'))remap({'alt'}, 'k', pressFn('up'))

其他人的配置参考

官方提供了一些大神们的配置样例,可以参考学习。

References

  1. Hammerspoon on Github
  2. Sample Configurations
  3. Creating a productive osx environment - hammerspoon
  4. Getting Started with Hammerspoon
  5. Powerful Hammerspoon