-
Notifications
You must be signed in to change notification settings - Fork 207
Change Hsimport to use configured Formatter on import #1167
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,3 @@ | ||
{-# LANGUAGE DeriveAnyClass #-} | ||
{-# LANGUAGE DeriveGeneric #-} | ||
{-# LANGUAGE OverloadedStrings #-} | ||
module Haskell.Ide.Engine.Plugin.Brittany where | ||
|
||
|
@@ -11,7 +9,6 @@ import Data.Coerce | |
import Data.Semigroup | ||
import Data.Text (Text) | ||
import qualified Data.Text as T | ||
import GHC.Generics | ||
import Haskell.Ide.Engine.MonadTypes | ||
import Haskell.Ide.Engine.PluginUtils | ||
import Language.Haskell.Brittany | ||
|
@@ -20,52 +17,62 @@ import qualified Language.Haskell.LSP.Types.Lens as J | |
import System.FilePath (FilePath, takeDirectory) | ||
import Data.Maybe (maybeToList) | ||
|
||
data FormatParams = FormatParams Int Uri (Maybe Range) | ||
deriving (Eq, Show, Generic, FromJSON, ToJSON) | ||
|
||
brittanyDescriptor :: PluginId -> PluginDescriptor | ||
brittanyDescriptor plId = PluginDescriptor | ||
{ pluginId = plId | ||
, pluginName = "Brittany" | ||
, pluginDesc = "Brittany is a tool to format source code." | ||
, pluginCommands = [] | ||
{ pluginId = plId | ||
, pluginName = "Brittany" | ||
, pluginDesc = "Brittany is a tool to format source code." | ||
, pluginCommands = [] | ||
, pluginCodeActionProvider = Nothing | ||
, pluginDiagnosticProvider = Nothing | ||
, pluginHoverProvider = Nothing | ||
, pluginSymbolProvider = Nothing | ||
, pluginHoverProvider = Nothing | ||
, pluginSymbolProvider = Nothing | ||
, pluginFormattingProvider = Just provider | ||
} | ||
|
||
-- | Formatter provider of Brittany. | ||
-- Formats the given source in either a given Range or the whole Document. | ||
-- If the provider fails an error is returned that can be displayed to the user. | ||
provider :: FormattingProvider | ||
provider uri formatType opts = pluginGetFile "brittanyCmd: " uri $ \file -> do | ||
confFile <- liftIO $ getConfFile file | ||
mtext <- readVFS uri | ||
case mtext of | ||
Nothing -> return $ IdeResultFail (IdeError InternalError "File was not open" Null) | ||
Just text -> case formatType of | ||
FormatRange r -> do | ||
res <- liftIO $ runBrittany tabSize confFile $ extractRange r text | ||
case res of | ||
Left err -> return $ IdeResultFail (IdeError PluginError | ||
(T.pack $ "brittanyCmd: " ++ unlines (map showErr err)) Null) | ||
Right newText -> do | ||
let textEdit = J.TextEdit (normalize r) newText | ||
return $ IdeResultOk [textEdit] | ||
FormatDocument -> do | ||
res <- liftIO $ runBrittany tabSize confFile text | ||
case res of | ||
Left err -> return $ IdeResultFail (IdeError PluginError | ||
(T.pack $ "brittanyCmd: " ++ unlines (map showErr err)) Null) | ||
Right newText -> | ||
return $ IdeResultOk [J.TextEdit (fullRange text) newText] | ||
provider | ||
:: MonadIO m | ||
=> Text | ||
-> Uri | ||
-> FormattingType | ||
-> FormattingOptions | ||
-> m (IdeResult [TextEdit]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you use this at another type of than There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No. It is an abstraction, so it might be used in another type, but not yet. Afaik, by using another Monad, it might be possible to either wait for a response or to get it immediately. |
||
provider text uri formatType opts = pluginGetFile "brittanyCmd: " uri $ \fp -> do | ||
confFile <- liftIO $ getConfFile fp | ||
let (range, selectedContents) = case formatType of | ||
FormatDocument -> (fullRange text, text) | ||
FormatRange r -> (normalize r, extractRange r text) | ||
|
||
res <- formatText confFile opts selectedContents | ||
case res of | ||
Left err -> return $ IdeResultFail | ||
(IdeError PluginError | ||
(T.pack $ "brittanyCmd: " ++ unlines (map showErr err)) | ||
Null | ||
) | ||
Right newText -> do | ||
let textEdit = J.TextEdit range newText | ||
return $ IdeResultOk [textEdit] | ||
|
||
-- | Primitive to format text with the given option. | ||
-- May not throw exceptions but return a Left value. | ||
-- Errors may be presented to the user. | ||
formatText | ||
:: MonadIO m | ||
=> Maybe FilePath -- ^ Path to configs. If Nothing, default configs will be used. | ||
-> FormattingOptions -- ^ Options for the formatter such as indentation. | ||
-> Text -- ^ Text to format | ||
-> m (Either [BrittanyError] Text) -- ^ Either formatted Text or a error from Brittany. | ||
formatText confFile opts text = | ||
liftIO $ runBrittany tabSize confFile text | ||
where tabSize = opts ^. J.tabSize | ||
|
||
-- | Extend to the line below to replace newline character, as above. | ||
normalize :: Range -> Range | ||
normalize (Range (Position sl _) (Position el _)) = | ||
-- Extend to the line below to replace newline character, as above | ||
Range (Position sl 0) (Position (el + 1) 0) | ||
|
||
-- | Recursively search in every directory of the given filepath for brittany.yaml | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,20 +31,16 @@ floskellDescriptor plId = PluginDescriptor | |
-- Formats the given source in either a given Range or the whole Document. | ||
-- If the provider fails an error is returned that can be displayed to the user. | ||
provider :: FormattingProvider | ||
provider uri typ _opts = | ||
provider contents uri typ _opts = | ||
pluginGetFile "Floskell: " uri $ \file -> do | ||
config <- liftIO $ findConfigOrDefault file | ||
mContents <- readVFS uri | ||
case mContents of | ||
Nothing -> return $ IdeResultFail (IdeError InternalError "File was not open" Null) | ||
Just contents -> | ||
let (range, selectedContents) = case typ of | ||
FormatDocument -> (fullRange contents, contents) | ||
FormatRange r -> (r, extractRange r contents) | ||
result = reformat config (uriToFilePath uri) (BS.fromStrict (T.encodeUtf8 selectedContents)) | ||
in case result of | ||
Left err -> return $ IdeResultFail (IdeError PluginError (T.pack err) Null) | ||
Right new -> return $ IdeResultOk [TextEdit range (T.decodeUtf8 (BS.toStrict new))] | ||
let (range, selectedContents) = case typ of | ||
FormatDocument -> (fullRange contents, contents) | ||
FormatRange r -> (r, extractRange r contents) | ||
result = reformat config (uriToFilePath uri) (BS.fromStrict (T.encodeUtf8 selectedContents)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No particular reason, it was like that when I found it. |
||
case result of | ||
Left err -> return $ IdeResultFail (IdeError PluginError (T.pack $ "floskellCmd: " ++ err) Null) | ||
Right new -> return $ IdeResultOk [TextEdit range (T.decodeUtf8 (BS.toStrict new))] | ||
|
||
-- | Find Floskell Config, user and system wide or provides a default style. | ||
-- Every directory of the filepath will be searched to find a user configuration. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,7 @@ module Haskell.Ide.Engine.Support.HieExtras | |
, runGhcModCommand | ||
, splitCaseCmd' | ||
, splitCaseCmd | ||
, getFormattingPlugin | ||
) where | ||
|
||
import ConLike | ||
|
@@ -55,6 +56,7 @@ import qualified GhcMod.Gap as GM | |
import qualified GhcMod.LightGhc as GM | ||
import qualified GhcMod.Utils as GM | ||
import Haskell.Ide.Engine.ArtifactMap | ||
import Haskell.Ide.Engine.Config | ||
import Haskell.Ide.Engine.Context | ||
import Haskell.Ide.Engine.MonadFunctions | ||
import Haskell.Ide.Engine.MonadTypes | ||
|
@@ -799,3 +801,12 @@ prefixes = | |
, "$c" | ||
, "$m" | ||
] | ||
|
||
-- --------------------------------------------------------------------- | ||
|
||
getFormattingPlugin :: Config -> IdePlugins -> Maybe (PluginDescriptor, FormattingProvider) | ||
getFormattingPlugin config plugins = do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this happen inside There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hardly, since IdeGhcM does not implement R, or can execute R, so other plugins would have no access to it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, I didn't notice it was being called elsewhere outside of LspStdio.hs. I feel like this should be moved into PluginIdeMonads.hs though, and made polymorphic over |
||
let providerName = formattingProvider config | ||
fmtPlugin <- Map.lookup providerName (ipMap plugins) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is actually wrong, since we look up the name, but the Plugins Map is of Type There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But it works, since due to some lucky coincidence There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be better if PluginId would be a |
||
fmtProvider <- pluginFormattingProvider fmtPlugin | ||
return (fmtPlugin, fmtProvider) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This used to be
IdeDeferM
because brittany needs the GHC parsed source to work. Now that it isIdeM
, will be potentially lose formatting under some circumstances?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Brittany's
FormattingProvider
doesn't useifCachedModule/withCachedModule
on master at the moment, so it looks like it wouldn't have been waiting for the parsed sourcehaskell-ide-engine/src/Haskell/Ide/Engine/Plugin/Brittany.hs
Lines 42 to 64 in a48a02c
It looks like I moved it from
IdeGhcM
toIdeM
in this commit, and in retrospect I should have given a second thought as to why it was InIdeGhcM
in the first place.runBrittany
just runs inIO
: does it access the same ghc-mod instance as HIE?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question. I think once my HaRe update to use the new hie-bios is done we will have a clearer view of the API, and can ask brittany to expose something that makes use of the module caching
If it does not, then IdeDeferM makes no difference.