@[TOC](Lua quick start and complete a Hammerspoon-based memo project)

The environment that

System version: macOS 10.15.7 Hammerspoon version: 0.9.81 Lua version: 5.4.0Copy the code

Need to sort out

Demand origin

After using macOS for a while. There are a lot of terminal instructions to remember. You don’t forget the usual instructions. However, some less commonly used directives, such as the SCP directive. So I just wanted a feature to keep track of these common commands. Then it came down to wanting to record some common comment snippets.

Of course, I knew that these notes could have been stored in macOS memos or on a blog, but after a while I realized that there was a big drawback: I had to open an app to record, find, and modify them, which was often a multi-step process.

Alfred, the other macOS app I’m using, also has a snippet feature. It seems to satisfy my needs, too. However, I am not satisfied with the fact that it is not very convenient to add, modify and delete the notes. You need to enter the setting screen to perform the operation. The second problem is data synchronization between multiple Macs. Dropbox is the official recommendation, but network issues aside, you really don’t want to install another piece of useless software. It is theoretically possible to synchronize configuration files with Git, but instead of trying to do so, you will want to manually write one :-). You are in control and you can change what you want.

Need to sort out

  • Quick operational memos, preferably using shortcut keys, not too much mouse operation
  • The ability to add notes via shortcut keys
  • Can use shortcut keys to call up the memo list
  • The ability to quickly apply selected memos
  • Ability to edit notes
  • The ability to delete memos
  • Can save memo data locally

The project address

Github.com/sugood/hamm…

The directory structure

| history. Json | init. Lua │ README. Md │ README_zh - CN. Md └ ─ modules │ hotkey. Lua - │ the launcher. The lua - │ reload. Lua - │ Snippet. Lua -- │ system. Lua -- │ WindowsCopy the code

Memorandum Implementation Steps

1. Import script files

Insert the following code into the init.lua file

require "modules/snippet"
Copy the code

2. Bind shortcuts for adding a memo and displaying a memo list

-- Add a fragment (make a copy when you press the shortcut key and record what you copied into the fragment list)
hs.hotkey.bind({"ctrl"."cmd"}, "A".function (a)
-- TODO
end)

-- Select the contents of the fragment (press the shortcut key to display the list of fragments, click the selected shortcut key will automatically paste)
hs.hotkey.bind({ "ctrl"."cmd" }, "V".function (a)
--TODO
end)
Copy the code

3. Add memos

  1. Send the copy and paste shortcut keys to copy the selected content to the clipboard (the official document does not find the API to directly get the selected content on the computer)
hs.eventtap.keyStroke({ "cmd" }, "C")
Copy the code
  1. Inserts the last piece of text from the clipboard into the memo list. And save it to a local file called history.json, but the actual code will need to repeat the judgment and sort it
Insert the memo into the memo list
table.insert(history, 1, item)
Save the memo list to a JSON file
hs.json.write(history,historyPath, true.true)
Copy the code

4. Display the memo list

  1. Create a selector and bind the selected completed processing method. The main purpose of this process is to insert the selected memo contents into the clipboard. Then send the pasted message and paste the content out.
-- Create a selector
hs.chooser.new(completionFn)
    :choices(history)
    :rightClickCallback(menuFn)
    :searchSubText(true)
    :show()
Paste the selected fragment
local completionFn = function(result)
    if result then
        hs.pasteboard.setContents(result.text)
        hs.eventtap.keyStroke({ "cmd" }, "V")
        print("keywords:"..result.text)
    end
end
Copy the code
  1. Listen for the right mouse button event. When a right-click operation is detected, pass the index of the list to the bound method. And then you can deal with it. Delete and modify the operation.
-- Right-click to pop up a menu
local menuFn = function(index)
menubar = hs.menubar.new(false)
        menubar:setTitle("Hidden Menu")
        menubar:setMenu( {
            { title = "Menu", fn = function(a) print("you clicked my menu item!") end },
            { title = "-" },
            { title = "Modify fragment content", fn = function(a)
                result,text = hs.dialog.textPrompt("Modify fragment content"."Please enter new content", history[index].text, "Sure"."Cancel")
                if result == "Sure" then
                    modifyHistory(text,history[index].subText,true,index)
                end
            end },
            { title = "Modify section description", fn = function(a)
                result,subText = hs.dialog.textPrompt("Modify section description"."Please enter new instructions", history[index].subText, "Sure"."Cancel")
                if result == "Sure" then
                    modifyHistory(history[index].text,subText,false,index)
                end
            end },
            { title = "-" },
            { title = "Delete current fragment", fn = function(a)
                if hs.dialog.blockAlert("Are you sure to delete the following?",history[index].text,"Sure"."Cancel"."informational") = ="Sure" then
                    table.remove(history,index)
                    hs.json.write(history,historyPath, true.true)
                    hs.alert.show("Successful deletion of fragment")
                end
            end },
        })
        menubar:popupMenu(hs.mouse.getAbsolutePosition(), true)
end
Copy the code

5. Remember to Reload Config

6. How to synchronize configurations on different computers

Use Github or Gitee. The specific operation is not detailed.

A quick introduction to Hammerspoon script development

The main reason I left the quick start at the end is that programming learning is all about writing and practicing. You just need to know the basics of grammar. In particular, the scripting language itself has been designed to simplify a lot of syntax, as long as there is a certain programming foundation, the basic use. If you don’t know anything, just look it up. Of course, if you’re going to do a big project, make sure you lay the groundwork. Let’s just get started fast and get to work. Because Hammerspoon uses Lua scripts. So, part of this is an introduction to some of the syntax for Lua scripts and part of the Hammerspoon API.

Reference documentation

Hammerspoon API documentation: www.hammerspoon.org/docs/index….

Lua reference: www.lua.org/manual/5.4/ If you’re using a different Hammerspoon version than mine, you might be using a different Lua version. You might want to look for the Lua version of the document. The Lua version can be printed in the script with the following instructions

print(_VERSION) 
Copy the code

Lua syntax description

You can look at the documentation for the basic syntax, but I’m just going to talk about what’s not written in the documentation, or what I think is useful.

Lua is a dynamically typed language, so there is no type definition in the language. There is no need to declare variable types, and each variable holds its own type. There are eight basic types: nil, Boolean, number, String, userdata, function, Thread, and Table.

	print(type(nil))                    - output nil
    print(type(99.7+12*9))              Output number -
    print(type(true))                   - output Boolean
    print(type("Hello Wikipedia"))      - output string
    print(type(print))                  Output function -
    print(type{1.2, test = "test"})    Output the table -
Copy the code

The most common types we use are nil, string, and table

  • Nil is a similar concept to null in Java. Before you use some variables, you need to make a non-null judgment, and you need to make an error
if str == nil then
	print("String is nil")
else
	print("String is not nil")
end
Copy the code
  • String The value is a string.
-- String concatenation using..
str = "Hello"." World"
print(str) The output is Hello World
To see the length of the string, use #
str = "Hello World"
print(#str) Output 11 -
A string is nil. An empty string usually means that the string is nil or that the string has no content, so we can encapsulate it as a method
local function isEmpty(s)
  return s == nil or s == ' '
end

if isEmpty(s) then
  print("String is empty")
end
Copy the code
  • The table is an associative array.

Let’s talk about multi-dimensional table operations, including adding, deleting, modifying, checking and sorting. The table format is as follows

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"}},Insert a piece of data
local item = {}
item.text = "Fourth Choice"
item.subText = "This is the subtext of the fourth choice"
table.insert(history, 1, item)
Select * from table where index = 1
table.remove(history,1)
-- Modify the first data
history[index].text = "First Choice change"
history[index].subText = "This is the subtext of the first choice change"
-- View the first data
print("text:"..history[1].text)
print("subText:"..history[1].subText)
-- sort, manually select a subitem to sort, here select subText
table.sort(history,function(a,b) return a.subText<b.subText end )
To see the length of the table, use # as well
print(#history)
Copy the code
  • Use of function

Similar to other programming languages, you can pass parameters and return values. The function must be defined before it is called, and the body of the function must be written above the calling code.

Hammerspoon Api specification

  • Shortcut keys bind and open the application
hs.hotkey.bind({ "ctrl"."shift" }, "T".function (a)
	hs.application.launchOrFocus('Terminal')
end)
Copy the code
  • Triggering keyboard events
-- Triggers the replication event
hs.eventtap.keyStroke({ "cmd" }, "C")
-- Triggers the paste event
hs.eventtap.keyStroke({ "cmd" }, "V")
Copy the code
  • Local JSON file operation
local historyPath= "~/.hammerspoon/history.json"
-- Read local files
history = hs.json.read(historyPath)
-- Write to a local file
hs.json.write(history,historyPath, true.true)
Copy the code
  • Pop-up menu bar
RightClickCallback listens for the right mouse button, and the menu pops up when the right mouse button is used
 menubar = hs.menubar.new(false)
        menubar:setTitle("Hidden Menu")
        menubar:setMenu( {
            { title = "Menu", fn = function(a) print("you clicked my menu item!") end },
            { title = "-" },
        menubar:popupMenu(hs.mouse.getAbsolutePosition(), true)
Copy the code

conclusion

Writing this article is a summary for yourself. Also recommend Hammerspoon, really good 🙂