hsdev 0.1.4.0 → 0.1.4.1
raw patch · 28 files changed
+315/−217 lines, 28 filesdep +lifted-basedep +monad-controldep +transformers-base
Dependencies added: lifted-base, monad-control, transformers-base
Files
- hsdev.cabal +5/−2
- src/Control/Concurrent/Task.hs +8/−4
- src/Control/Concurrent/Util.hs +1/−1
- src/Control/Concurrent/Worker.hs +1/−1
- src/Data/Mark.hs +34/−10
- src/HsDev/Client/Commands.hs +18/−66
- src/HsDev/Commands.hs +1/−1
- src/HsDev/Database.hs +7/−3
- src/HsDev/Database/Update.hs +40/−26
- src/HsDev/Database/Update/Types.hs +11/−1
- src/HsDev/Inspect.hs +5/−5
- src/HsDev/Project.hs +21/−11
- src/HsDev/Scan.hs +3/−3
- src/HsDev/Scan/Browse.hs +1/−1
- src/HsDev/Symbols.hs +20/−19
- src/HsDev/Symbols/Location.hs +38/−10
- src/HsDev/Symbols/Resolve.hs +2/−2
- src/HsDev/Symbols/Types.hs +1/−1
- src/HsDev/Tools/AutoFix.hs +2/−2
- src/HsDev/Tools/Ghc/Check.hs +5/−15
- src/HsDev/Tools/GhcMod.hs +6/−4
- src/HsDev/Tools/HDocs.hs +4/−3
- src/HsDev/Tools/HLint.hs +57/−4
- src/HsDev/Tools/Types.hs +2/−1
- src/HsDev/Watcher.hs +12/−11
- src/HsDev/Watcher/Types.hs +1/−1
- src/System/Directory/Watcher.hs +6/−6
- src/System/Win32/PowerShell.hs +3/−3
hsdev.cabal view
@@ -2,7 +2,7 @@ -- see http://haskell.org/cabal/users-guide/ name: hsdev -version: 0.1.4.0 +version: 0.1.4.1 synopsis: Haskell development library and tool with support of autocompletion, symbol info, go to declaration, find references etc. description: Haskell development library and tool with support of autocompletion, symbol info, go to declaration, find references, hayoo search etc. @@ -113,6 +113,8 @@ hlint >= 1.9.0 && < 2.0.0, HTTP >= 4000.2.0, lens >= 4.8, + lifted-base >= 0.2, + monad-control >= 1.0, monad-loops >= 0.4, mtl >= 2.2.0, MonadCatchIO-transformers >= 0.3.1, @@ -126,6 +128,7 @@ text >= 1.2.0, time >= 1.5.0, transformers >= 0.4.0, + transformers-base >= 0.4.0, uniplate >= 1.6.0, unordered-containers >= 0.2.0, vector >= 0.10.0 @@ -133,7 +136,7 @@ executable hsdev main-is: hsdev.hs hs-source-dirs: tools - ghc-options: -threaded -Wall -fno-warn-tabs + ghc-options: -threaded -Wall -fno-warn-tabs "-with-rtsopts=-N4" build-depends: base >= 4.7 && < 5,
src/Control/Concurrent/Task.hs view
@@ -3,7 +3,7 @@ module Control.Concurrent.Task ( Task(..), TaskException(..), TaskResult(..), taskStarted, taskRunning, taskStopped, taskDone, taskFailed, taskCancelled, - taskWaitStart, taskWait, taskKill, taskCancel, taskStop, + taskWaitStart, taskWait, taskJoin, taskKill, taskCancel, taskStop, runTask, runTask_, runTaskTry, runTaskError, forkTask, tryT, -- * Reexports @@ -37,8 +37,8 @@ instance Functor TaskResult where fmap f r = TaskResult { taskResultEmpty = taskResultEmpty r, - taskResultTryRead = fmap (fmap (fmap f)) $ taskResultTryRead r, - taskResultTake = fmap (fmap f) $ taskResultTake r, + taskResultTryRead = fmap (fmap f) <$> taskResultTryRead r, + taskResultTake = fmap f <$> taskResultTake r, taskResultFail = taskResultFail r } instance Functor Task where @@ -72,6 +72,10 @@ taskWait :: Task a -> IO (Either SomeException a) taskWait = taskResultTake . taskResult +-- | Join task, rethrowing its exceptions +taskJoin :: Task a -> IO a +taskJoin = taskWait >=> either throwM return + -- | Kill task taskKill :: Task a -> IO () taskKill = @@ -89,7 +93,7 @@ taskStop :: Task a -> IO Bool taskStop t = do cancelled <- taskCancel t - when (not cancelled) $ taskKill t + unless cancelled $ taskKill t return cancelled runTask :: (MonadCatch m, MonadIO m, MonadIO n) => (m () -> n ()) -> m a -> n (Task a)
src/Control/Concurrent/Util.hs view
@@ -30,7 +30,7 @@ withSync :: a -> ((a -> IO ()) -> IO b) -> IO a withSync v act = do sync <- newEmptyMVar - void $ forkIO $ void $ act (putMVar sync) `onException` (putMVar sync v) + void $ forkIO $ void $ act (putMVar sync) `onException` putMVar sync v takeMVar sync withSync_ :: (IO () -> IO a) -> IO ()
src/Control/Concurrent/Worker.hs view
@@ -30,7 +30,7 @@ taskVar <- newEmptyMVar let start = forkTask $ do - run $ initialize $ processWork + run $ initialize processWork processSkip processWork = whileJust (liftM (fmap snd) $ liftIO $ getChan ch) id processSkip = whileJust (liftM (fmap fst) $ getChan ch) taskStop
src/Data/Mark.hs view
@@ -1,10 +1,10 @@ {-# LANGUAGE ViewPatterns, OverloadedStrings, GeneralizedNewtypeDeriving, TypeSynonymInstances, FlexibleInstances, RankNTypes, MultiParamTypeClasses #-} module Data.Mark ( - Point(..), point, (.-.), (.+.), till, Size, linesSize, stringSize, + Point(..), point, lineStart, (.-.), (.+.), till, Size, linesSize, stringSize, Range(..), rangeLines, emptyRange, line, - range, rangeSize, at, + range, rangeSize, expandLines, at, -- * Mappings Map(..), apply, back, cut, insert, @@ -17,7 +17,7 @@ Editable(..), measure, -- * Actions EditAction(..), - Replace(..), run + Replace(..), inverse, reverseEdits, run ) where import Prelude hiding (splitAt, length, lines, unlines) @@ -69,6 +69,9 @@ | c < 0 = error "Column can't be less then zero" | otherwise = Point l c +lineStart :: Int -> Point +lineStart l = point l 0 + -- | Range from "Point" to "Point" data Range = Range { rangeFrom :: Point, @@ -81,6 +84,9 @@ mempty = Point 0 0 l `mappend` r = r .+. l +rangeLength :: Range -> Size +rangeLength (Range f t) = t .-. f + -- | Range from one @Point@ to another till :: Point -> Point -> Range l `till` r = Range (min l r) (max l r) @@ -91,7 +97,7 @@ -- | Distance in @n@ chars within one line stringSize :: Int -> Point -stringSize n = Point 0 n +stringSize = Point 0 instance ToJSON Range where toJSON (Range f t) = object ["from" .= f, "to" .= t] @@ -119,6 +125,10 @@ rangeSize :: Point -> Size -> Range rangeSize pt sz = range pt (sz .+. pt) +-- | Expand range to contain full lines +expandLines :: Range -> Range +expandLines (Range (Point sline _) (Point eline _)) = Range (Point sline 0) (Point (succ eline) 0) + -- | Get contents at specified range at :: Editable a => Contents a -> Range -> Contents a at cts r = @@ -147,7 +157,7 @@ instance Monoid Map where mempty = Map $ iso id id - (Map l) `mappend` (Map r) = Map (r . l) + (Map l) `mappend` (Map r) = Map (l . r) -- | Apply mapping apply :: Map -> Range -> Range @@ -157,6 +167,10 @@ back :: Map -> Map back (Map f) = Map (from f) +-- | Apply mapping to starting position only +applyStart :: Map -> Range -> Range +applyStart m r = rangeFrom (apply m r) `rangeSize` rangeLength r + -- | Cut range mapping cut :: Range -> Map cut rgn = Map $ iso (cutRange rgn) (insertRange rgn) @@ -174,7 +188,7 @@ -- | Update second range position as if it was data inserted at first range insertRange :: Range -> Range -> Range insertRange (Range is ie) (Range s e) = Range - (if is <= s then (s .-. is) .+. ie else s) + (if is < s then (s .-. is) .+. ie else s) (if is < e then (e .-. is) .+. ie else e) -- | Contents is list of lines @@ -199,7 +213,7 @@ editRange :: Range -> (Range -> Edit a) -> EditM a () editRange rgn edit' = do rgn' <- mapRange rgn - modify (`mappend` (edit' rgn')) + modify (`mappend` edit' rgn') -- | Get mapped range mapRange :: Range -> EditM a Range @@ -287,7 +301,7 @@ erase' :: Editable a => Range -> Contents a -> Contents a erase' rgn' cts = fst (splitCts (rangeFrom rgn') cts) `concatCts` snd (splitCts (rangeTo rgn') cts) - write pt cts = editRange (pt `rangeSize` measure cts') (\r -> Edit (write' r) (insert r)) where + write pt cts = editRange (pt `till` pt) (\r -> Edit (write' r) (insert $ rangeFrom r `rangeSize` measure cts')) where cts' = lines cts write' rgn' origin = prefix (before' `concatCts` suffix cts') `concatCts` after' where (before', after') = splitCts (rangeFrom rgn') origin @@ -308,8 +322,18 @@ instance EditAction Replace where erase rgn = Replace rgn mempty - write pt cts = Replace (range pt pt) cts - replace rgn cts = Replace rgn cts + write pt = Replace (range pt pt) + replace = Replace + +-- | Inverted operation - restores previous state +inverse :: Editable s => Contents s -> Replace s a -> Replace s a +inverse cts (Replace r s) = Replace (rangeFrom r `rangeSize` measure (lines s)) (unlines $ cts `at` r) + +-- | Reverse list of operations +reverseEdits :: Editable s => Contents s -> [Replace s ()] -> [Replace s ()] +reverseEdits cts rs = map (remap . inverse cts) $ reverse rs where + edit' = snd $ runEdit $ run rs + remap r = r { replaceRange = editMap edit' `applyStart` replaceRange r } -- | Run replace actions to get monadic action run :: Editable s => [Replace s ()] -> EditM s ()
src/HsDev/Client/Commands.hs view
@@ -12,7 +12,6 @@ import Control.Monad.Catch (try, SomeException(..)) import Control.Monad.Trans.Maybe import Data.Aeson hiding (Result, Error) -import Data.Either import Data.List import Data.Maybe import qualified Data.Map as M @@ -28,7 +27,6 @@ import qualified HsDev.Cache.Structured as SC import HsDev.Commands import qualified HsDev.Database.Async as DB -import HsDev.Scan import HsDev.Server.Message as M import HsDev.Server.Types import HsDev.Symbols @@ -64,13 +62,6 @@ ghcOpts, docsFlag, inferFlag]) "scan sources" scan', - cmd' "rescan" [] (sandboxes ++ [ - manyReq $ projectArg `desc` "project path or .cabal", - manyReq $ fileArg `desc` "source file", - manyReq $ pathArg `desc` "path to rescan", - ghcOpts, docsFlag, inferFlag]) - "rescan sources" - rescan', cmdList' "remove" [] (sandboxes ++ [ projectArg `desc` "module project", fileArg `desc` "module source file", @@ -278,45 +269,6 @@ ("path", Update.scanDirectory)], map (Update.scanCabal (listArg "ghc" as)) cabals] - -- | Rescan data - rescan' :: [String] -> Opts String -> CommandActionT () - rescan' _ as copts = do - cabals <- getSandboxes copts as - updateProcess copts as $ map (Update.scanCabal (listArg "ghc" as)) cabals - - dbval <- getDb copts - let - fileMap = M.fromList $ mapMaybe toPair $ - selectModules (byFile . view moduleId) dbval - - (errors, filteredMods) <- liftM partitionEithers $ mapM runExceptT $ concat [ - [do - p' <- findProject copts p - return $ M.fromList $ mapMaybe toPair $ - selectModules (inProject p' . view moduleId) dbval | - p <- listArg "project" as], - [do - f' <- findPath copts f - maybe - (throwError $ commandStrMsg $ "Unknown file: " ++ f') - (return . M.singleton f') - (lookupFile f' dbval) | - f <- listArg "file" as], - [do - d' <- findPath copts d - return $ M.filterWithKey (\f _ -> isParent d' f) fileMap | - d <- listArg "path" as]] - let - rescanMods = map (getInspected dbval) $ - M.elems $ if null filteredMods then fileMap else M.unions filteredMods - - if not (null errors) - then commandError (intercalate ", " [err | CommandError err _ <- errors]) (concat [ps | CommandError _ ps <- errors]) - else updateProcess copts as [Update.runTask "rescanning modules" [] $ do - needRescan <- Update.liftExceptT $ filterM (changedModule dbval (listArg "ghc" as) . view inspectedId) rescanMods - Update.scanModules (listArg "ghc" as) - (map (view inspectedId &&& fromMaybe [] . preview (inspection . inspectionOpts)) needRescan)] - -- | Remove data remove' :: [String] -> Opts String -> CommandActionT [ModuleId] remove' _ as copts = do @@ -438,7 +390,7 @@ case rs of [] -> commandError "Module not found" [] [m] -> return m - ms' -> commandError "Ambiguous modules" ["modules" .= (map (view moduleId) ms')] + ms' -> commandError "Ambiguous modules" ["modules" .= map (view moduleId) ms'] -- | Resolve module scope resolve' :: [String] -> Opts String -> CommandActionT Module @@ -463,7 +415,7 @@ case rs of [] -> commandError "Module not found" [] [m] -> return $ getScope $ resolveOne cabaldb m - ms' -> commandError "Ambiguous modules" ["modules" .= (map (view moduleId) ms')] + ms' -> commandError "Ambiguous modules" ["modules" .= map (view moduleId) ms'] -- | Get project info project' :: [String] -> Opts String -> CommandActionT Project @@ -576,9 +528,9 @@ column' <- maybe (commandError "column must be a number" []) return $ readMaybe column dbval <- getDb copts (srcFile, cabal) <- getCtx copts as - (srcFile', _, _) <- mapCommandErrorStr $ fileCtx dbval srcFile + (srcFile', m', _) <- mapCommandErrorStr $ fileCtx dbval srcFile mapCommandErrorStr $ GhcMod.waitMultiGhcMod (commandGhcMod copts) srcFile' $ - GhcMod.typeOf (listArg "ghc" as) cabal srcFile' line' column' + GhcMod.typeOf (listArg "ghc" as ++ moduleOpts (allPackages dbval) m') cabal srcFile' line' column' ghcmodType' [] _ _ = commandError "Specify line" [] ghcmodType' _ _ _ = commandError "Too much arguments" [] @@ -589,9 +541,11 @@ files' <- mapM (findPath copts) files mproj <- (listToMaybe . catMaybes) <$> liftIO (mapM locateProject files') cabal <- getCabal copts as - mapCommandErrorStr $ liftM concat $ forM files' $ \file' -> + dbval <- getDb copts + mapCommandErrorStr $ liftM concat $ forM files' $ \file' -> do + (_, m', _) <- fileCtx dbval file' GhcMod.waitMultiGhcMod (commandGhcMod copts) file' $ - GhcMod.check (listArg "ghc" as) cabal [file'] mproj + GhcMod.check (listArg "ghc" as ++ moduleOpts (allPackages dbval) m') cabal [file'] mproj -- | Ghc-mod lint ghcmodLint' :: [String] -> Opts String -> CommandActionT [Tools.Note Tools.OutputMessage] @@ -609,9 +563,11 @@ files' <- mapM (findPath copts) files mproj <- (listToMaybe . catMaybes) <$> liftIO (mapM locateProject files') cabal <- getCabal copts as - mapCommandErrorStr $ liftM concat $ forM files' $ \file' -> + dbval <- getDb copts + mapCommandErrorStr $ liftM concat $ forM files' $ \file' -> do + (_, m', _) <- fileCtx dbval file' GhcMod.waitMultiGhcMod (commandGhcMod copts) file' $ do - checked <- GhcMod.check (listArg "ghc" as) cabal [file'] mproj + checked <- GhcMod.check (listArg "ghc" as ++ moduleOpts (allPackages dbval) m') cabal [file'] mproj linted <- GhcMod.lint (listArg "hlint" as) file' return $ checked ++ linted @@ -795,14 +751,16 @@ -- | Find dependency: it may be source, project file or project name, also returns sandbox found findDep :: MonadIO m => CommandOptions -> String -> ExceptT CommandError m (Project, Maybe FilePath, Cabal) findDep copts depName = do + depPath <- findPath copts depName proj <- msum [ mapCommandErrorStr $ do - p <- liftIO (locateProject depName) + p <- liftIO (locateProject depPath) maybe (throwError $ "Project " ++ depName ++ " not found") (mapExceptT liftIO . loadProject) p, findProject copts depName] - src <- if takeExtension depName == ".hs" - then liftM Just (findPath copts depName) - else return Nothing + let + src + | takeExtension depPath == ".hs" = Just depPath + | otherwise = Nothing sbox <- liftIO $ searchSandbox $ view projectPath proj return (proj, src, sbox) @@ -812,12 +770,6 @@ deps' = case src of Nothing -> inDepsOfProject proj Just src' -> inDepsOfFile proj src' - --- | Supply module with its source file path if any -toPair :: Module -> Maybe (FilePath, Module) -toPair m = case view moduleLocation m of - FileModule f _ -> Just (f, m) - _ -> Nothing -- | Wait for DB to complete update waitDb :: CommandOptions -> Opts String -> CommandM ()
src/HsDev/Commands.hs view
@@ -153,7 +153,7 @@ -- | Check whether module is visible from source file visibleFrom :: Maybe Project -> Module -> ModuleId -> Bool visibleFrom (Just p) this m = visible p (view moduleId this) m -visibleFrom Nothing this m = (view moduleId this) == m || byCabal m +visibleFrom Nothing this m = view moduleId this == m || byCabal m -- | Split identifier into module name and identifier itself splitIdentifier :: String -> (Maybe String, String)
src/HsDev/Database.hs view
@@ -2,7 +2,7 @@ module HsDev.Database ( Database(..), - databaseIntersection, nullDatabase, databaseLocals, allModules, allDeclarations, + databaseIntersection, nullDatabase, databaseLocals, allModules, allDeclarations, allPackages, fromModule, fromProject, filterDB, projectDB, cabalDB, standaloneDB, @@ -17,7 +17,7 @@ Map ) where -import Control.Lens (set, view) +import Control.Lens (set, view, preview, _Just) import Control.Monad (msum, join) import Control.DeepSeq (NFData(..)) import Data.Aeson @@ -94,6 +94,10 @@ m <- allModules db moduleModuleDeclarations m +-- | All packages +allPackages :: Database -> [ModulePackage] +allPackages = ordNub . mapMaybe (preview (moduleLocation . modulePackage . _Just)) . allModules + -- | Make database from module fromModule :: InspectedModule -> Database fromModule m = zero { @@ -120,7 +124,7 @@ -- | Standalone database standaloneDB :: Database -> Database -standaloneDB db = filterDB (check') (const False) db where +standaloneDB db = filterDB check' (const False) db where check' m = standalone m && byFile m -- | Select module by predicate
src/HsDev/Database/Update.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE FlexibleContexts, GeneralizedNewtypeDeriving, OverloadedStrings, MultiParamTypeClasses #-} +{-# LANGUAGE FlexibleContexts, OverloadedStrings, MultiParamTypeClasses #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module HsDev.Database.Update ( @@ -23,12 +23,15 @@ module Control.Monad.Except ) where +import Control.Concurrent.Lifted (fork) +import Control.DeepSeq import Control.Lens (preview, _Just, view) import Control.Monad.Catch import Control.Monad.CatchIO import Control.Monad.Except import Control.Monad.Reader import Control.Monad.Writer +import Control.Monad.Trans.Control import Data.Aeson import Data.Aeson.Types import qualified Data.HashMap.Strict as HM @@ -60,7 +63,7 @@ isStatus = isJust . parseMaybe (parseJSON :: Value -> Parser Task) -- | Run `UpdateDB` monad -updateDB :: MonadCatchIO m => Settings -> ExceptT String (UpdateDB m) () -> m () +updateDB :: (MonadBaseControl IO m, MonadCatchIO m) => Settings -> ExceptT String (UpdateDB m) () -> m () updateDB sets act = Log.scopeLog (settingsLogger sets) "update" $ do updatedMods <- execWriterT (runUpdateDB (runExceptT act' >> return ()) `runReaderT` sets) wait $ database sets @@ -80,21 +83,22 @@ mlocs' <- liftM (filter (isJust . preview moduleFile) . snd) $ listen act wait $ database sets let + getMods :: (MonadIO m) => m [InspectedModule] getMods = do db' <- liftIO $ readAsync $ database sets return $ catMaybes [M.lookup mloc' (databaseModules db') | mloc' <- mlocs'] when (updateDocs sets) $ do - Log.log Log.Trace "inspecting source docs" - (getMods >>= waiter . runTask "inspecting source docs" [] . runTasks . map scanDocs) + Log.log Log.Trace "forking inspecting source docs" + void $ fork (getMods >>= waiter . mapM_ scanDocs) when (runInferTypes sets) $ do - Log.log Log.Trace "inferring types" - (getMods >>= waiter . runTask "inferring types" [] . runTasks . map inferModTypes) - scanDocs :: MonadIO m => InspectedModule -> ExceptT String (UpdateDB m) () - scanDocs im = runTask "scanning docs" (subject (view inspectedId im) ["module" .= view inspectedId im]) $ do + Log.log Log.Trace "forking inferring types" + void $ fork (getMods >>= waiter . mapM_ inferModTypes) + scanDocs :: (MonadIO m, MonadReader Settings m, MonadWriter [ModuleLocation] m) => InspectedModule -> ExceptT String m () + scanDocs im = do im' <- liftExceptT $ S.scanModify (\opts _ -> inspectDocs opts) im updater $ return $ fromModule im' - inferModTypes :: MonadIO m => InspectedModule -> ExceptT String (UpdateDB m) () - inferModTypes im = runTask "inferring types" (subject (view inspectedId im) ["module" .= view inspectedId im]) $ do + inferModTypes :: (MonadIO m, MonadReader Settings m, MonadWriter [ModuleLocation] m) => InspectedModule -> ExceptT String m () + inferModTypes im = do -- TODO: locate sandbox im' <- liftExceptT $ S.scanModify infer' im updater $ return $ fromModule im' @@ -122,15 +126,15 @@ updater act = do db <- asks database db' <- act - update db $ return db' - tell $ map (view moduleLocation) $ allModules db' + update db $ return $!! db' + tell $!! map (view moduleLocation) $ allModules db' -- | Clear obsolete data from database cleaner :: (MonadIO m, MonadReader Settings m, MonadWriter [ModuleLocation] m) => m Database -> m () cleaner act = do db <- asks database db' <- act - clear db $ return db' + clear db $ return $!! db' -- | Get data from cache without updating DB loadCache :: (MonadIO m, MonadReader Settings m, MonadWriter [ModuleLocation] m) => (FilePath -> ExceptT String IO Structured) -> m Database @@ -216,7 +220,7 @@ return $ FileModule fpath' mproj return [(mloc, [])] False -> return [] - mapM_ (watch watchModule) $ map fst mlocs + mapM_ watch [(`watchModule` m) | (m, _) <- mlocs] scan (Cache.loadFiles (== fpath')) (filterDB (inFile fpath') (const False) . standaloneDB) @@ -229,7 +233,7 @@ -- | Scan cabal modules scanCabal :: (MonadIO m, MonadCatch m) => [String] -> Cabal -> ExceptT String (UpdateDB m) () scanCabal opts cabalSandbox = runTask "scanning" (subject cabalSandbox ["sandbox" .= cabalSandbox]) $ do - watch watchSandbox cabalSandbox + watch (\w -> watchSandbox w cabalSandbox opts) mlocs <- runTask "getting list of cabal modules" [] $ liftExceptT $ listModules opts cabalSandbox scan (Cache.loadCabal cabalSandbox) (cabalDB cabalSandbox) (zip mlocs $ repeat []) opts $ \mlocs' -> do @@ -250,7 +254,7 @@ scanProject :: (MonadIO m, MonadCatch m) => [String] -> FilePath -> ExceptT String (UpdateDB m) () scanProject opts cabal = runTask "scanning" (subject (project cabal) ["project" .= cabal]) $ do proj <- scanProjectFile opts cabal - watch watchProject proj + watch (\w -> watchProject w proj opts) (_, sources) <- liftExceptT $ S.enumProject proj scan (Cache.loadProject $ view projectCabal proj) (projectDB proj) sources opts $ \ms -> do scanModules opts ms @@ -263,7 +267,7 @@ liftExceptT $ S.enumDirectory dir runTasks [scanProject opts (view projectCabal p) | (p, _) <- projSrcs] runTasks $ map (scanCabal opts) sboxes - mapM_ (watch watchModule) $ map fst standSrcs + mapM_ watch [(`watchModule` m) | (m, _) <- standSrcs] scan (Cache.loadFiles (dir `isParent`)) (filterDB inDir (const False) . standaloneDB) standSrcs opts $ scanModules opts where inDir = maybe False (dir `isParent`) . preview (moduleIdLocation . moduleFile) @@ -292,25 +296,35 @@ instance Format Cabal where updateEvent :: (MonadIO m, MonadCatch m, MonadCatchIO m) => Watched -> Event -> ExceptT String (UpdateDB m) () -updateEvent (WatchedProject proj) e +updateEvent (WatchedProject proj projOpts) e | isSource e = do Log.log Log.Info $ "File '${file}' in project ${proj} changed" ~~ ("file" %= view eventPath e) % ("proj" %= view projectName proj) - scanFile [] $ view eventPath e + dbval <- readDB + let + opts = fromMaybe [] $ do + m <- lookupFile (view eventPath e) dbval + preview (inspection . inspectionOpts) $ getInspected dbval m + scanFile opts $ view eventPath e | isCabal e = do Log.log Log.Info $ "Project ${proj} changed" ~~ ("proj" %= view projectName proj) - scanProject [] $ view projectCabal proj + scanProject projOpts $ view projectCabal proj | otherwise = return () -updateEvent (WatchedSandbox cabal) e +updateEvent (WatchedSandbox cabal cabalOpts) e | isConf e = do Log.log Log.Info $ "Sandbox ${cabal} changed" ~~ ("cabal" %= cabal) - scanCabal [] cabal + scanCabal cabalOpts cabal | otherwise = return () updateEvent WatchedModule e | isSource e = do Log.log Log.Info $ "Module ${file} changed" ~~ ("file" %= view eventPath e) - scanFile [] $ view eventPath e + dbval <- readDB + let + opts = fromMaybe [] $ do + m <- lookupFile (view eventPath e) dbval + preview (inspection . inspectionOpts) $ getInspected dbval m + scanFile opts $ view eventPath e | otherwise = return () processEvent :: Settings -> Watched -> Event -> IO () @@ -323,7 +337,7 @@ subject :: Display a => a -> [Pair] -> [Pair] subject x ps = ["name" .= display x, "type" .= displayType x] ++ ps -watch :: (MonadIO m, MonadReader Settings m) => (Watcher -> a -> IO ()) -> a -> m () -watch f x = do +watch :: (MonadIO m, MonadReader Settings m) => (Watcher -> IO ()) -> m () +watch f = do w <- asks settingsWatcher - liftIO $ f w x + liftIO $ f w
src/HsDev/Database/Update/Types.hs view
@@ -1,15 +1,17 @@-{-# LANGUAGE GeneralizedNewtypeDeriving, OverloadedStrings #-} +{-# LANGUAGE GeneralizedNewtypeDeriving, OverloadedStrings, FlexibleInstances, MultiParamTypeClasses, TypeFamilies, UndecidableInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module HsDev.Database.Update.Types ( Status(..), Progress(..), Task(..), Settings(..), settings, UpdateDB(..) ) where +import Control.Monad.Base import Control.Monad.Catch import Control.Monad.CatchIO import Control.Monad.Except import Control.Monad.Reader import Control.Monad.Writer +import Control.Monad.Trans.Control import Data.Aeson import qualified System.Log.Simple as Log @@ -111,3 +113,11 @@ instance Log.MonadLog m => Log.MonadLog (ExceptT e m) where askLog = lift Log.askLog + +instance MonadBase b m => MonadBase b (UpdateDB m) where + liftBase = UpdateDB . liftBase + +instance MonadBaseControl b m => MonadBaseControl b (UpdateDB m) where + type StM (UpdateDB m) a = StM (ReaderT Settings (WriterT [ModuleLocation] m)) a + liftBaseWith f = UpdateDB $ liftBaseWith (\f' -> f (f' . runUpdateDB)) + restoreM = UpdateDB . restoreM
src/HsDev/Inspect.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE TypeSynonymInstances, ViewPatterns #-}+{-# LANGUAGE TypeSynonymInstances #-} module HsDev.Inspect ( analyzeModule, inspectDocsChunk, inspectDocs,@@ -82,7 +82,7 @@ offsets = scanl (+) 0 $ map length parts' break' :: [String] -> Maybe ([String], [String]) break' [] = Nothing- break' (l:ls) = Just $ first (l:) $ break (not . maybe True isSpace . listToMaybe) ls+ break' (l:ls) = Just $ first (l:) $ span (maybe True isSpace . listToMaybe) ls parseModuleName :: String -> Either String String parseModuleName cts = maybe (Left "match fail") Right $ do@@ -193,7 +193,7 @@ H.RecDecl n fields -> mkFun loc n (Function (Just $ oneLinePrint $ map snd fields `tyFun` dataRes) [] Nothing) : concatMap (uncurry (getRec loc dataRes)) fields where dataRes :: H.Type- dataRes = foldr H.TyApp (H.TyCon (H.UnQual t)) $ map (H.TyVar . nameOf) as where+ dataRes = foldr (H.TyApp . H.TyVar . nameOf) (H.TyCon (H.UnQual t)) as where nameOf :: H.TyVarBind -> H.Name nameOf (H.KindedVar n' _) = n' nameOf (H.UnkindedVar n') = n'@@ -296,7 +296,7 @@ docsMap <- liftE $ if hdocsWorkaround then hdocsProcess (fromMaybe (T.unpack $ view moduleName m) (preview (moduleLocation . moduleFile) m)) opts else liftM Just $ hdocs (view moduleLocation m) opts- return $ maybe id addDocs docsMap $ m+ return $ maybe id addDocs docsMap m -- | Inspect contents inspectContents :: String -> [String] -> String -> ExceptT String IO InspectedModule@@ -366,4 +366,4 @@ modules <- mapM inspectFile' srcs return (p', catMaybes modules) where- inspectFile' exts = liftM return (inspectFile (opts ++ extensionsOpts (view extensions exts)) (view entity exts)) <|> return Nothing+ inspectFile' exts = liftM return (inspectFile (opts ++ extensionsOpts exts) (view entity exts)) <|> return Nothing
src/HsDev/Project.hs view
@@ -12,8 +12,8 @@ libraryModules, libraryBuildInfo, executableName, executablePath, executableBuildInfo, testName, testEnabled, testBuildInfo, - infoDepends, infoLanguage, infoExtensions, infoSourceDirs, - extensions, entity, + infoDepends, infoLanguage, infoExtensions, infoGHCOptions, infoSourceDirs, + extensions, ghcOptions, entity, -- * Helpers showExtension, flagExtension, extensionFlag, @@ -30,6 +30,7 @@ import Data.List import Data.Maybe import Data.Ord +import Distribution.Compiler (CompilerFlavor(GHC)) import qualified Distribution.Package as P import qualified Distribution.PackageDescription as PD import Distribution.PackageDescription.Parse @@ -190,6 +191,7 @@ _infoDepends :: [String], _infoLanguage :: Maybe Language, _infoExtensions :: [Extension], + _infoGHCOptions :: [String], _infoSourceDirs :: [FilePath] } deriving (Eq, Read) @@ -203,11 +205,14 @@ set' i dirs = i { _infoSourceDirs = dirs } instance Show Info where - show i = unlines $ lang ++ exts ++ sources where + show i = unlines $ lang ++ exts ++ opts ++ sources where lang = maybe [] (\l -> ["default-language: " ++ display l]) $ _infoLanguage i exts | null (_infoExtensions i) = [] - | otherwise = ["extensions:"] ++ map (tab 1) (map display (_infoExtensions i)) + | otherwise = ["extensions:"] ++ map (tab 1 . display) (_infoExtensions i) + opts + | null (_infoGHCOptions i) = [] + | otherwise = ["ghc-options:"] ++ map (tab 1) (_infoGHCOptions i) sources = ["source-dirs:"] ++ (map (tab 1) $ _infoSourceDirs i) @@ -216,6 +221,7 @@ "build-depends" .= _infoDepends i, "language" .= fmap display (_infoLanguage i), "extensions" .= map display (_infoExtensions i), + "ghc-options" .= _infoGHCOptions i, "source-dirs" .= _infoSourceDirs i] instance FromJSON Info where @@ -223,6 +229,7 @@ v .: "build-depends" <*> ((v .:: "language") >>= traverse (parseDT "Language")) <*> ((v .:: "extensions") >>= traverse (parseDT "Extension")) <*> + v .:: "ghc-options" <*> v .:: "source-dirs" -- | Analyze cabal file @@ -241,6 +248,7 @@ _infoDepends = map pkgName (PD.targetBuildDepends info), _infoLanguage = PD.defaultLanguage info, _infoExtensions = PD.defaultExtensions info, + _infoGHCOptions = fromMaybe [] $ lookup GHC (PD.options info), _infoSourceDirs = PD.hsSourceDirs info } pkgName :: P.Dependency -> String @@ -300,6 +308,7 @@ -- | Entity with project extensions data Extensions a = Extensions { _extensions :: [Extension], + _ghcOptions :: [String], _entity :: a } deriving (Eq, Read, Show) @@ -307,22 +316,23 @@ compare = comparing _entity instance Functor Extensions where - fmap f (Extensions e x) = Extensions e (f x) + fmap f (Extensions e o x) = Extensions e o (f x) instance Applicative Extensions where - pure = Extensions [] - (Extensions l f) <*> (Extensions r x) = Extensions (l ++ r) (f x) + pure = Extensions [] [] + (Extensions l lo f) <*> (Extensions r ro x) = Extensions (ordNub $ l ++ r) (ordNub $ lo ++ ro) (f x) instance Foldable Extensions where - foldMap f (Extensions _ x) = f x + foldMap f (Extensions _ _ x) = f x instance Traversable Extensions where - traverse f (Extensions e x) = Extensions e <$> f x + traverse f (Extensions e o x) = Extensions e o <$> f x -- | Extensions for target withExtensions :: a -> Info -> Extensions a withExtensions x i = Extensions { _extensions = _infoExtensions i, + _ghcOptions = _infoGHCOptions i, _entity = x } -- | Returns build targets infos @@ -374,8 +384,8 @@ extensionFlag = ("-X" ++) -- | Extensions as opts to GHC -extensionsOpts :: [Extension] -> [String] -extensionsOpts = map (extensionFlag . showExtension) +extensionsOpts :: Extensions a -> [String] +extensionsOpts e = map (extensionFlag . showExtension) (_extensions e) ++ _ghcOptions e makeLenses ''Project makeLenses ''ProjectDescription
src/HsDev/Scan.hs view
@@ -62,7 +62,7 @@ ["-package " ++ view projectName p'], ["-package " ++ dep | dep <- view infoDepends i, dep `elem` pkgs]] srcs <- projectSources p' - return (p', [(FileModule (view entity src) (Just p'), extensionsOpts (view extensions src) ++ projOpts (view entity src)) | src <- srcs]) + return (p', [(FileModule (view entity src) (Just p'), extensionsOpts src ++ projOpts (view entity src)) | src <- srcs]) -- | Enum directory modules enumDirectory :: FilePath -> ExceptT String IO ScanContents @@ -76,7 +76,7 @@ projs <- triesMap (enumProject . project) projects let projPaths = map (view projectPath . fst) projs - standalone = map (\f -> FileModule f Nothing) $ filter (\s -> not (any (`isParent` s) projPaths)) sources + standalone = map ((`FileModule` Nothing)) $ filter (\s -> not (any (`isParent` s) projPaths)) sources return $ ScanContents { modulesToScan = [(s, []) | s <- standalone], projectsToScan = projs, @@ -129,4 +129,4 @@ -- | Returns new (to scan) and changed (to rescan) modules changedModules :: Database -> [String] -> [ModuleToScan] -> ExceptT String IO [ModuleToScan] -changedModules db opts ms = filterM (\(m, opts') -> changedModule db (opts ++ opts') m) ms +changedModules db opts = filterM (\ (m, opts') -> changedModule db (opts ++ opts') m)
src/HsDev/Scan/Browse.hs view
@@ -39,7 +39,7 @@ -- | Browse packages browsePackages :: [String] -> Cabal -> ExceptT String IO [ModulePackage] -browsePackages opts cabal = liftIOErrors $ withPackages (cabalOpt cabal ++ opts) $ \dflags -> do +browsePackages opts cabal = liftIOErrors $ withPackages (cabalOpt cabal ++ opts) $ \dflags -> return $ mapMaybe readPackage $ fromMaybe [] $ GHC.pkgDatabase dflags listModules :: [String] -> Cabal -> ExceptT String IO [ModuleLocation]
src/HsDev/Symbols.hs view
@@ -22,8 +22,7 @@ Canonicalize(..), locateProject, searchProject, locateSourceDir, - sourceModuleRoot, - importedModulePath, + moduleOpts, -- * Modifiers addDeclaration, @@ -47,7 +46,7 @@ import Data.Maybe (fromMaybe) import Data.Ord (comparing) import Data.Text (Text) -import qualified Data.Text as T (concat, split, unpack) +import qualified Data.Text as T (concat) import System.Directory import System.FilePath @@ -213,22 +212,24 @@ proj <- MaybeT $ fmap (either (const Nothing) Just) $ runExceptT $ loadProject p MaybeT $ return $ findSourceDir proj file --- | Get source module root directory, i.e. for "...\src\Foo\Bar.hs" with module 'Foo.Bar' will return "...\src" -sourceModuleRoot :: Text -> FilePath -> FilePath -sourceModuleRoot mname = - joinPath . - reverse . drop (length $ T.split (== '.') mname) . reverse . - splitDirectories - --- | Get path of imported module --- >importedModulePath "Foo.Bar" "...\src\Foo\Bar.hs" "Quux.Blah" = "...\src\Quux\Blah.hs" -importedModulePath :: Text -> FilePath -> Text -> FilePath -importedModulePath mname file imp = - (`addExtension` "hs") . joinPath . - (++ ipath) . splitDirectories $ - sourceModuleRoot mname file - where - ipath = map T.unpack $ T.split (== '.') imp +-- | Options for GHC of module and project +moduleOpts :: [ModulePackage] -> Module -> [String] +moduleOpts pkgs m = case view moduleLocation m of + FileModule file proj -> concat [ + ["-i" ++ s | s <- srcDirs], + concatMap extensionsOpts exts, + hidePackages, + ["-package " ++ p | p <- deps, p `elem` pkgs']] + where + infos' = maybe [] (`fileTargets` file) proj + srcDirs = concatMap (view infoSourceDirs) infos' + exts = map (file `withExtensions`) infos' + deps = concatMap (view infoDepends) infos' + pkgs' = map (view packageName) pkgs + hidePackages + | null infos' = [] + | otherwise = ["-hide-all-packages"] + _ -> [] -- | Add declaration to module addDeclaration :: Declaration -> Module -> Module
src/HsDev/Symbols/Location.hs view
@@ -11,6 +11,8 @@ regionFrom, regionTo, locationModule, locationPosition, + sourceModuleRoot, + importedModulePath, packageOpt, RecalcTabs(..), @@ -25,6 +27,9 @@ import Data.Char (isSpace, isDigit) import Data.List (intercalate, findIndex) import Data.Maybe +import Data.Text (Text) +import qualified Data.Text as T (split, unpack) +import System.FilePath import Text.Read (readMaybe) import HsDev.Cabal @@ -140,13 +145,9 @@ -- | Get string at region regionStr :: Region -> String -> String -regionStr r@(Region f t) s = intercalate "\n" $ drop (pred $ view positionColumn f) fline' : tl where +regionStr r@(Region f t) s = intercalate "\n" $ drop (pred $ view positionColumn f) fline : tl where s' = take (regionLines r) $ drop (pred (view positionLine f)) $ lines s (fline:tl) = init s' ++ [take (pred $ view positionColumn t) (last s')] - fline' = concatMap untab fline where - untab :: Char -> String - untab '\t' = replicate 8 ' ' - untab ch = [ch] instance NFData Region where rnf (Region f t) = rnf f `seq` rnf t @@ -188,15 +189,35 @@ v .:: "module" <*> v .:: "pos" +-- | Get source module root directory, i.e. for "...\src\Foo\Bar.hs" with module 'Foo.Bar' will return "...\src" +sourceModuleRoot :: Text -> FilePath -> FilePath +sourceModuleRoot mname = + joinPath . + reverse . drop (length $ T.split (== '.') mname) . reverse . + splitDirectories + +-- | Get path of imported module +-- >importedModulePath "Foo.Bar" "...\src\Foo\Bar.hs" "Quux.Blah" = "...\src\Quux\Blah.hs" +importedModulePath :: Text -> FilePath -> Text -> FilePath +importedModulePath mname file imp = + (`addExtension` "hs") . joinPath . + (++ ipath) . splitDirectories $ + sourceModuleRoot mname file + where + ipath = map T.unpack $ T.split (== '.') imp + packageOpt :: Maybe ModulePackage -> [String] packageOpt = maybeToList . fmap (("-package " ++) . view packageName) --- | Recalc positions to interpret '\t' as one symbol instead of 8 +-- | Recalc positions to interpret '\t' as one symbol instead of N class RecalcTabs a where - recalcTabs :: String -> a -> a + -- | Interpret '\t' as one symbol instead of N + recalcTabs :: String -> Int -> a -> a + -- | Inverse of `recalcTabs`: interpret '\t' as N symbols instead of 1 + calcTabs :: String -> Int -> a -> a instance RecalcTabs Position where - recalcTabs cts (Position l c) = Position l c' where + recalcTabs cts n (Position l c) = Position l c' where line = listToMaybe $ drop (pred l) $ lines cts c' = case line of Nothing -> c @@ -205,8 +226,15 @@ findIndex (>= pred c) . scanl (+) 0 $ sizes charSize :: Char -> Int - charSize '\t' = 8 + charSize '\t' = n charSize _ = 1 + calcTabs cts n (Position l c) = Position l c' where + line = listToMaybe $ drop (pred l) $ lines cts + c' = maybe c (succ . sum . map charSize . take (pred c)) line + charSize :: Char -> Int + charSize '\t' = n + charSize _ = 1 instance RecalcTabs Region where - recalcTabs cts (Region f t) = Region (recalcTabs cts f) (recalcTabs cts t) + recalcTabs cts n (Region f t) = Region (recalcTabs cts n f) (recalcTabs cts n t) + calcTabs cts n (Region f t) = Region (calcTabs cts n f) (calcTabs cts n t)
src/HsDev/Symbols/Resolve.hs view
@@ -135,8 +135,8 @@ db <- ask return $ fromMaybe [] $ - listToMaybe $ dropWhile null $ - [selectModules (select' f) db | f <- byImport i' : fs] + listToMaybe $ dropWhile null + [selectModules (select' f) db | f <- byImport i' : fs] where select' f md = view moduleName md == view importModuleName i' && f (view moduleId md) filterImportList :: [Declaration] -> [Declaration]
src/HsDev/Symbols/Types.hs view
@@ -518,7 +518,7 @@ qualifiedName :: ModuleId -> Declaration -> Text qualifiedName m' d' = T.concat [_moduleIdName m', ".", _declarationName d'] symbolDocs = _declarationDocs . _moduleDeclaration - symbolLocation d = set locationPosition (_declarationPosition $ _moduleDeclaration d) $ + symbolLocation d = set locationPosition (_declarationPosition $ _moduleDeclaration d) (symbolLocation . _declarationModuleId $ d) instance Documented ModuleId where
src/HsDev/Tools/AutoFix.hs view
@@ -77,9 +77,9 @@ correctors :: [CorrectorMatch] correctors = [ match "^The import of `([\\w\\.]+)' is redundant" $ \_ rgn -> Correction - "Redunant import" + "Redundant import" (replace - (rangeFrom rgn `rangeSize` linesSize 1) + (expandLines rgn) ""), match "^(.*?)\nFound:\n (.*?)\nWhy not:\n (.*?)$" $ \g rgn -> Correction (g `at` 1)
src/HsDev/Tools/Ghc/Check.hs view
@@ -26,7 +26,7 @@ import FastString (unpackFS) import qualified ErrUtils as E -import HsDev.Symbols (Canonicalize(..), sourceModuleRoot) +import HsDev.Symbols (Canonicalize(..), moduleOpts) import HsDev.Symbols.Location import HsDev.Symbols.Types import HsDev.Tools.Base @@ -41,7 +41,7 @@ modifyFlags (\fs -> fs { log_action = logAction ch }) _ <- addCmdOpts ("-Wall" : (cabalOpt cabal ++ opts)) clearTargets - mapM (flip makeTarget Nothing) files >>= loadTargets + mapM (`makeTarget` Nothing) files >>= loadTargets notes <- liftIO $ stopChan ch liftIO $ recalcNotesTabs notes @@ -50,27 +50,17 @@ check opts cabal m = case view moduleLocation m of FileModule file proj -> do ch <- liftIO newChan - pkgs <- lift $ liftM (map $ view packageName) listPackages + pkgs <- lift listPackages let dir = fromMaybe (sourceModuleRoot (view moduleName m) file) $ preview (_Just . projectPath) proj - infos' = maybe [] (`fileTargets` file) proj - srcDirs = concatMap (view infoSourceDirs) infos' - exts = concatMap (view infoExtensions) infos' - deps = concatMap (view infoDepends) infos' - hidePackages - | null infos' = [] - | otherwise = ["-hide-all-packages"] lift $ withFlags $ withCurrentDirectory dir $ do modifyFlags (\fs -> fs { log_action = logAction ch }) _ <- addCmdOpts $ concat [ ["-Wall"], cabalOpt cabal, - ["-i" ++ s | s <- srcDirs], - extensionsOpts exts, - hidePackages, - ["-package " ++ p | p <- deps, p `elem` pkgs], + moduleOpts pkgs m, opts] clearTargets target <- makeTarget (makeRelative dir file) Nothing @@ -122,7 +112,7 @@ recalc' n = fromMaybe n $ do fname <- preview (noteSource . moduleFile) n cts' <- lookup fname (zip files cts) - return $ recalcTabs cts' n + return $ recalcTabs cts' 8 n return $ map recalc' notes where files = ordNub $ notes ^.. each . noteSource . moduleFile
src/HsDev/Tools/GhcMod.hs view
@@ -124,7 +124,7 @@ maybe (throwError $ GhcMod.GMEString $ "Can't parse info: '" ++ sname ++ "'") return $ parseData s `mplus` parseFunction s recalcDeclTabs :: String -> Declaration -> Declaration - recalcDeclTabs fstr = over (declarationPosition . _Just) (recalcTabs fstr) + recalcDeclTabs fstr = over (declarationPosition . _Just) (recalcTabs fstr 8) parseFunction s = do groups <- matchRx (sname ++ "\\s+::\\s+(.*?)(\\s+-- Defined (at (.*)|in `(.*)'))?$") s return (decl (fromString sname) (Function (Just $ fromString $ groups `at` 1) [] Nothing)) { @@ -175,7 +175,9 @@ typeOf :: [String] -> Cabal -> FilePath -> Int -> Int -> GhcModT IO [TypedRegion] typeOf opts cabal file line col = withOptions (\o -> o { GhcMod.ghcUserOptions = cabalOpt cabal ++ opts }) $ do fileCts <- liftIO $ readFileUtf8 file - ts <- lines <$> GhcMod.types file line col + let + Position line' col' = calcTabs fileCts 8 (Position line col) + ts <- lines <$> GhcMod.types file line' col' return $ mapMaybe (toRegionType fileCts) ts where toRegionType :: String -> String -> Maybe TypedRegion @@ -185,7 +187,7 @@ parseRegion :: String -> ReadM Region parseRegion fstr = Region <$> parsePosition fstr <*> parsePosition fstr parsePosition :: String -> ReadM Position - parsePosition fstr = recalcTabs fstr <$> (Position <$> readParse <*> readParse) + parsePosition fstr = recalcTabs fstr 8 <$> (Position <$> readParse <*> readParse) parseOutputMessages :: String -> [Note OutputMessage] parseOutputMessages = mapMaybe parseOutputMessage . lines @@ -205,7 +207,7 @@ recalcOutputMessageTabs fileCts n = fromMaybe n $ do src <- preview (noteSource . moduleFile) n cts <- lookup src fileCts - return $ recalcTabs cts n + return $ recalcTabs cts 8 n -- | Replace NULL with newline nullToNL :: String -> String
src/HsDev/Tools/HDocs.hs view
@@ -9,6 +9,7 @@ ) where import Control.Exception +import Control.DeepSeq import Control.Lens (set, view, over) import Control.Monad () import Control.Monad.Except @@ -27,13 +28,13 @@ -- | Get docs for modules hdocsy :: [ModuleLocation] -> [String] -> IO [Map String String] -hdocsy mlocs opts = runExceptT (docs' mlocs) >>= return . either (const $ replicate (length mlocs) M.empty) (map HDocs.formatDocs) where +hdocsy mlocs opts = runExceptT (docs' mlocs) >>= return . either (const $ replicate (length mlocs) M.empty) (map $ force . HDocs.formatDocs) where docs' :: [ModuleLocation] -> ExceptT String IO [HDocs.ModuleDocMap] docs' ms = liftM (map snd) $ HDocs.readSources_ opts $ map (view moduleFile) ms -- | Get docs for module hdocs :: ModuleLocation -> [String] -> IO (Map String String) -hdocs mloc opts = runExceptT (docs' mloc) >>= return . either (const M.empty) HDocs.formatDocs where +hdocs mloc opts = runExceptT (docs' mloc) >>= return . either (const M.empty) (force . HDocs.formatDocs) where docs' :: ModuleLocation -> ExceptT String IO HDocs.ModuleDocMap docs' (FileModule fpath _) = liftM snd $ HDocs.readSource opts fpath docs' (CabalModule _ _ mname) = HDocs.moduleDocs opts mname @@ -41,7 +42,7 @@ -- | Get all docs hdocsCabal :: Cabal -> [String] -> ExceptT String IO (Map String (Map String String)) -hdocsCabal cabal opts = liftM (M.map HDocs.formatDocs) $ HDocs.installedDocs (cabalOpt cabal ++ opts) +hdocsCabal cabal opts = liftM (M.map $ force . HDocs.formatDocs) $ HDocs.installedDocs (cabalOpt cabal ++ opts) -- | Set docs for module setDocs :: Map String String -> Module -> Module
src/HsDev/Tools/HLint.hs view
@@ -4,24 +4,30 @@ module Control.Monad.Except ) where +import Control.Arrow +import Control.Lens (over, view, _Just) import Control.Monad.Except -import Language.Haskell.HLint3 (autoSettings, parseModuleEx, applyHints, Idea(..), parseErrorMessage) +import Data.Char +import Data.List +import Data.Maybe (mapMaybe) +import Data.Ord +import Language.Haskell.HLint3 (autoSettings, parseModuleEx, applyHints, Idea(..), parseErrorMessage, ParseFlags(..), CppFlags(..)) import Language.Haskell.Exts.SrcLoc import qualified Language.Haskell.HLint3 as HL (Severity(..)) import HsDev.Symbols (Canonicalize(..)) import HsDev.Symbols.Location import HsDev.Tools.Base -import HsDev.Util (readFileUtf8) +import HsDev.Util (readFileUtf8, split) hlint :: FilePath -> ExceptT String IO [Note OutputMessage] hlint file = do file' <- liftIO $ canonicalize file cts <- liftIO $ readFileUtf8 file' (flags, classify, hint) <- liftIO autoSettings - p <- liftIO $ parseModuleEx flags file' (Just cts) + p <- liftIO $ parseModuleEx (flags { cppFlags = CppSimple }) file' (Just cts) m <- either (throwError . parseErrorMessage) return p - return $ map (recalcTabs cts . fromIdea) $ applyHints classify hint [m] + return $ map (recalcTabs cts 8 . indentIdea cts . fromIdea) $ applyHints classify hint [m] fromIdea :: Idea -> Note OutputMessage fromIdea idea = Note { @@ -36,3 +42,50 @@ _messageSuggestion = ideaTo idea } } where src = ideaSpan idea + +indentIdea :: String -> Note OutputMessage -> Note OutputMessage +indentIdea cts idea = case analyzeIndent cts of + Nothing -> idea + Just i -> over (note . messageSuggestion . _Just) (indent' i) idea + where + indent' i' = intercalate "\n" . indentTail . map (uncurry (++) . first (concat . (`replicate` i') . (`div` 2) . length) . span isSpace) . split (== '\n') + indentTail [] = [] + indentTail (h : hs) = h : map (firstIndent ++) hs + firstIndent = takeWhile isSpace firstLine + firstLine = regionStr (Position firstLineNum 1 `region` Position (succ firstLineNum) 1) cts + firstLineNum = view (noteRegion . regionFrom . positionLine) idea + +-- | Indent in source +data Indent = Spaces Int | Tabs deriving (Eq, Ord) + +instance Show Indent where + show (Spaces n) = replicate n ' ' + show Tabs = "\t" + +-- | Analyze source indentation to convert suggestion to same indentation +-- Returns one indent +analyzeIndent :: String -> Maybe String +analyzeIndent = + fmap show . selectIndent . map fst . dropUnusual . + sortBy (comparing $ negate . snd) . + map (head &&& length) . + group . sort . + mapMaybe (guessIndent . takeWhile isSpace) . lines + where + selectIndent :: [Indent] -> Maybe Indent + selectIndent [] = Nothing + selectIndent (Tabs : _) = Just Tabs + selectIndent indents = Just $ Spaces $ foldr1 gcd $ mapMaybe spaces indents where + spaces :: Indent -> Maybe Int + spaces Tabs = Nothing + spaces (Spaces n) = Just n + dropUnusual :: [(Indent, Int)] -> [(Indent, Int)] + dropUnusual [] = [] + dropUnusual is@((_, freq):_) = takeWhile ((> freq `div` 5) . snd) is + +-- | Guess indent of one line +guessIndent :: String -> Maybe Indent +guessIndent s + | all (== ' ') s = Just $ Spaces $ length s + | all (== '\t') s = Just Tabs + | otherwise = Nothing
src/HsDev/Tools/Types.hs view
@@ -67,7 +67,8 @@ v .:: "note" instance RecalcTabs (Note a) where - recalcTabs cts (Note s r l n) = Note s (recalcTabs cts r) l n + recalcTabs cts n' (Note s r l n) = Note s (recalcTabs cts n' r) l n + calcTabs cts n' (Note s r l n) = Note s (calcTabs cts n' r) l n instance Canonicalize (Note a) where canonicalize (Note s r l n) = Note <$> canonicalize s <*> pure r <*> pure l <*> pure n
src/HsDev/Watcher.hs view
@@ -7,7 +7,7 @@ ) where import Control.Lens (view) -import System.FilePath (takeDirectory, takeExtension) +import System.FilePath (takeDirectory, takeExtension, (</>)) import System.Directory.Watcher hiding (Watcher) import HsDev.Project @@ -15,24 +15,25 @@ import HsDev.Watcher.Types -- | Watch for project sources changes -watchProject :: Watcher -> Project -> IO () -watchProject w proj = do - mapM_ (\dir -> watchTree w dir isSource (WatchedProject proj)) dirs - watchDir w (view projectPath proj) isCabal (WatchedProject proj) +watchProject :: Watcher -> Project -> [String] -> IO () +watchProject w proj opts = do + mapM_ (\dir -> watchTree w dir isSource (WatchedProject proj opts)) dirs + watchDir w projDir isCabal (WatchedProject proj opts) where - dirs = map (view entity) $ maybe [] sourceDirs $ view projectDescription proj + dirs = map ((projDir </>) . view entity) $ maybe [] sourceDirs $ view projectDescription proj + projDir = view projectPath proj -- | Watch for standalone source watchModule :: Watcher -> ModuleLocation -> IO () watchModule w (FileModule f Nothing) = watchDir w (takeDirectory f) isSource WatchedModule -watchModule w (FileModule _ (Just proj)) = watchProject w proj -watchModule w (CabalModule cabal _ _) = watchSandbox w cabal +watchModule w (FileModule _ (Just proj)) = watchProject w proj [] +watchModule w (CabalModule cabal _ _) = watchSandbox w cabal [] watchModule _ _ = return () -- | Watch for sandbox -watchSandbox :: Watcher -> Cabal -> IO () -watchSandbox _ Cabal = return () -watchSandbox w (Sandbox f) = watchTree w f isConf (WatchedSandbox $ Sandbox f) +watchSandbox :: Watcher -> Cabal -> [String] -> IO () +watchSandbox _ Cabal _ = return () +watchSandbox w (Sandbox f) opts = watchTree w f isConf (WatchedSandbox (Sandbox f) opts) isSource :: Event -> Bool isSource (Event _ f _) = takeExtension f == ".hs"
src/HsDev/Watcher/Types.hs view
@@ -9,6 +9,6 @@ import HsDev.Project (Project) import HsDev.Cabal (Cabal) -data Watched = WatchedProject Project | WatchedSandbox Cabal | WatchedModule +data Watched = WatchedProject Project [String] | WatchedSandbox Cabal [String] | WatchedModule type Watcher = W.Watcher Watched
src/System/Directory/Watcher.hs view
@@ -53,16 +53,16 @@ -- | Watch directory watchDir :: Watcher a -> FilePath -> (Event -> Bool) -> a -> IO () watchDir w f p v = do - f' <- canonicalizePath f - e <- doesDirectoryExist f' + e <- doesDirectoryExist f when e $ do + f' <- canonicalizePath f watching <- isWatchingDir w f' unless watching $ do stop <- FS.watchDir (watcherMan w) (fromString f') (p . fromEvent) - (writeChan (watcherChan w) . ((,) v) . fromEvent) + (writeChan (watcherChan w) . (,) v . fromEvent) modifyMVar_ (watcherDirs w) $ return . M.insert f' (False, stop) watchDir_ :: Watcher () -> FilePath -> (Event -> Bool) -> IO () @@ -92,16 +92,16 @@ -- | Watch directory tree watchTree :: Watcher a -> FilePath -> (Event -> Bool) -> a -> IO () watchTree w f p v = do - f' <- canonicalizePath f - e <- doesDirectoryExist f' + e <- doesDirectoryExist f when e $ do + f' <- canonicalizePath f watching <- isWatchingTree w f' unless watching $ do stop <- FS.watchTree (watcherMan w) (fromString f') (p . fromEvent) - (writeChan (watcherChan w) . ((,) v) . fromEvent) + (writeChan (watcherChan w) . (,) v . fromEvent) modifyMVar_ (watcherDirs w) $ return . M.insert f' (True, stop) watchTree_ :: Watcher () -> FilePath -> (Event -> Bool) -> IO ()
src/System/Win32/PowerShell.hs view
@@ -110,8 +110,8 @@ invoke' = case e of Var n -> n _ -> unwords ["&", compile $ cbra e] - p' = intercalate " " $ map (compile . cbra) p - ns' = intercalate " " $ concatMap named' $ M.toList ns where + p' = unwords $ map (compile . cbra) p + ns' = unwords $ concatMap named' $ M.toList ns where named' :: (String, Maybe Expr) -> [String] named' (n, Just v) = ['-':n, compile $ cbra v] named' (n, Nothing) = ['-':n] @@ -156,7 +156,7 @@ call f pos named' = CmdLet f $ Args pos (M.fromList named') cmdlet :: String -> [Expr] -> [(String, Maybe Expr)] -> CmdLet -cmdlet n pos = call (name n) pos +cmdlet n = call (name n) lambda :: [String] -> ([Expr] -> Expr) -> Expr lambda = Lambda