6

snakemake is a python-like replacement for make that is geared more towards workflows than compilation. It's quite nice, but also quite new, and I cannot seem to find a mode for it in Emacs. I just want something very simple: a very slight modification from fundamental-mode, so I in perusing the emacs manual, I started the following in init.el:

(define-derived-mode snake-mode fundamental-mode
   ...
)

like make, snakemake is strict about indents (actual tab "\t" characters, not how emacs behaves by default when one types TAB). When I instead type "C-q TAB" it puts a real tab character in the buffer : this works, I tried it with a Snakefile in fundamental-mode and it runs perfectly. So to avoid typing "C-q TAB" each time I want a TAB in this mode, the first addition I would like to make to snake-mode is to rebind the TAB key to "C-q TAB" (or something like this). So I perused the emacs manual and tried:

(define-derived-mode snake-mode fundamental-mode
  (global-set-key (kbd "\t") (kbd "C-q \t"))
  ...
)

but this and other alternatives aren't working ... maybe rebinding standard keys like the TAB key is not a recommended practice?

the other addition to the snake-mode I would like is for it to highlight syntax according to python (but not have any python behaviour, e.g., python indenting behaviour)

To conclude, just these 2 simple modifications to fundamental-mode in creating a "snake-mode" and a way to also invoke snake-mode if the filename is "Snakefile" was all I was looking for, but I've already spent several hours perusing the emacs manual and doing some googling, and it seems I'm not even close. This is so simple, and I'm quite sure it is possible; any advice?

Thanks

Murray

Johannes Köster
  • 1,809
  • 6
  • 8

4 Answers4

5

Your prayers have been answered:

https://github.com/kyleam/snakemake-mode

I am very happy with it.

Can be gotten from melpa as snakemake-mode.

The Unfun Cat
  • 29,987
  • 31
  • 114
  • 156
4

The following (somewhat elegant, I think ... well at least it is short) kludge does the trick for now. It indeed does the two things I was asking for, i.e., (1) rebinds the TAB key (in a nice way), and (2) does syntax highlighting according to python (plus it only goes to this mode when the file is called "Snakefile", which is nice), and hence this answers my question

; snake-mode
(add-to-list 'auto-mode-alist '("Snakefile" . snake-mode))

(defun insert-tab ()
  (interactive)
  (insert "     ")) ; a "real" tab, i.e., what "C-q \t" would give

(define-minor-mode snake-mode
  "Snakemake."
  :lighter " snake-make"
     (python-mode)
     (setq indent-line-function 'insert-tab)
)

; how to hard-code "\t" to a "real" tab (not recommended)
; (global-set-key "\t" `insert-tab)

; end snake-mode

How elegant this is, is, I'm sure, up for debate. And it is only a start on a journey for a proper mode for snakemake (that does highlighting for snakemake specific words like "rule" and "output:", etc., etc.)

  • A small addendum to this "solution": the "insert-tab" function is already defined in emacs, so one should rather (defun snake-tab () ... or some other name that is not already in the namespace – Murray Patterson Jun 26 '13 at 13:34
1

The define-derived-mode macro automatically provides a keymap named after the mode it defines. You can use that together with define-key to make the TAB key simply insert a tab like this:

(define-derived-mode snake-mode fundamental-mode "Snake"
  "A mode for Python's snakemake."
  (define-key snake-mode-map "\t" 'self-insert-command))

Alternatively, you could set up the indentation mechanism of your mode so that it intelligently indents a line by inserting a (single) TAB at the beginning of the line whenever that is appropriate. That way you don't have to rebind TAB, although of course it's much harder to implement intelligent indentation correctly than to simply rebind a key. See lunaryorn's answer for more information.

Thomas
  • 17,016
  • 4
  • 46
  • 70
1

Don't do this. It's not how major modes are supposed to handle indentation. They should never rebind TAB, see C-h v indent-line-function:

Function to indent the current line. This function will be called with no arguments. If it is called somewhere where auto-indentation cannot be done (e.g. inside a string), the function should simply return `noindent'. Setting this function is all you need to make TAB indent appropriately. Don't rebind TAB unless you really need to.

It won't work anyway, because you can't bind key bindings to other key bindings.

Instead, set indent-tabs-mode to t in your mode function, to make Emacs use tab characters for indentation, and set indent-line-function buffer-locally, to a function that indents appropriately according to the rules of the language. You have to write this function yourself, obviously.