Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement support for fractional precedence #1133

Merged
merged 1 commit into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

* Format multiple files in parallel. [Issue
1128](https://github.com/tweag/ormolu/issues/1128).
* Fractional precedences are now allowed in `.ormolu` files for more precise
control over formatting of complex operator chains. [Issue
1106](https://github.com/tweag/ormolu/issues/1106).

## Ormolu 0.7.7.0

Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,10 +184,16 @@ infixl 1 >>, >>=
infixr 1 =<<
infixr 0 $, $!
infixl 4 <*>, <*, *>, <**>

infixr 3 >~<
infixr 3.3 |~|
infixr 3.7 <~>
```

It uses exactly the same syntax as usual Haskell fixity declarations to make
it easier for Haskellers to edit and maintain.
it easier for Haskellers to edit and maintain. Since Ormolu 0.7.8.0
fractional precedences are supported for more precise control over
formatting of complex operator chains.

As of Ormolu 0.7.0.0, `.ormolu` files can also contain instructions about
module re-exports that Ormolu should be aware of. This might be desirable
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
startFormTok |~| messageTag
>~< startMessageTok |~| name
>~< p' |~| endMessageTok |~| endFormTok
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
startFormTok |~| messageTag
>~< startMessageTok |~| name
>~< p' |~| endMessageTok |~| endFormTok
Binary file modified extract-hackage-info/hackage-info.bin
Binary file not shown.
35 changes: 30 additions & 5 deletions src/Ormolu/Fixity/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ where

import Control.DeepSeq (NFData)
import Data.Binary (Binary)
import Data.Binary qualified as Binary
import Data.Binary.Get qualified as Binary
import Data.Binary.Put qualified as Binary
import Data.ByteString.Short (ShortByteString)
import Data.ByteString.Short qualified as SBS
import Data.Choice (Choice)
Expand Down Expand Up @@ -96,10 +99,20 @@ data FixityInfo = FixityInfo
{ -- | Fixity direction
fiDirection :: FixityDirection,
-- | Precedence
fiPrecedence :: Int
fiPrecedence :: Double
}
deriving stock (Eq, Ord, Show, Generic)
deriving anyclass (Binary, NFData)
deriving anyclass (NFData)

instance Binary FixityInfo where
put FixityInfo {..} = do
Binary.put fiDirection
Binary.putDoublele fiPrecedence

get = do
fiDirection <- Binary.get
fiPrecedence <- Binary.getDoublele
pure FixityInfo {..}

-- | Fixity info of the built-in colon data constructor.
colonFixityInfo :: FixityInfo
Expand All @@ -116,13 +129,25 @@ data FixityApproximation = FixityApproximation
faDirection :: Maybe FixityDirection,
-- | Minimum precedence level found in the (maybe conflicting)
-- definitions for the operator (inclusive)
faMinPrecedence :: Int,
faMinPrecedence :: Double,
-- | Maximum precedence level found in the (maybe conflicting)
-- definitions for the operator (inclusive)
faMaxPrecedence :: Int
faMaxPrecedence :: Double
mrkkrp marked this conversation as resolved.
Show resolved Hide resolved
}
deriving stock (Eq, Ord, Show, Generic)
deriving anyclass (Binary, NFData)
deriving anyclass (NFData)

instance Binary FixityApproximation where
put FixityApproximation {..} = do
Binary.put faDirection
Binary.putDoublele faMinPrecedence
Binary.putDoublele faMaxPrecedence

get = do
faDirection <- Binary.get
faMinPrecedence <- Binary.getDoublele
faMaxPrecedence <- Binary.getDoublele
pure FixityApproximation {..}

-- | Gives the ability to merge two (maybe conflicting) definitions for an
-- operator, keeping the higher level of compatible information from both.
Expand Down
4 changes: 3 additions & 1 deletion src/Ormolu/Fixity/Parser.hs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ pFixity = do
fiDirection <- pFixityDirection
hidden hspace1
offsetAtPrecedence <- getOffset
fiPrecedence <- L.decimal
fiPrecedence <-
try L.float
<|> (fromIntegral <$> (L.decimal :: Parser Integer))
when (fiPrecedence > 9) $
region
(setErrorOffset offsetAtPrecedence)
Expand Down
12 changes: 11 additions & 1 deletion src/Ormolu/Fixity/Printer.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import Data.Text.Lazy qualified as TL
import Data.Text.Lazy.Builder (Builder)
import Data.Text.Lazy.Builder qualified as B
import Data.Text.Lazy.Builder.Int qualified as B
import Data.Text.Lazy.Builder.RealFloat qualified as B
import Distribution.ModuleName (ModuleName)
import Distribution.ModuleName qualified as ModuleName
import Distribution.Types.PackageName
Expand All @@ -44,7 +45,7 @@ renderSingleFixityOverride (OpName operator, FixityInfo {..}) =
InfixR -> "infixr"
InfixN -> "infix",
" ",
B.decimal fiPrecedence,
renderPrecedence fiPrecedence,
" ",
if isTickedOperator operator
then "`" <> B.fromText operator <> "`"
Expand Down Expand Up @@ -75,3 +76,12 @@ renderSingleModuleReexport (exportingModule, exports) =

renderModuleName :: ModuleName -> Builder
renderModuleName = B.fromString . intercalate "." . ModuleName.components

-- | Render precedence using integer representation for whole numbers.
renderPrecedence :: Double -> Builder
renderPrecedence x =
let (n :: Int, fraction :: Double) = properFraction x
isWholeEnough = fraction < 0.0001
in if isWholeEnough
then B.decimal n
else B.realFloat x
mrkkrp marked this conversation as resolved.
Show resolved Hide resolved
22 changes: 22 additions & 0 deletions tests/Ormolu/Fixity/ParserSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ spec = do
`shouldParse` ( exampleFixityOverrides,
ModuleReexports Map.empty
)
it "accepts fractional operator precedences" $
parseDotOrmolu
""
( T.unlines
[ "infixr 3 >~<",
"infixr 3.3 |~|",
"infixr 3.7 <~>"
]
)
`shouldParse` ( fractionalFixityOverrides,
ModuleReexports Map.empty
)
it "combines conflicting fixity declarations correctly" $
parseDotOrmolu
""
Expand Down Expand Up @@ -231,6 +243,16 @@ exampleFixityOverrides =
]
)

fractionalFixityOverrides :: FixityOverrides
fractionalFixityOverrides =
FixityOverrides
( Map.fromList
[ (">~<", FixityInfo InfixR 3),
("|~|", FixityInfo InfixR 3.3),
("<~>", FixityInfo InfixR 3.7)
]
)

exampleModuleReexports :: ModuleReexports
exampleModuleReexports =
ModuleReexports . Map.fromList $
Expand Down
7 changes: 6 additions & 1 deletion tests/Ormolu/Fixity/PrinterSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@ instance Arbitrary FixityOverrides where
InfixR,
InfixN
]
fiPrecedence <- chooseInt (0, 9)
precedenceWholePart <- fromIntegral <$> chooseInt (0, 9)
precedenceFractionalPart <-
if precedenceWholePart < 9.0
then (* 0.1) . fromIntegral <$> chooseInt (0, 1)
else return 0
let fiPrecedence = precedenceWholePart + precedenceFractionalPart
return FixityInfo {..}

instance Arbitrary ModuleReexports where
Expand Down
5 changes: 4 additions & 1 deletion tests/Ormolu/PrinterSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ testsuiteOverrides =
FixityOverrides
( Map.fromList
[ (".=", FixityInfo InfixR 8),
("#", FixityInfo InfixR 5)
("#", FixityInfo InfixR 5),
(">~<", FixityInfo InfixR 3),
("|~|", FixityInfo InfixR 3.3),
("<~>", FixityInfo InfixR 3.7)
]
)

Expand Down