Git Product home page Git Product logo

spacehammer's Introduction

Hammerspoon config inspired by Spacemacs

Rationale

Keyboard-oriented workflows are often far more efficient and less frustrating than similar mouse-driven techniques. However, the most popular strategy in that space is to use a multitude of keyboard shortcuts. And obviously, that approach is not very scalable. You start adding keyboard shortcuts for various actions, and soon you will be blocked by conflicting shortcuts.

Command composability (first explored in Vi and later expanded in its successor Vim), although does require some initial learning and getting used to, allows you to expand your keyboard-oriented workflow with a minimal effort to memorize keys. There’s so much you can do with the h/j/k/l keys alone.

However, the “one-dimensional” approach utilized in vanilla Vim, where a single modal (to switch from Normal to Edit to Select mode) is used, also has limitations. Fortunately, the basic idea of modality can be expanded further. The Spacemacs project is an excellent example of where that was done. In Spacemacs there is a single primary “modifier” key SPACE. To trigger an action, user is required to press a mnemonically recognizable combination of keys (that usually starts with SPACE key), e.g., SPC w m is used to maximize the current window/buffer.

The Spacehammer project explores these ideas to allow you to take your keyboard-driven workflow to the next level. Jumping between applications, controlling the size and position of their windows, searching for things, etc. - everything follows simple, mnemonic semantics. It lets you keep your fingers on the home row and liberates you from having to memorize a myriad of keystrokes, or require you to drag your hand to reach for mouse/touchpad/arrow keys - which inevitably slows you down.

Fennel

Spacehammer initially was written in Lua (as the majority of Hammerspoon configs), but later was completely re-written in Fennel - a tiny Lisp that compiles to Lua. There is nothing wrong with Lua, but Lisp has many benefits (sadly often overlooked and ignored by majority of programmers today). Switching to Fennel allowed us to keep the code more structured and concise.

Installation

Install Hammerspoon

You can use brew:

brew install hammerspoon

Install Fennel >= v1.0.0

brew install luarocks

luarocks install fennel

Older versions of Fennel are incompatible with Spacehammer.

Clone Spacehammer

git clone https://github.com/agzam/spacehammer ~/.hammerspoon

LEAD keybinding

LEAD is the main and major keybinding that invokes the main Spacehammer modal. By default it is set to Option+SPC, but it can be re-configured in ~/.spacehammer/config.fnl by changing the :mods and :key bindings for the lib.modal:activate-modal action string. You might want to set it, for example, to Ctrl+Shift+SPC.

If you want to use Cmd+SPC as LEAD you will have to rebind it in your system, since it is normally used for Spotlight.

Unbinding Cmd+SPC in system preferences.

Go to your Preferences/Keyboard, find Cmd+SPC keybinding and change it to something else. Unfortunately, simply disabling it sometimes is not enough. You’d have to set it to be something else e.g. Ctrl+Cmd+Shift+\ or anything else , it doesn’t really matter, since you can then un-check the checkbox and disable it.

Features

LEAD w - Window management

  • hjkl - moving windows around halves of the screen
  • Ctrl + hjkl - for jumping between application windows (handy for side by side windows)
  • w - jump to previous window
  • n/p - moving current window to prev/next monitor
  • Option + hjkl - moving in increments (works across monitors)
  • Shift + hjkl - re-sizing active window
  • g - re-sizing with hs.grid
  • m - maximize active window
  • c - center active window
  • u - undo last window operation (similar to Spacemacs’s SPC w u)

LEAD a - Apps (quick jump)

  • e - Emacs
  • g - Chrome
  • i - iTerm
  • s - Slack

you can add more, also try LEAD j j

LEAD SPC - open Alfred search bar

pressing SPC in the main modal takes you to Alfred search popup, pressing SPC in other modals returns to previous modal.

LEAD m - multimedia controls

Why not use media-keys?

a) because different external keyboards impose their own ways to control media.

b) because Spacehammer allows you to keep fingers on the home row.

By default LEAD m a - jump to music app is configured to work with Spotify, but you can change that in ~/.spacehammer/config.fnl

Edit anything [with Emacs]

You can edit any text in any app Cmd+Ctrl+O. Currently, it supports only Emacs. Read more here.

Other features

Alternative App Switcher Option n/p

Simple tab switcher for Chrome and iTerm Option j/k

Slack Desktop App enhancements

  • Scroll through current Slack thread Ctrl-j/Ctrl-k (slow) or Ctrl-e/Ctrl-y (fast)
  • Jump to the end of the thread with Cmd-g
  • Add emoji to the last message - Cmd-r (Slack’s default Cmd-Shift+\ is quite inconvenient)
  • Jump back and forth through history - Ctrl-o/Ctrl-i

Customizing

Update menus, menu items, bindings, and app-specific features

All menu, app, and key bindings are defined in ~/.spacehammer/config.fnl. That is your custom config and will be safe from any upstream changes to the default config.fnl. The reason to keep it in its own directory is so that it can be maintained in version-control in your own repo.

Modal Menu Items

Menu items are listed when you press LEAD and they can be nested.

Items map a key binding to an action, either a function or =”module:function-name”= string.

Menu items may either define an action or a table list of items.

For menu items that should be repeated, add repeatable: true to the item table. The repeatable flag keeps the menu option after the action has been triggered. Repeating a menu item is ideal for actions like window layouts where you may wish to move the window from the left third to the right third.

(local launch-alfred {:title  "Alfred"
                      :key    :SPACE
                      :action (fn [] (hs.appplication.launchOrFocus "Alfred"))})
(local slack-jump {:title  "Slack"
                   :key    :s
                   :action "slack:quick-switcher"})
(local window-inc {:title  "Window Halves"
                   :mods   [:cmd]
                   :key    :l
                   :action "windows:resize-inc-right"})
(local submenu {:title "Submenu"
                :key   :t
                :items [{:key    :m
                         :title  "Show a message"
                         :action (fn [] (alert "I'm a submenu action"))}]})
(local config {:items [launch-alfred
                       slack-jump
                       window-inc
                       submenu]})
Lifecycle methods

Menu items may also define :enter and :exit functions or action strings. The parent menu item will call the enter function when it is opened and exit when it is closed. This may be used to manage more complex or dynamic menus.

Global keys

Global keys are used to set up universal hot-keys for the actions you specify. Unlike menu items they do not require a title attribute. Additionally you may specify :repeat true to repeat the action while the key is held down.

If you place :hyper as a mod, it will use a hyper mode that can be configured by the hyper config attribute. This can be used to help create bindings that won’t interfere with other apps. For instance you may make your hyper trigger the virtual :F18 key and use a program like karabiner-elements to map caps-lock to F18.

(local config {:hyper {:key :F18}
               :keys  [{:mods   [:cmd]
                        :key    :space
                        :action "lib.modal:activate-modal"}
                       {:mods   [:cmd]
                        :key    :h
                        :action "chrome:prev-tab"
                        :repeat true}
                       {:mods   [:hyper]
                        :key    :f
                        :action (fn [] (alert "Haha you pressed f!"))}]})

App specific customizations

Configure separate menu options and key bindings while specified apps are active. Additionally, several lifecycle functions or action strings may be provided for each app.

  • :activate When an application receives keyboard focus
  • :deactivate When an application loses keyboard focus
  • :launch When an application is launched
  • :close When an application is terminated
(local emacs-config
       {:key "Emacs"
        :activate "vim:disable"
        :deactivate "vim:enable"
        :launch "emacs:maximize"
        :items []
        :keys []})

(local config {:apps [emacs-config]})

Replacing spacehammer behavior

The ~/.spacehammer directory is added to the module search paths. If you wish to change the behavior of a feature, such as vim mode, you can create ~/.spacehammer/vim.fnl to override the default implementation.

spacehammer's People

Contributors

agzam avatar aisamu avatar arthurkarganyan avatar grazfather avatar haochenx avatar hyunggyujang avatar jaawerth avatar jaidetree avatar kaii-zen avatar kevinjfoley avatar mattiaslundberg avatar valrus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

spacehammer's Issues

feature request: brew formula.

Would be nice to be able to just brew install spacehammer without having to separately install Hammerspoon, lua, fennel...

VIM Mode Feedback Wanted

Hello space travelers,

We would like to know if you have any interest in VIM mode and providing some feedback so we can deliver a strong feature set and cover the most use cases as possible. Please answer the questions below and feel free to add your own.

What would you like out a vim mode from spacehammer?
What problems could it help you solve?
What top 3 vim features would you like to see included?

We appreciate your help!

[BUG] Spacehammer can't find fennel.lua after updating to Hammerspoon 0.9.79

Reproduction

  1. Install the latest Hammerspoon application
  2. Restart Hammerspoon (close the app, or restart via auto-update)

Expected

Hammerspoon should continue to work

Actual

Hammerspoon breaks, will not load Spacehammer, and displays an error

2020-09-20 12:15:28: -- Lazy extension loading enabled
2020-09-20 12:15:28: -- Loading ~/.hammerspoon/init.lua
2020-09-20 12:15:28: -- Loading extension: alert
2020-09-20 12:15:28: *** ERROR: ...app/Contents/Resources/extensions/hs/_coresetup/init.lua:651: module 'fennel' not found:
	no field package.preload['fennel']
	no file '/Users/davelu/.hammerspoon/fennel.lua'
	no file '/Users/davelu/.hammerspoon/fennel/init.lua'
	no file '/Users/davelu/.hammerspoon/Spoons/fennel.spoon/init.lua'
	no file '/usr/local/share/lua/5.4/fennel.lua'
	no file '/usr/local/share/lua/5.4/fennel/init.lua'
	no file '/usr/local/lib/lua/5.4/fennel.lua'
	no file '/usr/local/lib/lua/5.4/fennel/init.lua'
	no file './fennel.lua'
	no file './fennel/init.lua'
	no file '/Applications/Hammerspoon.app/Contents/Resources/extensions/fennel.lua'
	no file '/Applications/Hammerspoon.app/Contents/Resources/extensions/fennel/init.lua'
	no file '/Users/davelu/.hammerspoon/fennel.so'
	no file '/usr/local/lib/lua/5.4/fennel.so'
	no file '/usr/local/lib/lua/5.4/loadall.so'
	no file './fennel.so'
	no file '/Applications/Hammerspoon.app/Contents/Resources/extensions/fennel.so'
stack traceback:
	[C]: in function 'rawrequire'
	...app/Contents/Resources/extensions/hs/_coresetup/init.lua:651: in function 'require'
	/Users/davelu/.hammerspoon/init.lua:16: in main chunk
	[C]: in function 'xpcall'
	...app/Contents/Resources/extensions/hs/_coresetup/init.lua:702: in function 'hs._coresetup.setup'
	(...tail calls...)

Thanks to @tntmarket for reporting this issue in #67.

Error Message on window management menu

I have installed spacehammer according to the instructions in the readme. The only changes I have made to my system were to modify line 349 of ~/.spacehammer/config.fnl to so that CMD+Space is the LEAD and to load spacehammer.el in my init.el file.

To reproduce: Focus a window and press LEAD + w
Expected Result: The window prompt appears
Actual result: The following is output to the Hammerspoon console and the Spacehammer overlay freezes until you focus the Hammerspoon app and press escape.

2020-05-13 22:16:37: 22:16:37 ERROR: LuaSkin: hs.hotkey callback: ./lib/functional.fnl:43: bad argument #1 to 'concat' (table expected, got nil) stack traceback: [C]: in function 'table.concat' ./lib/functional.fnl:43: in function 'lib.functional.join' ./lib/modal.fnl:107: in upvalue 'format_key' ./lib/modal.fnl:127: in upvalue 'f' ./lib/functional.fnl:137: in local 'f' ./lib/functional.fnl:117: in function 'lib.functional.reduce' (...tail calls...) ./lib/modal.fnl:132: in upvalue 'modal_alert' ./lib/modal.fnl:148: in upvalue 'show_modal_menu' ./lib/modal.fnl:229: in function <./lib/modal.fnl:215> (...tail calls...) ./lib/statemachine.fnl:65: in function <./lib/statemachine.fnl:47> (...tail calls...)

I'm brand new to Hammerspoon, so I suspect this is user error, but I do not see a Spacehammer community to ask this question in so I'm raising it as an issue.

Suggestion: require config opt-in to enable Opt+N, Opt+P bindings

These interfere with using Opt+N to add an accent character ("ñ"), and since the way Hammerspoon binds keys isn't visible from the System Preferences Keyboard panel, this can lead to surprising the user.

IIRC this binding wasn't present in a prior version of Spacehammer, so I was surprised 😄

Moving windows should span multiple monitors

Right now moving app windows with <Lead w h/j/k/l> moves them up to the edge of the display but no further. I think it should continue onto then next display (if available), creating an illusion of a single big screen estate area.

Memory leak

I regularly see high memory usage from Hammerspoon when running spacehammer (more than 1GB). I have seen this at two different machines (intel and m1 macbooks) running the default config or this config.

Tested on macos 11.1 and 11.2, hammerspoon 0.9.82 and spacehammer 7d9ffe1.

From what I can see the memory usage increases about 1MB when running Option-SPC a e a single time and never decreases after. Reloading the hammerspoon config from the console releases some memory, but not all. Only solution I've found is to fully restart hammerspoon to release the memory.

I don't know if this issue is in spacehammer/fennel/hammerspoon/something else but starting here if anyone has ideas on how to proceed!

Setting "hyper" to mods only crashes Hammerspoon

E.g. from the README:

(local config
       {:title "Main Menu"
        :items menu-items
        :keys  common-keys
        :enter (fn [] (windows.hide-display-numbers))
        :exit  (fn [] (windows.hide-display-numbers))
        :apps  apps
        :hyper {:mods [:shift :alt :cmd :ctrl]}})

Results in this error:

2021-03-25 18:22:12: *** ERROR: ...oon.app/Contents/Resources/extensions/hs/hotkey/init.lua:416: key must be a string or a number
stack traceback:
	[C]: in function 'error'
	...oon.app/Contents/Resources/extensions/hs/hotkey/init.lua:20: in upvalue 'getKeycode'
	...oon.app/Contents/Resources/extensions/hs/hotkey/init.lua:195: in function 'hs.hotkey.new'
	...oon.app/Contents/Resources/extensions/hs/hotkey/init.lua:416: in function 'hs.hotkey.bind'
	(...tail calls...)
	./core.fnl:98: in upvalue 'f'
	./lib/functional.fnl:144: in local 'f'
	./lib/functional.fnl:124: in function 'lib.functional.reduce'
	(...tail calls...)
	./core.fnl:100: in main chunk
	(...tail calls...)
	[C]: in function 'rawrequire'
	...app/Contents/Resources/extensions/hs/_coresetup/init.lua:651: in function 'require'
	/Users/isaachodes/.hammerspoon/init.lua:25: in main chunk
	[C]: in function 'xpcall'
	...app/Contents/Resources/extensions/hs/_coresetup/init.lua:702: in function 'hs._coresetup.setup'
	(...tail calls...)

edit-with-emacs: selection

Edit-with-Emacs future could be improved further. It would be nice to be able to detect if there's a selection already, instead of emulating "Select all" all the time. That would allow editing a pre-selected chunk of text. I don't know if there's a "proper" way of detecting if there's a selection. Perhaps we can read the clipboard, copy, compare currently copied with the previous thing, and somehow decide if it has to be done with Cmd+A and Cmd+C, or doing Cmd+C (without selecting all) is enough.

Default binding stomps over (presumably) popular ⌘+N for new window shortcut

More a wondering than an issue, explicitly: the default bindings for spacehammer use ⌘+N for alternative app switcher. Unless I'm missing something, there is consequently no shortcut left for making new windows. Is that something that you don't need in your workflow by default or do you fallback to a different means of making new windows in, e.g., your browser?

enable vim mode?

how does this work?
I've tried
{:vim {:enabled true}}
(vim.enable)
in config.fnl.
Thanks!

Tab switcher and local keys get deactivated sometimes

Sometimes Tab-switcher CMD+j/k for browser apps, gets deactivated seemingly for no apparent reason. Not sure what's causing it. For now the only workaround is to reload the Hammerspoon config.

upd: it seems App local keybindings, e.g. Slack specific keybindings stop working as well.

Patreon / sponsorship support?

I'd like to support spacehammer via Patreon or some other sort of sponsorship (open collective, etc.)

Do you have something like this?

Create a small, focused FRP stream library

There's quite a lot of async code in this library. Currently, it's managed with callbacks, but we all know how much fun those are, especially if you come from node.

I really like working with FRP streams such as RxJS, Highland JS, and Bacon so creating a more focused version for this project (and other people's fennel projects) could help us model complex interactions very trivially.

Intended interface

(local stream {:require stream})

(-> (stream.fromCallback fs.timer.doAfter 3)
    (stream.map (fn [_] "hello"))
    (stream.subscribe print))

With a few more constructors and some higher-order operations, we can do some really cool things to tie two or more otherwise unrelated async operations together.

For example:

(-> (stream.fromWatcher app-focus-watcher)
    (stream.combine (stream.fromCallback ts.eventtap.new [events.keyup]))
    (stream.map hs.inspect)
    (stream.subscribe print))

This would give us a stream of events where a user switches app focus and presses a key which is typically very complicated to model with callbacks and typically requires a lot of mutable state.

This will likely help improving more complex features like vim mode as well as even opening up greater possibilities for animation.

Animation with RxJS

I've started the research phase, learning about the Observer pattern which seems to be at the core of most stream implementations I've worked with.

If anyone can recommend an existing library or even tutorials on how to implement a library like that, it would be greatly appreciated.

Installation step missing?

I wanted to try this cool project out, but it looks like a step in the installation process is missing: it looks like you need to compile the fennel files. Is that right?

What I did

I didn't have luarocks or fennel installed, so I did this:

brew install luarocks
luarocks install fennel

git clone https://github.com/agzam/spacehammer ~/.hammerspoon

Then I reloaded the hammerspoon config with the button in the top-right. (Note I didn't reboot.)

Expected result

Cool spacehammer keybindings.

Actual result

2020-09-17 16:54:20: -- Lazy extension loading enabled
2020-09-17 16:54:20: -- Loading ~/.hammerspoon/init.lua
2020-09-17 16:54:20: -- Loading extension: alert
2020-09-17 16:54:20: *** ERROR: ...app/Contents/Resources/extensions/hs/_coresetup/init.lua:597: module 'core' not found:
	no field package.preload['core']
	no file '/Users/nielius/.hammerspoon/core.lua'
	no file '/Users/nielius/.hammerspoon/core/init.lua'
	no file '/Users/nielius/.hammerspoon/Spoons/core.spoon/init.lua'
	no file '/usr/local/share/lua/5.3/core.lua'
	no file '/usr/local/share/lua/5.3/core/init.lua'
	no file '/usr/local/lib/lua/5.3/core.lua'
	no file '/usr/local/lib/lua/5.3/core/init.lua'
	no file './core.lua'
	no file './core/init.lua'
	no file '/Applications/Hammerspoon.app/Contents/Resources/extensions/core.lua'
	no file '/Applications/Hammerspoon.app/Contents/Resources/extensions/core/init.lua'
	no file '/Users/nielius/.hammerspoon/core.so'
	no file '/usr/local/lib/lua/5.3/core.so'
	no file '/usr/local/lib/lua/5.3/loadall.so'
	no file './core.so'
	no file '/Applications/Hammerspoon.app/Contents/Resources/extensions/core.so'
stack traceback:
	[C]: in function 'rawrequire'
	...app/Contents/Resources/extensions/hs/_coresetup/init.lua:597: in function 'require'
	/Users/nielius/.hammerspoon/init.lua:19: in main chunk
	[C]: in function 'xpcall'
	...app/Contents/Resources/extensions/hs/_coresetup/init.lua:648: in function 'hs._coresetup.setup'
	(...tail calls...)

What resolved it for me

I added this makefile:

SOURCES := $(shell find ./ -type f -name '*.fnl')

TARGETS := $(patsubst %.fnl, %.lua, $(SOURCES))

all: $(TARGETS)

%.lua: %.fnl
	fennel --compile $< > $@

and ran make all.
(One file actually filed to compile, but that didn't seem to matter.)

P.S.

Awesome project! I look forward to exploring it more.

Modal doesn't cleanup

Sometimes modal keeps its hotkeys activated even after leaving it. For example in some cases "windows" modal stays active, and pressing h/j/k/l keys would move windows around and there's no other way to reset it but to reload the config. Very annoying

Can't load config in MacOS Big Sur

Hello,

I've always loved Spacemacs keybindings and was really looking forward to using this Hammerspoon config but unfortunately, I have yet to be able to get it running. I followed all of the install steps... Not sure if this is an issue in Big Sur, if so, I understand completely and will wait for the code to be updated. If not I'd really appreciate some guidance on how to fix this error. Thank you in advance!

2020-11-04 23:21:41: -- Lazy extension loading enabled
2020-11-04 23:21:42: -- Loading ~/.hammerspoon/init.lua
2020-11-04 23:21:42: -- Loading extension: alert
2020-11-04 23:21:42: *** ERROR: ...app/Contents/Resources/extensions/hs/_coresetup/init.lua:651: module 'fennel' not found:
	no field package.preload['fennel']
	no file '/Users/brick/.hammerspoon/fennel.lua'
	no file '/Users/brick/.hammerspoon/fennel/init.lua'
	no file '/Users/brick/.hammerspoon/Spoons/fennel.spoon/init.lua'
	no file '/usr/local/share/lua/5.4/fennel.lua'
	no file '/usr/local/share/lua/5.4/fennel/init.lua'
	no file '/usr/local/lib/lua/5.4/fennel.lua'
	no file '/usr/local/lib/lua/5.4/fennel/init.lua'
	no file './fennel.lua'
	no file './fennel/init.lua'
	no file '/Applications/Hammerspoon.app/Contents/Resources/extensions/fennel.lua'
	no file '/Applications/Hammerspoon.app/Contents/Resources/extensions/fennel/init.lua'
	no file '/Users/brick/.luarocks/share/lua/5.4/fennel.lua'
	no file '/Users/brick/.luarocks/share/lua/5.4/fennel/init.lua'
	no file '/Users/brick/.luarocks/share/lua/5.3/fennel.lua'
	no file '/Users/brick/.luarocks/share/lua/5.3/fennel/init.lua'
	no file '/Users/brick/.hammerspoon/fennel.so'
	no file '/usr/local/lib/lua/5.4/fennel.so'
	no file '/usr/local/lib/lua/5.4/loadall.so'
	no file './fennel.so'
	no file '/Applications/Hammerspoon.app/Contents/Resources/extensions/fennel.so'
	no file '/Users/brick/.luarocks/lib/lua/5.4/fennel.so'
	no file '/Users/brick/.luarocks/lib/lua/5.3/fennel.so'
stack traceback:
	[C]: in function 'rawrequire'
	...app/Contents/Resources/extensions/hs/_coresetup/init.lua:651: in function 'require'
	/Users/brick/.hammerspoon/init.lua:22: in main chunk
	[C]: in function 'xpcall'
	...app/Contents/Resources/extensions/hs/_coresetup/init.lua:702: in function 'hs._coresetup.setup'
	(...tail calls...)

Hotkeys to launch different modals

Is it possible to add to the below (basically, have multiple hotkeys which launch different modals)?

(local common-keys
        {:mods [:cmd :shift :alt :ctrl]
         :key :space
         :title "Apps"
         :items app-bindings})

(local config
       {:title "Main Menu"
        :items menu-items
        :keys  common-keys})

Feature request: advising + defadvice implementation

One aspect of Spacehammer that could stand to be improved is the ability to customize the implementation behavior of current or new features. For instance in an issue like #66 is going to require enhancing some core modal functions to change how bindings are created.

One option would be to design and architect some kind of hook or event system but then extra work will need to be done to update the system to use it, and it's a whole new API.

Another option would be to support more functions in the config.fnl. However, like the previous system we'll need to write checks for it in the relevant parts, additionally it muddles the purpose of config.fnl as it's now responsible for defining your configuration of spacehammer and the implementation.

My proposal would be to create a library similar to emacs-lisp advising functions, especially the doom emacs defadvice! macro.

An example from my dotconfig at https://github.com/eccentric-j/dotfiles/blob/c457fcba768f91a9fe2b7464221c97ba4dc3ad46/client/doom.d/config.el#L200-L206:

(defadvice! j/fix-js-multi-line-indent ()
  "Indent expression declarations by 2 just like the rest of the code"
  :after-while #'js--multi-line-declaration-indentation
  (let ((beg (match-beginning 0)))
    (when beg
      (goto-char beg)
      (+ js-indent-level (current-column)))))

Here I'm defining a function that fires after the js--multi-line-declaration-indentation function. Depending on the keyword used to represent how we're advising the function, I'll receive the original args sent to the function or the original function as the first argument.

Benefits

  • You can define advice before a function is defined
  • When inspecting the original function it will tell you that it's being advised and points to the new advising function
  • If adapted to fennel, we could also localize our implementation customizations
  • It's only marginally different from defining a function of the same name to replace it. This means less custom APIs to architect and maintain.
  • We only have to swap (fn ...) with the name of the macro that registers the function with the advising state so (fn) becomes either (advisable-fn ...) or (defn ...) to add this to existing functionality.
  • Provides the full power of fennel\lua at your disposal versus trying to build a custom API for specific behaviors in the system.

Tradeoffs

  • There is a bit more overhead in the state and dispatching but we can measure it with the time macro to make sure it's not too slow. Based on previous performance tests, I doubt it will have a noticable impact beyond a fraction of a ms.
  • Unlike emacs, system is enabled globally so all functions can be advised. In fennel will need to use a macro or pass a function to the register API to make it advisable.
  • May not be the most common in fennel but may be something that is very easy to incorporate into the existing functionality to make it extendable.
  • Could throw new users off if they are trying to debug when a function is called. However a.) New users likely won’t be using it early on b.) we will need to avoid using it in the default config other than registering advisable functions c.) We can enable a debug flag for logging the advising calls so it’s more clear.

API

  • add-advice! function to advise a function
  • remove-advice! function to remove an advising function
  • defadvice! macro to define a function and call add-advice!.
  • A advisable-fn or defn macro to register functions as advisable and create a handler to dispatch to advising functions.

toggle app?

how might I be able to write a toggle-app fn? specifically, I'd like cmd+k to show the kitty app if it's not the current screen, else hide it. I'm trying to reproduce something like kovidgoyal/kitty#45 (comment) but I'm using spacehammer :) I'd appreciate any pointers. Thanks!

I have

(fn toggle-app
  [app-name]
  (hs.application.launchOrFocus app-name)
  (let [app (hs.application.find app-name)]
    (when app
      (if (: app :isFrontmost)
        (: app :hide)
        (
          (: app :activate) ;; not sure if I need both
          (: app :unhide))))))

(fn toggler
  [app-name]
  (fn toggle []
    (toggle-app app-name)))

;; in common-keys
{:mods [:cmd]
         :key :k
         :repeatable false
         :action (toggler "Kitty")}

the window flashes- seems to activate then hide immediately- when I press cmd+k.

Modals have some delay

If I hit my keyboard shortcuts quickly enough (which I typically do), there is a <1s delay between hitting e.g. a in the first modal and the app switcher coming up in the next modal. In practice this means I have to wait between hitting keys in the modals of Spacehammer.

Fennel 0.4.0-1 broke windows modal

Seems some breaking changes in 0.4.0 Fennel.

To repro simply get to "windows" modal, LEAD w, results in this:

2020-05-13 12:45:46: 12:45:46 ERROR:   LuaSkin: hs.hotkey callback: ./lib/functional.fnl:43: bad argument #1 to 'concat' (table expected, got nil)
stack traceback:
    [C]: in function 'table.concat'
    ./lib/functional.fnl:43: in function 'lib.functional.join'
    ./lib/modal.fnl:107: in upvalue 'format_key'
    ./lib/modal.fnl:127: in upvalue 'f'
    ./lib/functional.fnl:137: in local 'f'
    ./lib/functional.fnl:117: in function 'lib.functional.reduce'
    (...tail calls...)
    ./lib/modal.fnl:132: in upvalue 'modal_alert'
    ./lib/modal.fnl:148: in upvalue 'show_modal_menu'
    ./lib/modal.fnl:229: in function <./lib/modal.fnl:215>
    (...tail calls...)
    ./lib/statemachine.fnl:65: in function <./lib/statemachine.fnl:47>
    (...tail calls...)

Errors while loading ~/.spacehammer/config.fnl

Seeing this:

2020-01-27 10:00:14: *** ERROR: Compile error in 'local' /Users/agibragimov/.spacehammer/config.fnl:340: expected name and value
stack traceback:
	[C]: in function 'error'
	/usr/local/share/lua/5.3/fennel.lua:484: in upvalue 'assertCompile'
	/usr/local/share/lua/5.3/fennel.lua:1557: in local 'special'
	/usr/local/share/lua/5.3/fennel.lua:1002: in function 'fennel.compile1'
	/usr/local/share/lua/5.3/fennel.lua:2160: in function 'fennel.compileStream'
	(...tail calls...)
	/usr/local/share/lua/5.3/fennel.lua:2286: in function 'fennel.eval'
	(...tail calls...)
	[C]: in function 'rawrequire'
	...app/Contents/Resources/extensions/hs/_coresetup/init.lua:597: in function 'require'
	./core.fnl:99: in main chunk
	(...tail calls...)
	[C]: in function 'rawrequire'
	...app/Contents/Resources/extensions/hs/_coresetup/init.lua:597: in function 'require'
	/Users/agibragimov/.hammerspoon/init.lua:6: in main chunk
	[C]: in function 'xpcall'
	...app/Contents/Resources/extensions/hs/_coresetup/init.lua:648: in function 'hs._coresetup.setup'
	(...tail calls...)

Setting hyper to mods doesn't work

The docs contain an example of binding the hyper key as a combination of modifiers:

{:hyper {:mods [:cmd :ctrl :alt :shift]}

However, when I try to do this in my own config, I get an error:

2020-06-08 22:43:59: *** ERROR: ...oon.app/Contents/Resources/extensions/hs/hotkey/init.lua:408: key must be a string or a number
stack traceback:
	[C]: in function 'error'
	...oon.app/Contents/Resources/extensions/hs/hotkey/init.lua:20: in upvalue 'getKeycode'
	...oon.app/Contents/Resources/extensions/hs/hotkey/init.lua:195: in function 'hs.hotkey.new'
	...oon.app/Contents/Resources/extensions/hs/hotkey/init.lua:408: in function 'hs.hotkey.bind'
	(...tail calls...)
	./core.fnl:98: in upvalue 'f'
	./lib/functional.fnl:144: in local 'f'
	./lib/functional.fnl:124: in function 'lib.functional.reduce'
	(...tail calls...)
	./core.fnl:100: in main chunk
	(...tail calls...)
	[C]: in function 'rawrequire'
	...app/Contents/Resources/extensions/hs/_coresetup/init.lua:597: in function 'require'
	/Users/valrus/.hammerspoon/init.lua:19: in main chunk
	[C]: in function 'xpcall'
	...app/Contents/Resources/extensions/hs/_coresetup/init.lua:648: in function 'hs._coresetup.setup'
	(...tail calls...)

I'm not sure as I don't really know Fennel, but it looks like hyper.fnl requires the :hyper table to have a :key (in init).

Slow loading of window behaviors

Reproduction

  1. Press cmd-space j or cmd-space w cmd+j

Expected

The command should run immediately and the alert window should close immediately

Actual

Those commands take a moment to fire and the alert window lingers for a second or two.

Suggestion: show modal on all displays not just one

Given an open laptop connected to an external display (or several?), I found that the modal appeared only on the laptop's screen and not the external display. I think it would be better if the modal appeared on all displays so you would always see it, no matter which screen you are looking at.

I have a small edit that does this, that's working for me locally, and I'd be glad to submit a pull request if there is interest.

(Also, many thanks for this wonderful project! I am really enjoying the ways it is improving my time spent staring at screens and pushing buttons.)

Opening Alfred through direct shortcut seems to disable shortcuts globally

Repro steps:

cmd + space => Spacehammer => space => alfred
Shortcuts seem to work fine. For example, I can I can use cmd + w to close tabs in a browser.

I had also bound alfred previously to opt + spacebar per default instructions. However, if I use alfred directly without going through Spacehammer, cmd + w to close tabs stops working.

My guess is it's related to some app specific shortcuts, but I haven't had time to debug. Hammerspoon console doesn't print anything when I invoke Alfred directly and modal state remains idle.

Implement FSM configuration

Modals switching mechanism can be implemented as a finite state machine, that would make it easier to add new modals.

:exit function doesn't execute all the time

:exit function of a modal seems to be firing only when explicitly exiting modal. e.g.:

(local menu-items
  {:key   :w
   :title "Window"
   :enter (fn [] (print "enter"))
   :exit  (fn [] (print "exit"))
   :items window-bindings}

When pressed Alt+SPC w - it prints "enter", when "Esc" - prints "exit". But if instead of pressing "Esc" (while in the modal) - you press "SPC" - it would go back to previous modal, but won't fire :exit handler, and it should.

Any way to swallow all keys while the modal is up?

Hi!

Thanks for making spacehammer!

I've been wondering if there's a way to eat all keys while the modal is up (after I press the LEADER key). Sometimes I press a non-mapped key, and that gets passed through to the window in focus instead of doing something in the modal; is it currently possible to make all pressed keys go to spacehammer?

Thank you!
Evan

Modal for VIM keybindings

I love this product. Thanks for building this!

It will be great if there is a new mode with basic VIM normal mode keybindings.
Let's call it VIM mode.
Once user is in this mode, we can initially have 'hjkl' mapped to respective arrow keys.
For Example, If I press 'h' key when this mode is enabled, right arrow key should be triggered.

After 'hjkl' we can add more bindings.

I will work on this mode and send a pull request if you are ok with this idea.

How do I get the edit with emacs to work with doom?

First of all, great work!

I'm keen to get this working with doom. I have the below in my config:

(defun on-spacehammer-edit-with-emacs (buffer-name pid title)
  (with-current-buffer (get-buffer buffer-name)
    ;; (spacemacs/evil-search-clear-highlight)
    ;; (spacemacs/toggle-visual-line-navigation-on)
    (markdown-mode)
    (evil-insert 1)))

(use-package! spacehammer
  :demand t
  :load-path "~/git/dotfiles/keyboard/spacehammer" ; this is the spacehammer.el file is
  :config
  (add-hook 'edit-with-emacs-hook 'on-spacehammer-edit-with-emacs))

I'm not sure what to do about the spacemacs related functions.

So far the ctrl+cmd+O works to open in an emacs session, however it opens in Funamental-mode and the C-c C-c binding gives and error: Symbol's function definition is void: spacemacs/copy-whole-buffer-to-clipboard.

Any help would be greatly appreciated.

feature request: layouts

Would be nice to have window layouts or workspaces that can be persisted and restored at will.

Not working

I'm not sure is the spacehammer bug on my mac or I just did totally wrong. I downloaded the latest version of Hammerspoon ( 0.9.52 ), then clone this repo. after restart os, when I press CMD SPC nothing happened. Did i missing some thing?

emacs opens a new instance instead of focusing existing one

When activating Emacs, spacehammer opens a new instance instead of focusing on an existing one.
I'm using the default config and a vanilla emacs setup.

My emacs 28 was installed by building it with build-emacs-for-macos, and symlinked to /Applications/Emacs.app.
I open emacs by running emacs &! on my zsh.

Is there anything suspicious in my setup that could lead to this issue?
I do have a server running and can connect to it via emacsclient.

Other potentially relevant info:
Hammerspoon latest (0.9.86)
spacehammer latest (81ca4e0)
MacOS Catalina 10.15.7

Outsourcing modules? Integration with other plugins

Hi! I overjoyed by finding this superb project! I love the philosophy with which this set of package made, and I’m very happy, as an Emacs and Nix user, it will complete my missing part of current environment, that is, the managing the overall dynamic behavior outside of Emacs. I think the static one is handled by Nix for good.

Currently I’m using the combination of yabai + skhd, however, which mostly covers only for the window and some quick jumps to the specific applications, not the rest. So for the rest, e.g. key remapping, etc. should be handled yet another application, in my case, karabiner.

To integrate those decentralized ones into one ecosystem, I thought Hammerspoon will do the right job for me, meanwhile, I also found this repository. I love the idea exploiting Lisp’s flexibility and willing to learn fennel for this amazing ecosystem, spacehammer, so I’m thinking about to migrate to this over other combination of applications, which lack of consistency.

My question is, as a newbie for the Lua and Hammerspoon, is it feasible or planned to incorporate other Spoons, say hhtwm? What I’m missing most is the yabai’s tiling feature and the ability to move an application to a specified space, which seems not implemented in spacehammer (please teach me if I’m wrong about it!).

Plus, I believe allowing other existing plugins to be merged into spacehammer easily, it can be a huge momentum for it.

Thanks for your awesome contribution!

Best regards,

Hyunggyu Jang

Multiple mod keys break command?

I'm sure this is just a problem on my end but I can't seem to sort it out.

Here's my code

;; Select all the way left ;; {:mods [:cmd :shift] :key :h :action (key-fn [:cmd :shift] :left) :repeat true}

If i change :mods [:cmd :shift] to mods [:ctrl] the shortcut works as expected. Am I using multiple mods incorrectly?

Typo in init.lua file?

While exploring this repo, I was reading the init.lua file and came across what seemed like a typo, so I figured I'd open an issue just in case.

Is package.loadeers supposed to be package.loaders? See line 4 of init.lua

Sorry if this is noise.

Slack scroll-up and scroll-down are backwards

(fn scroll-to-bottom
  []
  (windows.set-mouse-cursor-at :Slack)
  (hs.eventtap.scrollWheel [0 -20000] {}))
...
(fn scroll-up
  []
  (scroll-slack -3))

(fn scroll-down
  []
  (scroll-slack 3))

We can see here that scroll-down has a positive direction, while scroll-to-bottom is negative. Clearly these should have the same sign, and it seems that scroll-to-bottom works as expected.

Local app-keys are leaking

Sometimes app-local-keys would not get deactivated when switching from one app to another. The only workaround is to switch back to the previous app and return to current again. This is really annoying.

Not starting after hammerspoon 0.9.80

After updating to hammerspoon 0.9.80 spacehammer fails to load, have tried both with my configuration and the default one. I get the following error message:

2020-09-22 13:54:37: -- Loading ~/.hammerspoon/init.lua
2020-09-22 13:54:37: -- Loading extension: alert
2020-09-22 13:54:37: *** ERROR: ...app/Contents/Resources/extensions/hs/_coresetup/init.lua:651: module 'fennel' not found:
	no field package.preload['fennel']
	no file '/Users/mattias/.hammerspoon/fennel.lua'
	no file '/Users/mattias/.hammerspoon/fennel/init.lua'
	no file '/Users/mattias/.hammerspoon/Spoons/fennel.spoon/init.lua'
	no file '/usr/local/share/lua/5.4/fennel.lua'
	no file '/usr/local/share/lua/5.4/fennel/init.lua'
	no file '/usr/local/lib/lua/5.4/fennel.lua'
	no file '/usr/local/lib/lua/5.4/fennel/init.lua'
	no file './fennel.lua'
	no file './fennel/init.lua'
	no file '/Applications/Hammerspoon.app/Contents/Resources/extensions/fennel.lua'
	no file '/Applications/Hammerspoon.app/Contents/Resources/extensions/fennel/init.lua'
	no file '/Users/mattias/.luarocks/share/lua/5.4/fennel.lua'
	no file '/Users/mattias/.luarocks/share/lua/5.4/fennel/init.lua'
	no file '/Users/mattias/.luarocks/share/lua/5.3/fennel.lua'
	no file '/Users/mattias/.luarocks/share/lua/5.3/fennel/init.lua'
	no file '/Users/mattias/.hammerspoon/fennel.so'
	no file '/usr/local/lib/lua/5.4/fennel.so'
	no file '/usr/local/lib/lua/5.4/loadall.so'
	no file './fennel.so'
	no file '/Applications/Hammerspoon.app/Contents/Resources/extensions/fennel.so'
	no file '/Users/mattias/.luarocks/lib/lua/5.4/fennel.so'
	no file '/Users/mattias/.luarocks/lib/lua/5.3/fennel.so'
stack traceback:
	[C]: in function 'rawrequire'
	...app/Contents/Resources/extensions/hs/_coresetup/init.lua:651: in function 'require'
	/Users/mattias/.hammerspoon/init.lua:22: in main chunk
	[C]: in function 'xpcall'
	...app/Contents/Resources/extensions/hs/_coresetup/init.lua:702: in function 'hs._coresetup.setup'
	(...tail calls...)

Downgrading to 0.9.78 solves the issue. Have tried to reinstall luarocks and fennel with brew but that doesn't help.

I'm not sure if this is something that's broken on my machine or if it's a bug somewhere, any assistance to figure this out is appreciated!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.