haskell-mode icon indicating copy to clipboard operation
haskell-mode copied to clipboard

Alignment doesn't understand QualifiedDo

Open scuellar opened this issue 1 year ago • 1 comments

haskell mode does not recognize QualifiedDo notation and gets alignment wrong.

The following simple example, from the Qualified Do user guide, should align like

{-# LANGUAGE QualifiedDo #-}
import qualified Some.Module.Monad as M

action :: M.SomeType a
action = M.do x <- u
                   res
                   M.return x

But haskell-mod aligns it as

{-# LANGUAGE QualifiedDo #-}
import qualified Some.Module.Monad as M

action :: M.SomeType a
action = M.do x <- u
                                res
                                M.return x

Probably thinking that M.do is a function, and not special notation.

It currently accepts the following alignment which also parses correctly, but looks ugly.

action :: M.SomeType a
action =
  M.do
  x <- u
  res
  M.return x

Furthermore, since the notation is not recognized, it will get confused with other multi-line notation, like list. Such as in the following example, which Haskell mode will align to configurations that don't parse.

action :: M.SomeType a
action =
  M.do
  x <- u
  fooList
    [  M.do
       res1
    , M.do
      res2
    ]  
  M.return

scuellar avatar Jun 07 '24 14:06 scuellar

I hit the same problem

hellwolf avatar Mar 26 '25 00:03 hellwolf

This is my current workaround in a project specific basis:

  1. Add the "LVM.do" to haskell-indentation-expression-list), the same as how do is.
  2. override function haskell-indentation-peek-token with your additional qualified do.
(add-hook 'haskell-mode-hook
          (lambda ()
            (xref-etags-mode)
            ;; support LVM.do keyword
            (push
              `("LVM.do" . ,(apply-partially 'haskell-indentation-with-starter
                                             'haskell-indentation-expression-layout))
              haskell-indentation-expression-list)
            (defun haskell-indentation-peek-token ()
              "Return token starting at point."
              (cond ((looking-at "\\(if\\|then\\|else\\|let\\|in\\|mdo\\|rec\\|do\\|LVM.do\\|proc\\|case\\|of\\|where\\|module\\|signature\\|deriving\\|import\\|data\\|type\\|newtype\\|class\\|instance\\)\\([^[:alnum:]'_]\\|$\\)")
                     (match-string-no-properties 1))
                    ((looking-at "[][(){}[,;]")
                     (match-string-no-properties 0))
                    ((looking-at "\\(\\\\\\|->\\|<-\\|::\\|=\\||\\|=>\\)\\([^-:!#$%&*+./<=>?@\\\\^|~]\\|$\\)")
                     (match-string-no-properties 1))
                    ((looking-at "\\(→\\|←\\|∷\\|⇒\\)\\([^-:!#$%&*+./<=>?@\\\\^|~]\\|$\\)")
                     (let ((tok (match-string-no-properties 1)))
                       (or (cdr (assoc tok haskell-indentation-unicode-tokens)) tok)))
                    ((looking-at"[-:!#$%&*+./<=>?@\\\\^|~`]" )
                     'operator)
                    (t 'value)))
            ))

I will think a bit how to make this part of a PR to upstream.

hellwolf avatar Apr 16 '25 21:04 hellwolf