brittany icon indicating copy to clipboard operation
brittany copied to clipboard

Add support for sorting and aligning LANGUAGE pragmas

Open ruhatch opened this issue 6 years ago • 4 comments

I'd like to see similar behaviour to stylish-haskell where the pragmas are sorted, nubed, and aligned. I started looking into this a bit and the pragmas are stored as comments, so this might be a little complicated. @lspitzner I'd love some guidance on how to get started on this.

ruhatch avatar Nov 15 '18 14:11 ruhatch

thanks for the interest!

my first thought was: i bet there is a quick and dirty approach that works for 90% of cases that just works on the un-parsed lines and uses some variation of span ("{-# LANGUAGE" `isPrefixOf`) but after thinking a bit i think it might break quickly, see point 4) below.

Before talking any implementation, I would like to nail down the desired behaviour a bit more. Even for such a simple thing there are some corner-cases:

  1. I guess we want per-block sorting (and uniq). I.e.

    {-# LANGUAGE TypeFamilies #-}
    {-# LANGUAGE DataKinds #-}
    {-# LANGUAGE TypeFamilies #-}
    
    {-# LANGUAGE RankNTypes #-}
    {-# LANGUAGE TypeFamilies #-}
    

    turns into

    {-# LANGUAGE DataKinds #-}
    {-# LANGUAGE TypeFamilies #-}
    
    {-# LANGUAGE RankNTypes #-}
    {-# LANGUAGE TypeFamilies #-}
    
  2. We could treat "commented-out" ones like regular ones with respect to sorting:

    -- {-# LANGUAGE MonadComprehensions #-}
    {-# LANGUAGE LambdaCase #-}
    {-# LANGUAGE RecursiveDo #-}
    

    turns into

    {-# LANGUAGE LambdaCase #-}
    -- {-# LANGUAGE MonadComprehensions #-}
    {-# LANGUAGE RecursiveDo #-}
    

    and also:

  3. collapse commented/not commented pragmas:

    -- {-# LANGUAGE LambdaCase #-}
    {-# LANGUAGE LambdaCase #-}
    

    turns into

    {-# LANGUAGE LambdaCase #-}
    
  4. transpose pragma-of-list into list-of-pragma

    {-# LANGUAGE LambdaCase, RecursiveDo, MonadComprehensions #-}
    

    turns into

    {-# LANGUAGE LambdaCase #-}
    {-# LANGUAGE MonadComprehensions #-}
    {-# LANGUAGE RecursiveDo #-}
    

    btw this is a good case against any line-based approach: the input might be

    {-# LANGUAGE LambdaCase,
                 RecursiveDo,
                 MonadComprehensions #-}
    
  5. Apart from commented-out-pragmas, other comments are treated like whitespace (and consequently break up blocks).

    This includes any comments before at file start that precede pragmas.

I have somewhat intentionally omitted the alignment aspect again here, because it is another instance of "context sensitivity": Do we really want to touch all pragmas because we add -XGeneralizedNewtypeDeriving? (Or whatever is the longest pragma :p)

Assuming we are roughly in agreement on this behaviour, I think the implementation is just a plain recursion over the list of comments in the annotation. Must gather elements of a "blocks" (as discussed above); blocks are identified by DPs; for each completed block: stash the DP of the first comment and replace it with DP (1,0), sort, uniq, then restore the DP of the (new) first comment to the stashed one; continue on remaining comments.

The task of taking apart the pragma-of-list almost is sufficient reason to use some proper parser, but plain List functions will work too. No strong opinion - e.g. the existing extractCommentConfigs implementation is not pretty, but works without any additional dependencies *shrugs*, see https://github.com/lspitzner/brittany/blob/621e00bf3f24896d603978c3d4e5fd61dac3841a/src/Language/Haskell/Brittany/Internal.hs#L97-L114

lspitzner avatar Nov 19 '18 20:11 lspitzner

Thanks @lspitzner - I'm also working on a PR for sorting import lists and it uses a similar stash, sort, uniq, restore approach. I'll give it a go and get back to you!

ruhatch avatar Nov 20 '18 16:11 ruhatch

Any news on the progress of this?

drewboardman avatar Nov 10 '19 17:11 drewboardman

Just adding an note that this should only be in effect for appropriate ColumnAlignModes :wink:

eborden avatar Nov 11 '19 16:11 eborden