当前位置: 首页 > 工具软件 > hammerspoon > 使用案例 >

Lua 快速入门并完成一个基于Hammerspoon的备忘录项目

钮承恩
2023-12-01

环境说明

系统版本:macOS 10.15.7
Hammerspoon版本:0.9.81
Lua版本:5.4.0

需求整理

需求由来

用了一段时间的macOS后发现。有很多终端的指令需要记。常用的指令一般都不会忘。但是,一些不常用的指令,例如scp指令。所以就想要一个功能帮我记录这些常用指令的。后来引申到了想要记录一些常用的注释代码片段。
当然,我是知道这些备忘完全可以随便记录在macOS提供的备忘录中或者是写在博客里,但是,操作了一段时间后就发现了一个很大的弊端:记录、查找、修改备忘都要先打开某个应用后才能操作,往往要经过很多步骤。同时我在用的另外一个macOS神级应用Alfred里也有一个片段功能。似乎也能满足我的需求。但是,我不满意的地方是新增修改删除备忘的时候,不是很方便。需要进入设置界面才能操作。第二个问题是多台mac数据同步的问题,官方推荐Dropbox,网络问题就不说了,真心不想装多一个没用的软件。理论上是能通过git来同步配置文件的,不过不想试了,就想自己手动写一个:-)。控制权在自己手上,想怎么改就怎么改。

需求整理

  • 快速的操作备忘,最好使用快捷键,不要太多的鼠标操作
  • 能够通过快捷键添加备忘
  • 能够使用快捷键调出备忘列表
  • 能够快速的应用选中的备忘内容
  • 能够编辑备忘内容
  • 能够删除备忘内容
  • 能够本地保存备忘数据

项目地址

https://github.com/sugood/hammerspoon

目录结构

│ history.json
│ init.lua
│ README.md
│ README_zh-CN.md
└─modules
│ hotkey.lua
——│ launcher.lua
——│ reload.lua
——│ snippet.lua
——│ system.lua
——│ windows.lua

备忘录实现步骤

1、导入脚本文件

在init.lua文件中插入以下代码

require "modules/snippet"

2、绑定添加备忘和显示备忘列表的快捷键

-- 添加片段(按下快捷键时做一个复制操作,并记录复制的内容到片段列表中)
hs.hotkey.bind({"ctrl", "cmd"}, "A", function ()
-- TODO
end)

-- 选取片段内容(按下快捷键时显示片段列表,点击选中的快捷键将自动粘贴)
hs.hotkey.bind({ "ctrl", "cmd" }, "V", function ()
--TODO
end)

3、添加备忘

  1. 发送复制粘贴的快捷键将选中的内容复制到剪贴板中(官方文档没有找到直接获取电脑上选中内容的api)
hs.eventtap.keyStroke({ "cmd" }, "C")
  1. 将剪贴板上的最后一条文本插入备忘录列表中。并保存到本地文件history.json中,实际代码中还需要做重复判断还有排序
-- 将备忘插入备忘录列表中
table.insert(history, 1, item)
-- 将备忘列表保存到json文件中
hs.json.write(history,historyPath, true, true)

4、显示备忘列表

  1. 创建一个选择器,并绑定选中完成的处理方法。这个处理方法主要作用是,将我们选中的备忘录内容插入剪贴板中。然后发送粘贴的消息,将该内容粘贴出去。
-- 创建一个选择器
hs.chooser.new(completionFn)
    :choices(history)
    :rightClickCallback(menuFn)
    :searchSubText(true)
    :show()
-- 粘贴选中的片段
local completionFn = function(result)
    if result then
        hs.pasteboard.setContents(result.text)
        hs.eventtap.keyStroke({ "cmd" }, "V")
        print("keywords:"..result.text)
    end
end
  1. 监听鼠标右键事件。判断到有右键的操作时,将列表的index传递到绑定的方法中。然后就能处理。删除还有修改的操作了。
-- 右键弹出菜单
local menuFn = function(index)
menubar = hs.menubar.new(false)
        menubar:setTitle("Hidden Menu")
        menubar:setMenu( {
            { title = "菜单", fn = function() print("you clicked my menu item!") end },
            { title = "-" },
            { title = "修改片段内容", fn = function()
                result,text = hs.dialog.textPrompt("修改片段内容", "请输入新的内容", history[index].text, "确定", "取消")
                if result == "确定" then
                    modifyHistory(text,history[index].subText,true,index)
                end
            end },
            { title = "修改片段说明", fn = function()
                result,subText = hs.dialog.textPrompt("修改片段说明", "请输入新的说明", history[index].subText, "确定", "取消")
                if result == "确定" then
                    modifyHistory(history[index].text,subText,false,index)
                end
            end },
            { title = "-" },
            { title = "删除当前片段", fn = function()
                if hs.dialog.blockAlert("确定删除以下片段?",history[index].text,"确定","取消","informational") == "确定" then
                    table.remove(history,index)
                    hs.json.write(history,historyPath, true, true)
                    hs.alert.show("成功删除片段")
                end
            end },
        })
        menubar:popupMenu(hs.mouse.getAbsolutePosition(), true)
end

5、最后记得 Reload Config

6、如何同步不同电脑上的配置

使用github或者是gitee。具体的操作就不详细说了。

快速入门Hammerspoon脚本开发

我把快速入门放最后,主要的原因就是,编程学习主要还是要多写多练。基础语法这些只要知道个大概就可以了。特别是脚本语言,本身设计的时候都已经简化了很多语法,只要有一定的编程基础,基本拿来就能用了。不懂的地方再查资料就好。当然,如果你要做个大项目,请一定要打好基础。咱们快速入门就直接开干就好。
因为Hammerspoon 使用Lua脚本。所以,我们一部分是介绍Lua脚本的一些语法,一部分是介绍Hammerspoon API的。

参考文档

Hammerspoon API文档:https://www.hammerspoon.org/docs/index.html

Lua 参考文档:http://www.lua.org/manual/5.4/
如果使用的Hammerspoon和我的版本不同,可能Lua的版本也不同。那你可能要找对应的Lua版本的文档来看。在脚本中通过以下指令可以打印出Lua版本

print(_VERSION) 

Lua语法说明

基础语法大家可以看文档,这里只说一些文档没有写的,或者是我觉得比较实用的。

Lua是一种动态类型语言,因此语言中没有类型的定义,不需要声明变量类型,每个变量自己保存了类型。有8种基本类型:nil、布尔值(boolean)、数字体(number)、字符串型(string)、用户自定义类型(userdata)、函数(function)、线程(thread)和表(table)。

	print(type(nil))                    -- 输出 nil
    print(type(99.7+12*9))              -- 输出 number
    print(type(true))                   -- 输出 boolean
    print(type("Hello Wikipedia"))      -- 输出 string
    print(type(print))                  -- 输出 function
    print(type{1, 2, test = "test"})    -- 输出 table

我们用的比较多的类型是 nil 、 string 、 table

  • nil 和java中的null差不多的概念。在使用一些变量前需要是要做非空判断,要不会报错
if str == nil then
	print("字符串为nil")
else
	print("字符串不为nil")
end
  • string 字符串类型。
-- 字符串拼接,使用..
str = "Hello".." World"
print(str) -- 输出的结果就是 Hello World
-- 查看字符串长度使用 #
str = "Hello World"
print(#str) --输出11
--字符串判空,所谓的空字符串一般是指字符串是nil或者字符串没有内容,所以我们可以封装成一个方法
local function isEmpty(s)
  return s == nil or s == ''
end

if isEmpty(s) then
  print("字符串为空")
end
  • table 就是表,其实是一个"关联数组"(associative arrays)
    我们说下多维表的操作,包含增删改查和排序。表的格式如下
local history = {
 {
  ["text"] = "First Choice",
  ["subText"] = "This is the subtext of the first choice"
 },
 { ["text"] = "Second Choice",
   ["subText"] = "This is the subtext of the second choice"
 },
 { ["text"] = "three Choice",
   ["subText"] = "This is the subtext of the three choice"
 },
}

--插入一条数据
local item = {}
item.text = "Fourth Choice"
item.subText = "This is the subtext of the fourth choice"
table.insert(history, 1, item)
--删除第一条数据,table索引是从1开始的
table.remove(history,1)
--修改第一条数据
history[index].text = "First Choice change"
history[index].subText = "This is the subtext of the first choice change"
--查看第一条的数据
print("text:"..history[1].text)
print("subText:"..history[1].subText)
-- 排序,手动选择一个子项来排序,这里选择subText
table.sort(history,function(a,b) return a.subText<b.subText end )
--查看table的长度,也使用#这个符号
print(#history)
  • function 函数的使用
    跟其他编程语言的也差不多,可以传入参数,能返回值。就是调用函数前必须先定义好函数,函数体要写在调用代码的上面。

Hammerspoon Api说明

  • 快捷键绑定并打开应用
hs.hotkey.bind({ "ctrl", "shift" }, "T", function ()
	hs.application.launchOrFocus('Terminal')
end)
  • 触发键盘事件
-- 触发复制事件
hs.eventtap.keyStroke({ "cmd" }, "C")
-- 触发粘贴事件
hs.eventtap.keyStroke({ "cmd" }, "V")
  • 本地json文件操作
local historyPath= "~/.hammerspoon/history.json"
-- 读取本地文件
history = hs.json.read(historyPath)
-- 写入本地文件
hs.json.write(history,historyPath, true, true)
  • 弹出菜单栏
-- 最好配合rightClickCallback 监听右键,鼠标右键时才弹出菜单
 menubar = hs.menubar.new(false)
        menubar:setTitle("Hidden Menu")
        menubar:setMenu( {
            { title = "菜单", fn = function() print("you clicked my menu item!") end },
            { title = "-" },
        menubar:popupMenu(hs.mouse.getAbsolutePosition(), true)

总结

写这篇文章就是自己做个总结。同时推荐下Hammerspoon,真的很好用:-)

 类似资料: