共计 3062 个字符,预计需要花费 8 分钟才能阅读完成。
有一天,我用 Homebrew
装置了一些软件——因为曾经是一个月前的事件了,所以曾经记不清是装置了什么。装置后并没有立刻呈现什么问题,只是又过了两天我重新启动电脑后,发现同样是由 Homebrew
装置的 Emacs
不由分说地无奈启动了。这下可麻烦了,毕竟我是 org-mode
的重度使用者,还须要偶然用 SLIME
写点 Common Lisp 的代码,而它们都运行在 Emacs
中。
直觉通知我,兴许重新安装一下 Emacs
,所有就能够恢复正常。重装了Emacs
后,又遇到了别的问题——用 BetterTouchTools
在 Touch Bar 中增加的按钮,无奈在 Emacs
曾经启动的状况下,切换到它的窗口上。
非要说,问题其实也不大,毕竟很多时候是将 MacBook Pro 合上盖子当主机用的,Touch Bar 在工作时的应用频率并不高。此外,糊 Node.js
等语言的代码时也用不到 Emacs
——还是VSCode
更适合。
但这就是令人不爽,因而我决定要解决它——用Hammerspoon
。
Hammerspoon 是什么?
Hammerspoon
的官网很好地阐明了这款工具的定位和原理
This is a tool for powerful automation of OS X. At its core, Hammerspoon is just a bridge between the operating system and a Lua scripting engine. What gives Hammerspoon its power is a set of extensions that expose specific pieces of system functionality, to the user.
- 它运行在 OS X 上——当初应该叫 macOS;
- 它是用来自动化操作的——就像零碎内置的
Automator
或第三方的Alfred Workflow
那样; - 它的原理是将操作系统的性能封装成了能够用
Lua
代码调用的模块;
例如上面的代码
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "E", function()
hs.alert.show("Hello World!")
end)
就能够让使用者在按下组合键 ⌘⌥⌃e
的时候,在屏幕正中间显示 Hello World!
这段文本
为什么用 Hammerspoon?
Hammerspoon
正好能够解决我的问题,它的 hs.window
模块既能够让使用者遍历所有关上的窗口(用 hs.window.allWindows
函数),也能够聚焦到指定的窗口上(用 focus
办法)。有了它们,将 Emacs
调到最后面(front-most)来也就是瓜熟蒂落的事件了:
- 调用函数
hs.window.allWindows
函数,取得所有窗口的列表; - 一一查看列表中的窗口对象,如果属于
Emacs
的,就调用窗口的办法focus
,并跳出循环。
剩下的两个问题便是:
Emacs
的bundle ID
是什么;- 如何晓得一个窗口对象的
bundle ID
。
Emacs 的 bundle ID
Bundle ID
能够在 macOS 中举世无双地标识一个利用。要想晓得 Emacs
的bundle ID
是什么,只须要关上文件 /Applications/Emacs.app/Contents/Info.plist
,看看其中键为CFBundleIdentifier
的值即可。
➜ Contents grep -A 1 'CFBundleIdentifier' Info.plist
<key>CFBundleIdentifier</key>
<string>org.gnu.Emacs</string>
➜ Contents
能够看到,Emacs
的 bundle ID
是org.gnu.Emacs
。
来点 Lua 代码吧
有了 Emacs
的bundle ID
,接下来就能够在 Hammerspoon
中定义快捷键了。因为最初会通过 Touch Bar 上的按钮来触发这组快捷键,简单点也不要紧,因而我间接沿用了 Hammerspoon
的入门指引中作为例子的⌘⌥⌃w
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "W", function()
end)
为了在一个循环中一一遍历窗口对象,将 hs.window.allWindows
的返回值保留到一个局部变量中
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "W", function()
local windows = hs.window.allWindows()
end)
照着简书上的这篇文章,依葫芦画瓢地用 for
和pairs
来遍历变量windows
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "W", function()
local windows = hs.window.allWindows()
-- 在 Lua 中遍历表的办法:https://www.jianshu.com/p/de5a4b132918
for _, win in pairs(windows) do
end
end)
窗口本身没有 bundle ID
,为此须要先获取窗口所属的利用。查看文档能够晓得,有一个application
办法正是用来获取利用对象的
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "W", function()
local windows = hs.window.allWindows()
-- 在 Lua 中遍历表的办法:https://www.jianshu.com/p/de5a4b132918
for _, win in pairs(windows) do
local app = win:application()
end
end)
调用 allWindows
时应用的是英文句号(.
),调用 application
则是用冒号(:
),这正是 Lua
中调用函数与办法时语法上的差别。
再用利用的 bundleID
办法取得它的bundle ID
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "W", function()
local windows = hs.window.allWindows()
-- 在 Lua 中遍历表的办法:https://www.jianshu.com/p/de5a4b132918
for _, win in pairs(windows) do
local app = win:application()
local bundleID = app:bundleID()
end
end)
当初,只有变量 bundleID
等于 Emacs
的bundle ID
就能够聚焦到以后遍历的窗口上了
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "W", function()
local windows = hs.window.allWindows()
-- 在 Lua 中遍历表的办法:https://www.jianshu.com/p/de5a4b132918
for _, win in pairs(windows) do
local app = win:application()
local bundleID = app:bundleID()
if bundleID == "org.gnu.Emacs" then
win:focus()
end
end
end)
让 Touch Bar 按钮触发这所有
只须要在 BetterTouchTools
中配置一下即可
这个办法比此前唤起 /Applications/Emacs.app
的形式更好,因为它只依赖于 Emacs
逻辑上亘古不变的货色——bundle ID
,而不依赖于其物理上的装置地位。
浏览原文