Difference between revisions of "Emacs/Indentation"

From HaskellWiki
Jump to navigation Jump to search
Line 124: Line 124:
 
<code>(global-set-key (kbd "C-x a r") 'align-regexp)</code>.
 
<code>(global-set-key (kbd "C-x a r") 'align-regexp)</code>.
   
Note that you also just use these Haskell rules for use with the aligner:
+
Note that you also just use these rules for telling the aligner about Haskell. once you evaluate this, you can just use M-x align-code, which I like to bind to M-[.
   
 
<code>
 
<code>

Revision as of 22:49, 15 May 2013

Emacs for Haskell

Inferior Haskell processes
Automatic unit testing
Automatic building
API searching
Project navigation
Snippets
Literate programming

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:

  1. 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"
    
  2. You want to add a second equation for f. Haskell-mode will indent to line up with the first argument, and fill in the f 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. (If f had been something without arguments, like where f = "hello", then it's impossible to have more than one equation and haskell-mode won't offer this indentation level.)

  3. You want to add a second binding to the where-block. Haskell-mode indents to line up with the f:
    where f 4 = "hello" ++
          !
    
  4. 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).

Note that you also just use these rules for telling the aligner about Haskell. once you evaluate this, you can just use M-x align-code, which I like to bind to M-[.

 (add-to-list 'align-rules-list
              '(haskell-types
                (regexp . "\\(\\s-+\\)\\(::\\|∷\\)\\s-+")
                (modes quote (haskell-mode literate-haskell-mode))))
 (add-to-list 'align-rules-list
              '(haskell-assignment
                (regexp . "\\(\\s-+\\)=\\s-+")
                (modes quote (haskell-mode literate-haskell-mode))))
 (add-to-list 'align-rules-list
              '(haskell-arrows
                (regexp . "\\(\\s-+\\)\\(->\\|→\\)\\s-+")
                (modes quote (haskell-mode literate-haskell-mode))))
 (add-to-list 'align-rules-list
              '(haskell-left-arrows
                (regexp . "\\(\\s-+\\)\\(<-\\|←\\)\\s-+")
                (modes quote (haskell-mode literate-haskell-mode))))