-
Notifications
You must be signed in to change notification settings - Fork 207
Load all possible haskell source files #1569
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 |
---|---|---|
@@ -0,0 +1,128 @@ | ||
{-# LANGUAGE NamedFieldPuns #-} | ||
{-# LANGUAGE TupleSections #-} | ||
{-# LANGUAGE OverloadedStrings #-} | ||
module RunTest | ||
( findAllSourceFiles | ||
, compileTarget | ||
, runServer | ||
, prettyPrintDiags | ||
) | ||
where | ||
|
||
import GhcMonad | ||
import qualified GHC | ||
import Control.Monad | ||
import qualified Control.Concurrent.STM as STM | ||
import Data.List ( isPrefixOf ) | ||
import qualified Data.Text as T | ||
import qualified Data.Map as Map | ||
import Data.Default | ||
import System.Directory ( doesDirectoryExist | ||
, listDirectory | ||
, canonicalizePath | ||
, doesFileExist | ||
) | ||
import System.FilePath | ||
import Language.Haskell.LSP.Core | ||
import Language.Haskell.LSP.Types | ||
import Haskell.Ide.Engine.PluginsIdeMonads | ||
hiding ( withIndefiniteProgress | ||
, withProgress | ||
) | ||
import Haskell.Ide.Engine.GhcModuleCache | ||
import qualified Haskell.Ide.Engine.ModuleCache | ||
as MC | ||
import qualified Haskell.Ide.Engine.Ghc as Ghc | ||
|
||
findAllSourceFiles :: FilePath -> IO [FilePath] | ||
findAllSourceFiles fp = do | ||
absFp <- canonicalizePath fp | ||
isDir <- doesDirectoryExist absFp | ||
if isDir | ||
then findFilesRecursively | ||
isHaskellSource | ||
(\path -> any (\p -> p path) [isHidden, isSpecialDir]) | ||
absFp | ||
else filterM doesFileExist [absFp] | ||
where | ||
isHaskellSource = (== ".hs") . takeExtension | ||
isHidden = ("." `isPrefixOf`) . takeFileName | ||
isSpecialDir = (== "dist-newstyle") . takeFileName | ||
|
||
findFilesRecursively | ||
:: (FilePath -> Bool) -> (FilePath -> Bool) -> FilePath -> IO [FilePath] | ||
findFilesRecursively p exclude dir = do | ||
dirContents' <- listDirectory dir | ||
let dirContents = map (dir </>) dirContents' | ||
|
||
files <- forM dirContents $ \fp -> do | ||
isDirectory <- doesDirectoryExist fp | ||
if isDirectory | ||
then if not $ exclude fp | ||
then findFilesRecursively p exclude fp | ||
else return [] | ||
else if p fp then return [fp] else return [] | ||
|
||
return $ concat files | ||
|
||
|
||
-- --------------------------------------------------------------------- | ||
|
||
compileTarget | ||
:: GHC.DynFlags | ||
-> FilePath | ||
-> IdeGhcM (IdeResult (Ghc.Diagnostics, Ghc.AdditionalErrs)) | ||
compileTarget dynFlags fp = do | ||
let pubDiags _ _ _ = return () | ||
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. Capturing this could make sense. |
||
let defAction = return (mempty, mempty) | ||
let action = Ghc.setTypecheckedModule (filePathToUri fp) | ||
actionResult <- MC.runActionWithContext pubDiags | ||
dynFlags | ||
(Just fp) | ||
defAction | ||
action | ||
return $ join actionResult | ||
|
||
-- --------------------------------------------------------------------- | ||
|
||
runServer | ||
:: Maybe FilePath | ||
-> IdePlugins | ||
-> [FilePath] | ||
-> IO [(FilePath, IdeResult (Ghc.Diagnostics, Ghc.AdditionalErrs))] | ||
runServer mlibdir ideplugins targets = do | ||
let initialState = IdeState emptyModuleCache Map.empty Map.empty Nothing | ||
stateVar <- STM.newTVarIO initialState | ||
|
||
runIdeGhcM mlibdir ideplugins dummyLspFuncs stateVar $ do | ||
dynFlags <- getSessionDynFlags | ||
mapM (\fp -> (fp, ) <$> compileTarget dynFlags fp) targets | ||
|
||
-- --------------------------------------------------------------------- | ||
|
||
prettyPrintDiags | ||
:: FilePath -> IdeResult (Ghc.Diagnostics, Ghc.AdditionalErrs) -> T.Text | ||
prettyPrintDiags fp diags = T.pack fp <> ": " <> case diags of | ||
IdeResultFail IdeError { ideMessage } -> "FAILED\n\t" <> ideMessage | ||
IdeResultOk (_diags, errs) -> | ||
if null errs then "OK" else T.unlines (map (T.append "\t") errs) | ||
|
||
-- --------------------------------------------------------------------- | ||
|
||
dummyLspFuncs :: Default a => LspFuncs a | ||
dummyLspFuncs = LspFuncs | ||
{ clientCapabilities = def | ||
, config = return (Just def) | ||
, sendFunc = const (return ()) | ||
, getVirtualFileFunc = const (return Nothing) | ||
, persistVirtualFileFunc = \uri -> | ||
return (uriToFilePath (fromNormalizedUri uri)) | ||
, reverseFileMapFunc = return id | ||
, publishDiagnosticsFunc = mempty | ||
, flushDiagnosticsBySourceFunc = mempty | ||
, getNextReqId = pure (IdInt 0) | ||
, rootPath = Nothing | ||
, getWorkspaceFolders = return Nothing | ||
, withProgress = \_ _ f -> f (const (return ())) | ||
, withIndefiniteProgress = \_ _ f -> f | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,11 +6,13 @@ import Options.Applicative.Simple | |
data GlobalOpts = GlobalOpts | ||
{ optDebugOn :: Bool | ||
, optLogFile :: Maybe String | ||
, optLsp :: Bool -- Kept for a while, to not break legacy clients | ||
, optLsp :: Bool | ||
, projectRoot :: Maybe String | ||
, optBiosVerbose :: Bool | ||
, optCaptureFile :: Maybe FilePath | ||
, optExamplePlugin :: Bool | ||
, optDryRun :: Bool | ||
, optFiles :: [FilePath] | ||
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. better name required. 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 you list directories as well, in 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. Yeah. If it is a directory, it is recursed into it and if it is a FilePath, its existence is checked and filtered if it does not exist. Which is maybe not the desired behaviour. 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. Following question remain for me: 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 don't think it's that dangerous to load every file in the project, it's not like its destructively editing anything. I'm happy if it loads them by default |
||
} deriving (Show) | ||
|
||
globalOptsParser :: Parser GlobalOpts | ||
|
@@ -53,3 +55,13 @@ globalOptsParser = GlobalOpts | |
<*> switch | ||
( long "example" | ||
<> help "Enable Example2 plugin. Useful for developers only") | ||
<*> flag False True | ||
( long "dry-run" | ||
<> help "Perform a dry-run of loading files. Only searches for Haskell source files to load. Does nothing if run as LSP server." | ||
) | ||
<*> many | ||
( argument str | ||
( metavar "FILES..." | ||
<> help "Directories and Filepaths to load. Does nothing if run as LSP server.") | ||
) | ||
|
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.
silently discarding unknown filepaths does not seem pretty, but neither does throwing an exception.