diff --git a/.ghci b/.ghci index 835a4e2c..fbd42bf2 100644 --- a/.ghci +++ b/.ghci @@ -1,4 +1,4 @@ -:set -fwarn-unused-binds -fwarn-unused-imports -fwarn-orphans +:set -Wunused-binds -Wunused-imports -Worphans :set -isrc :load Ghcid src/Paths.hs Test :def docs_ const $ return $ unlines [":!cabal haddock"] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 443d74bb..6218b100 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - ghc: ['9.2', '9.0', '8.10', '8.8', '8.6'] + ghc: ['9.6', '9.4', '9.2', '9.0', '8.10', '8.8'] include: - os: windows-latest - os: macOS-latest @@ -20,7 +20,7 @@ jobs: steps: - run: git config --global core.autocrlf false - uses: actions/checkout@v2 - - uses: haskell/actions/setup@v1 + - uses: haskell/actions/setup@v2 id: setup-haskell with: ghc-version: ${{ matrix.ghc }} diff --git a/CHANGES.txt b/CHANGES.txt index 7468b27c..7abc1588 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,11 @@ Changelog for ghcid (* = breaking change) +0.8.9, released 2023-07-02 + #375, fix crash when modules are renamed or deleted in GHC 9.6 + #378, write output of the linter into output files + Support newer Win32 libraries +0.8.8, released 2022-09-17 + #365, support fsnotify 0.4 #348, improve the no files loaded message #321, print Ghcide has stopped to the output file. 0.8.7, released 2020-06-06 diff --git a/LICENSE b/LICENSE index 387e2b75..87b62de0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright Neil Mitchell 2014-2022. +Copyright Neil Mitchell 2014-2023. All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index d9164189..f6f96907 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,13 @@ I've gone for simplicity over features. It's a point in the design space, but no Yes, that's a [bug in GHCi](https://ghc.haskell.org/trac/ghc/ticket/9648). If you see GHCi getting confused just kill `ghcid` and start it again. #### I want to run arbitrary commands when arbitrary files change. -This project reloads `ghci` when files loaded by `ghci` change. If you want a more general mechanism something like [Steel Overseer](https://github.com/schell/steeloverseer) or [Watchman](https://facebook.github.io/watchman/) will probably work better. +This project reloads `ghci` when files loaded by `ghci` change. If you want a more general mechanism, consider: + +* [Steel Overseer](https://github.com/schell/steeloverseer) ([Hackage](https://hackage.haskell.org/package/steeloverseer)) +* [Watchman](https://facebook.github.io/watchman/) +* [`feedback`](https://github.com/NorfairKing/feedback) ([Hackage](https://hackage.haskell.org/package/feedback)) +* [Watchexec](https://github.com/watchexec/watchexec) +* [`entr`](https://github.com/eradman/entr) #### I want syntax highlighting in the error messages. One option is to use Neovim or Emacs and run the terminal in a buffer whose file type is set to Haskell. Another option is to pipe `ghcid` through [source-highlight](https://www.gnu.org/software/src-highlite/) (`ghcid | source-highlight -s haskell -f esc`). diff --git a/ghcid.cabal b/ghcid.cabal index 6e3a299e..1af63c8d 100644 --- a/ghcid.cabal +++ b/ghcid.cabal @@ -1,19 +1,19 @@ -cabal-version: >= 1.18 +cabal-version: 1.18 build-type: Simple name: ghcid -version: 0.8.7 +version: 0.8.9 license: BSD3 license-file: LICENSE category: Development author: Neil Mitchell , jpmoresmau maintainer: Neil Mitchell -copyright: Neil Mitchell 2014-2022 +copyright: Neil Mitchell 2014-2023 synopsis: GHCi based bare bones IDE description: Either \"GHCi as a daemon\" or \"GHC + a bit of an IDE\". A very simple Haskell development tool which shows you the errors in your project and updates them whenever you save. Run @ghcid --topmost --command=ghci@, where @--topmost@ makes the window on top of all others (Windows only) and @--command@ is the command to start GHCi on your project (defaults to @ghci@ if you have a @.ghci@ file, or else to @cabal repl@). homepage: https://github.com/ndmitchell/ghcid#readme bug-reports: https://github.com/ndmitchell/ghcid/issues -tested-with: GHC==9.0, GHC==8.10, GHC==8.8, GHC==8.6 +tested-with: GHC==9.6, GHC==9.4, GHC==9.2, GHC==9.0, GHC==8.10, GHC==8.8 extra-doc-files: CHANGES.txt README.md @@ -55,14 +55,14 @@ executable ghcid time >= 1.5, directory >= 1.2, containers, - fsnotify, + fsnotify >= 0.4, extra >= 1.6.20, process >= 1.1, cmdargs >= 0.10, ansi-terminal, terminal-size >= 0.3 if os(windows) - build-depends: Win32 + build-depends: Win32 >= 2.13.2.1 else build-depends: unix other-modules: @@ -89,7 +89,7 @@ test-suite ghcid_test directory >= 1.2, process, containers, - fsnotify, + fsnotify >= 0.4, extra >= 1.6.6, ansi-terminal, terminal-size >= 0.3, diff --git a/plugins/emacs/ghcid.el b/plugins/emacs/ghcid.el index dfc16f77..8f486080 100644 --- a/plugins/emacs/ghcid.el +++ b/plugins/emacs/ghcid.el @@ -16,7 +16,7 @@ ;; Use M-x ghcid to launch ;;; Code: - +(require 'compile) (require 'term) ;; Set ghcid-target to change the stack target diff --git a/plugins/nvim/ftplugin/haskell.vim b/plugins/nvim/ftplugin/haskell.vim index 18ba548a..e45764ca 100644 --- a/plugins/nvim/ftplugin/haskell.vim +++ b/plugins/nvim/ftplugin/haskell.vim @@ -9,8 +9,12 @@ if exists("g:loaded_ghcid") || &cp || !has('nvim') endif let g:loaded_ghcid = 1 -if !exists("g:ghcid_lines") - let g:ghcid_lines = 10 +if !exists("g:ghcid_size") + if !exists("g:ghcid_length") + let g:ghcid_size = 10 + else + let g:ghcid_size = g:ghcid_length + endif endif if !exists("g:ghcid_keep_open") @@ -29,6 +33,10 @@ if !exists("g:ghcid_verbosity") let g:ghcid_verbosity = 2 endif +if !exists("g:ghcid_placement") + let g:ghcid_placement = 'below' +endif + let s:ghcid_command_args = {} let s:ghcid_base_sign_id = 100 let s:ghcid_sign_id = s:ghcid_base_sign_id @@ -94,13 +102,20 @@ function! s:ghcid_closewin_force() quit endfunction +function s:placement_cmd() + if g:ghcid_placement == 'above' | return 'above' | endif + if g:ghcid_placement == 'below' | return 'below' | endif + if g:ghcid_placement == 'left' | return 'vertical topleft' | endif + if g:ghcid_placement == 'right' | return 'vertical rightb' | endif +endfunction + function! s:ghcid_openwin() let buf = s:ghcid_bufnr() if buf > 0 - exe 'keepalt' 'below' g:ghcid_lines . 'split' '#' . buf + exe 'keepalt' 'above' g:ghcid_size . 'split' '#' . buf else - exe 'keepalt' 'below' g:ghcid_lines . 'new' + exe 'keepalt' s:placement_cmd() g:ghcid_size . 'new' file ghcid endif diff --git a/plugins/vscode/yarn.lock b/plugins/vscode/yarn.lock index f464d17c..aa6bde13 100644 --- a/plugins/vscode/yarn.lock +++ b/plugins/vscode/yarn.lock @@ -708,9 +708,9 @@ punycode@^2.1.0, punycode@^2.1.1: integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== qs@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + version "6.5.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" + integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== querystringify@^2.1.1: version "2.2.0" diff --git a/src/Ghcid.hs b/src/Ghcid.hs index 9b20b8bb..79ebf09e 100644 --- a/src/Ghcid.hs +++ b/src/Ghcid.hs @@ -405,7 +405,10 @@ runGhcid session waiter termSize termOutput opts@Options{..} = do whenJust lint $ \lintcmd -> unless hasErrors $ do (exitcode, stdout, stderr) <- readCreateProcessWithExitCode (shell . unwords $ lintcmd : map escape touched) "" - unless (exitcode == ExitSuccess) $ outStrLn (stdout ++ stderr) + unless (exitcode == ExitSuccess) $ do + let output = stdout ++ stderr + outStrLn output + forM_ outputfile $ flip writeFile output reason <- nextWait $ map (,Restart) restart ++ map (,Reload) reload diff --git a/src/Language/Haskell/Ghcid/Parser.hs b/src/Language/Haskell/Ghcid/Parser.hs index bd76332c..fe6a3f4d 100644 --- a/src/Language/Haskell/Ghcid/Parser.hs +++ b/src/Language/Haskell/Ghcid/Parser.hs @@ -64,7 +64,7 @@ parseLoad (map Esc -> xs) = nubOrd $ f xs -- : error: f (x:xs) - | unescapeE x == ": error:" + | ": error:" `isPrefixOfE` x , (xs,rest) <- span leadingWhitespaceE xs = Message Error "" (0,0) (0,0) (map fromEsc $ x:xs) : f rest diff --git a/src/Language/Haskell/Ghcid/Terminal.hs b/src/Language/Haskell/Ghcid/Terminal.hs index 958e78d6..eef88de1 100644 --- a/src/Language/Haskell/Ghcid/Terminal.hs +++ b/src/Language/Haskell/Ghcid/Terminal.hs @@ -18,12 +18,8 @@ import Graphics.Win32.GDI.Types import System.Win32.Types -wM_SETICON = 0x0080 :: WindowMessage wM_GETICON = 0x007F :: WindowMessage -iCON_BIG = 1 -iCON_SMALL = 0 - #ifdef x86_64_HOST_ARCH #define CALLCONV ccall #else diff --git a/src/Session.hs b/src/Session.hs index 13e7701e..343bd9f5 100755 --- a/src/Session.hs +++ b/src/Session.hs @@ -81,11 +81,15 @@ kill ghci = ignored $ do -- See: https://github.com/ndmitchell/ghcid/issues/254 showCursor -loadedModules :: [Load] -> [FilePath] -loadedModules = nubOrd . map loadFile . filter (not . isLoadConfig) +loadedModules :: FilePath -> [Load] -> [FilePath] +loadedModules dir = nubOrd . map (loadFile . qualify dir) . filter predicate + where + predicate Message{loadFile = loadFile} = loadFile /= "" + predicate Loading{loadFile = loadFile} = loadFile /= "" + predicate _ = False -qualify :: FilePath -> [Load] -> [Load] -qualify dir xs = [x{loadFile = dir loadFile x} | x <- xs] +qualify :: FilePath -> Load -> Load +qualify dir message = message{loadFile = dir loadFile message} -- | Spawn a new Ghci process at a given command line. Returns the load messages, plus -- the list of files that were observed (both those loaded and those that failed to load). @@ -112,9 +116,9 @@ sessionStart Session{..} cmd setup = do -- deal with current directory (dir, _) <- showPaths v writeIORef curdir dir - messages <- pure $ qualify dir messages + messages <- pure $ map (qualify dir) messages - let loaded = loadedModules messages + let loaded = loadedModules dir messages evals <- performEvals v allowEval loaded -- install a handler @@ -208,9 +212,9 @@ sessionReload session@Session{..} = do -- actually reload Just ghci <- readIORef ghci dir <- readIORef curdir - messages <- mapMaybe tidyMessage . qualify dir <$> reload ghci + messages <- mapMaybe tidyMessage <$> reload ghci loaded <- map ((dir ) . snd) <$> showModules ghci - let reloaded = loadedModules messages + let reloaded = loadedModules dir messages warn <- readIORef warnings evals <- performEvals ghci allowEval reloaded diff --git a/src/Test/Parser.hs b/src/Test/Parser.hs index 8447a1b8..0381045d 100644 --- a/src/Test/Parser.hs +++ b/src/Test/Parser.hs @@ -105,7 +105,7 @@ testParseLoadGhc82 = testCase "GHC 8.2 Load Parsing" $ parseLoad testMissingFile = testCase "Starting ghci with a non-existent filename" $ parseLoad [": error: can't find file: bob.hs" ] @?= - [] + [Message Error "" (0,0) (0,0) [": error: can't find file: bob.hs"]] testParseLoadCyclesSelf = testCase "Module cycle with itself" $ parseLoad ["Module imports form a cycle:" diff --git a/src/Wait.hs b/src/Wait.hs index 2b9c04d6..fae954a9 100644 --- a/src/Wait.hs +++ b/src/Wait.hs @@ -29,7 +29,7 @@ withWaiterPoll :: Seconds -> (Waiter -> IO a) -> IO a withWaiterPoll x f = f $ WaiterPoll x withWaiterNotify :: (Waiter -> IO a) -> IO a -withWaiterNotify f = withManagerConf defaultConfig{confDebounce=NoDebounce} $ \manager -> do +withWaiterNotify f = withManagerConf defaultConfig $ \manager -> do mvar <- newEmptyMVar var <- newVar Map.empty f $ WaiterNotify manager mvar var