Emacs/Indentation: Difference between revisions
(→Indentation using tab cycle: Added rectangle functions.) |
(Added aligning code notes.) |
||
Line 1: | Line 1: | ||
= Indentation Approaches = | |||
Emacs can indent Haskell in various ways. The most common is the tab cycle. | Emacs can indent Haskell in various ways. The most common is the tab cycle. | ||
Line 94: | Line 96: | ||
As with all Emacs modifier combos, you can type <code>C-x r C-h</code> to find out what keys are bound beginning with the <code>C-x r</code> prefix. | As with all Emacs modifier combos, you can type <code>C-x r C-h</code> to find out what keys are bound beginning with the <code>C-x r</code> prefix. | ||
== Aligning code == | |||
Emacs22 has a neat tool called: align-regexp. Select a region you want to align text within, M-x align-regexp, and type a regexp representing the alignment delimiter. | |||
For example, I often line up my Haddock comments: | |||
<haskell> | |||
f :: a -- ^ does a | |||
-> Foo b -- ^ and b | |||
-> c -- ^ to c | |||
</haskell> | |||
Select the region, and let the regexp be: <code>--</code> | |||
<haskell> | |||
f :: a -- ^ does a | |||
-> Foo b -- ^ and b | |||
-> c -- ^ to c | |||
</haskell> | |||
Of course, this works for just about anything. Personally, I've globally bound it to <code>C-x a r</code>: | |||
<code>(global-set-key (kbd "C-x a r") 'align-regexp)</code>. |
Revision as of 10:24, 18 May 2012
Indentation Approaches
Emacs can indent Haskell in various ways. The most common is the tab cycle.
Indentation using tab cycle
Haskell-mode offers intelligent indentation. As Haskell source code uses indentation aware code blocks, there is usually more than one column for which indentation makes sense.
Hit tab a few times to see a few different indentation possibilities.
For example, imagine the following is open in a haskell-mode buffer, where !
represents the point:
foo :: Int -> String
foo 0 = f 4 ++ s
where f 4 = "hello" ++
!
If you ask haskell-mode
to indent for you, where should it indent to? There are four basic options:
-
You want to finish off the expression you were writing in the last line.
Haskell-mode
indents to be underneath the"
character at the beginning of"hello"
:where f 4 = "hello" ++ !
This is debatably a bad choice as you'd probably want to indent a bit further in to make it clear that you were carrying on an expression, but the layout rule would accept something like the following:
where f 4 = "hello" ++ "world"
-
You want to add a second equation for
f
.Haskell-mode
will indent to line up with the first argument, and fill in thef
in the equation:where f 4 = "hello" ++ f !
This is an unlikely choice as the expression in the previous line isn't complete, but
haskell-mode
isn't smart enough to know that. (Iff
had been something without arguments, likewhere f = "hello"
, then it's impossible to have more than one equation and haskell-mode won't offer this indentation level.) -
You want to add a second binding to the
where
-block. Haskell-mode indents to line up with thef
:where f 4 = "hello" ++ !
- You want to start an entirely new top-level binding. Haskell-mode indents to the first column:
foo :: Int -> String foo 0 = f 4 ++ s where f 4 = "hello" ++ !
These four locations can be reached by repeatedly pressing TAB
. This is what's known as the tab-cycle. The innermost location is offered first, then cycling progresses outwards. Although this may seem like an inefficient system (and it is indeed a shame that Haskell's design didn't result in an unambiguous indentation system), you do quickly get used to the tab-cycle and indenting Haskell code.
Notes:
Do not use indent-region
Using indent-region is generally a bad idea on Haskell code, because it would need to know which of the tab-cycle stops you wish to choose for each line. The innermost one is chosen in each case, which often results in unusable code. Moral: just don't use indent-region with haskell-mode.
Using rectangular region commands
Emacs has a set of commands which operate on the region as if it were rectangular. This turns out to be extremely useful when dealing with whitespace sensitive languages.
C-x r o
is "Open Rectangle". It will shift any text within the rectangle to the right side. Also see:
C-x r t
is "String Rectangle". It will shift any text within the rectangle over to the right, and insert a given string prefixing all the lines in the region. If comment-region didn't already exist, you could use this instead, for example.
C-x r d
is "Delete Rectangle". It will delete the contents of the rectangle and move anything on the right over.
C-x r r
is "Copy Rectangle to Register". It will prompt you for a register number so it can save it for later.
C-x r g
is "Insert register". This will insert the contents of the given register, overwriting whatever happens to be within the target rectangle. (So make room)
C-x r k
is "Kill rectangle". Delete rectangle and save contents for:
C-x r y
is "Yank rectangle". This will insert the contents of
the last killed rectangle.
As with all Emacs modifier combos, you can type C-x r C-h
to find out what keys are bound beginning with the C-x r
prefix.
Aligning code
Emacs22 has a neat tool called: align-regexp. Select a region you want to align text within, M-x align-regexp, and type a regexp representing the alignment delimiter.
For example, I often line up my Haddock comments:
f :: a -- ^ does a
-> Foo b -- ^ and b
-> c -- ^ to c
Select the region, and let the regexp be: --
f :: a -- ^ does a
-> Foo b -- ^ and b
-> c -- ^ to c
Of course, this works for just about anything. Personally, I've globally bound it to C-x a r
:
(global-set-key (kbd "C-x a r") 'align-regexp)
.