Add support for sorting and aligning LANGUAGE pragmas #201

Open
opened 2018-11-15 15:53:23 +01:00 by ruhatch · 4 comments
ruhatch commented 2018-11-15 15:53:23 +01:00 (Migrated from github.com)

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.

I'd like to see similar behaviour to `stylish-haskell` where the pragmas are sorted, `nub`ed, 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.
lspitzner commented 2018-11-19 21:18:38 +01:00 (Migrated from github.com)

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 621e00bf3f/src/Language/Haskell/Brittany/Internal.hs (L97-L114)

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. ~~~~.hs {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeFamilies #-} ~~~~ turns into ~~~~.hs {-# LANGUAGE DataKinds #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeFamilies #-} ~~~~ 2) We could treat "commented-out" ones like regular ones with respect to sorting: ~~~~.hs -- {-# LANGUAGE MonadComprehensions #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE RecursiveDo #-} ~~~~ turns into ~~~~.hs {-# LANGUAGE LambdaCase #-} -- {-# LANGUAGE MonadComprehensions #-} {-# LANGUAGE RecursiveDo #-} ~~~~ and also: 3) collapse commented/not commented pragmas: ~~~~.hs -- {-# LANGUAGE LambdaCase #-} {-# LANGUAGE LambdaCase #-} ~~~~ turns into ~~~~.hs {-# LANGUAGE LambdaCase #-} ~~~~ 4) transpose pragma-of-list into list-of-pragma ~~~~.hs {-# LANGUAGE LambdaCase, RecursiveDo, MonadComprehensions #-} ~~~~ turns into ~~~~.hs {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MonadComprehensions #-} {-# LANGUAGE RecursiveDo #-} ~~~~ btw this is a good case against any line-based approach: the input might be ~~~~.hs {-# 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
ruhatch commented 2018-11-20 17:47:35 +01:00 (Migrated from github.com)

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!

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!
drewboardman commented 2019-11-10 18:24:37 +01:00 (Migrated from github.com)

Any news on the progress of this?

Any news on the progress of this?
eborden commented 2019-11-11 17:15:57 +01:00 (Migrated from github.com)

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

Just adding an note that this should only be in effect for appropriate `ColumnAlignMode`s :wink:
Sign in to join this conversation.
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: hexagoxel/brittany#201
There is no content yet.