Run a language-specific formatter on save 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 an executable on disk) 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. The $file variable isn't expanded by the Sublime Text command exec. (As a side note, though exec will not expand variables, Sublime's build system process supports expansion in case that's useful to you for whatever else.)

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
        })

This 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",
    },
],

Update one: A better working alternative: use this custom command. It's a stand-in replacement for exec, except that it expands variables.

Update two: For Haskell specifically, you may be better off using LSP for formatting in Sublime.