tweag / ormolu

A formatter for Haskell source code
https://ormolu-live.tweag.io
Other
958 stars 83 forks source link

Allow GHC DynFlags to passed directly to the API #639

Open lukel97 opened 4 years ago

lukel97 commented 4 years ago

Is your feature request related to a problem? Please describe. Currently in the Ormolu plugin in haskell-language-server, we have access to the GHC ParsedModule. The whole plugin is short enough to include here so here it is in all its glory:

provider :: FormattingProvider IO
provider _lf ideState typ contents fp _ = do
  let
    fromDyn :: ParsedModule -> IO [DynOption]
    fromDyn pmod =
      let
        df = ms_hspp_opts $ pm_mod_summary pmod
        pp =
          let p = D.sPgm_F $ D.settings df
          in  if null p then [] else ["-pgmF=" <> p]
        pm = map (("-fplugin=" <>) . moduleNameString) $ D.pluginModNames df
        ex = map (("-X" <>) . show) $ S.toList $ D.extensionFlags df
      in
        return $ map DynOption $ pp <> pm <> ex

  m_parsed <- runAction "Ormolu" ideState $ getParsedModule fp
  fileOpts <- case m_parsed of
          Nothing -> return []
          Just pm -> fromDyn pm

  let
    fullRegion = RegionIndices Nothing Nothing
    rangeRegion s e = RegionIndices (Just s) (Just e)
    mkConf o region = defaultConfig { cfgDynOptions = o,  cfgRegion = region }
    fmt :: T.Text -> Config RegionIndices -> IO (Either OrmoluException T.Text)
    fmt cont conf =
      try @OrmoluException (ormolu conf (fromNormalizedFilePath fp) $ T.unpack cont)

  case typ of
    FormatText -> ret <$> fmt contents (mkConf fileOpts fullRegion)
    FormatRange r ->
      let
        Range (Position sl _) (Position el _) = normalize r
      in
        ret <$> fmt contents (mkConf fileOpts (rangeRegion sl el))

One hairy thing is that although we have direct access to the ParsedModule's DynFlags, we still need to print it to a string with this hacky fromDyn flag, to then parse to Ormolu.

Describe the solution you'd like To avoid this workaround it would be nice if the public API exposed a field that allowed DynFlags to be set directly instead of doing this roundtrip from DynFlags to strings back to DynFlags, which seem to get converted back here: https://github.com/tweag/ormolu/blob/35f4fb39326ab7d4847c5c8940f4d4185f3d3d24/src/Ormolu/Parser.hs#L193-L207

Something like

data Config region = Config
  { -- | Dynamic options to pass to GHC parser
    cfgDynOptions :: !(Either DynFlags [DynOption]),
    -- | Do formatting faster but without automatic detection of defects
    cfgUnsafe :: !Bool,
    -- | Output information useful for debugging
    cfgDebug :: !Bool,
    -- | Checks if re-formatting the result is idempotent
    cfgCheckIdempotence :: !Bool,
    -- | Region selection
    cfgRegion :: !region
  }
  deriving (Eq, Show, Functor)
lukel97 commented 4 years ago

Even if these were the ghc-lib-parser DynFlags and not GHC's, it would still be something to work with

mrkkrp commented 4 years ago

Can you open a PR?