diff --git a/app/MainHie.hs b/app/MainHie.hs index 617695ff3..4592d1d79 100644 --- a/app/MainHie.hs +++ b/app/MainHie.hs @@ -43,6 +43,7 @@ import Haskell.Ide.Engine.Plugin.Liquid import Haskell.Ide.Engine.Plugin.Ormolu import Haskell.Ide.Engine.Plugin.Package import Haskell.Ide.Engine.Plugin.Pragmas +import Haskell.Ide.Engine.Plugin.Stylish (stylishDescriptor) -- --------------------------------------------------------------------- @@ -66,6 +67,7 @@ plugins includeExamples = pluginDescToIdePlugins allPlugins , genericDescriptor "generic" , ghcmodDescriptor "ghcmod" , ormoluDescriptor "ormolu" + , stylishDescriptor "stylish" ] examplePlugins = [example2Descriptor "eg2" diff --git a/haskell-ide-engine.cabal b/haskell-ide-engine.cabal index b801f7c2d..09fc62386 100644 --- a/haskell-ide-engine.cabal +++ b/haskell-ide-engine.cabal @@ -38,6 +38,7 @@ library Haskell.Ide.Engine.Plugin.Package Haskell.Ide.Engine.Plugin.Package.Compat Haskell.Ide.Engine.Plugin.Pragmas + Haskell.Ide.Engine.Plugin.Stylish Haskell.Ide.Engine.Plugin.Generic Haskell.Ide.Engine.Plugin.GhcMod Haskell.Ide.Engine.Scheduler @@ -80,6 +81,8 @@ library , hoogle >= 5.0.13 , hsimport , hslogger + , HsYAML >= 0.2.1.0 + , HsYAML-aeson >= 0.2.0.0 , lifted-async , lens >= 4.15.2 , monoid-subclasses > 0.4 @@ -90,6 +93,7 @@ library , safe , sorted-list >= 0.2.1.0 , stm + , stylish-haskell >= 0.10.0.0 , syb , tagsoup , text diff --git a/src/Haskell/Ide/Engine/Plugin/Stylish.hs b/src/Haskell/Ide/Engine/Plugin/Stylish.hs new file mode 100644 index 000000000..67625c4d7 --- /dev/null +++ b/src/Haskell/Ide/Engine/Plugin/Stylish.hs @@ -0,0 +1,43 @@ +{-# LANGUAGE OverloadedStrings #-} +module Haskell.Ide.Engine.Plugin.Stylish where + +import Control.Monad.IO.Class (liftIO) +import Data.Aeson (Value (Null)) +import Data.List (intercalate) +import qualified Data.Text as T +import Haskell.Ide.Engine.MonadTypes +import Haskell.Ide.Engine.PluginUtils (fullRange, pluginGetFile) +import Language.Haskell.Stylish (ConfigPath (..), format) + + +stylishDescriptor :: PluginId -> PluginDescriptor +stylishDescriptor plId = PluginDescriptor + { pluginId = plId + , pluginName = "Stylish" + , pluginDesc = "Stylish is a tool to format source code." + , pluginCommands = [] + , pluginCodeActionProvider = Nothing + , pluginDiagnosticProvider = Nothing + , pluginHoverProvider = Nothing + , pluginSymbolProvider = Nothing + , pluginFormattingProvider = Just provider + } + +provider :: FormattingProvider +provider contents uri typ _ = + case typ of + FormatRange _ -> + return $ IdeResultFail (IdeError PluginError (T.pack "Selection formatting for Stylish is not currently supported.") Null) + FormatText -> pluginGetFile "stylish:" uri $ \file -> do + res <- liftIO $ runStylish Nothing file contents + case res of + Left err -> return $ IdeResultFail + (IdeError PluginError + (T.pack $ "stylish: " ++ err) + Null + ) + Right new -> return $ IdeResultOk [TextEdit (fullRange contents) (T.pack $ ((intercalate "\n" new) <> "\n"))] + + +runStylish :: Maybe ConfigPath -> FilePath -> T.Text -> IO (Either String [String]) +runStylish config file contents = format config (Just file) (T.unpack contents) diff --git a/stack-8.4.2.yaml b/stack-8.4.2.yaml index ecd86545c..fecd5c55e 100644 --- a/stack-8.4.2.yaml +++ b/stack-8.4.2.yaml @@ -36,6 +36,8 @@ extra-deps: - hoogle-5.0.17.11 - hsimport-0.11.0 - hslogger-1.3.1.0 +- HsYAML-0.2.1.0 +- HsYAML-aeson-0.2.0.0 - invariant-0.5.3 - lens-4.18.1 - libyaml-0.1.1.0 @@ -50,6 +52,7 @@ extra-deps: - rope-utf16-splay-0.3.1.0 - simple-sendfile-0.2.30 # for network and network-bsd - socks-0.6.1 # for network and network-bsd +- stylish-haskell-0.10.0.0 - syz-0.2.0.0 - type-equality-1 - unix-compat-0.5.2 diff --git a/stack-8.4.3.yaml b/stack-8.4.3.yaml index ea534dbf4..eb7ab6436 100644 --- a/stack-8.4.3.yaml +++ b/stack-8.4.3.yaml @@ -45,6 +45,8 @@ extra-deps: - hspec-2.7.1 - hspec-core-2.7.1 - hspec-discover-2.7.1 +- HsYAML-0.2.1.0 +- HsYAML-aeson-0.2.0.0 - indexed-profunctors-0.1 - invariant-0.5.3 - lens-4.18.1 @@ -66,6 +68,7 @@ extra-deps: - singleton-bool-0.1.5 - socks-0.6.1 # for network and network-bsd - splitmix-0.0.3 +- stylish-haskell-0.10.0.0 - tagged-0.8.6 - th-abstraction-0.3.1.0 - these-1.0.1 diff --git a/stack-8.4.4.yaml b/stack-8.4.4.yaml index 50f15caa4..fcef22b00 100644 --- a/stack-8.4.4.yaml +++ b/stack-8.4.4.yaml @@ -12,6 +12,7 @@ extra-deps: - bifunctors-5.5.6 - brittany-0.12.1.1 - bytestring-trie-0.2.5.0 +- Cabal-2.2.0.1 - cabal-helper-1.0.0.0 - cabal-plan-0.5.0.0 - connection-0.3.1 # for network and network-bsd @@ -35,6 +36,8 @@ extra-deps: - hoogle-5.0.17.11 - hsimport-0.11.0 - hslogger-1.3.1.0 +- HsYAML-0.2.1.0 +- HsYAML-aeson-0.2.0.0 - invariant-0.5.3 - lens-4.18.1 - libyaml-0.1.1.0 @@ -50,6 +53,7 @@ extra-deps: - rope-utf16-splay-0.3.1.0 - simple-sendfile-0.2.30 # for network and network-bsd - socks-0.6.1 # for network and network-bsd +- stylish-haskell-0.10.0.0 - syz-0.2.0.0 - unix-compat-0.5.2 - unordered-containers-0.2.10.0 @@ -61,6 +65,7 @@ extra-deps: - temporary-1.2.1.1 - time-compat-1.9.2.2 - time-manager-0.0.0 # for http2 +- vector-0.12.1.2 - warp-3.2.28 # for network and network-bsd - wai-3.2.2.1 # for network and network-bsd diff --git a/stack-8.6.4.yaml b/stack-8.6.4.yaml index 2fab67abb..e931488ef 100644 --- a/stack-8.6.4.yaml +++ b/stack-8.6.4.yaml @@ -27,6 +27,8 @@ extra-deps: - hlint-2.2.10 - hoogle-5.0.17.11 - hsimport-0.11.0 +- HsYAML-0.2.1.0 +- HsYAML-aeson-0.2.0.0 - lsp-test-0.10.1.0 - monad-dijkstra-0.1.1.2@rev:1 - monad-memo-0.4.1 @@ -34,6 +36,7 @@ extra-deps: - ormolu-0.0.3.1 - parser-combinators-1.2.1 - rope-utf16-splay-0.3.1.0 +- stylish-haskell-0.10.0.0 - syz-0.2.0.0 - temporary-1.2.1.1 - time-compat-1.9.2.2 diff --git a/stack-8.6.5.yaml b/stack-8.6.5.yaml index e8a77c1e3..ed9f01685 100644 --- a/stack-8.6.5.yaml +++ b/stack-8.6.5.yaml @@ -28,6 +28,8 @@ extra-deps: - hlint-2.2.10 - hoogle-5.0.17.11 - hsimport-0.11.0 +- HsYAML-0.2.1.0 +- HsYAML-aeson-0.2.0.0 - indexed-profunctors-0.1 - lsp-test-0.10.1.0 - monad-dijkstra-0.1.1.2 @@ -36,6 +38,7 @@ extra-deps: - ormolu-0.0.3.1 - parser-combinators-1.2.1 - semialign-1.1 +- stylish-haskell-0.10.0.0 - temporary-1.2.1.1 - topograph-1 diff --git a/stack-8.8.1.yaml b/stack-8.8.1.yaml index 53441bb5a..ace014b26 100644 --- a/stack-8.8.1.yaml +++ b/stack-8.8.1.yaml @@ -25,10 +25,13 @@ extra-deps: - hlint-2.2.10 - hoogle-5.0.17.11 - hsimport-0.11.0 +- HsYAML-0.2.1.0 +- HsYAML-aeson-0.2.0.0 - ilist-0.3.1.0 - monad-dijkstra-0.1.1.2 - ormolu-0.0.3.1 - semigroups-0.18.5 +- stylish-haskell-0.10.0.0 - temporary-1.2.1.1 flags: diff --git a/stack-8.8.2.yaml b/stack-8.8.2.yaml index 3037a3d81..8a342614f 100644 --- a/stack-8.8.2.yaml +++ b/stack-8.8.2.yaml @@ -28,10 +28,13 @@ extra-deps: - hlint-2.2.10 - hoogle-5.0.17.11 - hsimport-0.11.0 +- HsYAML-0.2.1.0 +- HsYAML-aeson-0.2.0.0 - ilist-0.3.1.0 - monad-dijkstra-0.1.1.2 - ormolu-0.0.3.1 - semigroups-0.18.5 +- stylish-haskell-0.10.0.0 - temporary-1.2.1.1 flags: diff --git a/stack.yaml b/stack.yaml index 3037a3d81..0c1c99aa5 100644 --- a/stack.yaml +++ b/stack.yaml @@ -28,9 +28,12 @@ extra-deps: - hlint-2.2.10 - hoogle-5.0.17.11 - hsimport-0.11.0 +- HsYAML-0.2.1.0 +- HsYAML-aeson-0.2.0.0 - ilist-0.3.1.0 - monad-dijkstra-0.1.1.2 - ormolu-0.0.3.1 +- stylish-haskell-0.10.0.0 - semigroups-0.18.5 - temporary-1.2.1.1 diff --git a/test/functional/FormatSpec.hs b/test/functional/FormatSpec.hs index bbb8052cd..17082a0af 100644 --- a/test/functional/FormatSpec.hs +++ b/test/functional/FormatSpec.hs @@ -32,9 +32,7 @@ spec = do documentContents doc >>= liftIO . (`shouldBe` formattedRangeTabSize5) describe "formatting provider" $ do - let formatLspConfig provider = - object [ "languageServerHaskell" .= object ["formattingProvider" .= (provider :: Value)] ] - formatConfig provider = defaultConfig { lspConfig = Just (formatLspConfig provider) } + let formatConfig provider = defaultConfig { lspConfig = Just (formatLspConfig provider) } it "respects none" $ runSessionWithConfig (formatConfig "none") hieCommand fullCaps "test/testdata" $ do doc <- openDoc "Format.hs" "haskell" @@ -93,9 +91,7 @@ spec = do "foo x y = do\n print x\n return 42\n"] describe "ormolu" $ do - let formatLspConfig provider = - object [ "languageServerHaskell" .= object ["formattingProvider" .= (provider :: Value)] ] - + it "formats correctly" $ runSession hieCommand fullCaps "test/testdata" $ do sendNotification WorkspaceDidChangeConfiguration (DidChangeConfigurationParams (formatLspConfig "ormolu")) doc <- openDoc "Format.hs" "haskell" @@ -107,6 +103,9 @@ spec = do GHC86 -> formatted _ -> liftIO $ docContent `shouldBe` unchangedOrmolu +formatLspConfig :: Value -> Value +formatLspConfig provider = + object [ "languageServerHaskell" .= object ["formattingProvider" .= provider] ] formattedDocTabSize2 :: T.Text formattedDocTabSize2 = diff --git a/test/unit/HsImportSpec.hs b/test/unit/HsImportSpec.hs index 5bc2107a7..dbad6b2a2 100644 --- a/test/unit/HsImportSpec.hs +++ b/test/unit/HsImportSpec.hs @@ -2,15 +2,16 @@ module HsImportSpec where import Control.Monad.IO.Class -import qualified Data.Text as T -import qualified Data.HashMap.Strict as Map +import qualified Data.HashMap.Strict as Map +import qualified Data.Text as T +import qualified Haskell.Ide.Engine.Config as Config import Haskell.Ide.Engine.MonadTypes -import Haskell.Ide.Engine.PluginUtils -import Haskell.Ide.Engine.Plugin.HsImport -import qualified Haskell.Ide.Engine.Config as Config import qualified Haskell.Ide.Engine.Plugin.Brittany as Brittany -import qualified Haskell.Ide.Engine.Plugin.Ormolu as Ormolu import qualified Haskell.Ide.Engine.Plugin.Floskell as Floskell +import Haskell.Ide.Engine.Plugin.HsImport +import qualified Haskell.Ide.Engine.Plugin.Ormolu as Ormolu +import qualified Haskell.Ide.Engine.Plugin.Stylish as Stylish +import Haskell.Ide.Engine.PluginUtils import System.Directory import System.FilePath import Test.Hspec @@ -31,6 +32,7 @@ testPlugins = pluginDescToIdePlugins [ Brittany.brittanyDescriptor "brittany" , Floskell.floskellDescriptor "floskell" , Ormolu.ormoluDescriptor "ormolu" + , Stylish.stylishDescriptor "stylish" ] codeActionImportList :: FilePath @@ -126,7 +128,27 @@ hsImportSpec = do ] _ -> it "is NOP formatter" $ pendingWith "Ormolu only supported by GHC >= 8.6. Need to restore this." - + describe "formats with stylish" $ hsImportSpecRunner "stylish" + [ -- Expected output for simple format. + [ TextEdit (Range (toPos (2, 1)) (toPos (2, 1))) "import Control.Monad\n" + ] + , [ TextEdit (Range (toPos (2, 1)) (toPos (2, 1))) "import Control.Monad (when)\n" + ] + , [ TextEdit (Range (toPos (2, 1)) (toPos (2, 1))) "import Data.Maybe (Maybe)\n" + ] + , [ TextEdit (Range (toPos (2, 1)) (toPos (2, 1))) "import Data.Maybe (Maybe (..))\n" + ] + , [ TextEdit (Range (toPos (2, 1)) (toPos (2, 1))) "import Data.Maybe (Maybe (Nothing))\n" + ] + , [ TextEdit (Range (toPos (2, 1)) (toPos (2, 1))) "import Data.Function (($))\n" + ] + , [ TextEdit (Range (toPos (2, 1)) (toPos (2, 32))) "import System.IO (IO, hPutStrLn)" + ] + , [ TextEdit (Range (toPos (3, 1)) (toPos (3, 99))) $ + "import Data.List (cons, find, head, init, last, length, null, reverse,\n" <> + " tail, uncons, union, (\\\\))" + ] + ] -- --------------------------------------------------------------------- -- Parameterized HsImport Spec. -- --------------------------------------------------------------------- @@ -196,4 +218,4 @@ expectHsImportResult formatterName fp uri expectedChanges act = do IdeResultOk (WorkspaceEdit (Just changes) _) <- runSingle' (setFormatter formatterName) testPlugins fp act case Map.lookup uri changes of Just (List val) -> val `shouldBe` expectedChanges - Nothing -> fail "No Change found" \ No newline at end of file + Nothing -> fail "No Change found"