Run a language-specific formatter on save in Sublime
Update: I’ve seen a better working alternative. Use this custom
command. It’s a stand-in replacement for exec
, except that it
expands variables.
Update 2: For Haskell specifically, you may be better off using LSP for formatting in Sublime.
The Hooks package for
Sublime Text provides a hook called on_post_save_async_language
, which
you can use to run a Sublime Text command (mind, not directly a binary
on the computer) after a file is saved.
So, for example, to run the hindent
formatter for Haskell code, you
could put this in the “Settings - Syntax Specific” file for Haskell.
Note the $file
variable, which is intended to indicate the current
file.
"on_post_save_async_language": [
{
"command": "exec",
"args": { "cmd": ["hindent", "$file"] },
"scope": "window",
},
],
Unfortunately, this doesn’t work with Sublime Text build 4121, the
latest version right now. The $file
variable isn’t expanded by the
Sublime Text command exec
, though it is documented as being
supported (update: I’ve since learned that exec
will not expand
variables; Sublime’s build system process does the expansion before
calling exec
).
A working alternative is to write a custom command that can run
hindent
with the current view’s file. Place the following in a .py
file in Sublime’s Packages/User
directory.
import sublime
import sublime_plugin
class HindentCommand(sublime_plugin.WindowCommand):
def run(self):
filename = self.window.active_view().file_name()
if not filename:
print("hindent: no usable file_name(); not running")
return
self.window.run_command("exec", {
"cmd": ["hindent", filename],
"quiet": True
})
It sets up a Sublime Text command named “hindent”, which calls the
hindent
binary, still using exec like in the earlier code block. But
here, we can obtain the filename programmatically with the
file_name()
API, instead of relying on the $file
variable
expansion.
Now all that’s left is to call this custom command on save:
"on_post_save_async_language": [
{
"command": "hindent",
"scope": "window",
},
],