Personal tools

Emacs/Indentation

From HaskellWiki

< Emacs(Difference between revisions)
Jump to: navigation, search
(First commit.)
 
(Aligning code)
(6 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 +
[[Category:Emacs|*]]
 +
{{Haskell infobox}}
 +
 +
= 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 73: Line 78:
  
 
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 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.
 +
 +
<code>C-x r o</code> is "Open Rectangle".  It will shift any text within the rectangle to the right side.  Also see:
 +
 +
<code>C-x r t</code> 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.
 +
 +
<code>C-x r d</code> is "Delete Rectangle".  It will delete the contents of the rectangle and move anything on the right over.
 +
 +
<code>C-x r r</code> is "Copy Rectangle to Register".  It will prompt you for a register number so it can save it for later.
 +
 +
<code>C-x r g</code> is "Insert register".  This will insert the contents of the given register, overwriting whatever happens to be within the target rectangle. (So make room)
 +
 +
<code>C-x r k</code> is "Kill rectangle".  Delete rectangle and save contents for:
 +
 +
<code>C-x r y</code> is "Yank rectangle".  This will insert the contents of
 +
the last killed rectangle.
 +
 +
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>.
 +
 +
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>
 +
  (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))))
 +
</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

Contents

1 Indentation Approaches

Emacs can indent Haskell in various ways. The most common is the tab cycle.

1.1 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.

1.2 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.

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