Skip to content

Commit

Permalink
Add hammerspoon helpers for Keepit. Update installation scripts
Browse files Browse the repository at this point in the history
  • Loading branch information
Tim Deeb-Swihart committed Jan 21, 2019
1 parent e67d33e commit f6d6473
Show file tree
Hide file tree
Showing 10 changed files with 233 additions and 26 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "hammerspoon/git/Lunette"]
path = hammerspoon/git/Lunette
url = https://github.com/scottwhudson/Lunette.git
24 changes: 14 additions & 10 deletions bin/watchman-process-files.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,7 @@ def parse_rule(line: str):

def expand_variables(variables, text):
for variable, value in variables.items():
key = f'{{{variable}}}'
if key in text:
text = text.replace(key, value)
text = text.replace(f'{{{variable}}}', value)
return text


Expand All @@ -74,24 +72,24 @@ def process_file(rules, variables, path: str):
dirname = os.path.dirname(path)
tags = []
name = basename
fldr = "inbox"
folder = None
for (pattern, name_tmpl, folder_tmpl, tag_tmpl) in rules:
m = pattern.match(name)
if not m:
continue
groups = m.groups()
group_vars = {f'{idx + 1}': group for idx, group in enumerate(m.groups())}
local_vars = {'{basename}': name, **group_vars, **variables}
numbered_groups = {f'{idx + 1}': group for idx, group in enumerate(m.groups())} # Numbered groups
named_groups = {f'{k}': v for k, v in m.groupdict().items()} # Named groups
local_vars = {'{basename}': name, **named_groups, **numbered_groups, **variables}
if name_tmpl:
name = expand_variables(local_vars, name_tmpl)
if folder_tmpl:
folder = os.path.expanduser(expand_variables(local_vars, folder_tmpl))
if tag_tmpl:
tags.extend(expand_variables(local_vars, tag_tmpl).split(','))
if folder:
fldr = " of ".join(f"folder \"{fl}\"" for fl in reversed(folder.split('/'))) + " of top level folder"
fldr = f"({fldr})"
folder = " of ".join(f"folder \"{fl}\"" for fl in reversed(folder.split('/'))) + " of top level folder"
folder = f"({fldr})"
new_name = name
if dirname:
new_name = f'{dirname}/{name}'
Expand All @@ -105,7 +103,12 @@ def process_file(rules, variables, path: str):
if not os.path.exists('/usr/local/bin/tag'):
raise RuntimeError("Please run `brew install tag'")
check_output(['/usr/local/bin/tag', '--add', ','.join(tags), new_name])
return add_tmpl.format(name=os.path.abspath(new_name), fldr=fldr, tags=', '.join(f'"{t}"' for t in tags))
# TODO: find a better way to specify this...
if folder.startswith('!keepit'):
folder = folder.replace('!keepit', '').strip('/') or "inbox"
return add_tmpl.format(name=os.path.abspath(new_name), fldr=folder, tags=', '.join(f'"{t}"' for t in tags))
else:
return None

if __name__ == '__main__':
confdir = os.path.expanduser('~/.config/watchman/')
Expand All @@ -115,7 +118,8 @@ def process_file(rules, variables, path: str):
with open(os.path.join(confdir, 'rules.conf'), 'r') as f:
variables, rules = parse_config(f)
if len(sys.argv) > 0:
items = [process_file(rules, variables, infile) for infile in sys.argv[1:]]
exclusions = re.compile(variables.get('exclusions', '^$'))
items = [process_file(rules, variables, infile) for infile in sys.argv[1:] if not exclusions.match(infile)]
items = [i for i in items if i]
if items:
with NamedTemporaryFile(mode='w', delete=True) as tempf:
Expand Down
13 changes: 10 additions & 3 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"tag",
"tailor",
"terminal-notifier",
"tesseract",
"texinfo",
"the_silver_searcher",
"tmux",
Expand Down Expand Up @@ -117,19 +118,25 @@
"1Password 7"
],
"sources": {
"git@github.com:larkery/zsh-histdb.git": "~/.zsh-histdb/",
"git@github.com:tarjoilija/zgen": {
"https://github.com/larkery/zsh-histdb.git": "~/.zsh-histdb/",
"https://github.com/tarjoilija/zgen": {
"symlinks": {
"zgen.zsh": "~/.zgen.zsh",
"_zgen": "~/.zsh/_zgen"
}
},
"https://github.com/scottwhudson/Lunette.git": {
"symlinks": {
"Lunette.spoon": "~/.hammerspoon/Spoons/"
}
}
},
"symlinks": {
"config/.*": "~/",
"shell/*": "~/.config/zsh/config.sh",
"bin/*": "~/.local/bin/",
"launchagents/*":"~/Library/LaunchAgents/"
"launchagents/*":"~/Library/LaunchAgents/",
"hammerspoon/*": "~/.hammerspoon/"
},
"after-scripts": [
"brew cleanup"
Expand Down
6 changes: 6 additions & 0 deletions hammerspoon/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-- ~/.hammerspoon/init.lua
hs.loadSpoon("Lunette")
spoon.Lunette:bindHotkeys()

local keepit = require 'keepit'
local mail = require 'mail'
104 changes: 104 additions & 0 deletions hammerspoon/keepit.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
-- keepit.lua
-- helpers for Keep It

local module = {}
local db = hs.sqlite3.open(os.getenv("HOME") .. "/Library/Group Containers/D75L7R8266.com.reinvented.KeepIt/Keep It/Keep It Library.kptlib/KeepIt.sqlite")

local IDENT_SEL_NOPAR = "select ZIDENTIFIER from ZITEM where ZFILENAME = ?"
local IDENT_SEL_ONEPAR = "select ZIDENTIFIER from ZITEM where ZFOLDER =(SELECT Z_PK from ZGROUP where ZNAME = ?) and ZFILENAME = ?"

function runcmd(cmd)
local handle = assert(io.popen(cmd, "r"))
local res = handle:read("*all")
handle:close()
return res
end

module.window_filter = hs.window.filter.new{'Keep It'}
module.keybinds = {
hs.hotkey.bind({"ctrl", "cmd"}, "r", function ()
hs.osascript.javascriptFromFile("~/.hammerspoon/scripts/keepit-refile.js")
end),
hs.hotkey.bind({"ctrl", "cmd"}, "l", function ()
-- insert Link
local files = runcmd("cd '" .. os.getenv("HOME") .. "/Library/Group Containers/D75L7R8266.com.reinvented.KeepIt/Keep It/Files' && " .. "/usr/local/bin/fd")
-- Build the list of emojis to be displayed.
local choices = {}
for file in string.gmatch(files, '(.-)[\n\r]') do
if file ~= nil then
local path, name = string.match(file, "(.-)/([^/]+)$")
if name ~= nil and path ~= nil then
table.insert(choices,
{text=name,
subText=path})
end
end
end

-- Focus the last used window.
local function focusLastFocused()
local wf = hs.window.filter
local lastFocused = wf.defaultCurrentSpace:getWindows(wf.sortByFocusedLast)
if #lastFocused > 0 then lastFocused[1]:focus() end
end

-- Create the chooser.
-- On selection, copy the emoji and type it into the focused application.
local chooser = hs.chooser.new(function(choice)
if not choice then focusLastFocused(); return end
local path, folder = string.match(choice.subText, "(.-/)-([^/]+)")
local name = choice.text
local stmt = IDENT_SEL_NOPAR
if path ~= nil then
stmt = IDENT_SEL_ONEPAR
end
stmt = db:prepare(stmt)
if path ~= nil then
stmt:bind_values(path, name)
else
stmt:bind_values(name)
end
-- local ident = runcmd(IDENT_SEL_ONEPAR)
-- TODO: get identifier from DB
local ident = nil
for row in stmt:nrows() do
ident = row.ZIDENTIFIER
break
end
stmt:finalize()
focusLastFocused()
if ident ~= nil then
hs.eventtap.keyStrokes("[" .. name .. "](keepit://link?item=" .. ident .. ")")
end
end)

chooser:rows(5)
-- chooser:bgDark(true)


chooser:searchSubText(true)
chooser:choices(choices)
chooser:show()
end)
}
for k, binding in pairs(module.keybinds) do
binding:disable()
end
local focus = function ()
-- set up keybindings, etc
for k, binding in pairs(module.keybinds) do
binding:enable()
end
end

local unfocus = function ()
-- remove keybindings, etc
for k, binding in pairs(module.keybinds) do
binding:disable()
end
end

module.window_filter:subscribe('windowFocused', focus)
module.window_filter:subscribe('windowUnfocused', unfocus)

return module
29 changes: 29 additions & 0 deletions hammerspoon/mail.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
-- mail.lua
-- helpers for mail

local module = {}
module.keybinds = {
hs.hotkey.bind({"ctrl", "cmd"}, "a", function ()
hs.osascript.javascriptFromFile("~/.hammerspoon/scripts/mail-archive.js")
end)}

for k, binding in pairs(module.keybinds) do
binding:disable()
end
module.window_filter = hs.window.filter.new{'Mail'}
local focus = function ()
for k, binding in pairs(module.keybinds) do
binding:enable()
end
end

local unfocus = function ()
for k, binding in pairs(module.keybinds) do
binding:disable()
end
end

module.window_filter:subscribe('windowFocused', focus)
module.window_filter:subscribe('windowUnfocused', unfocus)

return module
29 changes: 29 additions & 0 deletions hammerspoon/scripts/ctrl-cmd-r.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
function run(argv) {;
// Rename keepit files according to their modification date, then move them into Life/Year;
var keepit = Application("Keep It");
var life = null;;
let folders = keepit.folders();
for (let folder of folders) {;
if (folder.name() == "Life") {;
life = folder;
break;
};
};
var years = {};;
for (let folder of life.folders()) {;
years[folder.name()] = folder;
};
;
keepit.selectedItems().forEach(function(item) {;
let ctime = JSON.stringify(item.created()).split('T')[0].replace('"', '');
let year = ctime.split('-')[0];
/*if (!item.name().startsWith(ctime)) {
item.name = ctime + " " + item.name()
}*/
if (!years.hasOwnProperty(year)) {;
years[year] = new keepit.Folder({name: year, parentFolder: life});
};
console.log(item.name());
item.move({to: years[year]});
});
};
42 changes: 31 additions & 11 deletions install.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,20 @@


def is_str(s):
return isinstance(s, (str, unicode))
return isinstance(s, str)

def getpath(source):
if '@' in source:
# SSH
chunks = source.split(':')[-1].split('/')
site = source.split('@')[-1].split(':')[0]
user = chunks[0]
repo = chunks[-1].replace('.git', '')
else:
url = source.split('://')[-1]
chunks = url.split('/')
site, user, repo = chunks[:3]
return site, user, repo

@contextmanager
def chdir(path):
Expand Down Expand Up @@ -40,7 +53,7 @@ def install_sources(sources):
mkdir(os.path.expanduser('~/.config/zsh/repos'))
for source, config in sources.items():
print("Cloning {}".format(source))
if isinstance(config, (str, unicode)):
if isinstance(config, str):
# TODO: clone directly here
config = os.path.expanduser(config)
if not os.path.isdir(config):
Expand All @@ -49,11 +62,8 @@ def install_sources(sources):
with chdir(config):
runcmd("git pull")
else:
chunks = source.split(':')[-1].split('/')
user = chunks[0]
repo = chunks[-1].replace('.git', '')
repo_dir = os.path.expanduser("~/.config/zsh/repos/{}-{}".format(user, repo))
mkdir(repo_dir)
site, user, repo = getpath(source)
repo_dir = os.path.expanduser("~/.config/zsh/repos/{}/{}-{}".format(site, user, repo))
if not os.path.isdir(repo_dir):
runcmd("git clone {} {}".format(source, repo_dir))
else:
Expand All @@ -73,20 +83,22 @@ def install_symlinks(config):
# relative paths
src = os.path.join(os.getcwd(), src)
sources = sorted(glob(os.path.expanduser(src)))

if not sources:
continue
if dst.endswith('/'):
# Its a directory. each file should be copied
for path in sources:
# print('Linking {} -> {}{}'.format(path, dst, os.path.basename(path)))
mkdir(dst)
mkdir(os.path.expanduser(dst))
ldst = os.path.expanduser('{}{}'.format(dst, os.path.basename(path)))
if os.path.islink(ldst):
os.unlink(ldst)
elif os.path.exists(ldst):
raise RuntimeError('{} already exists and is not controlled by us!'.format(ldst))
assert not os.path.islink(ldst)
os.symlink(os.path.expanduser(path), ldst)
else:
path = os.path.expanduser(path)
os.symlink(path, ldst, target_is_directory=os.path.isdir(path))
elif os.path.isfile(sources[0]):
# Combine/link into file
# print('Combining {} into {}'.format(sources, dst))
dst = os.path.expanduser(dst)
Expand All @@ -98,6 +110,14 @@ def install_symlinks(config):
outf.write('## {}\n'.format(path))
outf.write(f.read())
outf.write('\n')
elif os.path.isdir(sources[0]):
ldst = os.path.expanduser(dst)
if os.path.islink(ldst):
os.unlink(ldst)
elif os.path.exists(ldst):
raise RuntimeError('{} already exists and is not controlled by us!'.format(ldst))
assert not os.path.islink(ldst)
os.symlink(os.path.expanduser(path), ldst, target_is_directory=True)

def install_taps(taps):
for tap in taps:
Expand Down
4 changes: 2 additions & 2 deletions setup.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/bash

python install.py config.json $*
python3 install.py config.json $*
# cask requires passwords sometimes
xargs </tmp/casks brew cask install
test -f /tmp/casks && xargs </tmp/casks brew cask install
5 changes: 5 additions & 0 deletions shell/funcs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -288,4 +288,9 @@ datename () {
test -f "$1" && echo "$(ctime "$1")_$1"
}

makesearchable () {
local fname="$1"
test -f "$1" && tesseract "${fname}" "${fname}.ocr" PDF
}

test -f ~/.local.sh && source ~/.local.sh

0 comments on commit f6d6473

Please sign in to comment.