packages feed

haskell-language-server 2.13.0.0 → 2.14.0.0

raw patch · 142 files changed

+2760/−2245 lines, 142 filesdep −floskelldep −regex-applicativedep −string-interpolatedep ~basedep ~extradep ~ghcidePVP ok

version bump matches the API change (PVP)

Dependencies removed: floskell, regex-applicative, string-interpolate

Dependency ranges changed: base, extra, ghcide, hiedb, hls-graph, hls-plugin-api, hls-test-utils, lsp, lsp-test, lsp-types, mtl

API changes (from Hackage documentation)

+ Ide.Arguments: [argsInitialWorkingDirectory] :: GhcideArguments -> FilePath
- Ide.Arguments: GhcideArguments :: Command -> Maybe FilePath -> Maybe FilePath -> Bool -> Bool -> Priority -> Maybe String -> Bool -> Bool -> Int -> Bool -> GhcideArguments
+ Ide.Arguments: GhcideArguments :: Command -> Maybe FilePath -> Maybe FilePath -> Bool -> Bool -> Priority -> Maybe String -> Bool -> Bool -> Int -> Bool -> FilePath -> GhcideArguments

Files

ChangeLog.md view
@@ -1,5 +1,155 @@ # Changelog for haskell-language-server +## 2.14.0.0++- Bindists for GHC 9.14.1+- Bindists for GHC 9.12.4+- Bindists for GHC 9.12.2+- Bindists for GHC 9.10.3+- Bindists for GHC 9.8.4+- Bindists for GHC 9.6.7+- Remove `hls-floskell-plugin`+- Merge `hls-module-name-plugin` into `hls-rename-plugin`+- Support for `ExplicitLevelImports` in GHC 9.14+- Improved startup responsiveness via decoupled session loader+- Improvements to ghcide+  - Print hover types on a single line+  - Only show linear arrows when `LinearTypes` is enabled+  - Smart-case matching for module and filepath completions+- Improvements to `hls-rename-plugin`+  - Cross-module rename enabled by default+  - `prepareRename` returns exact identifier range+- Improvements to `hls-refactor-plugin`+  - Don't offer 'Add Argument' for qualified names+  - Fix spacing in constraint reduction+  - Fix `makeDeltaAst` regression (#4731)+- Improvements to `hls-explicit-record-fields-plugin`+  - Cursor-aware record expansion+  - Distinguish wildcard expansion from record syntax conversion+  - Ignore compiler-generated match groups+- Improvements to `hls-notes-plugin`+  - Hover and autocompletion for note references and declarations+  - Fix crash on single-line comments+- Improvements to `hls-cabal-plugin`+  - Completions in `custom-setup` stanzas+- Improvements to `hls-fourmolu-plugin` / `hls-ormolu-plugin`+  - Show actual parse errors from external formatters++### Pull Requests++- Allow skipping over non-required progress messages+  ([#4896](https://github.com/haskell/haskell-language-server/pull/4896)) by @crtschin+- Cleanup 9.4 leftovers, fix couple -Wunused-packages and import warnings+  ([#4893](https://github.com/haskell/haskell-language-server/pull/4893)) by @jhrcek+- Keep communication pipes between lsp-test and ghcide alive for the entire test+  ([#4892](https://github.com/haskell/haskell-language-server/pull/4892)) by @crtschin+- Cancel outstanding threads prior to closing sqlite connection+  ([#4886](https://github.com/haskell/haskell-language-server/pull/4886)) by @crtschin+- Bump cachix/cachix-action from 16 to 17+  ([#4872](https://github.com/haskell/haskell-language-server/pull/4872)) by @dependabot[bot]+- Fix cache clearing job+  ([#4868](https://github.com/haskell/haskell-language-server/pull/4868)) by @vidit-od+- Improve `prepareRename` responses+  ([#4867](https://github.com/haskell/haskell-language-server/pull/4867)) by @izuzu-izuzu+- Migrate stackage build on GHA+  ([#4865](https://github.com/haskell/haskell-language-server/pull/4865)) by @vidit-od+- new snapshot for stack build+  ([#4864](https://github.com/haskell/haskell-language-server/pull/4864)) by @vidit-od+- Pin version of extra to 1.8.1 for stack+  ([#4863](https://github.com/haskell/haskell-language-server/pull/4863)) by @fendor+- Only show linear arrows on data constructors with LinearTypes+  ([#4862](https://github.com/haskell/haskell-language-server/pull/4862)) by @crtschin+- Use 2 spaces for tab in HLS+  ([#4860](https://github.com/haskell/haskell-language-server/pull/4860)) by @fendor+- Produce structured error for unknown module+  ([#4857](https://github.com/haskell/haskell-language-server/pull/4857)) by @vidit-od+- Use Server side Diagnostics+  ([#4856](https://github.com/haskell/haskell-language-server/pull/4856)) by @vidit-od+- Fix: Show actual parse errors for external Fourmolu/Ormolu+  ([#4855](https://github.com/haskell/haskell-language-server/pull/4855)) by @ijatinydv+- Fixes redundant hash in ghcide cache path (getCacheDirsDefault)+  ([#4854](https://github.com/haskell/haskell-language-server/pull/4854)) by @adpad-13+- fix(refactor): do not offer 'Add Argument' for qualified names+  ([#4852](https://github.com/haskell/haskell-language-server/pull/4852)) by @ijatinydv+- Prefer extending import code actions+  ([#4851](https://github.com/haskell/haskell-language-server/pull/4851)) by @vherrmann+- Custom-setups completion in cabal files+  ([#4848](https://github.com/haskell/haskell-language-server/pull/4848)) by @vidit-od+- Move hls-module-name-plugin into hls-rename-plugin+  ([#4847](https://github.com/haskell/haskell-language-server/pull/4847)) by @fendor+- ghcide: implement level-aware module graph edges for GHC 9.14++  ([#4846](https://github.com/haskell/haskell-language-server/pull/4846)) by @ijatinydv+- fix spacing issues while refactor-reduce+  ([#4841](https://github.com/haskell/haskell-language-server/pull/4841)) by @vidit-od+- Remove Floskell support+  ([#4838](https://github.com/haskell/haskell-language-server/pull/4838)) by @omarjatoi+- Ignore records in matching groups that are compiler-generated+  ([#4837](https://github.com/haskell/haskell-language-server/pull/4837)) by @ozkutuk+- Add tasty-options to control test execution+  ([#4835](https://github.com/haskell/haskell-language-server/pull/4835)) by @observant2+- Use `VirtualFileTree` in `hls-call-hierarchy-plugin-tests`+  ([#4834](https://github.com/haskell/haskell-language-server/pull/4834)) by @fendor+- fix notes crashing and leaking single comments notes+  ([#4828](https://github.com/haskell/haskell-language-server/pull/4828)) by @vidit-od+- Also add max-backjumps to caching job+  ([#4827](https://github.com/haskell/haskell-language-server/pull/4827)) by @jhrcek+- docs: add WSL build location note for contributors+  ([#4824](https://github.com/haskell/haskell-language-server/pull/4824)) by @Siya-05+- Smart Casing in modules and file paths+  ([#4823](https://github.com/haskell/haskell-language-server/pull/4823)) by @vidit-od+- Prune allow-newer for ghc 9.14, bump hiedb to 0.8+  ([#4821](https://github.com/haskell/haskell-language-server/pull/4821)) by @jhrcek+- Distinguish between wildcard expansion and traditional record syntax+  ([#4819](https://github.com/haskell/haskell-language-server/pull/4819)) by @ozkutuk+- Enable `crossModule` option by default+  ([#4817](https://github.com/haskell/haskell-language-server/pull/4817)) by @Aster89+- Re-introduce call to `makeDeltaAst` to fix #4731+  ([#4814](https://github.com/haskell/haskell-language-server/pull/4814)) by @Aster89+- Cursor aware record expansion+  ([#4813](https://github.com/haskell/haskell-language-server/pull/4813)) by @vidit-od+- Bump GHA versions+  ([#4812](https://github.com/haskell/haskell-language-server/pull/4812)) by @fendor+- Use macos-15-intel runners instead of arm macos-15+  ([#4811](https://github.com/haskell/haskell-language-server/pull/4811)) by @fendor+- Bump haskell-actions/setup from 2.8.2 to 2.10.3+  ([#4809](https://github.com/haskell/haskell-language-server/pull/4809)) by @dependabot[bot]+- Bump hlint action, move options to .hlint.yaml+  ([#4808](https://github.com/haskell/haskell-language-server/pull/4808)) by @philderbeast+- Add nix devShell for GHC 9.14+  ([#4804](https://github.com/haskell/haskell-language-server/pull/4804)) by @jian-lin+- Print type in one line+  ([#4803](https://github.com/haskell/haskell-language-server/pull/4803)) by @jian-lin+- Use Data.HashMap.lookupKey and Data.HashSet.lookupElement+  ([#4802](https://github.com/haskell/haskell-language-server/pull/4802)) by @sjakobi+- Mark 9.4.8 as deprecated+  ([#4801](https://github.com/haskell/haskell-language-server/pull/4801)) by @fendor+- Fix cache job on windows+  ([#4800](https://github.com/haskell/haskell-language-server/pull/4800)) by @fendor+- Fix GHC support table for 9.4.8+  ([#4799](https://github.com/haskell/haskell-language-server/pull/4799)) by @fendor+- More ghcup-metadata tweaks+  ([#4798](https://github.com/haskell/haskell-language-server/pull/4798)) by @fendor+- Bump haskell-actions/setup from 2.8.2 to 2.10.3 in /.github/actions/setup-build+  ([#4796](https://github.com/haskell/haskell-language-server/pull/4796)) by @dependabot[bot]+- Bump actions/cache from 3 to 5 in /.github/actions/setup-build+  ([#4795](https://github.com/haskell/haskell-language-server/pull/4795)) by @dependabot[bot]+- Bump actions/cache from 3 to 5+  ([#4793](https://github.com/haskell/haskell-language-server/pull/4793)) by @dependabot[bot]+- Improving Notes Plugin+  ([#4791](https://github.com/haskell/haskell-language-server/pull/4791)) by @vidit-od+- fix: git sub-repository files listing (#4788)+  ([#4790](https://github.com/haskell/haskell-language-server/pull/4790)) by @blackheaven+- Prepare release 2.13.0.0+  ([#4785](https://github.com/haskell/haskell-language-server/pull/4785)) by @fendor+- ghcide: Require unordered-containers >= 0.2.21+  ([#4774](https://github.com/haskell/haskell-language-server/pull/4774)) by @sjakobi+- Fix broken pipe during test and gracefully exit the server+  ([#4701](https://github.com/haskell/haskell-language-server/pull/4701)) by @soulomoon+- Change tracking of file types to language kinds+  ([#4621](https://github.com/haskell/haskell-language-server/pull/4621)) by @VeryMilkyJoe+- Decouple the session loader into reader and writer over the cache+  ([#4445](https://github.com/haskell/haskell-language-server/pull/4445)) by @soulomoon+ ## 2.13.0.0  - Bindists for GHC 9.14.1@@ -7,7 +157,6 @@ - Bindists for GHC 9.10.3 - Bindists for GHC 9.8.4 - Bindists for GHC 9.6.7-- Last release supporting GHC 9.6  Support for GHC 9.14.1 is preliminary, some plugins are not yet support but will be in the future.
README.md view
@@ -6,7 +6,6 @@ [![Release][badge-github-release]][github-release] [![Hackage][badge-hackage]][hackage] [![License Apache 2.0][badge-license]][license]-[![CircleCI][badge-circleci]][circleci] [![GitHub Testing Workflow](https://github.com/haskell/haskell-language-server/actions/workflows/test.yml/badge.svg)](https://github.com/haskell/haskell-language-server/actions/workflows/test.yml) [![GitHub Nix Workflow](https://github.com/haskell/haskell-language-server/actions/workflows/nix.yml/badge.svg)](https://github.com/haskell/haskell-language-server/actions/workflows/nix.yml) [![matrix][badge-matrix]][matrix]@@ -15,8 +14,6 @@ [logo]: ./docs/logos/logo-256.png [badge-license]: https://img.shields.io/badge/license-Apache2-green.svg?dummy [license]: https://github.com/haskell/haskell-language-server/blob/master/LICENSE-[badge-circleci]: https://img.shields.io/circleci/project/github/haskell/haskell-language-server/master.svg-[circleci]: https://circleci.com/gh/haskell/haskell-language-server/ [badge-hackage]: https://img.shields.io/hackage/v/haskell-language-server.svg?logo=haskell [badge-github-release]:https://img.shields.io/github/v/release/haskell/haskell-language-server.svg [hackage]: https://hackage.haskell.org/package/haskell-language-server
docs/contributing/plugin-tutorial.lhs view
@@ -75,7 +75,7 @@  The HLS codebase includes several plugins (found in `./plugins`). For example: -- The `ormolu`, `fourmolu`, `floskell` and `stylish-haskell` plugins used to format code+- The `ormolu`, `fourmolu`, and `stylish-haskell` plugins used to format code - The `eval` plugin, a code lens provider to evaluate code in comments - The `retrie` plugin, a code action provider to execute retrie commands @@ -95,7 +95,6 @@       allPlugins =         [ GhcIde.descriptor "ghcide"         , Pragmas.descriptor "pragmas"-        , Floskell.descriptor "floskell"         , Fourmolu.descriptor "fourmolu"         , Ormolu.descriptor "ormolu"         , StylishHaskell.descriptor "stylish-haskell"
exe/Wrapper.hs view
@@ -132,7 +132,13 @@   hPutStrLn stderr $ "Current directory: " ++ d   hPutStrLn stderr $ "Operating system: " ++ os   args <- getArgs-  hPutStrLn stderr $ "Arguments: " ++ show args+  let args' = case parsedArgs of+        Ghcide GhcideArguments{..}+            -- If no --cwd is given, assume the current working directory as the --cwd+           | isNothing argsCwd -> args ++ ["--cwd", argsInitialWorkingDirectory]+        _ -> args++  hPutStrLn stderr $ "Arguments: " ++ show args'   hPutStrLn stderr $ "Cradle directory: " ++ cradleRootDir cradle   hPutStrLn stderr $ "Cradle type: " ++ show (actionName (cradleOptsProg cradle))   programsOfInterest <- findProgramVersions@@ -162,7 +168,7 @@           liftIO $ hPutStrLn stderr $ "Launching haskell-language-server exe at:" ++ e  #ifdef mingw32_HOST_OS-          liftIO $ callProcess e args+          liftIO $ callProcess e args' #else            let Cradle { cradleOptsProg = CradleAction { runGhcCmd } } = cradle@@ -177,7 +183,7 @@            env <- Map.fromList <$> liftIO getEnvironment           let newEnv = Map.insert "GHC_BIN" ghcBinary $ Map.insert "GHC_LIBDIR" libdir env-          liftIO $ executeFile e True args (Just (Map.toList newEnv))+          liftIO $ executeFile e True args' (Just (Map.toList newEnv)) #endif  
+ ghcide-test/data/hover/Constructors.hs view
@@ -0,0 +1,8 @@+module Constructors where++data A = A+data B = B Int Word Bool+data C = C ![Int] {-# UNPACK #-} !Bool+data D = D { da :: Int, db :: Bool }+data E = E { ea :: !Int, eb :: {-# UNPACK #-} ![Bool] }+newtype F = F Int
+ ghcide-test/data/hover/ConstructorsLinear.hs view
@@ -0,0 +1,9 @@+{-# LANGUAGE LinearTypes #-}+module ConstructorsLinear where++data A = A+data B = B Int Word Bool+data C = C ![Int] {-# UNPACK #-} !Bool+data D = D { da :: Int, db :: Bool }+data E = E { ea :: !Int, eb :: {-# UNPACK #-} ![Bool] }+newtype F = F Int
ghcide-test/data/hover/hie.yaml view
@@ -1,1 +1,1 @@-cradle: {direct: {arguments: ["Foo", "Bar", "GotoHover", "RecordDotSyntax", "GotoImplementation"]}}+cradle: {direct: {arguments: ["Foo", "Bar", "GotoHover", "RecordDotSyntax", "GotoImplementation", "Constructors", "ConstructorsLinear"]}}
+ ghcide-test/data/multi/dist-newstyle/build/x86_64-linux/ghc-9.10.3/a-1.0.0/build/autogen/PackageInfo_a.hs view
@@ -0,0 +1,46 @@+{-# LANGUAGE NoRebindableSyntax #-}+{-# OPTIONS_GHC -Wno-missing-import-lists #-}+{-# OPTIONS_GHC -w #-}++{-|+Module      : PackageInfo_a+Description : Contents of some of the package's Cabal file's fields.++WARNING: This module was generated by Cabal. Any modifications will be+overwritten if the module is regenerated.++This module exports values that record information from some of the fields of+the package's Cabal package description file (Cabal file).++For further information about the fields in a Cabal file, see the Cabal User+Guide.+-}++module PackageInfo_a (+    name,+    version,+    synopsis,+    copyright,+    homepage,+  ) where++import Data.Version (Version(..))+import Prelude++-- |The content of the @name@ field of the package's Cabal file, but with any+-- hyphen characters replaced by underscore characters.+name :: String+name = "a"+-- |The content of the @version@ field of the package's Cabal file.+version :: Version+version = Version [1,0,0] []++-- |The content of the @synopsis@ field of the package's Cabal file.+synopsis :: String+synopsis = ""+-- |The content of the @copyright@ field of the package's Cabal file.+copyright :: String+copyright = ""+-- |The content of the @homepage@ field of the package's Cabal file.+homepage :: String+homepage = ""
+ ghcide-test/data/multi/dist-newstyle/build/x86_64-linux/ghc-9.10.3/a-1.0.0/build/autogen/Paths_a.hs view
@@ -0,0 +1,132 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE NoRebindableSyntax #-}+#if __GLASGOW_HASKELL__ >= 810+{-# OPTIONS_GHC -Wno-prepositive-qualified-module #-}+#endif+{-# OPTIONS_GHC -Wno-missing-import-lists #-}+{-# OPTIONS_GHC -w #-}++{-|+Module      : Paths_a+Description : Data file location, and package version and installation+              directories.++WARNING: This module was generated by Cabal. Any modifications will be+overwritten if the module is regenerated.++This module exports a function to locate data files, and values that record+the version of the package and some directories which the package has been+configured to be installed into.++For further information about Cabal's options for its configuration step, and+their default values, see the Cabal User Guide.+-}++module Paths_a (+    version,+    getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir,+    getDataFileName, getSysconfDir+  ) where+++import qualified Control.Exception as Exception+import Data.Version (Version(..))+import System.Environment (getEnv)+import Prelude+++#if defined(VERSION_base)++#if MIN_VERSION_base(4,0,0)+catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a+#else+catchIO :: IO a -> (Exception.Exception -> IO a) -> IO a+#endif++#else+catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a+#endif+catchIO = Exception.catch++-- |The package version.+version :: Version+version = Version [1,0,0] []++-- |If the argument is a filename, the result is the name of a corresponding+-- file on the system on which the program is running, if the file were listed+-- in the @data-files@ field of the package's Cabal package description file.+-- No check is performed that the given filename is listed in that field.+getDataFileName :: FilePath -> IO FilePath+getDataFileName name = do+  dir <- getDataDir+  return (dir `joinFileName` name)++-- |The location of the directory specified by Cabal's @--bindir@ option (where+-- executables that the user might invoke are installed). This can be overridden+-- at runtime using the environment variable a_bindir.+getBinDir :: IO FilePath++-- |The location of the directory specified by Cabal's @--libdir@ option (where+-- object libraries are installed). This can be overridden at runtime using the+-- environment variable a_libdir.+getLibDir :: IO FilePath++-- |The location of the directory specified by Cabal's @--dynlibdir@ option+-- (where dynamic libraries are installed). This can be overridden at runtime+-- using the environment variable a_dynlibdir.+getDynLibDir :: IO FilePath++-- |The location of the directory specified by Cabal's @--datadir@ option (where+-- architecture-independent data files are installed). This can be overridden at+-- runtime using the environment variable a_datadir.+getDataDir :: IO FilePath++-- |The location of the directory specified by Cabal's @--libexedir@ option+-- (where executables that are not expected to be invoked directly by the user+-- are installed). This can be overridden at runtime using the environment+-- variable a_libexedir.+getLibexecDir :: IO FilePath++-- |The location of the directory specified by Cabal's @--sysconfdir@ option+-- (where configuration files are installed). This can be overridden at runtime+-- using the environment variable a_sysconfdir.+getSysconfDir :: IO FilePath+++++bindir, libdir, dynlibdir, datadir, libexecdir, sysconfdir :: FilePath+bindir     = "/home/zubin/.cabal/bin"+libdir     = "/home/zubin/.cabal/lib/x86_64-linux-ghc-9.10.3-415c/a-1.0.0-inplace"+dynlibdir  = "/home/zubin/.cabal/lib/x86_64-linux-ghc-9.10.3-415c"+datadir    = "/home/zubin/.cabal/share/x86_64-linux-ghc-9.10.3-415c/a-1.0.0"+libexecdir = "/home/zubin/.cabal/libexec/x86_64-linux-ghc-9.10.3-415c/a-1.0.0"+sysconfdir = "/home/zubin/.cabal/etc"++getBinDir     = catchIO (getEnv "a_bindir")     (\_ -> return bindir)+getLibDir     = catchIO (getEnv "a_libdir")     (\_ -> return libdir)+getDynLibDir  = catchIO (getEnv "a_dynlibdir")  (\_ -> return dynlibdir)+getDataDir    = catchIO (getEnv "a_datadir")    (\_ -> return datadir)+getLibexecDir = catchIO (getEnv "a_libexecdir") (\_ -> return libexecdir)+getSysconfDir = catchIO (getEnv "a_sysconfdir") (\_ -> return sysconfdir)++++joinFileName :: String -> String -> FilePath+joinFileName ""  fname = fname+joinFileName "." fname = fname+joinFileName dir ""    = dir+joinFileName dir@(c:cs) fname+  | isPathSeparator (lastChar c cs) = dir ++ fname+  | otherwise                       = dir ++ pathSeparator : fname+ where+  -- We do not use Data.List.NonEmpty.last, as that would limit the module to+  -- base >= 4.9.0.0 (GHC >= 8.0.1).+  lastChar x [] = x+  lastChar _ (x:xs) = lastChar x xs++pathSeparator :: Char+pathSeparator = '/'++isPathSeparator :: Char -> Bool+isPathSeparator c = c == '/'
+ ghcide-test/data/multi/dist-newstyle/build/x86_64-linux/ghc-9.14.1/a-1.0.0/build/autogen/PackageInfo_a.hs view
@@ -0,0 +1,46 @@+{-# LANGUAGE NoRebindableSyntax #-}+{-# OPTIONS_GHC -Wno-missing-import-lists #-}+{-# OPTIONS_GHC -w #-}++{-|+Module      : PackageInfo_a+Description : Contents of some of the package's Cabal file's fields.++WARNING: This module was generated by Cabal. Any modifications will be+overwritten if the module is regenerated.++This module exports values that record information from some of the fields of+the package's Cabal package description file (Cabal file).++For further information about the fields in a Cabal file, see the Cabal User+Guide.+-}++module PackageInfo_a (+    name,+    version,+    synopsis,+    copyright,+    homepage,+  ) where++import Data.Version (Version(..))+import Prelude++-- |The content of the @name@ field of the package's Cabal file, but with any+-- hyphen characters replaced by underscore characters.+name :: String+name = "a"+-- |The content of the @version@ field of the package's Cabal file.+version :: Version+version = Version [1,0,0] []++-- |The content of the @synopsis@ field of the package's Cabal file.+synopsis :: String+synopsis = ""+-- |The content of the @copyright@ field of the package's Cabal file.+copyright :: String+copyright = ""+-- |The content of the @homepage@ field of the package's Cabal file.+homepage :: String+homepage = ""
+ ghcide-test/data/multi/dist-newstyle/build/x86_64-linux/ghc-9.14.1/a-1.0.0/build/autogen/Paths_a.hs view
@@ -0,0 +1,132 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE NoRebindableSyntax #-}+#if __GLASGOW_HASKELL__ >= 810+{-# OPTIONS_GHC -Wno-prepositive-qualified-module #-}+#endif+{-# OPTIONS_GHC -Wno-missing-import-lists #-}+{-# OPTIONS_GHC -w #-}++{-|+Module      : Paths_a+Description : Data file location, and package version and installation+              directories.++WARNING: This module was generated by Cabal. Any modifications will be+overwritten if the module is regenerated.++This module exports a function to locate data files, and values that record+the version of the package and some directories which the package has been+configured to be installed into.++For further information about Cabal's options for its configuration step, and+their default values, see the Cabal User Guide.+-}++module Paths_a (+    version,+    getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir,+    getDataFileName, getSysconfDir+  ) where+++import qualified Control.Exception as Exception+import Data.Version (Version(..))+import System.Environment (getEnv)+import Prelude+++#if defined(VERSION_base)++#if MIN_VERSION_base(4,0,0)+catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a+#else+catchIO :: IO a -> (Exception.Exception -> IO a) -> IO a+#endif++#else+catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a+#endif+catchIO = Exception.catch++-- |The package version.+version :: Version+version = Version [1,0,0] []++-- |If the argument is a filename, the result is the name of a corresponding+-- file on the system on which the program is running, if the file were listed+-- in the @data-files@ field of the package's Cabal package description file.+-- No check is performed that the given filename is listed in that field.+getDataFileName :: FilePath -> IO FilePath+getDataFileName name = do+  dir <- getDataDir+  return (dir `joinFileName` name)++-- |The location of the directory specified by Cabal's @--bindir@ option (where+-- executables that the user might invoke are installed). This can be overridden+-- at runtime using the environment variable a_bindir.+getBinDir :: IO FilePath++-- |The location of the directory specified by Cabal's @--libdir@ option (where+-- object libraries are installed). This can be overridden at runtime using the+-- environment variable a_libdir.+getLibDir :: IO FilePath++-- |The location of the directory specified by Cabal's @--dynlibdir@ option+-- (where dynamic libraries are installed). This can be overridden at runtime+-- using the environment variable a_dynlibdir.+getDynLibDir :: IO FilePath++-- |The location of the directory specified by Cabal's @--datadir@ option (where+-- architecture-independent data files are installed). This can be overridden at+-- runtime using the environment variable a_datadir.+getDataDir :: IO FilePath++-- |The location of the directory specified by Cabal's @--libexedir@ option+-- (where executables that are not expected to be invoked directly by the user+-- are installed). This can be overridden at runtime using the environment+-- variable a_libexedir.+getLibexecDir :: IO FilePath++-- |The location of the directory specified by Cabal's @--sysconfdir@ option+-- (where configuration files are installed). This can be overridden at runtime+-- using the environment variable a_sysconfdir.+getSysconfDir :: IO FilePath+++++bindir, libdir, dynlibdir, datadir, libexecdir, sysconfdir :: FilePath+bindir     = "/home/zubin/.cabal/bin"+libdir     = "/home/zubin/.cabal/lib/x86_64-linux-ghc-9.14.1-ade7/a-1.0.0-inplace"+dynlibdir  = "/home/zubin/.cabal/lib/x86_64-linux-ghc-9.14.1-ade7"+datadir    = "/home/zubin/.cabal/share/x86_64-linux-ghc-9.14.1-ade7/a-1.0.0"+libexecdir = "/home/zubin/.cabal/libexec/x86_64-linux-ghc-9.14.1-ade7/a-1.0.0"+sysconfdir = "/home/zubin/.cabal/etc"++getBinDir     = catchIO (getEnv "a_bindir")     (\_ -> return bindir)+getLibDir     = catchIO (getEnv "a_libdir")     (\_ -> return libdir)+getDynLibDir  = catchIO (getEnv "a_dynlibdir")  (\_ -> return dynlibdir)+getDataDir    = catchIO (getEnv "a_datadir")    (\_ -> return datadir)+getLibexecDir = catchIO (getEnv "a_libexecdir") (\_ -> return libexecdir)+getSysconfDir = catchIO (getEnv "a_sysconfdir") (\_ -> return sysconfdir)++++joinFileName :: String -> String -> FilePath+joinFileName ""  fname = fname+joinFileName "." fname = fname+joinFileName dir ""    = dir+joinFileName dir@(c:cs) fname+  | isPathSeparator (lastChar c cs) = dir ++ fname+  | otherwise                       = dir ++ pathSeparator : fname+ where+  -- We do not use Data.List.NonEmpty.last, as that would limit the module to+  -- base >= 4.9.0.0 (GHC >= 8.0.1).+  lastChar x [] = x+  lastChar _ (x:xs) = lastChar x xs++pathSeparator :: Char+pathSeparator = '/'++isPathSeparator :: Char -> Bool+isPathSeparator c = c == '/'
− ghcide-test/data/plugin-parser/dist-newstyle/build/x86_64-linux/ghc-9.10.2/plugin-1.0.0/build/autogen/PackageInfo_plugin.hs
@@ -1,25 +0,0 @@-{-# LANGUAGE NoRebindableSyntax #-}-{-# OPTIONS_GHC -fno-warn-missing-import-lists #-}-{-# OPTIONS_GHC -w #-}-module PackageInfo_plugin (-    name,-    version,-    synopsis,-    copyright,-    homepage,-  ) where--import Data.Version (Version(..))-import Prelude--name :: String-name = "plugin"-version :: Version-version = Version [1,0,0] []--synopsis :: String-synopsis = ""-copyright :: String-copyright = ""-homepage :: String-homepage = ""
− ghcide-test/data/plugin-parser/dist-newstyle/build/x86_64-linux/ghc-9.10.2/plugin-1.0.0/build/autogen/Paths_plugin.hs
@@ -1,77 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE NoRebindableSyntax #-}-#if __GLASGOW_HASKELL__ >= 810-{-# OPTIONS_GHC -Wno-prepositive-qualified-module #-}-#endif-{-# OPTIONS_GHC -fno-warn-missing-import-lists #-}-{-# OPTIONS_GHC -w #-}-module Paths_plugin (-    version,-    getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir,-    getDataFileName, getSysconfDir-  ) where---import qualified Control.Exception as Exception-import qualified Data.List as List-import Data.Version (Version(..))-import System.Environment (getEnv)-import Prelude---#if defined(VERSION_base)--#if MIN_VERSION_base(4,0,0)-catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a-#else-catchIO :: IO a -> (Exception.Exception -> IO a) -> IO a-#endif--#else-catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a-#endif-catchIO = Exception.catch--version :: Version-version = Version [1,0,0] []--getDataFileName :: FilePath -> IO FilePath-getDataFileName name = do-  dir <- getDataDir-  return (dir `joinFileName` name)--getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir, getSysconfDir :: IO FilePath-----bindir, libdir, dynlibdir, datadir, libexecdir, sysconfdir :: FilePath-bindir     = "/home/hugin/.cabal/bin"-libdir     = "/home/hugin/.cabal/lib/x86_64-linux-ghc-9.10.2-d434/plugin-1.0.0-inplace"-dynlibdir  = "/home/hugin/.cabal/lib/x86_64-linux-ghc-9.10.2-d434"-datadir    = "/home/hugin/.cabal/share/x86_64-linux-ghc-9.10.2-d434/plugin-1.0.0"-libexecdir = "/home/hugin/.cabal/libexec/x86_64-linux-ghc-9.10.2-d434/plugin-1.0.0"-sysconfdir = "/home/hugin/.cabal/etc"--getBinDir     = catchIO (getEnv "plugin_bindir")     (\_ -> return bindir)-getLibDir     = catchIO (getEnv "plugin_libdir")     (\_ -> return libdir)-getDynLibDir  = catchIO (getEnv "plugin_dynlibdir")  (\_ -> return dynlibdir)-getDataDir    = catchIO (getEnv "plugin_datadir")    (\_ -> return datadir)-getLibexecDir = catchIO (getEnv "plugin_libexecdir") (\_ -> return libexecdir)-getSysconfDir = catchIO (getEnv "plugin_sysconfdir") (\_ -> return sysconfdir)----joinFileName :: String -> String -> FilePath-joinFileName ""  fname = fname-joinFileName "." fname = fname-joinFileName dir ""    = dir-joinFileName dir fname-  | isPathSeparator (List.last dir) = dir ++ fname-  | otherwise                       = dir ++ pathSeparator : fname--pathSeparator :: Char-pathSeparator = '/'--isPathSeparator :: Char -> Bool-isPathSeparator c = c == '/'
− ghcide-test/data/plugin-parser/dist-newstyle/build/x86_64-linux/ghc-9.10.2/usage-1.0.0/build/autogen/PackageInfo_usage.hs
@@ -1,25 +0,0 @@-{-# LANGUAGE NoRebindableSyntax #-}-{-# OPTIONS_GHC -fno-warn-missing-import-lists #-}-{-# OPTIONS_GHC -w #-}-module PackageInfo_usage (-    name,-    version,-    synopsis,-    copyright,-    homepage,-  ) where--import Data.Version (Version(..))-import Prelude--name :: String-name = "usage"-version :: Version-version = Version [1,0,0] []--synopsis :: String-synopsis = ""-copyright :: String-copyright = ""-homepage :: String-homepage = ""
− ghcide-test/data/plugin-parser/dist-newstyle/build/x86_64-linux/ghc-9.10.2/usage-1.0.0/build/autogen/Paths_usage.hs
@@ -1,77 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE NoRebindableSyntax #-}-#if __GLASGOW_HASKELL__ >= 810-{-# OPTIONS_GHC -Wno-prepositive-qualified-module #-}-#endif-{-# OPTIONS_GHC -fno-warn-missing-import-lists #-}-{-# OPTIONS_GHC -w #-}-module Paths_usage (-    version,-    getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir,-    getDataFileName, getSysconfDir-  ) where---import qualified Control.Exception as Exception-import qualified Data.List as List-import Data.Version (Version(..))-import System.Environment (getEnv)-import Prelude---#if defined(VERSION_base)--#if MIN_VERSION_base(4,0,0)-catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a-#else-catchIO :: IO a -> (Exception.Exception -> IO a) -> IO a-#endif--#else-catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a-#endif-catchIO = Exception.catch--version :: Version-version = Version [1,0,0] []--getDataFileName :: FilePath -> IO FilePath-getDataFileName name = do-  dir <- getDataDir-  return (dir `joinFileName` name)--getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir, getSysconfDir :: IO FilePath-----bindir, libdir, dynlibdir, datadir, libexecdir, sysconfdir :: FilePath-bindir     = "/home/hugin/.cabal/bin"-libdir     = "/home/hugin/.cabal/lib/x86_64-linux-ghc-9.10.2-d434/usage-1.0.0-inplace"-dynlibdir  = "/home/hugin/.cabal/lib/x86_64-linux-ghc-9.10.2-d434"-datadir    = "/home/hugin/.cabal/share/x86_64-linux-ghc-9.10.2-d434/usage-1.0.0"-libexecdir = "/home/hugin/.cabal/libexec/x86_64-linux-ghc-9.10.2-d434/usage-1.0.0"-sysconfdir = "/home/hugin/.cabal/etc"--getBinDir     = catchIO (getEnv "usage_bindir")     (\_ -> return bindir)-getLibDir     = catchIO (getEnv "usage_libdir")     (\_ -> return libdir)-getDynLibDir  = catchIO (getEnv "usage_dynlibdir")  (\_ -> return dynlibdir)-getDataDir    = catchIO (getEnv "usage_datadir")    (\_ -> return datadir)-getLibexecDir = catchIO (getEnv "usage_libexecdir") (\_ -> return libexecdir)-getSysconfDir = catchIO (getEnv "usage_sysconfdir") (\_ -> return sysconfdir)----joinFileName :: String -> String -> FilePath-joinFileName ""  fname = fname-joinFileName "." fname = fname-joinFileName dir ""    = dir-joinFileName dir fname-  | isPathSeparator (List.last dir) = dir ++ fname-  | otherwise                       = dir ++ pathSeparator : fname--pathSeparator :: Char-pathSeparator = '/'--isPathSeparator :: Char -> Bool-isPathSeparator c = c == '/'
− ghcide-test/data/plugin-parser/dist-newstyle/build/x86_64-linux/ghc-9.10.3/plugin-1.0.0/build/autogen/PackageInfo_plugin.hs
@@ -1,25 +0,0 @@-{-# LANGUAGE NoRebindableSyntax #-}-{-# OPTIONS_GHC -fno-warn-missing-import-lists #-}-{-# OPTIONS_GHC -w #-}-module PackageInfo_plugin (-    name,-    version,-    synopsis,-    copyright,-    homepage,-  ) where--import Data.Version (Version(..))-import Prelude--name :: String-name = "plugin"-version :: Version-version = Version [1,0,0] []--synopsis :: String-synopsis = ""-copyright :: String-copyright = ""-homepage :: String-homepage = ""
− ghcide-test/data/plugin-parser/dist-newstyle/build/x86_64-linux/ghc-9.10.3/plugin-1.0.0/build/autogen/Paths_plugin.hs
@@ -1,77 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE NoRebindableSyntax #-}-#if __GLASGOW_HASKELL__ >= 810-{-# OPTIONS_GHC -Wno-prepositive-qualified-module #-}-#endif-{-# OPTIONS_GHC -fno-warn-missing-import-lists #-}-{-# OPTIONS_GHC -w #-}-module Paths_plugin (-    version,-    getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir,-    getDataFileName, getSysconfDir-  ) where---import qualified Control.Exception as Exception-import qualified Data.List as List-import Data.Version (Version(..))-import System.Environment (getEnv)-import Prelude---#if defined(VERSION_base)--#if MIN_VERSION_base(4,0,0)-catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a-#else-catchIO :: IO a -> (Exception.Exception -> IO a) -> IO a-#endif--#else-catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a-#endif-catchIO = Exception.catch--version :: Version-version = Version [1,0,0] []--getDataFileName :: FilePath -> IO FilePath-getDataFileName name = do-  dir <- getDataDir-  return (dir `joinFileName` name)--getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir, getSysconfDir :: IO FilePath-----bindir, libdir, dynlibdir, datadir, libexecdir, sysconfdir :: FilePath-bindir     = "/home/hugin/.cabal/bin"-libdir     = "/home/hugin/.cabal/lib/x86_64-linux-ghc-9.10.3-415c/plugin-1.0.0-inplace"-dynlibdir  = "/home/hugin/.cabal/lib/x86_64-linux-ghc-9.10.3-415c"-datadir    = "/home/hugin/.cabal/share/x86_64-linux-ghc-9.10.3-415c/plugin-1.0.0"-libexecdir = "/home/hugin/.cabal/libexec/x86_64-linux-ghc-9.10.3-415c/plugin-1.0.0"-sysconfdir = "/home/hugin/.cabal/etc"--getBinDir     = catchIO (getEnv "plugin_bindir")     (\_ -> return bindir)-getLibDir     = catchIO (getEnv "plugin_libdir")     (\_ -> return libdir)-getDynLibDir  = catchIO (getEnv "plugin_dynlibdir")  (\_ -> return dynlibdir)-getDataDir    = catchIO (getEnv "plugin_datadir")    (\_ -> return datadir)-getLibexecDir = catchIO (getEnv "plugin_libexecdir") (\_ -> return libexecdir)-getSysconfDir = catchIO (getEnv "plugin_sysconfdir") (\_ -> return sysconfdir)----joinFileName :: String -> String -> FilePath-joinFileName ""  fname = fname-joinFileName "." fname = fname-joinFileName dir ""    = dir-joinFileName dir fname-  | isPathSeparator (List.last dir) = dir ++ fname-  | otherwise                       = dir ++ pathSeparator : fname--pathSeparator :: Char-pathSeparator = '/'--isPathSeparator :: Char -> Bool-isPathSeparator c = c == '/'
− ghcide-test/data/plugin-parser/dist-newstyle/build/x86_64-linux/ghc-9.10.3/usage-1.0.0/build/autogen/PackageInfo_usage.hs
@@ -1,25 +0,0 @@-{-# LANGUAGE NoRebindableSyntax #-}-{-# OPTIONS_GHC -fno-warn-missing-import-lists #-}-{-# OPTIONS_GHC -w #-}-module PackageInfo_usage (-    name,-    version,-    synopsis,-    copyright,-    homepage,-  ) where--import Data.Version (Version(..))-import Prelude--name :: String-name = "usage"-version :: Version-version = Version [1,0,0] []--synopsis :: String-synopsis = ""-copyright :: String-copyright = ""-homepage :: String-homepage = ""
− ghcide-test/data/plugin-parser/dist-newstyle/build/x86_64-linux/ghc-9.10.3/usage-1.0.0/build/autogen/Paths_usage.hs
@@ -1,77 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE NoRebindableSyntax #-}-#if __GLASGOW_HASKELL__ >= 810-{-# OPTIONS_GHC -Wno-prepositive-qualified-module #-}-#endif-{-# OPTIONS_GHC -fno-warn-missing-import-lists #-}-{-# OPTIONS_GHC -w #-}-module Paths_usage (-    version,-    getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir,-    getDataFileName, getSysconfDir-  ) where---import qualified Control.Exception as Exception-import qualified Data.List as List-import Data.Version (Version(..))-import System.Environment (getEnv)-import Prelude---#if defined(VERSION_base)--#if MIN_VERSION_base(4,0,0)-catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a-#else-catchIO :: IO a -> (Exception.Exception -> IO a) -> IO a-#endif--#else-catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a-#endif-catchIO = Exception.catch--version :: Version-version = Version [1,0,0] []--getDataFileName :: FilePath -> IO FilePath-getDataFileName name = do-  dir <- getDataDir-  return (dir `joinFileName` name)--getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir, getSysconfDir :: IO FilePath-----bindir, libdir, dynlibdir, datadir, libexecdir, sysconfdir :: FilePath-bindir     = "/home/hugin/.cabal/bin"-libdir     = "/home/hugin/.cabal/lib/x86_64-linux-ghc-9.10.3-415c/usage-1.0.0-inplace"-dynlibdir  = "/home/hugin/.cabal/lib/x86_64-linux-ghc-9.10.3-415c"-datadir    = "/home/hugin/.cabal/share/x86_64-linux-ghc-9.10.3-415c/usage-1.0.0"-libexecdir = "/home/hugin/.cabal/libexec/x86_64-linux-ghc-9.10.3-415c/usage-1.0.0"-sysconfdir = "/home/hugin/.cabal/etc"--getBinDir     = catchIO (getEnv "usage_bindir")     (\_ -> return bindir)-getLibDir     = catchIO (getEnv "usage_libdir")     (\_ -> return libdir)-getDynLibDir  = catchIO (getEnv "usage_dynlibdir")  (\_ -> return dynlibdir)-getDataDir    = catchIO (getEnv "usage_datadir")    (\_ -> return datadir)-getLibexecDir = catchIO (getEnv "usage_libexecdir") (\_ -> return libexecdir)-getSysconfDir = catchIO (getEnv "usage_sysconfdir") (\_ -> return sysconfdir)----joinFileName :: String -> String -> FilePath-joinFileName ""  fname = fname-joinFileName "." fname = fname-joinFileName dir ""    = dir-joinFileName dir fname-  | isPathSeparator (List.last dir) = dir ++ fname-  | otherwise                       = dir ++ pathSeparator : fname--pathSeparator :: Char-pathSeparator = '/'--isPathSeparator :: Char -> Bool-isPathSeparator c = c == '/'
ghcide-test/exe/BootTests.hs view
@@ -1,6 +1,6 @@ module BootTests (tests) where -import           Config                          (checkDefs, mkR, runInDir,+import           Config                          (checkDefs, mkR,                                                   runWithExtraFiles) import           Control.Applicative.Combinators import           Control.Monad
+ ghcide-test/exe/ConstructorHoverTests.hs view
@@ -0,0 +1,40 @@+{-# LANGUAGE OverloadedStrings #-}++module ConstructorHoverTests (tests) where++import           Config+import           Hover+import           Test.Hls+import           Test.Hls.FileSystem (copyDir)++tests :: TestTree+tests =+    testGroup+        "constructor hover (#2904)"+        [ testGroup+            "Constructors.hs"+            [ test "A" "Constructors.hs" (Position 2 9) [ExpectHoverText ["A :: A"]]+            , test "B" "Constructors.hs" (Position 3 9) [ExpectHoverText ["B :: Int -> Word -> Bool -> B"], ExpectHoverExcludeText ["%1 ->"]]+            , test "C" "Constructors.hs" (Position 4 9) [ExpectHoverText ["C :: [Int] -> Bool -> C"], ExpectHoverExcludeText ["%1 ->"]]+            , test "D" "Constructors.hs" (Position 5 9) [ExpectHoverText ["D :: Int -> Bool -> D"], ExpectHoverExcludeText ["%1 ->"]]+            , test "E" "Constructors.hs" (Position 6 9) [ExpectHoverText ["E :: Int -> [Bool] -> E"], ExpectHoverExcludeText ["%1 ->"]]+            , test "F" "Constructors.hs" (Position 7 12) [ExpectHoverText ["F :: Int -> F"], ExpectHoverExcludeText ["%1 ->"]]+            ]+        , testGroup+            "ConstructorsLinear.hs"+            [ test "A" "ConstructorsLinear.hs" (Position 3 9) [ExpectHoverText ["A :: A"]]+            , test "B" "ConstructorsLinear.hs" (Position 4 9) [ExpectHoverText ["B :: Int %1 -> Word %1 -> Bool %1 -> B"]]+            , test "C" "ConstructorsLinear.hs" (Position 5 9) [ExpectHoverText ["C :: [Int] %1 -> Bool %1 -> C"]]+            , test "D" "ConstructorsLinear.hs" (Position 6 9) [ExpectHoverText ["D :: Int %1 -> Bool %1 -> D"]]+            , test "E" "ConstructorsLinear.hs" (Position 7 9) [ExpectHoverText ["E :: Int %1 -> [Bool] %1 -> E"]]+            , test "F" "ConstructorsLinear.hs" (Position 8 12) [ExpectHoverText ["F :: Int %1 -> F"]]+            ]+        ]++test :: String -> FilePath -> Position -> [Expect] -> TestTree+test title fileName pos expectations =+    testWithDummyPlugin title (mkIdeTestFs [copyDir "hover"]) $ do+        doc <- openDoc fileName "haskell"+        waitForProgressDone+        hover <- getHover doc pos+        checkHover hover expectations
ghcide-test/exe/CradleTests.hs view
@@ -1,17 +1,22 @@ -{-# LANGUAGE GADTs #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE GADTs     #-}  module CradleTests (tests) where -import           Config                          (checkDefs, mkL,+import           Config                          (checkDefs, dummyPlugin,+                                                  lspTestCaps, mkIdeTestFs, mkL,                                                   runWithExtraFiles,                                                   testWithDummyPluginEmpty') import           Control.Applicative.Combinators import           Control.Lens                    ((^.)) import           Control.Monad.IO.Class          (liftIO)+import qualified Data.Aeson                      as A+import           Data.Proxy                      (Proxy (..)) import qualified Data.Text                       as T import           Development.IDE.GHC.Util-import           Development.IDE.Plugin.Test     (WaitForIdeRuleResult (..))+import           Development.IDE.Plugin.Test     (TestRequest (..),+                                                  WaitForIdeRuleResult (..)) import           Development.IDE.Test            (expectDiagnostics,                                                   expectDiagnosticsWithTags,                                                   expectNoMoreDiagnostics,@@ -19,6 +24,8 @@                                                   waitForAction) import           Development.IDE.Types.Location import           GHC.TypeLits                    (symbolVal)+import           Ide.Types                       (Config (..),+                                                  SessionLoadingPreferenceConfig (..)) import qualified Language.LSP.Protocol.Lens      as L import           Language.LSP.Protocol.Message import           Language.LSP.Protocol.Types     hiding@@ -28,7 +35,9 @@                                                   mkRange) import           Language.LSP.Test import           System.FilePath-import           System.IO.Extra                 hiding (withTempDir)+import           Test.Hls                        (TestConfig (..), def,+                                                  runSessionWithTestConfig,+                                                  waitForBuildQueue) import           Test.Hls.FileSystem import           Test.Hls.Util                   (EnvSpec (..), OS (..),                                                   ignoreInEnv)@@ -41,6 +50,7 @@     [testGroup "dependencies" [sessionDepsArePickedUp]     ,testGroup "ignore-fatal" [ignoreFatalWarning]     ,testGroup "loading" [loadCradleOnlyonce, retryFailedCradle]+    ,testGroup "regression.batch" batchLoadRegressionTests     ,testGroup "multi"   (multiTests "multi")     ,testGroup "multi-unit" (multiTests "multi-unit")     ,testGroup "sub-directory"   [simpleSubDirectoryTest]@@ -173,6 +183,218 @@     let fooL = mkL auri 2 0 2 3     checkDefs locs (pure [fooL])     expectNoMoreDiagnostics 0.5++runRegressionMultiOpenAThenB :: FilePath -> Session ()+runRegressionMultiOpenAThenB dir = do+    let aPath = dir </> "a/A.hs"+        bPath = dir </> "b/B.hs"+    adoc <- openDoc aPath "haskell"+    bdoc <- openDoc bPath "haskell"+    _ <- waitForBuildQueue+    [aRes, bRes] <- waitForTypeChecksBatched [adoc, bdoc]+    liftIO $ assertBool "A should typecheck" (ideResultSuccess aRes)+    liftIO $ assertBool "B should typecheck" (ideResultSuccess bRes)+    locs <- getDefinitions bdoc (Position 2 7)+    let fooL = mkL (adoc ^. L.uri) 2 0 2 3+    checkDefs locs (pure [fooL])+    expectNoMoreDiagnostics 0.5++runRegressionMultiOpenBThenA :: FilePath -> Session ()+runRegressionMultiOpenBThenA dir = do+    let aPath = dir </> "a/A.hs"+        bPath = dir </> "b/B.hs"+    bdoc <- openDoc bPath "haskell"+    adoc <- openDoc aPath "haskell"+    _ <- waitForBuildQueue+    [bRes, aRes] <- waitForTypeChecksBatched [bdoc, adoc]+    liftIO $ assertBool "B should typecheck" (ideResultSuccess bRes)+    liftIO $ assertBool "A should typecheck" (ideResultSuccess aRes)+    locs <- getDefinitions bdoc (Position 2 7)+    let TextDocumentIdentifier auri = adoc+    let fooL = mkL auri 2 0 2 3+    checkDefs locs (pure [fooL])+    expectNoMoreDiagnostics 0.5++runRegressionMultiOpenBThenAThenC :: FilePath -> Session ()+runRegressionMultiOpenBThenAThenC dir = do+    let aPath = dir </> "a/A.hs"+        bPath = dir </> "b/B.hs"+        cPath = dir </> "c/C.hs"+    bdoc <- openDoc bPath "haskell"+    adoc <- openDoc aPath "haskell"+    cdoc <- openDoc cPath "haskell"+    _ <- waitForBuildQueue+    [bRes, aRes, cRes] <- waitForTypeChecksBatched [bdoc, adoc, cdoc]+    liftIO $ assertBool "B should typecheck" (ideResultSuccess bRes)+    liftIO $ assertBool "A should typecheck" (ideResultSuccess aRes)+    liftIO $ assertBool "C should typecheck" (ideResultSuccess cRes)+    locs <- getDefinitions cdoc (Position 2 7)+    let TextDocumentIdentifier auri = adoc+    let fooL = mkL auri 2 0 2 3+    checkDefs locs (pure [fooL])+    expectNoMoreDiagnostics 0.5++sendTestRequest :: TestRequest -> Session A.Value+sendTestRequest req = do+  let method = SMethod_CustomMethod (Proxy @"test")+  reqId <- sendRequest method (A.toJSON req)+  TResponseMessage{_result} <- skipManyTill anyMessage $ responseForId method reqId+  case _result of+    Left err -> liftIO (assertFailure $ "test plugin request failed: " <> show err) >> pure A.Null+    Right val -> pure val++waitForTypeChecksBatched :: [TextDocumentIdentifier] -> Session [WaitForIdeRuleResult]+waitForTypeChecksBatched docs = do+  let uris = map (\TextDocumentIdentifier{_uri} -> _uri) docs+  val <- sendTestRequest (WaitForIdeRules "TypeCheck" uris)+  case A.fromJSON val of+    A.Success res -> pure res+    A.Error parseErr -> liftIO (assertFailure $ "batched typecheck parse failed: " <> parseErr) >> pure []++batchLoadRegressionTests :: [TestTree]+batchLoadRegressionTests =+  -- Note [Batch regression scheduling semantics]+  -- `didOpen` alone does not enqueue session-loader pending files.+  -- Pending entries come from GhcSession demand. For these tests, the `test`+  -- plugin uses `WaitForIdeRules` plus a pending-size barrier in session-loader+  -- to force all requested files into pending before load begins.+  [ testCase "m1-open-a-then-b-batch-pending-and-success" $+      runWithExtraFilesMultiComponent "multi" runRegressionMultiOpenAThenB+  , testCase "m2-open-b-then-a-batch-pending-and-success" $+      runWithExtraFilesMultiComponent "multi" runRegressionMultiOpenBThenA+  , testCase "m3-open-b-then-a-then-c-batch-pending-and-success" $+      runWithExtraFilesMultiComponent "multi" runRegressionMultiOpenBThenAThenC+  , testCase "f1-batch-pending-failure-isolates-broken-file" $+      runWithExtraFilesMultiComponent "multi" regressionBatchFailureIsolatesBrokenFile+  , testCase "f2-failed-file-keeps-failing-until-cradle-fix" $+      runWithExtraFilesMultiComponent "multi" regressionFailedFileKeepsFailingUntilFix+  , testCase "r1-failed-file-recovers-after-cradle-fix" $+      runWithExtraFilesMultiComponent "multi" regressionFailedFileRecoversAfterFix+  , testCase "s1-no-stale-outcomes-across-restart-paths" $+      runWithExtraFilesMultiComponent "multi" regressionNoStaleOutcomesOnRestart+  ]++runWithExtraFilesMultiComponent :: String -> (FilePath -> Session a) -> IO a+runWithExtraFilesMultiComponent dirName action = do+  let vfs = mkIdeTestFs [copyDir dirName]+      lspConfig :: Config+      lspConfig = def { sessionLoading = PreferMultiComponentLoading }+      conf :: TestConfig ()+      conf = def+        { testPluginDescriptor = dummyPlugin+        , testDirLocation = Right vfs+        , testConfigCaps = lspTestCaps+        , testShiftRoot = True+        , testDisableKick = True+        , testLspConfig = lspConfig+        }+  runSessionWithTestConfig conf action++brokenMultiHieYaml :: T.Text+brokenMultiHieYaml = T.unlines+  [ "cradle:"+  , "  cabal:"+  , "    - path: \"./a\""+  , "      component: \"lib:a\""+  , "    - path: \"./b\""+  , "      component: \"lib:does-not-exist\""+  , "    - path: \"./c\""+  , "      component: \"lib:c\""+  ]++writeBrokenMultiHieYaml :: FilePath -> Session ()+writeBrokenMultiHieYaml dir =+  liftIO $ atomicFileWriteStringUTF8 (dir </> "hie.yaml") (T.unpack brokenMultiHieYaml)++notifyHieYamlChanged :: FilePath -> Session ()+notifyHieYamlChanged dir =+  sendNotification SMethod_WorkspaceDidChangeWatchedFiles $ DidChangeWatchedFilesParams+    [FileEvent (filePathToUri $ dir </> "hie.yaml") FileChangeType_Changed]++assertTypeCheckSuccess :: TextDocumentIdentifier -> String -> Session ()+assertTypeCheckSuccess doc msg = do+  WaitForIdeRuleResult {..} <- waitForAction "TypeCheck" doc+  liftIO $ assertBool msg ideResultSuccess++assertTypeCheckFailure :: TextDocumentIdentifier -> String -> Session ()+assertTypeCheckFailure doc msg = do+  WaitForIdeRuleResult {..} <- waitForAction "TypeCheck" doc+  liftIO $ assertBool msg (not ideResultSuccess)++regressionBatchFailureIsolatesBrokenFile :: FilePath -> Session ()+regressionBatchFailureIsolatesBrokenFile dir = do+  writeBrokenMultiHieYaml dir+  let aPath = dir </> "a/A.hs"+      bPath = dir </> "b/B.hs"+  adoc <- openDoc aPath "haskell"+  bdoc <- openDoc bPath "haskell"+  _ <- waitForBuildQueue+  [aRes, bRes] <- waitForTypeChecksBatched [adoc, bdoc]+  liftIO $ assertBool "A should typecheck when B cradle mapping is broken" (ideResultSuccess aRes)+  liftIO $ assertBool "B should fail with a broken cradle mapping" (not $ ideResultSuccess bRes)++regressionFailedFileKeepsFailingUntilFix :: FilePath -> Session ()+regressionFailedFileKeepsFailingUntilFix dir = do+  writeBrokenMultiHieYaml dir+  let aPath = dir </> "a/A.hs"+      bPath = dir </> "b/B.hs"+      cPath = dir </> "c/C.hs"+  bdoc <- openDoc bPath "haskell"+  assertTypeCheckFailure bdoc "B should fail with broken cradle mapping"++  bSource <- liftIO $ readFileUtf8 bPath+  changeDoc bdoc+    [TextDocumentContentChangeEvent . InR . TextDocumentContentChangeWholeDocument $ bSource <> "\n"]+  assertTypeCheckFailure bdoc "B should keep failing until the cradle is fixed"++  adoc <- openDoc aPath "haskell"+  cdoc <- openDoc cPath "haskell"+  assertTypeCheckSuccess adoc "A should still typecheck while B remains broken"+  assertTypeCheckSuccess cdoc "C should still typecheck while B remains broken"++regressionFailedFileRecoversAfterFix :: FilePath -> Session ()+regressionFailedFileRecoversAfterFix dir = do+  let hiePath = dir </> "hie.yaml"+      bPath = dir </> "b/B.hs"+  validHie <- liftIO $ readFileUtf8 hiePath+  writeBrokenMultiHieYaml dir++  bdoc <- openDoc bPath "haskell"+  assertTypeCheckFailure bdoc "B should fail before fixing the cradle"++  liftIO $ atomicFileWriteStringUTF8 hiePath (T.unpack validHie)+  notifyHieYamlChanged dir++  bSource <- liftIO $ readFileUtf8 bPath+  changeDoc bdoc+    [TextDocumentContentChangeEvent . InR . TextDocumentContentChangeWholeDocument $ bSource <> "\n"]+  assertTypeCheckSuccess bdoc "B should recover after restoring the cradle"++regressionNoStaleOutcomesOnRestart :: FilePath -> Session ()+regressionNoStaleOutcomesOnRestart dir = do+  let hiePath = dir </> "hie.yaml"+      aPath = dir </> "a/A.hs"+      bPath = dir </> "b/B.hs"+      cPath = dir </> "c/C.hs"+  validHie <- liftIO $ readFileUtf8 hiePath+  writeBrokenMultiHieYaml dir++  bdoc <- openDoc bPath "haskell"+  assertTypeCheckFailure bdoc "B should fail before cradle fix"++  adoc <- openDoc aPath "haskell"+  assertTypeCheckSuccess adoc "A should remain healthy while B is broken"++  liftIO $ atomicFileWriteStringUTF8 hiePath (T.unpack validHie)+  notifyHieYamlChanged dir++  cdoc <- openDoc cPath "haskell"+  assertTypeCheckSuccess cdoc "C should typecheck after cradle restart"++  bSource <- liftIO $ readFileUtf8 bPath+  changeDoc bdoc+    [TextDocumentContentChangeEvent . InR . TextDocumentContentChangeWholeDocument $ bSource <> "\n"]+  assertTypeCheckSuccess bdoc "B should not keep stale failure after cradle restart"  -- Like simpleMultiTest but open the files in component 'a' in a separate session simpleMultiDefTest :: FilePath -> TestTree
ghcide-test/exe/DiagnosticTests.hs view
@@ -9,7 +9,6 @@ import           Control.Monad.IO.Class          (liftIO) import           Data.List.Extra import qualified Data.Text                       as T-import           Development.IDE.GHC.Compat      (GhcVersion (..), ghcVersion) import           Development.IDE.GHC.Util import           Development.IDE.Test            (diagnostic,                                                   expectCurrentDiagnostics,@@ -28,7 +27,6 @@ import           Language.LSP.Test import           System.Directory import           System.FilePath-import           System.IO.Extra                 hiding (withTempDir)  import           Config import           Control.Lens                    ((^.))
ghcide-test/exe/FindDefinitionAndHoverTests.hs view
@@ -17,6 +17,7 @@ import           Control.Lens               ((^.)) import           Development.IDE.Test       (expectDiagnostics,                                              standardizeQuotes)+import           Hover import           Test.Hls import           Test.Hls.FileSystem        (copyDir) import           Text.Regex.TDFA            ((=~))@@ -90,16 +91,6 @@       _     -> liftIO $ assertFailure $         "expected: " <> show ("[...]" <> sourceFileName <> ":<LINE>:<COL>**[...]", Just expectedRange) <>         "\n but got: " <> show (msg, rangeInHover)--  assertFoundIn :: T.Text -> T.Text -> Assertion-  assertFoundIn part whole = assertBool-    (T.unpack $ "failed to find: `" <> part <> "` in hover message:\n" <> whole)-    (part `T.isInfixOf` whole)--  assertNotFoundIn :: T.Text -> T.Text -> Assertion-  assertNotFoundIn part whole = assertBool-    (T.unpack $ "found unexpected: `" <> part <> "` in hover message:\n" <> whole)-    (not . T.isInfixOf part $ whole)    sourceFilePath = T.unpack sourceFileName   sourceFileName = "GotoHover.hs"
ghcide-test/exe/FindImplementationAndHoverTests.hs view
@@ -14,6 +14,7 @@  import           Config import           Development.IDE.Test (standardizeQuotes)+import           Hover import           Test.Hls import           Test.Hls.FileSystem  (copyDir) @@ -47,16 +48,6 @@             _ -> pure () -- all other expectations not relevant to hover         _ -> liftIO $ assertFailure $ "test not expecting this kind of hover info" <> show hover -  assertFoundIn :: T.Text -> T.Text -> Assertion-  assertFoundIn part whole = assertBool-    (T.unpack $ "failed to find: `" <> part <> "` in hover message:\n" <> whole)-    (part `T.isInfixOf` whole)--  assertNotFoundIn :: T.Text -> T.Text -> Assertion-  assertNotFoundIn part whole = assertBool-    (T.unpack $ "found unexpected: `" <> part <> "` in hover message:\n" <> whole)-    (not . T.isInfixOf part $ whole)-   sourceFilePath = T.unpack sourceFileName   sourceFileName = "GotoImplementation.hs" @@ -177,7 +168,7 @@   ]   where yes :: (TestTree -> Maybe TestTree)         yes = Just -- test should run and pass-        no = const Nothing -- don't run this test at all+        _no = const Nothing -- don't run this test at all  -- ---------------------------------------------------------------------------- -- Helper functions for creating hover message verification
+ ghcide-test/exe/Hover.hs view
@@ -0,0 +1,40 @@+module Hover where++import           Config+import           Control.Monad+import           Data.Foldable+import qualified Data.Text            as T+import           Development.IDE.Test+import           Test.Hls+import           Text.Regex.TDFA++assertFoundIn :: T.Text -> T.Text -> Assertion+assertFoundIn part whole =+    assertBool+        (T.unpack $ "failed to find: `" <> part <> "` in hover message:\n" <> whole)+        (part `T.isInfixOf` whole)++assertNotFoundIn :: T.Text -> T.Text -> Assertion+assertNotFoundIn part whole =+    assertBool+        (T.unpack $ "found unexpected: `" <> part <> "` in hover message:\n" <> whole)+        (not . T.isInfixOf part $ whole)++checkHover :: (HasCallStack) => Maybe Hover -> [Expect] -> Session ()+checkHover hover expectations = traverse_ check expectations+  where+    check :: (HasCallStack) => Expect -> Session ()+    check expected =+      case hover of+        Nothing -> unless (expected == ExpectNoHover) $ liftIO $ assertFailure "no hover found"+        Just Hover{_contents = (InL MarkupContent{_value = standardizeQuotes -> msg})+                  ,_range    = _rangeInHover } ->+          case expected of+            ExpectRange  _expectedRange -> liftIO $ assertFailure $ "ExpectRange assertion not implemented, yet."+            ExpectHoverRange _expectedRange -> liftIO $ assertFailure $ "ExpectHoverRange assertion not implemented, yet."+            ExpectHoverText snippets -> liftIO $ traverse_ (`assertFoundIn` msg) snippets+            ExpectHoverExcludeText snippets -> liftIO $ traverse_ (`assertNotFoundIn` msg) snippets+            ExpectHoverTextRegex re -> liftIO $ assertBool ("Regex not found in " <> T.unpack msg) (msg =~ re :: Bool)+            ExpectNoHover -> liftIO $ assertFailure $ "Expected no hover but got " <> show hover+            _ -> pure () -- all other expectations not relevant to hover+        _ -> liftIO $ assertFailure $ "test not expecting this kind of hover info" <> show hover
ghcide-test/exe/IfaceTests.hs view
@@ -17,7 +17,6 @@ import           Language.LSP.Test import           System.Directory import           System.FilePath-import           System.IO.Extra               hiding (withTempDir) import           Test.Hls.FileSystem import           Test.Tasty import           Test.Tasty.HUnit
ghcide-test/exe/InitializeResponseTests.hs view
@@ -55,7 +55,14 @@                                                                            { _supported = Just True                                                                            , _changeNotifications = Just (InR True)                                                                            }-                                                                      , _fileOperations = Nothing+                                                                      , _fileOperations = Just $ FileOperationOptions+                                                                          { _didCreate = Nothing+                                                                          , _willCreate = Nothing+                                                                          , _didRename = Nothing+                                                                          , _willRename = Nothing+                                                                          , _didDelete = Nothing+                                                                          , _willDelete = Nothing+                                                                          }                                                                       })     , chk "NO experimental"             (^. L.experimental) Nothing     ] where@@ -89,4 +96,3 @@    release :: TResponseMessage Method_Initialize -> IO ()   release = mempty-
ghcide-test/exe/Main.hs view
@@ -39,6 +39,7 @@ import           ClientSettingsTests import           CodeLensTests import           CompletionTests+import           ConstructorHoverTests import           CPPTests import           CradleTests import           DependentFileTest@@ -79,6 +80,7 @@     , CodeLensTests.tests     , OutlineTests.tests     , HighlightTests.tests+    , ConstructorHoverTests.tests     , FindDefinitionAndHoverTests.tests     , FindImplementationAndHoverTests.tests     , PluginSimpleTests.tests
ghcide-test/exe/THTests.hs view
@@ -4,6 +4,7 @@ import           Config import           Control.Monad.IO.Class      (liftIO) import qualified Data.Text                   as T+import           Development.IDE.GHC.Compat  (GhcVersion (..), ghcVersion) import           Development.IDE.GHC.Util import           Development.IDE.Test        (expectCurrentDiagnostics,                                               expectDiagnostics,@@ -18,8 +19,7 @@  tests :: TestTree tests =-  testGroup-    "TemplateHaskell"+  testGroup "TemplateHaskell" $     [ -- Test for https://github.com/haskell/ghcide/pull/212       testWithDummyPluginEmpty "load" $ do         let sourceA =@@ -104,6 +104,116 @@     _ <- openDoc cPath "haskell"     expectDiagnostics [ ( cPath, [(DiagnosticSeverity_Warning, (3, 0), "Top-level binding with no type signature: a :: A", Just "GHC-38417")] ) ]     ]+    -- Regression test for GHC 9.14 ExplicitLevelImports.+    -- Without level-aware module graph edges, HLS crashes with+    -- `expectJust` in mgQueryZero when `import splice` is used.+    ++ if ghcVersion >= GHC914+       then+        [ testWithDummyPluginEmpty "ExplicitLevelImports-splice-import" $ do+            let sourceA =+                  T.unlines+                    [ "{-# LANGUAGE TemplateHaskell #-}"+                    , "module A (a) where"+                    , "import Language.Haskell.TH"+                    , "a :: ExpQ"+                    , "a = [| 42 :: Int |]"+                    ]+                sourceB =+                  T.unlines+                    [ "{-# OPTIONS_GHC -Wall #-}"+                    , "{-# LANGUAGE ExplicitLevelImports #-}"+                    , "{-# LANGUAGE TemplateHaskell #-}"+                    , "module B where"+                    , "import splice A (a)"+                    , "b :: Int"+                    , "b = $a"+                    , "dummy = 5 :: Int"+                    ]+            _ <- createDoc "A.hs" "haskell" sourceA+            _ <- createDoc "B.hs" "haskell" sourceB+            expectDiagnostics [ ( "B.hs", [(DiagnosticSeverity_Warning, (7, 0), "Top-level binding with no type signature: dummy :: Int", Just "GHC-38417")] ) ]+        , testWithDummyPluginEmpty "ExplicitLevelImports-dual-import" $ do+            let sourceM =+                  T.unlines+                    [ "{-# LANGUAGE TemplateHaskell #-}"+                    , "module M (m) where"+                    , "import Language.Haskell.TH"+                    , "m :: ExpQ"+                    , "m = [| 100 :: Int |]"+                    ]+                sourceC =+                  T.unlines+                    [ "{-# OPTIONS_GHC -Wmissing-signatures #-}"+                    , "{-# LANGUAGE ExplicitLevelImports #-}"+                    , "{-# LANGUAGE TemplateHaskell #-}"+                    , "module C where"+                    , "import splice M (m)"+                    , "import M (m)" -- Normal import alongside splice import+                    , "c :: Int"+                    , "c = $m"+                    , "dummy = 5 :: Int"+                    ]+            _ <- createDoc "M.hs" "haskell" sourceM+            _ <- createDoc "C.hs" "haskell" sourceC+            expectDiagnostics [ ( "C.hs", [(DiagnosticSeverity_Warning, (8, 0), "Top-level binding with no type signature: dummy :: Int", Just "GHC-38417")] ) ]+        , testWithDummyPluginEmpty "ExplicitLevelImports-redundant-mix" $ do+            let sourceM =+                  T.unlines+                    [ "{-# LANGUAGE TemplateHaskell #-}"+                    , "module M (m) where"+                    , "import Language.Haskell.TH"+                    , "m :: ExpQ"+                    , "m = [| 1 :: Int |]"+                    ]+                sourceD =+                  T.unlines+                    [ "{-# OPTIONS_GHC -Wmissing-signatures #-}"+                    , "{-# LANGUAGE ExplicitLevelImports #-}"+                    , "{-# LANGUAGE TemplateHaskell #-}"+                    , "module D where"+                    , "import splice M"+                    , "import M"+                    , "import splice M" -- Redundant splice import+                    , "d :: Int"+                    , "d = $m"+                    , "dummy = 5 :: Int"+                    ]+            _ <- createDoc "M.hs" "haskell" sourceM+            _ <- createDoc "D.hs" "haskell" sourceD+            expectDiagnostics [ ( "D.hs", [(DiagnosticSeverity_Warning, (9, 0), "Top-level binding with no type signature: dummy :: Int", Just "GHC-38417")] ) ]+        , testWithDummyPluginEmpty "ExplicitLevelImports-transitive" $ do+            let sourceBase =+                  T.unlines+                    [ "{-# LANGUAGE TemplateHaskell #-}"+                    , "module BaseTH (baseMacro) where"+                    , "import Language.Haskell.TH"+                    , "baseMacro :: ExpQ"+                    , "baseMacro = [| 50 :: Int |]"+                    ]+                sourceInter =+                  T.unlines+                    [ "{-# LANGUAGE ExplicitLevelImports #-}"+                    , "{-# LANGUAGE TemplateHaskell #-}"+                    , "module Intermediate where"+                    , "import splice BaseTH" -- Splice import here+                    , "interVal :: Int"+                    , "interVal = $baseMacro"+                    ]+                sourceConsumer =+                  T.unlines+                    [ "{-# OPTIONS_GHC -Wall #-}"+                    , "module Consumer where"+                    , "import Intermediate" -- Normal import here+                    , "cons :: Int"+                    , "cons = interVal"+                    , "dummy = 5 :: Int"+                    ]+            _ <- createDoc "BaseTH.hs" "haskell" sourceBase+            _ <- createDoc "Intermediate.hs" "haskell" sourceInter+            _ <- createDoc "Consumer.hs" "haskell" sourceConsumer+            expectDiagnostics [ ( "Consumer.hs", [(DiagnosticSeverity_Warning, (5, 0), "Top-level binding with no type signature: dummy :: Int", Just "GHC-38417")] ) ]+        ]+       else []   -- | Test that all modules have linkables@@ -147,7 +257,7 @@     expectDiagnostics         [("THC.hs", [(DiagnosticSeverity_Error, (4, 4), "Couldn't match expected type '()' with actual type 'Bool'", Just "GHC-83865")])         ,("THC.hs", [(DiagnosticSeverity_Warning, (6,0), "Top-level binding", Just "GHC-38417")])-        ,("THB.hs", [(DiagnosticSeverity_Warning, (4,1), "Top-level bindin", Just "GHC-38417")])+        ,("THB.hs", [(DiagnosticSeverity_Warning, (4,1), "Top-level binding", Just "GHC-38417")])         ]      closeDoc adoc
haskell-language-server.cabal view
@@ -1,7 +1,7 @@ cabal-version:      3.4 category:           Development name:               haskell-language-server-version:            2.13.0.0+version:            2.14.0.0 synopsis:           LSP server for GHC description:   Please see the README on GitHub at <https://github.com/haskell/haskell-language-server#readme>@@ -14,7 +14,7 @@ license:            Apache-2.0 license-file:       LICENSE build-type:         Simple-tested-with:        GHC == {9.12.2, 9.10.2, 9.8.4, 9.6.7}+tested-with:        GHC == {9.14.1, 9.12.2, 9.10.3, 9.8.4, 9.6.7} extra-source-files:   README.md   ChangeLog.md@@ -136,8 +136,8 @@   build-depends:     , directory     , filepath-    , ghcide          == 2.13.0.0-    , hls-plugin-api  == 2.13.0.0+    , ghcide          == 2.14.0.0+    , hls-plugin-api  == 2.14.0.0     , lens     , lsp-types     , mtl@@ -157,8 +157,8 @@     , filepath     , haskell-language-server:hls-cabal-plugin     , haskell-language-server:hls-cabal-fmt-plugin-    , hls-plugin-api        == 2.13.0.0-    , hls-test-utils        == 2.13.0.0+    , hls-plugin-api        == 2.14.0.0+    , hls-test-utils        == 2.14.0.0    if flag(isolateCabalfmtTests)     build-tool-depends: cabal-fmt:cabal-fmt ^>=0.1.12@@ -193,8 +193,8 @@   build-depends:     , directory     , filepath-    , ghcide          == 2.13.0.0-    , hls-plugin-api  == 2.13.0.0+    , ghcide          == 2.14.0.0+    , hls-plugin-api  == 2.14.0.0     , lsp-types     , text     , mtl@@ -213,8 +213,8 @@     , filepath     , haskell-language-server:hls-cabal-plugin     , haskell-language-server:hls-cabal-gild-plugin-    , hls-plugin-api        == 2.13.0.0-    , hls-test-utils        == 2.13.0.0+    , hls-plugin-api        == 2.14.0.0+    , hls-test-utils        == 2.14.0.0    if flag(isolateCabalGildTests)     -- https://github.com/tfausak/cabal-gild/issues/89@@ -274,13 +274,13 @@     , directory     , filepath     , extra                 >=1.7.4-    , ghcide                == 2.13.0.0+    , ghcide                == 2.14.0.0     , hashable-    , hls-plugin-api        == 2.13.0.0-    , hls-graph             == 2.13.0.0+    , hls-plugin-api        == 2.14.0.0+    , hls-graph             == 2.14.0.0     , lens-    , lsp                   ^>=2.7-    , lsp-types             ^>=2.3+    , lsp                   ^>=2.8+    , lsp-types             ^>=2.4     , mtl     , regex-tdfa            ^>=1.3.1     , text@@ -316,7 +316,7 @@     , filepath     , ghcide     , haskell-language-server:hls-cabal-plugin-    , hls-test-utils    == 2.13.0.0+    , hls-test-utils    == 2.14.0.0     , lens     , lsp-types     , text@@ -353,9 +353,9 @@     , extra     , ghc     , ghc-exactprint  >= 1.5 && < 1.15-    , ghcide          == 2.13.0.0+    , ghcide          == 2.14.0.0     , hls-graph-    , hls-plugin-api  == 2.13.0.0+    , hls-plugin-api  == 2.14.0.0     , lens     , lsp     , mtl@@ -376,7 +376,7 @@   build-depends:     , filepath     , haskell-language-server:hls-class-plugin-    , hls-test-utils     == 2.13.0.0+    , hls-test-utils     == 2.14.0.0     , lens     , lsp-types     , text@@ -411,11 +411,11 @@     , containers     , extra     , ghc-    , ghcide                == 2.13.0.0-    , hiedb                 ^>= 0.7.0.0-    , hls-plugin-api        == 2.13.0.0+    , ghcide                == 2.14.0.0+    , hiedb                 ^>= 0.8.0.0+    , hls-plugin-api        == 2.14.0.0     , lens-    , lsp                    >=2.7+    , lsp                    >=2.8     , sqlite-simple     , text @@ -434,7 +434,7 @@     , extra     , filepath     , haskell-language-server:hls-call-hierarchy-plugin-    , hls-test-utils        == 2.13.0.0+    , hls-test-utils        == 2.14.0.0     , lens     , lsp     , lsp-test@@ -484,9 +484,9 @@     , filepath     , ghc     , ghc-boot-th-    , ghcide                == 2.13.0.0+    , ghcide                == 2.14.0.0     , hls-graph-    , hls-plugin-api        == 2.13.0.0+    , hls-plugin-api        == 2.14.0.0     , lens     , lsp     , lsp-types@@ -517,7 +517,7 @@     , filepath     , haskell-language-server:hls-eval-plugin     , hls-plugin-api-    , hls-test-utils   == 2.13.0.0+    , hls-test-utils   == 2.14.0.0     , lens     , lsp-types     , text@@ -547,9 +547,9 @@     , containers     , deepseq     , ghc-    , ghcide                == 2.13.0.0+    , ghcide                == 2.14.0.0     , hls-graph-    , hls-plugin-api        == 2.13.0.0+    , hls-plugin-api        == 2.14.0.0     , lens     , lsp     , mtl@@ -570,7 +570,7 @@     , extra     , filepath     , haskell-language-server:hls-explicit-imports-plugin-    , hls-test-utils   == 2.13.0.0+    , hls-test-utils   == 2.14.0.0     , lens     , lsp-types     , text@@ -593,15 +593,19 @@   import:           defaults, pedantic, warnings   if !flag(rename)     buildable: False-  exposed-modules:  Ide.Plugin.Rename+  exposed-modules:+    Ide.Plugin.Rename+    Ide.Plugin.Rename.ModuleName   hs-source-dirs:   plugins/hls-rename-plugin/src   build-depends:+    , aeson     , containers+    , filepath     , ghc-    , ghcide                == 2.13.0.0+    , ghcide                == 2.14.0.0     , hashable-    , hiedb                 ^>= 0.7.0.0-    , hls-plugin-api        == 2.13.0.0+    , hiedb                 ^>= 0.8.0.0+    , hls-plugin-api        == 2.14.0.0     , haskell-language-server:hls-refactor-plugin     , lens     , lsp-types@@ -609,6 +613,7 @@     , mod     , syb     , text+    , text-rope     , transformers     , unordered-containers @@ -626,7 +631,7 @@     , filepath     , hls-plugin-api     , haskell-language-server:hls-rename-plugin-    , hls-test-utils             == 2.13.0.0+    , hls-test-utils             == 2.14.0.0     , lens     , lsp-types     , text@@ -657,9 +662,9 @@     , containers     , extra     , ghc-    , ghcide                == 2.13.0.0+    , ghcide                == 2.14.0.0     , hashable-    , hls-plugin-api        == 2.13.0.0+    , hls-plugin-api        == 2.14.0.0     , haskell-language-server:hls-refactor-plugin     , lens     , lsp@@ -688,7 +693,7 @@     , filepath     , hls-plugin-api     , haskell-language-server:{hls-refactor-plugin, hls-retrie-plugin}-    , hls-test-utils             == 2.13.0.0+    , hls-test-utils             == 2.14.0.0     , text  -----------------------------@@ -732,10 +737,10 @@     , containers     , deepseq     , filepath-    , ghcide                == 2.13.0.0+    , ghcide                == 2.14.0.0     , hashable     , hlint                 >= 3.5 && < 3.11-    , hls-plugin-api        == 2.13.0.0+    , hls-plugin-api        == 2.14.0.0     , lens     , mtl     , refact@@ -788,7 +793,7 @@     , filepath     , haskell-language-server:hls-hlint-plugin     , hls-plugin-api-    , hls-test-utils      == 2.13.0.0+    , hls-test-utils      == 2.14.0.0     , lens     , lsp-types     , text@@ -842,7 +847,7 @@     , filepath     , haskell-language-server:hls-stan-plugin     , hls-plugin-api-    , hls-test-utils      == 2.13.0.0+    , hls-test-utils      == 2.14.0.0     , lens     , lsp-types     , text@@ -876,8 +881,8 @@   build-depends:     , containers     , ghc-    , ghcide                == 2.13.0.0-    , hls-plugin-api        == 2.13.0.0+    , ghcide                == 2.14.0.0+    , hls-plugin-api        == 2.14.0.0     , lsp-types     , text     , transformers@@ -893,60 +898,13 @@   build-depends:     , ghcide     , haskell-language-server:hls-signature-help-plugin-    , hls-test-utils             == 2.13.0.0-    , lens-    , lsp-types-    , string-interpolate+    , hls-test-utils             == 2.14.0.0     , text   default-extensions:     DerivingStrategies     OverloadedStrings  -------------------------------- module name plugin--------------------------------flag moduleName-  description: Enable moduleName plugin-  default:     True-  manual:      True--common moduleName-  if flag(moduleName)-    build-depends: haskell-language-server:hls-module-name-plugin-    cpp-options: -Dhls_moduleName--library hls-module-name-plugin-  import:           defaults, pedantic, warnings-  if !flag(modulename)-    buildable: False-  exposed-modules:  Ide.Plugin.ModuleName-  hs-source-dirs:   plugins/hls-module-name-plugin/src-  build-depends:-    , aeson-    , containers-    , filepath-    , ghcide                == 2.13.0.0-    , hls-plugin-api        == 2.13.0.0-    , lsp-    , text-    , text-rope-    , transformers---test-suite hls-module-name-plugin-tests-  import:           defaults, pedantic, test-defaults, warnings-  if !flag(modulename)-    buildable: False-  type:             exitcode-stdio-1.0-  hs-source-dirs:   plugins/hls-module-name-plugin/test-  main-is:          Main.hs-  build-depends:-    , filepath-    , haskell-language-server:hls-module-name-plugin-    , hls-test-utils          == 2.13.0.0------------------------------- -- pragmas plugin ----------------------------- @@ -970,13 +928,11 @@     , aeson     , extra     , fuzzy-    , ghcide                == 2.13.0.0-    , hls-plugin-api        == 2.13.0.0+    , ghcide                == 2.14.0.0+    , hls-plugin-api        == 2.14.0.0     , lens-    , lens-aeson     , lsp     , text-    , transformers     , containers  test-suite hls-pragmas-plugin-tests@@ -990,7 +946,7 @@     , aeson     , filepath     , haskell-language-server:hls-pragmas-plugin-    , hls-test-utils      == 2.13.0.0+    , hls-test-utils      == 2.14.0.0     , lens     , lsp-types     , text@@ -1023,8 +979,8 @@     , extra     , foldl     , ghc-    , ghcide                == 2.13.0.0-    , hls-plugin-api        == 2.13.0.0+    , ghcide                == 2.14.0.0+    , hls-plugin-api        == 2.14.0.0     , haskell-language-server:hls-refactor-plugin     , lens     , lsp@@ -1047,7 +1003,7 @@   build-depends:     , filepath     , haskell-language-server:hls-splice-plugin-    , hls-test-utils == 2.13.0.0+    , hls-test-utils == 2.14.0.0     , text  -----------------------------@@ -1074,12 +1030,12 @@   build-depends:     , containers     , extra-    , ghcide               == 2.13.0.0+    , ghcide               == 2.14.0.0     , ghc-boot-th     , hls-graph-    , hls-plugin-api       == 2.13.0.0+    , hls-plugin-api       == 2.14.0.0     , lens-    , lsp                  ^>=2.7+    , lsp                  ^>=2.8     , mtl     , syb     , text@@ -1102,7 +1058,7 @@     , containers     , filepath     , haskell-language-server:hls-alternate-number-format-plugin-    , hls-test-utils       == 2.13.0.0+    , hls-test-utils       == 2.14.0.0     , regex-tdfa     , tasty-quickcheck     , text@@ -1135,8 +1091,8 @@   build-depends:     , containers     , ghc-    , ghcide                == 2.13.0.0-    , hls-plugin-api        == 2.13.0.0+    , ghcide                == 2.14.0.0+    , hls-plugin-api        == 2.14.0.0     , lens     , lsp     , text@@ -1158,7 +1114,7 @@     , text     , filepath     , haskell-language-server:hls-qualify-imported-names-plugin-    , hls-test-utils             == 2.13.0.0+    , hls-test-utils             == 2.14.0.0  ----------------------------- -- code range plugin@@ -1189,9 +1145,9 @@     , deepseq     , extra     , ghc-    , ghcide           == 2.13.0.0+    , ghcide           == 2.14.0.0     , hashable-    , hls-plugin-api   == 2.13.0.0+    , hls-plugin-api   == 2.14.0.0     , lens     , lsp     , mtl@@ -1213,7 +1169,7 @@     , bytestring     , filepath     , haskell-language-server:hls-code-range-plugin-    , hls-test-utils             == 2.13.0.0+    , hls-test-utils             == 2.14.0.0     , lens     , lsp     , lsp-test@@ -1241,8 +1197,8 @@   exposed-modules:  Ide.Plugin.ChangeTypeSignature   hs-source-dirs:   plugins/hls-change-type-signature-plugin/src   build-depends:-    , ghcide           == 2.13.0.0-    , hls-plugin-api   == 2.13.0.0+    , ghcide           == 2.14.0.0+    , hls-plugin-api   == 2.14.0.0     , lens     , lsp-types     , regex-tdfa@@ -1268,8 +1224,7 @@   build-depends:     , filepath     , haskell-language-server:hls-change-type-signature-plugin-    , hls-plugin-api-    , hls-test-utils       == 2.13.0.0+    , hls-test-utils       == 2.14.0.0     , regex-tdfa     , text   default-extensions:@@ -1302,12 +1257,12 @@     , containers     , extra     , ghc-    , ghcide                 == 2.13.0.0+    , ghcide                 == 2.14.0.0     , ghc-exactprint-    , hls-plugin-api         == 2.13.0.0+    , hls-plugin-api         == 2.14.0.0     , haskell-language-server:hls-refactor-plugin     , lens-    , lsp                    >=2.7+    , lsp                    >=2.8     , mtl     , text     , transformers@@ -1324,7 +1279,7 @@   build-depends:     , filepath     , haskell-language-server:hls-gadt-plugin-    , hls-test-utils              == 2.13.0.0+    , hls-test-utils              == 2.14.0.0     , text  -----------------------------@@ -1351,10 +1306,10 @@     , containers     , deepseq     , extra-    , ghcide                == 2.13.0.0+    , ghcide                == 2.14.0.0     , hashable-    , hls-plugin-api        == 2.13.0.0-    , lsp                   >=2.7+    , hls-plugin-api        == 2.14.0.0+    , lsp                   >=2.8     , text    default-extensions: DataKinds@@ -1369,7 +1324,7 @@   build-depends:     , filepath     , haskell-language-server:hls-explicit-fixity-plugin-    , hls-test-utils              == 2.13.0.0+    , hls-test-utils              == 2.14.0.0     , text  -----------------------------@@ -1393,8 +1348,8 @@   exposed-modules:  Ide.Plugin.ExplicitFields   build-depends:     , ghc-    , ghcide                == 2.13.0.0-    , hls-plugin-api        == 2.13.0.0+    , ghcide                == 2.14.0.0+    , hls-plugin-api        == 2.14.0.0     , lsp     , lens     , hls-graph@@ -1420,7 +1375,7 @@     , text     , ghcide     , haskell-language-server:hls-explicit-record-fields-plugin-    , hls-test-utils              == 2.13.0.0+    , hls-test-utils              == 2.14.0.0  ----------------------------- -- overloaded record dot plugin@@ -1466,52 +1421,10 @@     , filepath     , text     , haskell-language-server:hls-overloaded-record-dot-plugin-    , hls-test-utils              == 2.13.0.0+    , hls-test-utils              == 2.14.0.0   -------------------------------- floskell plugin--------------------------------flag floskell-  description: Enable floskell plugin-  default:     True-  manual:      True--common floskell-  if flag(floskell) && (impl(ghc < 9.10) || flag(ignore-plugins-ghc-bounds))-    build-depends: haskell-language-server:hls-floskell-plugin-    cpp-options: -Dhls_floskell--library hls-floskell-plugin-  import:           defaults, pedantic, warnings-  -- https://github.com/ennocramer/floskell/pull/82-  if !(flag(floskell) && (impl(ghc < 9.10) || flag(ignore-plugins-ghc-bounds)))-    buildable: False-  exposed-modules:  Ide.Plugin.Floskell-  hs-source-dirs:   plugins/hls-floskell-plugin/src-  build-depends:-    , floskell        ^>=0.11.0-    , ghcide          == 2.13.0.0-    , hls-plugin-api  == 2.13.0.0-    , lsp-types       ^>=2.3-    , mtl-    , text---test-suite hls-floskell-plugin-tests-  import:           defaults, pedantic, test-defaults, warnings-  if !(flag(floskell) && (impl(ghc < 9.10) || flag(ignore-plugins-ghc-bounds)))-    buildable: False-  type:             exitcode-stdio-1.0-  hs-source-dirs:   plugins/hls-floskell-plugin/test-  main-is:          Main.hs-  build-depends:-    , filepath-    , haskell-language-server:hls-floskell-plugin-    , hls-test-utils       == 2.13.0.0------------------------------- -- fourmolu plugin ----------------------------- @@ -1537,8 +1450,8 @@     , filepath     , fourmolu        ^>= 0.14 || ^>= 0.15 || ^>= 0.16 || ^>=0.17 || ^>=0.18 || ^>=0.19     , ghc-boot-th-    , ghcide          == 2.13.0.0-    , hls-plugin-api  == 2.13.0.0+    , ghcide          == 2.14.0.0+    , hls-plugin-api  == 2.14.0.0     , lens     , lsp     , mtl@@ -1562,11 +1475,15 @@     fourmolu:fourmolu   build-depends:     , aeson+    , containers     , filepath     , haskell-language-server:hls-fourmolu-plugin     , hls-plugin-api-    , hls-test-utils       == 2.13.0.0+    , hls-test-utils       == 2.14.0.0+    , lens     , lsp-test+    , lsp-types+    , text  ----------------------------- -- ormolu plugin@@ -1594,8 +1511,8 @@     , extra     , filepath     , ghc-boot-th-    , ghcide          == 2.13.0.0-    , hls-plugin-api  == 2.13.0.0+    , ghcide          == 2.14.0.0+    , hls-plugin-api  == 2.14.0.0     , lsp     , mtl     , process-extras  >= 0.7.1@@ -1619,12 +1536,15 @@     ormolu:ormolu   build-depends:     , aeson+    , containers     , filepath     , haskell-language-server:hls-ormolu-plugin     , hls-plugin-api-    , hls-test-utils     == 2.13.0.0+    , hls-test-utils     == 2.14.0.0+    , lens     , lsp-types     , ormolu+    , text  ----------------------------- -- stylish-haskell plugin@@ -1653,8 +1573,8 @@     , directory     , filepath     , ghc-boot-th-    , ghcide           == 2.13.0.0-    , hls-plugin-api   == 2.13.0.0+    , ghcide           == 2.14.0.0+    , hls-plugin-api   == 2.14.0.0     , lsp-types     , mtl     , stylish-haskell  >=0.12 && <0.16@@ -1672,7 +1592,7 @@   build-depends:     , filepath     , haskell-language-server:hls-stylish-haskell-plugin-    , hls-test-utils              == 2.13.0.0+    , hls-test-utils              == 2.14.0.0  ----------------------------- -- refactor plugin@@ -1724,8 +1644,8 @@     , bytestring     , ghc-boot     , regex-tdfa-    , ghcide                == 2.13.0.0-    , hls-plugin-api        == 2.13.0.0+    , ghcide                == 2.14.0.0+    , hls-plugin-api        == 2.14.0.0     , lsp     , text     , text-rope@@ -1741,9 +1661,6 @@     , mtl     , lens     , time-    -- FIXME: Only needed to workaround for qualified imports in GHC 9.4-    , regex-applicative-    , parser-combinators   if impl(ghc < 9.10)     build-depends: data-default @@ -1763,7 +1680,7 @@     , filepath     , ghcide:ghcide     , haskell-language-server:hls-refactor-plugin-    , hls-test-utils      == 2.13.0.0+    , hls-test-utils      == 2.14.0.0     , lens     , lsp-test     , lsp-types@@ -1771,7 +1688,6 @@     , regex-tdfa     , shake     , tasty-    , tasty-expected-failure     , tasty-hunit     , text @@ -1811,18 +1727,17 @@     , text-rope     , mtl                   >= 2.2     , ghc-    , ghcide                == 2.13.0.0-    , hls-plugin-api        == 2.13.0.0+    , ghcide                == 2.14.0.0+    , hls-plugin-api        == 2.14.0.0     , lens-    , lsp                    >=2.6+    , lsp                    >=2.8     , text     , transformers     , bytestring-    , syb     , array     , deepseq     , dlist-    , hls-graph == 2.13.0.0+    , hls-graph == 2.14.0.0     , template-haskell     , data-default     , stm@@ -1843,10 +1758,10 @@     , containers     , data-default     , filepath-    , ghcide                == 2.13.0.0+    , ghcide                == 2.14.0.0     , haskell-language-server:hls-semantic-tokens-plugin-    , hls-plugin-api        == 2.13.0.0-    , hls-test-utils        == 2.13.0.0+    , hls-plugin-api        == 2.14.0.0+    , hls-test-utils        == 2.14.0.0     , lens     , lsp     , lsp-test@@ -1876,11 +1791,11 @@   hs-source-dirs:     plugins/hls-notes-plugin/src   build-depends:     , array-    , ghcide == 2.13.0.0-    , hls-graph == 2.13.0.0-    , hls-plugin-api == 2.13.0.0+    , ghcide == 2.14.0.0+    , hls-graph == 2.14.0.0+    , hls-plugin-api == 2.14.0.0     , lens-    , lsp >=2.7+    , lsp >=2.8     , mtl >= 2.2     , regex-tdfa >= 1.3.1     , text@@ -1904,7 +1819,7 @@   build-depends:     , filepath     , haskell-language-server:hls-notes-plugin-    , hls-test-utils == 2.13.0.0+    , hls-test-utils == 2.14.0.0   default-extensions: OverloadedStrings  ----------------------------@@ -1931,7 +1846,6 @@                   , hlint                   , stan                   , signatureHelp-                  , moduleName                   , pragmas                   , splice                   , alternateNumberFormat@@ -1940,7 +1854,6 @@                   , gadt                   , explicitFixity                   , explicitFields-                  , floskell                   , fourmolu                   , ormolu                   , stylishHaskell@@ -1965,10 +1878,10 @@     , extra     , filepath     , ghc-    , ghcide                == 2.13.0.0+    , ghcide                == 2.14.0.0     , githash               >=0.1.6.1     , hie-bios-    , hls-plugin-api        == 2.13.0.0+    , hls-plugin-api        == 2.14.0.0     , optparse-applicative     , optparse-simple     , prettyprinter         >= 1.7@@ -2071,7 +1984,7 @@     , ghcide:ghcide     , hashable     , hls-plugin-api-    , hls-test-utils == 2.13.0.0+    , hls-test-utils == 2.14.0.0     , lens     , lsp-test     , lsp-types@@ -2097,8 +2010,6 @@   if flag(eval)     cpp-options: -Dhls_eval -- formatters-  if flag(floskell) && (impl(ghc < 9.10) || flag(ignore-plugins-ghc-bounds))-    cpp-options: -Dhls_floskell   if flag(fourmolu) && (impl(ghc < 9.14) || flag(ignore-plugins-ghc-bounds))     cpp-options: -Dhls_fourmolu   if flag(ormolu) && (impl(ghc < 9.14) || flag(ignore-plugins-ghc-bounds))@@ -2115,7 +2026,7 @@    build-depends:     , extra-    , hls-test-utils              == 2.13.0.0+    , hls-test-utils              == 2.14.0.0     , process    hs-source-dirs:     test/wrapper@@ -2189,7 +2100,7 @@     , lens     , list-t     , lsp-    , lsp-test                ^>=0.17.1+    , lsp-test                ^>=0.18.0     , lsp-types     , mtl     , network-uri@@ -2208,7 +2119,7 @@     , text     , text-rope     , unordered-containers-    , hls-test-utils == 2.13.0.0+    , hls-test-utils == 2.14.0.0    if impl(ghc <9.3)     build-depends: ghc-typelits-knownnat@@ -2224,6 +2135,7 @@     ClientSettingsTests     CodeLensTests     CompletionTests+    ConstructorHoverTests     CPPTests     CradleTests     DependentFileTest@@ -2236,6 +2148,7 @@     HaddockTests     HieDbRetry     HighlightTests+    Hover     IfaceTests     InitializeResponseTests     LogType@@ -2346,7 +2259,7 @@     build-depends:         extra,         haskell-language-server:ghcide-bench-lib,-        lsp-test ^>= 0.17,+        lsp-test ^>= 0.18,         tasty,         tasty-hunit >= 0.10,         tasty-rerun
plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/CabalAdd/CodeAction.hs view
@@ -36,6 +36,8 @@                                                                 PluginId)  import           Control.Lens                                  ((^.))+import qualified Data.Aeson                                    as Aeson+import           Development.IDE.Session.Diagnostics import qualified Language.LSP.Protocol.Lens                    as JL import           Language.LSP.Protocol.Types                   (CodeActionKind (..),                                                                 VersionedTextDocumentIdentifier)@@ -43,7 +45,6 @@ import           System.FilePath import           Text.PrettyPrint                              (render) import           Text.Regex.TDFA- -------------------------------------------- -- Add module to cabal file --------------------------------------------@@ -148,13 +149,16 @@   cabalSrcPath = takeDirectory cabalSrcPath'  isUnknownModuleDiagnostic :: J.Diagnostic -> Bool-isUnknownModuleDiagnostic diag = (msg =~ regex)- where-  msg :: T.Text-  msg = diag ^. JL.message-  regex :: T.Text-  regex = "Loading the module [\8216'][^\8217']*[\8217'] failed."-+isUnknownModuleDiagnostic diag =+  case diag ^. JL.data_ of+    Nothing -> False+    Just v ->+      case Aeson.fromJSON v of+        Aeson.Success details ->+          case structuredCradleError details of+            Just (UnknownModuleError _) -> True+            _                           -> False+        _ -> False -------------------------- -- Below are several utility functions which create a StanzaItem for each of the possible Stanzas, -- these all have specific constructors we need to match, so we can't generalise this process well.
plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completer/FilePath.hs view
@@ -5,6 +5,8 @@ import           Control.Exception                            (evaluate, try) import           Control.Monad                                (filterM) import           Control.Monad.Extra                          (concatForM, forM)+import           Data.Char                                    (isUpper)+import           Data.List                                    (find) import qualified Data.Text                                    as T import           Distribution.PackageDescription              (GenericPackageDescription) import           Ide.Logger@@ -26,50 +28,39 @@   let prefInfo = cabalPrefixInfo cData       complInfo = pathCompletionInfoFromCabalPrefixInfo "" prefInfo   filePathCompletions <- listFileCompletions recorder complInfo-  let scored =-        Fuzzy.simpleFilter-          Fuzzy.defChunkSize-          Fuzzy.defMaxResults-          (pathSegment complInfo)-          (map T.pack filePathCompletions)-  forM-    scored-    ( \compl' -> do-        let compl = Fuzzy.original compl'-        fullFilePath <- mkFilePathCompletion complInfo compl-        pure $ mkCompletionItem (completionRange prefInfo) fullFilePath fullFilePath-    )+  let query = pathSegment complInfo+      originals = map T.pack filePathCompletions+      matched = smartCaseFuzzy query originals +  forM matched $ \compl -> do+    fullFilePath <- mkFilePathCompletion complInfo compl+    pure $ mkCompletionItem (completionRange prefInfo) fullFilePath fullFilePath+ mainIsCompleter :: (Maybe StanzaName -> GenericPackageDescription -> [FilePath]) -> Completer mainIsCompleter extractionFunction recorder cData = do   mGPD <- getLatestGPD cData   case mGPD of     Just gpd -> do-      let srcDirs = extractionFunction sName gpd-      concatForM srcDirs-        (\dir' -> do+      concatForM srcDirs $ \dir' -> do         let dir = FP.normalise dir'-        let pathInfo = pathCompletionInfoFromCabalPrefixInfo dir prefInfo+            pathInfo = pathCompletionInfoFromCabalPrefixInfo dir prefInfo+            query = pathSegment pathInfo+         completions <- listFileCompletions recorder pathInfo-        let scored = Fuzzy.simpleFilter-              Fuzzy.defChunkSize-              Fuzzy.defMaxResults-              (pathSegment pathInfo)-              (map T.pack completions)-        forM-          scored-          ( \compl' -> do-              let compl = Fuzzy.original compl'-              fullFilePath <- mkFilePathCompletion pathInfo compl-              pure $ mkCompletionItem (completionRange prefInfo) fullFilePath fullFilePath-          )-        )++        let originals = map T.pack completions+            matched = smartCaseFuzzy query originals++        forM matched $ \compl -> do+          fullFilePath <- mkFilePathCompletion pathInfo compl+          pure $ mkCompletionItem (completionRange prefInfo) fullFilePath fullFilePath+      where+          sName = stanzaName cData+          srcDirs = extractionFunction sName gpd+          prefInfo = cabalPrefixInfo cData     Nothing -> do       logWith recorder Debug LogUseWithStaleFastNoResult       pure []-  where-    sName = stanzaName cData-    prefInfo = cabalPrefixInfo cData   -- | Completer to be used when a directory can be completed for the field.@@ -79,20 +70,14 @@   let prefInfo = cabalPrefixInfo cData       complInfo = pathCompletionInfoFromCabalPrefixInfo "" prefInfo   directoryCompletions <- listDirectoryCompletions recorder complInfo-  let scored =-        Fuzzy.simpleFilter-          Fuzzy.defChunkSize-          Fuzzy.defMaxResults-          (pathSegment complInfo)-          (map T.pack directoryCompletions)-  forM-    scored-    ( \compl' -> do-        let compl = Fuzzy.original compl'-        let fullDirPath = mkPathCompletionDir complInfo compl-        pure $ mkCompletionItem (completionRange prefInfo) fullDirPath fullDirPath-    )+  let query = pathSegment complInfo+      originals = map T.pack directoryCompletions+      matched = smartCaseFuzzy query originals +  forM matched $ \compl -> do+    fullFilePath <- mkFilePathCompletion complInfo compl+    pure $ mkCompletionItem (completionRange prefInfo) fullFilePath fullFilePath+ {- Note [Using correct file path separators] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~   Since cabal files only allow for posix style file paths@@ -185,3 +170,49 @@   isFilePath <- doesFileExist $ T.unpack combinedPath   let completedPath = if isFilePath then applyStringNotation (isStringNotationPath complInfo) combinedPath else combinedPath   pure completedPath++-- | Applies smart-case filtering to fuzzy-matched candidates.+--   If the query contains uppercase characters, candidates must+--   contain the query as a case-sensitive substring.+--   If the query is all lowercase, all fuzzy matches are accepted.+smartCaseFuzzy :: T.Text -> [T.Text] -> [T.Text]+smartCaseFuzzy query originals =+  let smartSensitive :: Bool+      smartSensitive = T.any isUpper query++      filtered :: [T.Text]+      filtered+        | smartSensitive = filter (isCaseSensitiveSubsequence query) originals+        | otherwise =originals++      pairs :: [(T.Text, T.Text)]+      pairs = [ (o, T.toLower o) | o <- filtered ]++      matchQuery :: T.Text+      matchSpace :: [T.Text]+      (matchQuery, matchSpace) =+        if smartSensitive+          then (query, map fst pairs)+          else (T.toLower query, map snd pairs)++      scored :: [Fuzzy.Scored T.Text]+      scored = Fuzzy.simpleFilter Fuzzy.defChunkSize Fuzzy.defMaxResults matchQuery matchSpace++      restore :: T.Text -> T.Text+      restore matched =+        case find (\(o,l) -> o == matched || l == matched) pairs of+          Just (o, _) -> o+          Nothing     -> matched+  in+    map (restore . Fuzzy.original) scored++-- | Checks whether the query is a case-sensitive subsequence of the candidate.+--   Characters must appear in order and match exactly (including case).+isCaseSensitiveSubsequence :: T.Text -> T.Text -> Bool+isCaseSensitiveSubsequence q c = go (T.unpack q) (T.unpack c)+  where+    go [] _          = True+    go _  []         = False+    go (x:xs) (y:ys)+      | x == y       = go xs ys+      | otherwise    = go (x:xs) ys
plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completer/Module.hs view
@@ -14,14 +14,14 @@                                                                  WithPriority,                                                                  logWith) import           Ide.Plugin.Cabal.Completion.Completer.FilePath (listFileCompletions,-                                                                 mkCompletionDirectory)+                                                                 mkCompletionDirectory,+                                                                 smartCaseFuzzy) import           Ide.Plugin.Cabal.Completion.Completer.Paths import           Ide.Plugin.Cabal.Completion.Completer.Simple import           Ide.Plugin.Cabal.Completion.Completer.Types import           Ide.Plugin.Cabal.Completion.Types import           System.Directory                               (doesFileExist) import qualified System.FilePath                                as FP-import qualified Text.Fuzzy.Parallel                            as Fuzzy  -- | Completer to be used when module paths can be completed for the field. --@@ -50,7 +50,7 @@   -> CabalPrefixInfo   -> Matcher T.Text   -> IO [T.Text]-filePathsForExposedModules recorder srcDirs prefInfo matcher = do+filePathsForExposedModules recorder srcDirs prefInfo _matcher = do   concatForM     srcDirs     ( \dir' -> do@@ -58,18 +58,17 @@             pathInfo = pathCompletionInfoFromCabalPrefixInfo dir modPrefInfo         completions <- listFileCompletions recorder pathInfo         validExposedCompletions <- filterM (isValidExposedModulePath pathInfo) completions-        let toMatch = pathSegment pathInfo-            scored = runMatcher-              matcher-              toMatch-              (map T.pack validExposedCompletions)-        forM-          scored-          ( \compl' -> do-              let compl = Fuzzy.original compl'-              fullFilePath <- mkExposedModulePathCompletion pathInfo $ T.unpack compl-              pure fullFilePath-          )++        let query = pathSegment pathInfo+            candidates :: [T.Text]+            candidates = map T.pack validExposedCompletions++            matched :: [T.Text]+            matched = smartCaseFuzzy query candidates++        forM matched $ \compl -> do+          fullFilePath <- mkExposedModulePathCompletion pathInfo (T.unpack compl)+          pure fullFilePath     )   where     prefix =
plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Data.hs view
@@ -88,7 +88,8 @@       ("common", libExecTestBenchCommons Library),       ("common", libExecTestBenchCommons Common),       ("flag", flagFields),-      ("source-repository", sourceRepositoryFields)+      ("source-repository", sourceRepositoryFields),+      ("custom-setup", customSetupFields)     ]  libraryFields :: Map KeyWordName Completer@@ -180,7 +181,7 @@       ("hs-source-dirs:", directoryCompleter),       ("default-extensions:", constantCompleter $ map (T.pack . prettyShow) allExtensions),       ("other-extensions:", constantCompleter $ map (T.pack . prettyShow) allExtensions),-      ("default-language:", constantCompleter ["GHC2021", "Haskell2010", "Haskell98"]),+      ("default-language:", defaultLanguageCompleter),       ("other-languages:", noopCompleter),       ("build-tool-depends:", noopCompleter),       ("buildable:", constantCompleter ["True", "False"]),@@ -236,6 +237,15 @@         -- but not have erased the "common" stanza.         noopCompleter +customSetupFields :: Map KeyWordName Completer+customSetupFields =+  Map.fromList+    [ ("setup-depends:", noopCompleter)+    , ("build-depends:", noopCompleter)+    , ("build-tools:", noopCompleter)+    , ("default-language:", defaultLanguageCompleter)+    ]+ -- | Returns all possible language extensions including disabled ones. allExtensions :: [Extension] allExtensions =@@ -249,6 +259,10 @@     )     knownExtensions +-- | Returns all possible default languages+defaultLanguageCompleter :: Completer+defaultLanguageCompleter = constantCompleter $ map (T.pack . prettyShow) knownLanguages+ -- | Contains a map of the most commonly used licenses, weighted by their popularity. -- --  The data was extracted by Kleidukos from the alternative hackage frontend flora.pm.@@ -309,3 +323,4 @@  ghcOptions :: [T.Text] ghcOptions = map T.pack $ flagsForCompletion False+
plugins/hls-cabal-plugin/test/Completer.hs view
@@ -101,16 +101,19 @@     "File Completer Tests"     [ testCase "Current Directory - no leading ./ by default" $ do         completions <- completeFilePath "" filePathComplTestDir-        completions @?== [".hidden", "Content.hs", "dir1/", "dir2/", "textfile.txt", "main-is.cabal"],+        completions @?== [".hidden", "Content.hs", "Dir4/", "dir1/", "dir2/", "textfile.txt", "main-is.cabal"],       testCase "Current Directory - alternative writing" $ do         completions <- completeFilePath "./" filePathComplTestDir-        completions @?== ["./.hidden", "./Content.hs", "./dir1/", "./dir2/", "./textfile.txt", "./main-is.cabal"],+        completions @?== ["./.hidden", "./Content.hs", "./Dir4/", "./dir1/", "./dir2/", "./textfile.txt", "./main-is.cabal"],       testCase "Current Directory - hidden file start" $ do         completions <- completeFilePath "." filePathComplTestDir         completions @?== ["Content.hs", ".hidden", "textfile.txt", "main-is.cabal"],       testCase "Current Directory - incomplete directory path written" $ do         completions <- completeFilePath "di" filePathComplTestDir-        completions @?== ["dir1/", "dir2/"],+        completions @?== ["dir1/", "dir2/","Dir4/"],+      testCase "Current Directory - fuzzy Smart casing" $ do+        completions <- completeFilePath "Dr" filePathComplTestDir+        completions @?== ["Dir4/"],       testCase "Current Directory - incomplete filepath written" $ do         completions <- completeFilePath "te" filePathComplTestDir         completions @?== ["Content.hs", "textfile.txt"],@@ -120,6 +123,12 @@       testCase "Subdirectory - incomplete filepath written" $ do         completions <- completeFilePath "dir2/dir3/MA" filePathComplTestDir         completions @?== ["dir2/dir3/MARKDOWN.md"],+      testCase "Subdirectory - lowerCase" $ do+        completions <- completeFilePath "dir2/dir3/md" filePathComplTestDir+        completions @?== ["dir2/dir3/MARKDOWN.md"],+      testCase "Subdirectory - smart casing mismatch" $ do+        completions <- completeFilePath "dir2/dir3/Ma" filePathComplTestDir+        completions @?== [],       testCase "Nonexistent directory" $ do         completions <- completeFilePath "dir2/dir4/" filePathComplTestDir         completions @?== []@@ -171,7 +180,7 @@                 queryDirectory = "",                 workingDirectory = filePathComplTestDir               }-        compls @?== [".hidden", "Content.hs", "dir1/", "dir2/", "textfile.txt", "main-is.cabal"],+        compls @?== [".hidden", "Content.hs", "Dir4/", "dir1/", "dir2/", "textfile.txt", "main-is.cabal"],       testCase "In directory" $ do         compls <-           listFileCompletions@@ -200,13 +209,19 @@     "Directory Completer Tests"     [ testCase "Current Directory - no leading ./ by default" $ do         completions <- completeDirectory "" filePathComplTestDir-        completions @?== ["dir1/", "dir2/"],+        completions @?== ["Dir4/", "dir1/", "dir2/"],       testCase "Current Directory - alternative writing" $ do         completions <- completeDirectory "./" filePathComplTestDir-        completions @?== ["./dir1/", "./dir2/"],+        completions @?== ["./Dir4/", "./dir1/", "./dir2/"],       testCase "Current Directory - incomplete directory path written" $ do-        completions <- completeDirectory "di" filePathComplTestDir-        completions @?== ["dir1/", "dir2/"],+        completions <- completeDirectory "dr" filePathComplTestDir+        completions @?== ["Dir4/", "dir1/", "dir2/"],+      testCase "Current Directory - correct smart casing" $ do+        completions <- completeDirectory "Dr" filePathComplTestDir+        completions @?== ["Dir4/"],+      testCase "Current Directory - incorrect smart casing" $ do+        completions <- completeDirectory "DI" filePathComplTestDir+        completions @?== [],       testCase "Current Directory - incomplete filepath written" $ do         completions <- completeDirectory "te" filePathComplTestDir         completions @?== [],@@ -315,7 +330,16 @@         completions @?== ["File3"],       testCase "Name nothing but not library" $ do         completions <- callModulesCompleter Nothing sourceDirsExtractionTestSuite "3"-        completions @?== []+        completions @?== [],+      testCase "Exposed modules - smart casing rejects mixed-case mismatch" $ do+        completions <- callModulesCompleter (Just "benchie") sourceDirsExtractionBenchmark "FL"+        completions @?== [],+      testCase "Exposed modules - smart casing preserves fuzzy matching" $ do+        completions <- callModulesCompleter (Just "benchie") sourceDirsExtractionBenchmark "Fl1"+        completions @?== ["File1"],+      testCase "Exposed modules - lowercase remains case-insensitive" $ do+        completions <- callModulesCompleter (Just "benchie") sourceDirsExtractionBenchmark "fl"+        completions @?== ["File1"]     ]   where     callModulesCompleter :: Maybe StanzaName -> (Maybe StanzaName -> GenericPackageDescription -> [FilePath]) -> T.Text -> IO [T.Text]
plugins/hls-call-hierarchy-plugin/test/Main.hs view
@@ -16,6 +16,7 @@ import qualified Language.LSP.Test          as Test import           System.FilePath import           Test.Hls+import qualified Test.Hls.FileSystem        as FS  plugin :: PluginTestDescriptor () plugin = mkPluginTestDescriptor' descriptor "call-hierarchy"@@ -192,9 +193,9 @@   testGroup "Incoming Calls"   [ testGroup "single file"     [ testCase "xdata unavailable" $-        runSessionWithServer def plugin testDataDir $ do+        runCallHierarchySessionEmpty $ do           doc <- createDoc "A.hs" "haskell" $ T.unlines ["a=3", "b=a"]-          waitForIndex (testDataDir </> "A.hs")+          waitForIndex "A.hs"           item <- expectOneElement =<< Test.prepareCallHierarchy (mkPrepareCallHierarchyParam doc 1 0)           let expected = [CallHierarchyIncomingCall item [mkRange 1 2 1 3]]           item' <- expectOneElement =<< Test.prepareCallHierarchy (mkPrepareCallHierarchyParam doc 0 0)@@ -312,10 +313,10 @@ outgoingCallsTests =   testGroup "Outgoing Calls"   [ testGroup "single file"-    [ testCase "xdata unavailable" $ withCanonicalTempDir $ \dir ->-        runSessionWithServer def plugin dir $ do+    [ testCase "xdata unavailable" $+        runCallHierarchySessionEmpty $ do           doc <- createDoc "A.hs" "haskell" $ T.unlines ["a=3", "b=a"]-          waitForIndex (dir </> "A.hs")+          waitForIndex "A.hs"           item <- expectOneElement =<< Test.prepareCallHierarchy (mkPrepareCallHierarchyParam doc 0 1)           let expected = [CallHierarchyOutgoingCall item [mkRange 1 2 1 3]]           item' <- expectOneElement =<< Test.prepareCallHierarchy (mkPrepareCallHierarchyParam doc 1 0)@@ -411,10 +412,10 @@   incomingCallTestCase :: T.Text -> Int -> Int -> [(Int, Int)] -> [Range] -> Assertion-incomingCallTestCase contents queryX queryY positions ranges = withCanonicalTempDir $ \dir ->-  runSessionWithServer def plugin dir $ do+incomingCallTestCase contents queryX queryY positions ranges =+  runCallHierarchySessionEmpty $ do     doc <- createDoc "A.hs" "haskell" contents-    waitForIndex (dir </> "A.hs")+    waitForIndex "A.hs"     items <- concatMapM (\((x, y), range) ->       Test.prepareCallHierarchy (mkPrepareCallHierarchyParam doc x y)           <&> map (, range)@@ -428,9 +429,9 @@  incomingCallMultiFileTestCase :: FilePath -> Int -> Int -> M.Map FilePath [((Int, Int), Range)] -> Assertion incomingCallMultiFileTestCase filepath queryX queryY mp =-  runSessionWithServer def plugin testDataDir $ do+  runCallHierarchySession $ do     doc <- openDoc filepath "haskell"-    waitForIndex (testDataDir </> filepath)+    waitForIndex filepath     items <- fmap concat $ sequence $ M.elems $ M.mapWithKey (\fp pr -> do               p <- openDoc fp "haskell"               waitForKickDone@@ -445,10 +446,10 @@     closeDoc doc  outgoingCallTestCase :: T.Text -> Int -> Int -> [(Int, Int)] -> [Range] -> Assertion-outgoingCallTestCase contents queryX queryY positions ranges = withCanonicalTempDir $ \dir ->-  runSessionWithServer def plugin dir $ do+outgoingCallTestCase contents queryX queryY positions ranges =+  runCallHierarchySessionEmpty $ do     doc <- createDoc "A.hs" "haskell" contents-    waitForIndex (dir </> "A.hs")+    waitForIndex "A.hs"     items <- concatMapM (\((x, y), range) ->       Test.prepareCallHierarchy (mkPrepareCallHierarchyParam doc x y)           <&> map (, range)@@ -462,9 +463,9 @@  outgoingCallMultiFileTestCase :: FilePath -> Int -> Int -> M.Map FilePath [((Int, Int), Range)] -> Assertion outgoingCallMultiFileTestCase filepath queryX queryY mp =-  runSessionWithServer def plugin testDataDir $ do+  runCallHierarchySession $ do     doc <- openDoc filepath "haskell"-    waitForIndex (testDataDir </> filepath)+    waitForIndex filepath     items <- fmap concat $ sequence $ M.elems $ M.mapWithKey (\fp pr -> do               p <- openDoc fp "haskell"               waitForKickDone@@ -479,10 +480,10 @@     closeDoc doc  oneCaseWithCreate :: T.Text -> Int -> Int -> (Uri -> CallHierarchyItem -> Assertion) -> Assertion-oneCaseWithCreate contents queryX queryY expected = withCanonicalTempDir $ \dir ->-  runSessionWithServer def plugin dir $ do+oneCaseWithCreate contents queryX queryY expected =+  runCallHierarchySessionEmpty $ do     doc <- createDoc "A.hs" "haskell" contents-    waitForIndex (dir </> "A.hs")+    waitForIndex "A.hs"     item <- expectOneElement =<< Test.prepareCallHierarchy (mkPrepareCallHierarchyParam doc queryX queryY)     liftIO $ expected (doc ^. L.uri) item     closeDoc doc@@ -549,3 +550,23 @@       | isRelative fp1 = any (equalFilePath fp1 . joinPath) $ tails $ splitDirectories fp2       | otherwise = equalFilePath fp1 fp2 +runCallHierarchySession :: Session a -> IO a+runCallHierarchySession =+  runSessionWithServerInTmpDir def plugin+    (FS.mkVirtualFileTree+      testDataDir+      [ FS.copy "A.hs"+      , FS.copy "B.hs"+      , FS.copy "C.hs"+      , FS.copy "hie.yaml"+      ]+    )++runCallHierarchySessionEmpty :: Session a -> IO a+runCallHierarchySessionEmpty =+  runSessionWithServerInTmpDir def plugin+    (FS.mkVirtualFileTree+      testDataDir+      [+      ]+    )
plugins/hls-class-plugin/src/Ide/Plugin/Class/ExactPrint.hs view
@@ -6,7 +6,6 @@  import           Control.Monad.Trans.Maybe import           Data.Either.Extra                       (eitherToMaybe)-import           Data.Functor.Identity                   (Identity) import qualified Data.Text                               as T import           Development.IDE.GHC.Compat import           GHC.Parser.Annotation@@ -20,11 +19,15 @@ import           Control.Lens                            (_head, over) #endif +#if !MIN_VERSION_ghc_exactprint(1,10,0)+import           Data.Functor.Identity                   (Identity)+#endif+ makeEditText :: Monad m => ParsedModule -> DynFlags -> AddMinimalMethodsParams -> MaybeT m (T.Text, T.Text) makeEditText pm df AddMinimalMethodsParams{..} = do     mDecls <- MaybeT . pure $ traverse (makeMethodDecl df) methodGroup     let ps =-#if !MIN_VERSION_ghc(9,9,0)+#if !MIN_VERSION_ghc(9,10,0) || MIN_VERSION_ghc(9,11,0)             makeDeltaAst $ #endif                 pm_parsed_source pm
plugins/hls-class-plugin/test/Main.hs view
@@ -66,6 +66,12 @@       getActionByTitle "Add placeholders for 'g','h'"   , goldenWithClass "Creates a placeholder when all top-level decls are indented" "T7" "" $       getActionByTitle "Add placeholders for 'g','h','i'"+  , testGroup "with preprocessors"+    [ knownBrokenInEnv [GhcVer GHC910]+        "See issue https://github.com/haskell/haskell-language-server/issues/4731 for details." $+        goldenWithClass "Creates a placeholder for '<>'" "T8" "diamond" $+      getActionByTitle "Add placeholders for '<>'"+      ]   , goldenWithClass "Don't insert pragma with GHC2021" "InsertWithGHC2021Enabled" "" $       getActionByTitle "Add placeholders for '==' with signature(s)"   , goldenWithClass "Insert pragma if not exist" "InsertWithoutPragma" "" $
+ plugins/hls-class-plugin/test/testdata/T8.diamond.expected.hs view
@@ -0,0 +1,6 @@+data Set a = Set++instance Semigroup (Set a) where+  (<>) = _++instance Monoid (Set a) where
+ plugins/hls-class-plugin/test/testdata/T8.hs view
@@ -0,0 +1,5 @@+data Set a = Set++instance Semigroup (Set a) where++instance Monoid (Set a) where
plugins/hls-eval-plugin/src/Ide/Plugin/Eval/Handlers.hs view
@@ -90,7 +90,6 @@ #if MIN_VERSION_ghc(9,13,0) import           GHC.Types.Avail                              (DetOrdAvails (DefinitelyDeterministicAvails),                                                                sortAvails)-import           GHC.Tc.Types                                 (tcg_exports) import           GHC.Types.Name.Set                           (nameSetElemsStable) #endif import           Ide.Logger                                   (Priority (..),
− plugins/hls-eval-plugin/test/dist-newstyle/build/x86_64-linux/ghc-9.10.3/info-util-0.1.0.0/build/autogen/PackageInfo_info_util.hs
@@ -1,46 +0,0 @@-{-# LANGUAGE NoRebindableSyntax #-}-{-# OPTIONS_GHC -Wno-missing-import-lists #-}-{-# OPTIONS_GHC -w #-}--{-|-Module      : PackageInfo_info_util-Description : Contents of some of the package's Cabal file's fields.--WARNING: This module was generated by Cabal. Any modifications will be-overwritten if the module is regenerated.--This module exports values that record information from some of the fields of-the package's Cabal package description file (Cabal file).--For further information about the fields in a Cabal file, see the Cabal User-Guide.--}--module PackageInfo_info_util (-    name,-    version,-    synopsis,-    copyright,-    homepage,-  ) where--import Data.Version (Version(..))-import Prelude---- |The content of the @name@ field of the package's Cabal file, but with any--- hyphen characters replaced by underscore characters.-name :: String-name = "info_util"--- |The content of the @version@ field of the package's Cabal file.-version :: Version-version = Version [0,1,0,0] []---- |The content of the @synopsis@ field of the package's Cabal file.-synopsis :: String-synopsis = ""--- |The content of the @copyright@ field of the package's Cabal file.-copyright :: String-copyright = "2017 Author name here"--- |The content of the @homepage@ field of the package's Cabal file.-homepage :: String-homepage = ""
− plugins/hls-eval-plugin/test/dist-newstyle/build/x86_64-linux/ghc-9.10.3/info-util-0.1.0.0/build/autogen/Paths_info_util.hs
@@ -1,132 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE NoRebindableSyntax #-}-#if __GLASGOW_HASKELL__ >= 810-{-# OPTIONS_GHC -Wno-prepositive-qualified-module #-}-#endif-{-# OPTIONS_GHC -Wno-missing-import-lists #-}-{-# OPTIONS_GHC -w #-}--{-|-Module      : Paths_info_util-Description : Data file location, and package version and installation-              directories.--WARNING: This module was generated by Cabal. Any modifications will be-overwritten if the module is regenerated.--This module exports a function to locate data files, and values that record-the version of the package and some directories which the package has been-configured to be installed into.--For further information about Cabal's options for its configuration step, and-their default values, see the Cabal User Guide.--}--module Paths_info_util (-    version,-    getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir,-    getDataFileName, getSysconfDir-  ) where---import qualified Control.Exception as Exception-import Data.Version (Version(..))-import System.Environment (getEnv)-import Prelude---#if defined(VERSION_base)--#if MIN_VERSION_base(4,0,0)-catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a-#else-catchIO :: IO a -> (Exception.Exception -> IO a) -> IO a-#endif--#else-catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a-#endif-catchIO = Exception.catch---- |The package version.-version :: Version-version = Version [0,1,0,0] []---- |If the argument is a filename, the result is the name of a corresponding--- file on the system on which the program is running, if the file were listed--- in the @data-files@ field of the package's Cabal package description file.--- No check is performed that the given filename is listed in that field.-getDataFileName :: FilePath -> IO FilePath-getDataFileName name = do-  dir <- getDataDir-  return (dir `joinFileName` name)---- |The location of the directory specified by Cabal's @--bindir@ option (where--- executables that the user might invoke are installed). This can be overridden--- at runtime using the environment variable info_util_bindir.-getBinDir :: IO FilePath---- |The location of the directory specified by Cabal's @--libdir@ option (where--- object libraries are installed). This can be overridden at runtime using the--- environment variable info_util_libdir.-getLibDir :: IO FilePath---- |The location of the directory specified by Cabal's @--dynlibdir@ option--- (where dynamic libraries are installed). This can be overridden at runtime--- using the environment variable info_util_dynlibdir.-getDynLibDir :: IO FilePath---- |The location of the directory specified by Cabal's @--datadir@ option (where--- architecture-independent data files are installed). This can be overridden at--- runtime using the environment variable info_util_datadir.-getDataDir :: IO FilePath---- |The location of the directory specified by Cabal's @--libexedir@ option--- (where executables that are not expected to be invoked directly by the user--- are installed). This can be overridden at runtime using the environment--- variable info_util_libexedir.-getLibexecDir :: IO FilePath---- |The location of the directory specified by Cabal's @--sysconfdir@ option--- (where configuration files are installed). This can be overridden at runtime--- using the environment variable info_util_sysconfdir.-getSysconfDir :: IO FilePath-----bindir, libdir, dynlibdir, datadir, libexecdir, sysconfdir :: FilePath-bindir     = "/home/hugin/.cabal/bin"-libdir     = "/home/hugin/.cabal/lib/x86_64-linux-ghc-9.10.3-415c/info-util-0.1.0.0-inplace"-dynlibdir  = "/home/hugin/.cabal/lib/x86_64-linux-ghc-9.10.3-415c"-datadir    = "/home/hugin/.cabal/share/x86_64-linux-ghc-9.10.3-415c/info-util-0.1.0.0"-libexecdir = "/home/hugin/.cabal/libexec/x86_64-linux-ghc-9.10.3-415c/info-util-0.1.0.0"-sysconfdir = "/home/hugin/.cabal/etc"--getBinDir     = catchIO (getEnv "info_util_bindir")     (\_ -> return bindir)-getLibDir     = catchIO (getEnv "info_util_libdir")     (\_ -> return libdir)-getDynLibDir  = catchIO (getEnv "info_util_dynlibdir")  (\_ -> return dynlibdir)-getDataDir    = catchIO (getEnv "info_util_datadir")    (\_ -> return datadir)-getLibexecDir = catchIO (getEnv "info_util_libexecdir") (\_ -> return libexecdir)-getSysconfDir = catchIO (getEnv "info_util_sysconfdir") (\_ -> return sysconfdir)----joinFileName :: String -> String -> FilePath-joinFileName ""  fname = fname-joinFileName "." fname = fname-joinFileName dir ""    = dir-joinFileName dir@(c:cs) fname-  | isPathSeparator (lastChar c cs) = dir ++ fname-  | otherwise                       = dir ++ pathSeparator : fname- where-  -- We do not use Data.List.NonEmpty.last, as that would limit the module to-  -- base >= 4.9.0.0 (GHC >= 8.0.1).-  lastChar x [] = x-  lastChar _ (x:xs) = lastChar x xs--pathSeparator :: Char-pathSeparator = '/'--isPathSeparator :: Char -> Bool-isPathSeparator c = c == '/'
− plugins/hls-eval-plugin/test/dist-newstyle/build/x86_64-linux/ghc-9.10.3/test-0.1.0.0/build/autogen/PackageInfo_test.hs
@@ -1,46 +0,0 @@-{-# LANGUAGE NoRebindableSyntax #-}-{-# OPTIONS_GHC -Wno-missing-import-lists #-}-{-# OPTIONS_GHC -w #-}--{-|-Module      : PackageInfo_test-Description : Contents of some of the package's Cabal file's fields.--WARNING: This module was generated by Cabal. Any modifications will be-overwritten if the module is regenerated.--This module exports values that record information from some of the fields of-the package's Cabal package description file (Cabal file).--For further information about the fields in a Cabal file, see the Cabal User-Guide.--}--module PackageInfo_test (-    name,-    version,-    synopsis,-    copyright,-    homepage,-  ) where--import Data.Version (Version(..))-import Prelude---- |The content of the @name@ field of the package's Cabal file, but with any--- hyphen characters replaced by underscore characters.-name :: String-name = "test"--- |The content of the @version@ field of the package's Cabal file.-version :: Version-version = Version [0,1,0,0] []---- |The content of the @synopsis@ field of the package's Cabal file.-synopsis :: String-synopsis = ""--- |The content of the @copyright@ field of the package's Cabal file.-copyright :: String-copyright = "2017 Author name here"--- |The content of the @homepage@ field of the package's Cabal file.-homepage :: String-homepage = ""
− plugins/hls-eval-plugin/test/dist-newstyle/build/x86_64-linux/ghc-9.10.3/test-0.1.0.0/build/autogen/Paths_test.hs
@@ -1,132 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE NoRebindableSyntax #-}-#if __GLASGOW_HASKELL__ >= 810-{-# OPTIONS_GHC -Wno-prepositive-qualified-module #-}-#endif-{-# OPTIONS_GHC -Wno-missing-import-lists #-}-{-# OPTIONS_GHC -w #-}--{-|-Module      : Paths_test-Description : Data file location, and package version and installation-              directories.--WARNING: This module was generated by Cabal. Any modifications will be-overwritten if the module is regenerated.--This module exports a function to locate data files, and values that record-the version of the package and some directories which the package has been-configured to be installed into.--For further information about Cabal's options for its configuration step, and-their default values, see the Cabal User Guide.--}--module Paths_test (-    version,-    getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir,-    getDataFileName, getSysconfDir-  ) where---import qualified Control.Exception as Exception-import Data.Version (Version(..))-import System.Environment (getEnv)-import Prelude---#if defined(VERSION_base)--#if MIN_VERSION_base(4,0,0)-catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a-#else-catchIO :: IO a -> (Exception.Exception -> IO a) -> IO a-#endif--#else-catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a-#endif-catchIO = Exception.catch---- |The package version.-version :: Version-version = Version [0,1,0,0] []---- |If the argument is a filename, the result is the name of a corresponding--- file on the system on which the program is running, if the file were listed--- in the @data-files@ field of the package's Cabal package description file.--- No check is performed that the given filename is listed in that field.-getDataFileName :: FilePath -> IO FilePath-getDataFileName name = do-  dir <- getDataDir-  return (dir `joinFileName` name)---- |The location of the directory specified by Cabal's @--bindir@ option (where--- executables that the user might invoke are installed). This can be overridden--- at runtime using the environment variable test_bindir.-getBinDir :: IO FilePath---- |The location of the directory specified by Cabal's @--libdir@ option (where--- object libraries are installed). This can be overridden at runtime using the--- environment variable test_libdir.-getLibDir :: IO FilePath---- |The location of the directory specified by Cabal's @--dynlibdir@ option--- (where dynamic libraries are installed). This can be overridden at runtime--- using the environment variable test_dynlibdir.-getDynLibDir :: IO FilePath---- |The location of the directory specified by Cabal's @--datadir@ option (where--- architecture-independent data files are installed). This can be overridden at--- runtime using the environment variable test_datadir.-getDataDir :: IO FilePath---- |The location of the directory specified by Cabal's @--libexedir@ option--- (where executables that are not expected to be invoked directly by the user--- are installed). This can be overridden at runtime using the environment--- variable test_libexedir.-getLibexecDir :: IO FilePath---- |The location of the directory specified by Cabal's @--sysconfdir@ option--- (where configuration files are installed). This can be overridden at runtime--- using the environment variable test_sysconfdir.-getSysconfDir :: IO FilePath-----bindir, libdir, dynlibdir, datadir, libexecdir, sysconfdir :: FilePath-bindir     = "/home/hugin/.cabal/bin"-libdir     = "/home/hugin/.cabal/lib/x86_64-linux-ghc-9.10.3-415c/test-0.1.0.0-inplace"-dynlibdir  = "/home/hugin/.cabal/lib/x86_64-linux-ghc-9.10.3-415c"-datadir    = "/home/hugin/.cabal/share/x86_64-linux-ghc-9.10.3-415c/test-0.1.0.0"-libexecdir = "/home/hugin/.cabal/libexec/x86_64-linux-ghc-9.10.3-415c/test-0.1.0.0"-sysconfdir = "/home/hugin/.cabal/etc"--getBinDir     = catchIO (getEnv "test_bindir")     (\_ -> return bindir)-getLibDir     = catchIO (getEnv "test_libdir")     (\_ -> return libdir)-getDynLibDir  = catchIO (getEnv "test_dynlibdir")  (\_ -> return dynlibdir)-getDataDir    = catchIO (getEnv "test_datadir")    (\_ -> return datadir)-getLibexecDir = catchIO (getEnv "test_libexecdir") (\_ -> return libexecdir)-getSysconfDir = catchIO (getEnv "test_sysconfdir") (\_ -> return sysconfdir)----joinFileName :: String -> String -> FilePath-joinFileName ""  fname = fname-joinFileName "." fname = fname-joinFileName dir ""    = dir-joinFileName dir@(c:cs) fname-  | isPathSeparator (lastChar c cs) = dir ++ fname-  | otherwise                       = dir ++ pathSeparator : fname- where-  -- We do not use Data.List.NonEmpty.last, as that would limit the module to-  -- base >= 4.9.0.0 (GHC >= 8.0.1).-  lastChar x [] = x-  lastChar _ (x:xs) = lastChar x xs--pathSeparator :: Char-pathSeparator = '/'--isPathSeparator :: Char -> Bool-isPathSeparator c = c == '/'
− plugins/hls-eval-plugin/test/dist-newstyle/build/x86_64-linux/ghc-9.14.1/info-util-0.1.0.0/build/autogen/PackageInfo_info_util.hs
@@ -1,46 +0,0 @@-{-# LANGUAGE NoRebindableSyntax #-}-{-# OPTIONS_GHC -Wno-missing-import-lists #-}-{-# OPTIONS_GHC -w #-}--{-|-Module      : PackageInfo_info_util-Description : Contents of some of the package's Cabal file's fields.--WARNING: This module was generated by Cabal. Any modifications will be-overwritten if the module is regenerated.--This module exports values that record information from some of the fields of-the package's Cabal package description file (Cabal file).--For further information about the fields in a Cabal file, see the Cabal User-Guide.--}--module PackageInfo_info_util (-    name,-    version,-    synopsis,-    copyright,-    homepage,-  ) where--import Data.Version (Version(..))-import Prelude---- |The content of the @name@ field of the package's Cabal file, but with any--- hyphen characters replaced by underscore characters.-name :: String-name = "info_util"--- |The content of the @version@ field of the package's Cabal file.-version :: Version-version = Version [0,1,0,0] []---- |The content of the @synopsis@ field of the package's Cabal file.-synopsis :: String-synopsis = ""--- |The content of the @copyright@ field of the package's Cabal file.-copyright :: String-copyright = "2017 Author name here"--- |The content of the @homepage@ field of the package's Cabal file.-homepage :: String-homepage = ""
− plugins/hls-eval-plugin/test/dist-newstyle/build/x86_64-linux/ghc-9.14.1/info-util-0.1.0.0/build/autogen/Paths_info_util.hs
@@ -1,132 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE NoRebindableSyntax #-}-#if __GLASGOW_HASKELL__ >= 810-{-# OPTIONS_GHC -Wno-prepositive-qualified-module #-}-#endif-{-# OPTIONS_GHC -Wno-missing-import-lists #-}-{-# OPTIONS_GHC -w #-}--{-|-Module      : Paths_info_util-Description : Data file location, and package version and installation-              directories.--WARNING: This module was generated by Cabal. Any modifications will be-overwritten if the module is regenerated.--This module exports a function to locate data files, and values that record-the version of the package and some directories which the package has been-configured to be installed into.--For further information about Cabal's options for its configuration step, and-their default values, see the Cabal User Guide.--}--module Paths_info_util (-    version,-    getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir,-    getDataFileName, getSysconfDir-  ) where---import qualified Control.Exception as Exception-import Data.Version (Version(..))-import System.Environment (getEnv)-import Prelude---#if defined(VERSION_base)--#if MIN_VERSION_base(4,0,0)-catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a-#else-catchIO :: IO a -> (Exception.Exception -> IO a) -> IO a-#endif--#else-catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a-#endif-catchIO = Exception.catch---- |The package version.-version :: Version-version = Version [0,1,0,0] []---- |If the argument is a filename, the result is the name of a corresponding--- file on the system on which the program is running, if the file were listed--- in the @data-files@ field of the package's Cabal package description file.--- No check is performed that the given filename is listed in that field.-getDataFileName :: FilePath -> IO FilePath-getDataFileName name = do-  dir <- getDataDir-  return (dir `joinFileName` name)---- |The location of the directory specified by Cabal's @--bindir@ option (where--- executables that the user might invoke are installed). This can be overridden--- at runtime using the environment variable info_util_bindir.-getBinDir :: IO FilePath---- |The location of the directory specified by Cabal's @--libdir@ option (where--- object libraries are installed). This can be overridden at runtime using the--- environment variable info_util_libdir.-getLibDir :: IO FilePath---- |The location of the directory specified by Cabal's @--dynlibdir@ option--- (where dynamic libraries are installed). This can be overridden at runtime--- using the environment variable info_util_dynlibdir.-getDynLibDir :: IO FilePath---- |The location of the directory specified by Cabal's @--datadir@ option (where--- architecture-independent data files are installed). This can be overridden at--- runtime using the environment variable info_util_datadir.-getDataDir :: IO FilePath---- |The location of the directory specified by Cabal's @--libexedir@ option--- (where executables that are not expected to be invoked directly by the user--- are installed). This can be overridden at runtime using the environment--- variable info_util_libexedir.-getLibexecDir :: IO FilePath---- |The location of the directory specified by Cabal's @--sysconfdir@ option--- (where configuration files are installed). This can be overridden at runtime--- using the environment variable info_util_sysconfdir.-getSysconfDir :: IO FilePath-----bindir, libdir, dynlibdir, datadir, libexecdir, sysconfdir :: FilePath-bindir     = "/home/hugin/.cabal/bin"-libdir     = "/home/hugin/.cabal/lib/x86_64-linux-ghc-9.14.1-ade7/info-util-0.1.0.0-inplace"-dynlibdir  = "/home/hugin/.cabal/lib/x86_64-linux-ghc-9.14.1-ade7"-datadir    = "/home/hugin/.cabal/share/x86_64-linux-ghc-9.14.1-ade7/info-util-0.1.0.0"-libexecdir = "/home/hugin/.cabal/libexec/x86_64-linux-ghc-9.14.1-ade7/info-util-0.1.0.0"-sysconfdir = "/home/hugin/.cabal/etc"--getBinDir     = catchIO (getEnv "info_util_bindir")     (\_ -> return bindir)-getLibDir     = catchIO (getEnv "info_util_libdir")     (\_ -> return libdir)-getDynLibDir  = catchIO (getEnv "info_util_dynlibdir")  (\_ -> return dynlibdir)-getDataDir    = catchIO (getEnv "info_util_datadir")    (\_ -> return datadir)-getLibexecDir = catchIO (getEnv "info_util_libexecdir") (\_ -> return libexecdir)-getSysconfDir = catchIO (getEnv "info_util_sysconfdir") (\_ -> return sysconfdir)----joinFileName :: String -> String -> FilePath-joinFileName ""  fname = fname-joinFileName "." fname = fname-joinFileName dir ""    = dir-joinFileName dir@(c:cs) fname-  | isPathSeparator (lastChar c cs) = dir ++ fname-  | otherwise                       = dir ++ pathSeparator : fname- where-  -- We do not use Data.List.NonEmpty.last, as that would limit the module to-  -- base >= 4.9.0.0 (GHC >= 8.0.1).-  lastChar x [] = x-  lastChar _ (x:xs) = lastChar x xs--pathSeparator :: Char-pathSeparator = '/'--isPathSeparator :: Char -> Bool-isPathSeparator c = c == '/'
− plugins/hls-eval-plugin/test/dist-newstyle/build/x86_64-linux/ghc-9.14.1/test-0.1.0.0/build/autogen/PackageInfo_test.hs
@@ -1,46 +0,0 @@-{-# LANGUAGE NoRebindableSyntax #-}-{-# OPTIONS_GHC -Wno-missing-import-lists #-}-{-# OPTIONS_GHC -w #-}--{-|-Module      : PackageInfo_test-Description : Contents of some of the package's Cabal file's fields.--WARNING: This module was generated by Cabal. Any modifications will be-overwritten if the module is regenerated.--This module exports values that record information from some of the fields of-the package's Cabal package description file (Cabal file).--For further information about the fields in a Cabal file, see the Cabal User-Guide.--}--module PackageInfo_test (-    name,-    version,-    synopsis,-    copyright,-    homepage,-  ) where--import Data.Version (Version(..))-import Prelude---- |The content of the @name@ field of the package's Cabal file, but with any--- hyphen characters replaced by underscore characters.-name :: String-name = "test"--- |The content of the @version@ field of the package's Cabal file.-version :: Version-version = Version [0,1,0,0] []---- |The content of the @synopsis@ field of the package's Cabal file.-synopsis :: String-synopsis = ""--- |The content of the @copyright@ field of the package's Cabal file.-copyright :: String-copyright = "2017 Author name here"--- |The content of the @homepage@ field of the package's Cabal file.-homepage :: String-homepage = ""
− plugins/hls-eval-plugin/test/dist-newstyle/build/x86_64-linux/ghc-9.14.1/test-0.1.0.0/build/autogen/Paths_test.hs
@@ -1,132 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE NoRebindableSyntax #-}-#if __GLASGOW_HASKELL__ >= 810-{-# OPTIONS_GHC -Wno-prepositive-qualified-module #-}-#endif-{-# OPTIONS_GHC -Wno-missing-import-lists #-}-{-# OPTIONS_GHC -w #-}--{-|-Module      : Paths_test-Description : Data file location, and package version and installation-              directories.--WARNING: This module was generated by Cabal. Any modifications will be-overwritten if the module is regenerated.--This module exports a function to locate data files, and values that record-the version of the package and some directories which the package has been-configured to be installed into.--For further information about Cabal's options for its configuration step, and-their default values, see the Cabal User Guide.--}--module Paths_test (-    version,-    getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir,-    getDataFileName, getSysconfDir-  ) where---import qualified Control.Exception as Exception-import Data.Version (Version(..))-import System.Environment (getEnv)-import Prelude---#if defined(VERSION_base)--#if MIN_VERSION_base(4,0,0)-catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a-#else-catchIO :: IO a -> (Exception.Exception -> IO a) -> IO a-#endif--#else-catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a-#endif-catchIO = Exception.catch---- |The package version.-version :: Version-version = Version [0,1,0,0] []---- |If the argument is a filename, the result is the name of a corresponding--- file on the system on which the program is running, if the file were listed--- in the @data-files@ field of the package's Cabal package description file.--- No check is performed that the given filename is listed in that field.-getDataFileName :: FilePath -> IO FilePath-getDataFileName name = do-  dir <- getDataDir-  return (dir `joinFileName` name)---- |The location of the directory specified by Cabal's @--bindir@ option (where--- executables that the user might invoke are installed). This can be overridden--- at runtime using the environment variable test_bindir.-getBinDir :: IO FilePath---- |The location of the directory specified by Cabal's @--libdir@ option (where--- object libraries are installed). This can be overridden at runtime using the--- environment variable test_libdir.-getLibDir :: IO FilePath---- |The location of the directory specified by Cabal's @--dynlibdir@ option--- (where dynamic libraries are installed). This can be overridden at runtime--- using the environment variable test_dynlibdir.-getDynLibDir :: IO FilePath---- |The location of the directory specified by Cabal's @--datadir@ option (where--- architecture-independent data files are installed). This can be overridden at--- runtime using the environment variable test_datadir.-getDataDir :: IO FilePath---- |The location of the directory specified by Cabal's @--libexedir@ option--- (where executables that are not expected to be invoked directly by the user--- are installed). This can be overridden at runtime using the environment--- variable test_libexedir.-getLibexecDir :: IO FilePath---- |The location of the directory specified by Cabal's @--sysconfdir@ option--- (where configuration files are installed). This can be overridden at runtime--- using the environment variable test_sysconfdir.-getSysconfDir :: IO FilePath-----bindir, libdir, dynlibdir, datadir, libexecdir, sysconfdir :: FilePath-bindir     = "/home/hugin/.cabal/bin"-libdir     = "/home/hugin/.cabal/lib/x86_64-linux-ghc-9.14.1-ade7/test-0.1.0.0-inplace"-dynlibdir  = "/home/hugin/.cabal/lib/x86_64-linux-ghc-9.14.1-ade7"-datadir    = "/home/hugin/.cabal/share/x86_64-linux-ghc-9.14.1-ade7/test-0.1.0.0"-libexecdir = "/home/hugin/.cabal/libexec/x86_64-linux-ghc-9.14.1-ade7/test-0.1.0.0"-sysconfdir = "/home/hugin/.cabal/etc"--getBinDir     = catchIO (getEnv "test_bindir")     (\_ -> return bindir)-getLibDir     = catchIO (getEnv "test_libdir")     (\_ -> return libdir)-getDynLibDir  = catchIO (getEnv "test_dynlibdir")  (\_ -> return dynlibdir)-getDataDir    = catchIO (getEnv "test_datadir")    (\_ -> return datadir)-getLibexecDir = catchIO (getEnv "test_libexecdir") (\_ -> return libexecdir)-getSysconfDir = catchIO (getEnv "test_sysconfdir") (\_ -> return sysconfdir)----joinFileName :: String -> String -> FilePath-joinFileName ""  fname = fname-joinFileName "." fname = fname-joinFileName dir ""    = dir-joinFileName dir@(c:cs) fname-  | isPathSeparator (lastChar c cs) = dir ++ fname-  | otherwise                       = dir ++ pathSeparator : fname- where-  -- We do not use Data.List.NonEmpty.last, as that would limit the module to-  -- base >= 4.9.0.0 (GHC >= 8.0.1).-  lastChar x [] = x-  lastChar _ (x:xs) = lastChar x xs--pathSeparator :: Char-pathSeparator = '/'--isPathSeparator :: Char -> Bool-isPathSeparator c = c == '/'
plugins/hls-explicit-record-fields-plugin/src/Ide/Plugin/ExplicitFields.hs view
@@ -19,10 +19,12 @@ import           Control.Monad.Trans.Class            (lift) import           Control.Monad.Trans.Maybe import           Data.Aeson                           (ToJSON (toJSON))+import           Data.Function                        (on) import           Data.Generics                        (GenericQ, everything,                                                        everythingBut, extQ, mkQ) import qualified Data.IntMap.Strict                   as IntMap-import           Data.List                            (find, intersperse)+import           Data.List                            (find, intersperse,+                                                       sortOn) import qualified Data.Map                             as Map import           Data.Maybe                           (fromMaybe, isJust,                                                        mapMaybe, maybeToList)@@ -54,19 +56,22 @@                                                        GenLocated (L), GhcPass,                                                        GhcTc,                                                        HasSrcSpan (getLoc),+                                                       HsBindLR (..),                                                        HsConDetails (RecCon),                                                        HsExpr (HsApp, HsVar, XExpr),                                                        HsFieldBind (hfbLHS),                                                        HsRecFields (..),-                                                       HsWrap (HsWrap), LPat,-                                                       Located,+                                                       HsWrap (HsWrap), LHsBind,+                                                       LPat, Located,+                                                       MatchGroup (..),+                                                       MatchGroupTc (..),                                                        NamedThing (getName),                                                        Outputable,                                                        TcGblEnv (tcg_binds),                                                        Var (varName),                                                        XXExprGhcTc (..),                                                        conLikeFieldLabels,-                                                       nameSrcSpan,+                                                       isGenerated, nameSrcSpan,                                                        pprNameUnqualified,                                                        recDotDot, unLoc) import           Development.IDE.GHC.Compat.Core      (Extension (NamedFieldPuns),@@ -99,6 +104,7 @@ import           Ide.Plugin.RangeMap                  (RangeMap) import qualified Ide.Plugin.RangeMap                  as RangeMap import           Ide.Plugin.Resolve                   (mkCodeActionWithResolveAndCommand)+import           Ide.PluginUtils                      (subRange) import           Ide.Types                            (PluginDescriptor (..),                                                        PluginId (..),                                                        PluginMethodHandler,@@ -149,25 +155,45 @@   , pluginRules = collectRecordsRule recorder *> collectNamesRule   } +data RecordConversionType+  = RecordWildcardExpansion+  | RecordTraditionalSyntaxConversion++data RecordConversion =+  RecordConversion+    Int -- ^ uid+    RecordConversionType++-- | Given a record, determine whether it is a case of wildcard expansion+-- or a conversion to the traditional record syntax.+getConversionType :: RecordInfo -> Maybe RecordConversionType+getConversionType = \case+  -- Only fully saturated constructor applications can be converted to+  -- the record syntax through the code action+  RecordInfoApp _ (RecordAppExpr Unsaturated _ _) -> Nothing+  RecordInfoApp {} -> Just RecordTraditionalSyntaxConversion+  _ -> Just RecordWildcardExpansion+ codeActionProvider :: PluginMethodHandler IdeState 'Method_TextDocumentCodeAction codeActionProvider ideState _ (CodeActionParams _ _ docId range _) = do   nfp <- getNormalizedFilePathE (docId ^. L.uri)   CRR {crCodeActions, crCodeActionResolve, enabledExtensions} <- runActionE "ExplicitFields.CollectRecords" ideState $ useE CollectRecords nfp   -- All we need to build a code action is the list of extensions, and a int to   -- allow us to resolve it later.-  let recordUids = [ uid+  let recordsWithUid = [ (RecordConversion uid conversionType, record)                       | uid <- RangeMap.filterByRange range crCodeActions                       , Just record <- [IntMap.lookup uid crCodeActionResolve]-                      -- Only fully saturated constructor applications can be-                      -- converted to the record syntax through the code action-                      , isConvertible record+                      , Just conversionType <- [getConversionType record]                       ]-  let actions = map (mkCodeAction enabledExtensions) recordUids-  pure $ InL actions+      recordsOnly = map snd recordsWithUid+      sortedRecords = sortOn (recordDepth recordsOnly . snd) recordsWithUid+  pure $ InL $ case sortedRecords of+    (top : _) -> [mkCodeAction enabledExtensions (fst top)]+    []        -> []   where-    mkCodeAction :: [Extension] -> Int -> Command |? CodeAction-    mkCodeAction exts uid = InR CodeAction-      { _title = mkTitle exts -- TODO: `Expand positional record` without NamedFieldPuns if RecordInfoApp+    mkCodeAction :: [Extension] -> RecordConversion -> Command |? CodeAction+    mkCodeAction exts (RecordConversion uid conversionType) = InR CodeAction+      { _title = mkTitle exts conversionType       , _kind = Just CodeActionKind_RefactorRewrite       , _diagnostics = Nothing       , _isPreferred = Nothing@@ -177,11 +203,6 @@       , _data_ = Just $ toJSON uid       } -    isConvertible :: RecordInfo -> Bool-    isConvertible = \case-      RecordInfoApp _ (RecordAppExpr Unsaturated _ _) -> False-      _ -> True- codeActionResolveProvider :: ResolveFunction IdeState Int 'Method_CodeActionResolve codeActionResolveProvider ideState pId ca uri uid = do   nfp <- getNormalizedFilePathE uri@@ -246,7 +267,7 @@                           , _label = InR label                           , _kind = Nothing -- neither a type nor a parameter                           , _textEdits = Just textEdits -- same as CodeAction-                          , _tooltip = Just $ InL (mkTitle enabledExtensions) -- same as CodeAction+                          , _tooltip = Just $ InL (mkTitle enabledExtensions RecordWildcardExpansion) -- same as CodeAction                           , _paddingLeft = Just True -- padding after dotdot                           , _paddingRight = Nothing                           , _data_ = Nothing@@ -266,9 +287,12 @@     pure $ InL (concatMap (mkInlayHints nameMap pm) records)    where      mkInlayHints :: UniqFM Name [Name] -> PositionMapping -> RecordInfo -> [InlayHint]-     mkInlayHints nameMap pm record@(RecordInfoApp _ (RecordAppExpr _ _ fla)) =-       let textEdits = renderRecordInfoAsTextEdit nameMap record-       in mapMaybe (mkInlayHint textEdits pm) fla+     mkInlayHints nameMap pm record@(RecordInfoApp _ (RecordAppExpr sat _ fla)) =+       -- Only create inlay hints for fully saturated constructors+       case sat of+         Saturated -> let textEdits = renderRecordInfoAsTextEdit nameMap record+                      in mapMaybe (mkInlayHint textEdits pm) fla+         Unsaturated -> []      mkInlayHints _ _ _ = []       mkInlayHint :: Maybe TextEdit -> PositionMapping -> (Located FieldLabel, HsExpr GhcTc) -> Maybe InlayHint@@ -282,7 +306,7 @@                         , _label = InR $ pure (mkInlayHintLabelPart name fieldDefLoc)                         , _kind = Nothing -- neither a type nor a parameter                         , _textEdits = Just (maybeToList te) -- same as CodeAction-                        , _tooltip = Just $ InL "Expand positional record" -- same as CodeAction+                        , _tooltip = Just $ InL (mkTitle [] RecordTraditionalSyntaxConversion) -- same as CodeAction                         , _paddingLeft = Nothing                         , _paddingRight = Nothing                         , _data_ = Nothing@@ -290,12 +314,22 @@       mkInlayHintLabelPart name loc = InlayHintLabelPart (printFieldName (pprNameUnqualified name) <> "=") Nothing loc Nothing -mkTitle :: [Extension] -> Text-mkTitle exts = "Expand record wildcard"-                <> if NamedFieldPuns `elem` exts-                   then mempty-                   else " (needs extension: NamedFieldPuns)"+mkTitle :: [Extension] -> RecordConversionType -> Text+mkTitle exts = \case+  RecordWildcardExpansion ->+    "Expand record wildcard"+      <> if NamedFieldPuns `elem` exts+         then mempty+         else " (needs extension: NamedFieldPuns)"+  RecordTraditionalSyntaxConversion ->+    "Convert to traditional record syntax" +-- Calculate the nesting depth of a record by counting how many other records+-- contain it. Used to prioritize more deeply nested records in code actions.+recordDepth :: [RecordInfo] -> RecordInfo -> Int+recordDepth allRecords record =+  let isSubrangeOf = subRange `on` recordInfoToRange+  in length $ filter (`isSubrangeOf` record) allRecords  pragmaEdit :: [Extension] -> NextPragmaInfo -> Maybe TextEdit pragmaEdit exts pragma = if NamedFieldPuns `elem` exts@@ -565,8 +599,16 @@   where showFieldWithArg (field, arg) = printFieldName field <> " = " <> printOutputable arg  collectRecords :: GenericQ [RecordInfo]-collectRecords = everythingBut (<>) (([], False) `mkQ` getRecPatterns `extQ` getRecCons)+collectRecords = everythingBut (<>) (([], False) `mkQ` ignoreGenerated `extQ` getRecPatterns `extQ` getRecCons) +-- | Prevent the SYB traversal to descend further if the matching group+-- in question is compiler-generated (e.g. TH, deriving). Doing so is necessary+-- in order to not create inlay hints for TH-generated records.+ignoreGenerated :: LHsBind GhcTc -> ([RecordInfo], Bool)+ignoreGenerated (unLoc -> FunBind _ _ (MG (MatchGroupTc _ _ origin) _))+  | isGenerated origin = ([], True)+ignoreGenerated _ = ([], False)+ -- | Collect 'Name's into a map, indexed by the names' unique identifiers. -- The 'Eq' instance of 'Name's makes use of their unique identifiers, hence -- any 'Name' referring to the same entity is considered equal. In effect,@@ -602,7 +644,11 @@ getRecCons expr@(unLoc -> app@(HsApp _ _ _)) =   let fieldss = maybeToList $ getFields app []       recInfo = concatMap mkRecInfo fieldss-  in (recInfo, not (null recInfo))+  -- Search control for positional constructors.+  -- True stops further (nested) searching; False allows recursive search.+  -- Currently hardcoded to False to enable nested positional searches.+  -- Use `in (recInfo, not (null recInfo))` to disable nested searching.+  in (recInfo, False)   where     mkRecInfo :: RecordAppExpr -> [RecordInfo]     mkRecInfo appExpr =
plugins/hls-explicit-record-fields-plugin/test/Main.hs view
@@ -22,22 +22,24 @@ test :: TestTree test = testGroup "explicit-fields"   [ testGroup "code actions"-    [ mkTest "WildcardOnly" "WildcardOnly" 12 10 12 20-    , mkTest "Unused" "Unused" 12 10 12 20-    , mkTest "Unused2" "Unused2" 12 10 12 20-    , mkTest "WithPun" "WithPun" 13 10 13 25-    , mkTest "WithExplicitBind" "WithExplicitBind" 12 10 12 32-    , mkTest "Mixed" "Mixed" 14 10 14 37-    , mkTest "Construction" "Construction" 16 5 16 15-    , mkTest "PositionalConstruction" "PositionalConstruction" 15 5 15 15-    , mkTest "HsExpanded1" "HsExpanded1" 17 10 17 20-    , mkTest "HsExpanded2" "HsExpanded2" 23 10 23 22+    [ mkExpansionTest "WildcardOnly" "WildcardOnly" 12 10 12 20+    , mkExpansionTest "Unused" "Unused" 12 10 12 20+    , mkExpansionTest "Unused2" "Unused2" 12 10 12 20+    , mkExpansionTest "WithPun" "WithPun" 13 10 13 25+    , mkExpansionTest "WithExplicitBind" "WithExplicitBind" 12 10 12 32+    , mkExpansionTest "Mixed" "Mixed" 14 10 14 37+    , mkExpansionTest "Construction" "Construction" 16 5 16 15+    , mkConversionTest "PositionalConstruction" "PositionalConstruction" 15 5 15 15+    , mkExpansionTest "HsExpanded1" "HsExpanded1" 17 10 17 20+    , mkExpansionTest "HsExpanded2" "HsExpanded2" 23 10 23 22     , mkTestNoAction "ExplicitBinds" "ExplicitBinds" 11 10 11 52     , mkTestNoAction "Puns" "Puns" 12 10 12 31     , mkTestNoAction "Infix" "Infix" 11 11 11 31     , mkTestNoAction "Prefix" "Prefix" 10 11 10 28     , mkTestNoAction "PartiallyAppliedCon" "PartiallyAppliedCon" 7 8 7 12-    , mkTest "PolymorphicRecordConstruction" "PolymorphicRecordConstruction" 15 5 15 15+    , mkConversionTest "PolymorphicRecordConstruction" "PolymorphicRecordConstruction" 15 5 15 15+    , mkConversionTest "CursorAwarePositional" "CursorPositional" 15 26 15 34+    , mkExpansionTest "CursorAwareRecords" "CursorRecords" 9 40 9 40     ]   , testGroup "inlay hints"     [ mkInlayHintsTest "Construction" Nothing 16 $ \ih -> do@@ -84,19 +86,19 @@           [ defInlayHint { _position = Position 15 11                          , _label = InR [ foo ]                          , _textEdits = Just [ mkLineTextEdit "MyRec { foo = a, bar = b, baz = c }" 15 5 16 ]-                         , _tooltip = Just $ InL "Expand positional record"+                         , _tooltip = Just $ InL "Convert to traditional record syntax"                          , _paddingLeft = Nothing                          }           , defInlayHint { _position = Position 15 13                          , _label = InR [ bar ]                          , _textEdits = Just [ mkLineTextEdit "MyRec { foo = a, bar = b, baz = c }" 15 5 16 ]-                         , _tooltip = Just $ InL "Expand positional record"+                         , _tooltip = Just $ InL "Convert to traditional record syntax"                          , _paddingLeft = Nothing                          }           , defInlayHint { _position = Position 15 15                          , _label = InR [ baz ]                          , _textEdits = Just [ mkLineTextEdit "MyRec { foo = a, bar = b, baz = c }" 15 5 16 ]-                         , _tooltip = Just $ InL "Expand positional record"+                         , _tooltip = Just $ InL "Convert to traditional record syntax"                          , _paddingLeft = Nothing                          }           ]@@ -109,19 +111,19 @@           [ defInlayHint { _position = Position 15 11                          , _label = InR [ foo ]                          , _textEdits = Just [ mkLineTextEdit "MyRec { foo = a, bar = b, baz = c }" 15 5 16 ]-                         , _tooltip = Just $ InL "Expand positional record"+                         , _tooltip = Just $ InL "Convert to traditional record syntax"                          , _paddingLeft = Nothing                          }           , defInlayHint { _position = Position 15 13                          , _label = InR [ bar ]                          , _textEdits = Just [ mkLineTextEdit "MyRec { foo = a, bar = b, baz = c }" 15 5 16 ]-                         , _tooltip = Just $ InL "Expand positional record"+                         , _tooltip = Just $ InL "Convert to traditional record syntax"                          , _paddingLeft = Nothing                          }           , defInlayHint { _position = Position 15 15                          , _label = InR [ baz ]                          , _textEdits = Just [ mkLineTextEdit "MyRec { foo = a, bar = b, baz = c }" 15 5 16 ]-                         , _tooltip = Just $ InL "Expand positional record"+                         , _tooltip = Just $ InL "Convert to traditional record syntax"                          , _paddingLeft = Nothing                          }           ]@@ -142,7 +144,7 @@           [defInlayHint { _position = Position 13 21                         , _label = InR [ foo ]                         , _textEdits = Just [ mkLineTextEdit "MyRec { foo = 5 }" 13 15 22 ]-                        , _tooltip = Just $ InL "Expand positional record"+                        , _tooltip = Just $ InL "Convert to traditional record syntax"                         , _paddingLeft = Nothing                         }]     , mkInlayHintsTest "HsExpanded1DuplicateRecordFields" (Just " (positional)") 13 $ \ih -> do@@ -152,7 +154,7 @@           [defInlayHint { _position = Position 13 21                         , _label = InR [ foo ]                         , _textEdits = Just [ mkLineTextEdit "MyRec { foo = 5 }" 13 15 22 ]-                        , _tooltip = Just $ InL "Expand positional record"+                        , _tooltip = Just $ InL "Convert to traditional record syntax"                         , _paddingLeft = Nothing                         }]     , mkInlayHintsTest "HsExpanded2" Nothing 23 $ \ih -> do@@ -172,7 +174,7 @@           [defInlayHint { _position = Position 16 21                         , _label = InR [ foo ]                         , _textEdits = Just [ mkLineTextEdit "MyRec { foo = 5 }" 16 15 22 ]-                        , _tooltip = Just $ InL "Expand positional record"+                        , _tooltip = Just $ InL "Convert to traditional record syntax"                         , _paddingLeft = Nothing                         }]     , mkInlayHintsTest "Mixed" Nothing 14 $ \ih -> do@@ -276,25 +278,112 @@           [ defInlayHint { _position = Position 15 11                          , _label = InR [ foo ]                          , _textEdits = Just [ mkLineTextEdit "MyRec { foo = a, bar = b, baz = c }" 15 5 16 ]-                         , _tooltip = Just $ InL "Expand positional record"+                         , _tooltip = Just $ InL "Convert to traditional record syntax"                          , _paddingLeft = Nothing                          }           , defInlayHint { _position = Position 15 13                          , _label = InR [ bar ]                          , _textEdits = Just [ mkLineTextEdit "MyRec { foo = a, bar = b, baz = c }" 15 5 16 ]-                         , _tooltip = Just $ InL "Expand positional record"+                         , _tooltip = Just $ InL "Convert to traditional record syntax"                          , _paddingLeft = Nothing                          }           , defInlayHint { _position = Position 15 15                          , _label = InR [ baz ]                          , _textEdits = Just [ mkLineTextEdit "MyRec { foo = a, bar = b, baz = c }" 15 5 16 ]-                         , _tooltip = Just $ InL "Expand positional record"+                         , _tooltip = Just $ InL "Convert to traditional record syntax"                          , _paddingLeft = Nothing                          }           ]+    , mkInlayHintsTest "CursorRecords" Nothing 9 $ \ih -> do+        let mkLabelPart' = mkLabelPartOffsetLength "CursorRecords"+        a0  <- mkLabelPart' 3 14 "a0"+        a1  <- mkLabelPart' 4 14 "a1"+        a11 <- mkLabelPart' 4 25 "a11"+        a2  <- mkLabelPart' 5 14 "a2"+        a3  <- mkLabelPart' 6 14 "a3"+        (@?=) ih+          [ defInlayHint+              { _position = Position 9 52+              , _label = InR [ a3 ]+              , _textEdits = Just [ mkLineTextEdit "L1 {l2 = L2 {l3 = L3 {l4 = L4 {..}, ..}, ..}, a3}" 9 5 53 ]+              , _tooltip = Just $ InL "Expand record wildcard"+              , _paddingLeft = Just True+              , _paddingRight = Nothing+              , _data_ = Nothing+              }+          , defInlayHint+              { _position = Position 9 47+              , _label = InR [ a2 ]+              , _textEdits = Just [ mkLineTextEdit "L2 {l3 = L3 {l4 = L4 {..}, ..}, a2}" 9 14 48 ]+              , _tooltip = Just $ InL "Expand record wildcard"+              , _paddingLeft = Just True+              , _paddingRight = Nothing+              , _data_ = Nothing+              }+          , defInlayHint+              { _position = Position 9 42+              , _label = InR [ a1 , InlayHintLabelPart ", " Nothing Nothing Nothing , a11 ]+              , _textEdits = Just [ mkLineTextEdit "L3 {l4 = L4 {..}, a1, a11}" 9 23 43 ]+              , _tooltip = Just $ InL "Expand record wildcard"+              , _paddingLeft = Just True+              , _paddingRight = Nothing+              , _data_ = Nothing+              }+          , defInlayHint+              { _position = Position 9 37+              , _label = InR [ a0 ]+              , _textEdits = Just [ mkLineTextEdit "L4 {a0}" 9 31 38 ]+              , _tooltip = Just $ InL "Expand record wildcard"+              , _paddingLeft = Just True+              , _paddingRight = Nothing+              , _data_ = Nothing+              }+          ]+    , mkInlayHintsTest "CursorPositional" Nothing 15 $ \ih -> do+        let mkLabelPart' = mkLabelPartOffsetLengthSub1 "CursorPositional"+        middle <- mkLabelPart' 2 2 "middle="+        inner  <- mkLabelPart' 6 2 "inner="+        foo    <- mkLabelPart' 10 2 "foo="+        bar    <- mkLabelPart' 11 4 "bar="+        (@?=) ih+          [ defInlayHint+              { _position = Position 15 14+              , _label = InR [ middle ]+              , _textEdits = Just [ mkLineTextEdit "RecOuter { middle = (RecMiddle (RecInner 'c' 42)) }" 15 5 43 ]+              , _tooltip = Just $ InL "Convert to traditional record syntax"+              , _paddingLeft = Nothing+              }+          , defInlayHint+              { _position = Position 15 25+              , _label = InR [ inner ]+              , _textEdits = Just [ mkLineTextEdit "RecMiddle { inner = (RecInner 'c' 42) }" 15 15 42 ]+              , _tooltip = Just $ InL "Convert to traditional record syntax"+              , _paddingLeft = Nothing+              }+          , defInlayHint+              { _position = Position 15 35+              , _label = InR [ foo ]+              , _textEdits =+                  Just [ mkLineTextEdit "RecInner { foo = 'c', bar = 42 }" 15 26 41 ]+              , _tooltip = Just $ InL "Convert to traditional record syntax"+              , _paddingLeft = Nothing+              }+          , defInlayHint+              { _position = Position 15 39+              , _label = InR [ bar ]+              , _textEdits =+                  Just [ mkLineTextEdit "RecInner { foo = 'c', bar = 42 }" 15 26 41 ]+              , _tooltip = Just $ InL "Convert to traditional record syntax"+              , _paddingLeft = Nothing+              }+          ]+    , mkInlayHintsTest "InlayHintTH" Nothing 6 $ assertNoInlayHints     ]   ] +assertNoInlayHints :: [InlayHint] -> Assertion+assertNoInlayHints = assertBool "There exists an inlay hint whereas there should have been none" . null+ mkInlayHintsTest :: FilePath -> Maybe TestName -> UInt -> ([InlayHint] -> Assertion) -> TestTree mkInlayHintsTest fp postfix line assert =   testCase (fp ++ concat postfix) $@@ -310,34 +399,46 @@   testCase title $     runSessionWithServer def plugin (testDataDir </> "noop") $ do       doc <- openDoc (fp <.> "hs") "haskell"-      actions <- getExplicitFieldsActions doc x1 y1 x2 y2+      actions <- getActions isExplicitFieldsCodeAction doc x1 y1 x2 y2       liftIO $ actions @?= [] -mkTestWithCount :: Int -> TestName -> FilePath -> UInt -> UInt -> UInt -> UInt -> TestTree-mkTestWithCount cnt title fp x1 y1 x2 y2 =+mkTestWithCountAndMessage :: (CodeAction -> Bool) -> Int -> TestName -> FilePath -> UInt -> UInt -> UInt -> UInt -> TestTree+mkTestWithCountAndMessage msgPredicate cnt title fp x1 y1 x2 y2 =   goldenWithHaskellAndCaps def codeActionResolveCaps plugin title testDataDir fp "expected" "hs" $ \doc -> do-    acts@(act:_) <- getExplicitFieldsActions doc x1 y1 x2 y2+    acts@(act:_) <- getActions msgPredicate doc x1 y1 x2 y2     liftIO $ length acts @?= cnt     executeCodeAction act -mkTest :: TestName -> FilePath -> UInt -> UInt -> UInt -> UInt -> TestTree-mkTest = mkTestWithCount 1+mkExpansionTest :: TestName -> FilePath -> UInt -> UInt -> UInt -> UInt -> TestTree+mkExpansionTest = mkTestWithCountAndMessage isRecWildcardExpansionCodeAction 1 -getExplicitFieldsActions-  :: TextDocumentIdentifier+mkConversionTest :: TestName -> FilePath -> UInt -> UInt -> UInt -> UInt -> TestTree+mkConversionTest = mkTestWithCountAndMessage isRecLiteralConversionCodeAction 1++getActions+  :: (CodeAction -> Bool) -> TextDocumentIdentifier   -> UInt -> UInt -> UInt -> UInt   -> Session [CodeAction]-getExplicitFieldsActions doc x1 y1 x2 y2 =-  findExplicitFieldsAction <$> getAndResolveCodeActions doc range+getActions p doc x1 y1 x2 y2 =+  findAction p <$> getAndResolveCodeActions doc range   where     range = Range (Position x1 y1) (Position x2 y2) -findExplicitFieldsAction :: [a |? CodeAction] -> [CodeAction]-findExplicitFieldsAction = filter isExplicitFieldsCodeAction . rights . map toEither+findAction :: (CodeAction -> Bool) -> [a |? CodeAction] -> [CodeAction]+findAction p = filter p . rights . map toEither  isExplicitFieldsCodeAction :: CodeAction -> Bool-isExplicitFieldsCodeAction CodeAction {_title} =+isExplicitFieldsCodeAction act =+  isRecWildcardExpansionCodeAction act+    || isRecLiteralConversionCodeAction act++isRecWildcardExpansionCodeAction :: CodeAction -> Bool+isRecWildcardExpansionCodeAction CodeAction {_title} =   "Expand record wildcard" `T.isPrefixOf` _title++isRecLiteralConversionCodeAction :: CodeAction -> Bool+isRecLiteralConversionCodeAction CodeAction {_title} =+  "Convert to traditional record syntax" `T.isPrefixOf` _title  defInlayHint :: InlayHint defInlayHint =
+ plugins/hls-explicit-record-fields-plugin/test/testdata/CursorPositional.expected.hs view
@@ -0,0 +1,16 @@+module CursorPositional where+data RecOuter = RecOuter{+  middle :: RecMiddle+}++data RecMiddle = RecMiddle {+  inner :: RecInner+}++data RecInner = RecInner{+  foo   :: Char+  , bar :: Int+}++ex :: RecOuter+ex = RecOuter (RecMiddle (RecInner { foo = 'c', bar = 42 }))
+ plugins/hls-explicit-record-fields-plugin/test/testdata/CursorPositional.hs view
@@ -0,0 +1,16 @@+module CursorPositional where+data RecOuter = RecOuter{+  middle :: RecMiddle+}++data RecMiddle = RecMiddle {+  inner :: RecInner+}++data RecInner = RecInner{+  foo   :: Char+  , bar :: Int+}++ex :: RecOuter+ex = RecOuter (RecMiddle (RecInner 'c' 42))
+ plugins/hls-explicit-record-fields-plugin/test/testdata/CursorRecords.expected.hs view
@@ -0,0 +1,11 @@+{-# LANGUAGE RecordWildCards #-}+module CursorRecords where++data L4 = L4{ a0 :: Int}+data L3 = L3{ a1 :: Int, a11 :: Int, l4 :: L4}+data L2 = L2{ a2 :: Int, l3 :: L3}+data L1 = L1{ a3 :: Int, l2 :: L2}++test :: L1 -> Int+test L1 {l2 = L2{ l3 = L3 {l4 = L4 {..}, a1, a11}, ..}, ..} =+    a0 + a1 + a2 + a3 + a11
+ plugins/hls-explicit-record-fields-plugin/test/testdata/CursorRecords.hs view
@@ -0,0 +1,11 @@+{-# LANGUAGE RecordWildCards #-}+module CursorRecords where++data L4 = L4{ a0 :: Int}+data L3 = L3{ a1 :: Int, a11 :: Int, l4 :: L4}+data L2 = L2{ a2 :: Int, l3 :: L3}+data L1 = L1{ a3 :: Int, l2 :: L2}++test :: L1 -> Int+test L1 {l2 = L2{ l3 = L3{l4 = L4 {..}, ..}, ..}, ..} =+    a0 + a1 + a2 + a3 + a11
+ plugins/hls-explicit-record-fields-plugin/test/testdata/InlayHintTH.hs view
@@ -0,0 +1,7 @@+{-# LANGUAGE TemplateHaskell #-}++module InlayHintTH where++import TH (mkVal)++$(mkVal)
+ plugins/hls-explicit-record-fields-plugin/test/testdata/TH.hs view
@@ -0,0 +1,26 @@+{-# LANGUAGE TemplateHaskell #-}++module TH where++import Language.Haskell.TH++data MyRec+  = MyRec+  { foo :: Char+  , bar :: Int+  }++mkVal :: Q [Dec]+mkVal = do+  x <- newName "x"+  pure $+    [ ValD+        (VarP x)+        ( NormalB+            ( AppE+                (AppE (ConE 'MyRec) (LitE (CharL 'c')))+                (LitE (IntegerL 42))+            )+        )+        []+    ]
− plugins/hls-floskell-plugin/src/Ide/Plugin/Floskell.hs
@@ -1,62 +0,0 @@-{-# LANGUAGE CPP               #-}-{-# LANGUAGE OverloadedStrings #-}--module Ide.Plugin.Floskell-  ( descriptor-  , provider-  ) where--import           Control.Monad.Except             (throwError)-import           Control.Monad.IO.Class-import           Data.List                        (find)-import qualified Data.Text                        as T-import qualified Data.Text.Lazy                   as TL-import           Development.IDE                  hiding (pluginHandlers)-import           Development.IDE.Core.PluginUtils (mkFormattingHandlers)-import           Floskell-import           Ide.Plugin.Error-import           Ide.PluginUtils-import           Ide.Types-import           Language.LSP.Protocol.Types---- -----------------------------------------------------------------------descriptor :: PluginId -> PluginDescriptor IdeState-descriptor plId = (defaultPluginDescriptor plId desc)-  { pluginHandlers = mkFormattingHandlers provider-  }-  where-    desc = "Provides formatting of Haskell files via floskell. Built with floskell-" <> VERSION_floskell---- ------------------------------------------------------------------------- | Format provider of Floskell.--- 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 :: FormattingHandler IdeState-provider _ideState _token typ contents fp _ = do-    let file = fromNormalizedFilePath fp-    config <- liftIO $ findConfigOrDefault file-    let (range, selectedContents) = case typ of-          FormatText    -> (fullRange contents, contents)-          FormatRange r -> (normalize r, extractTextInRange (extendToFullLines r) contents)-        result = reformat config (Just file) $ TL.fromStrict selectedContents-    case result of-      Left  err -> throwError $ PluginInternalError $ T.pack $ "floskellCmd: " ++ err-      Right new -> pure $ InL [TextEdit range $ TL.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.--- Also looks into places such as XDG_CONFIG_DIRECTORY<https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html>.--- This function may not throw an exception and returns a default config.-findConfigOrDefault :: FilePath -> IO AppConfig-findConfigOrDefault file = do-  mbConf <- findAppConfigIn file-  case mbConf of-    Just confFile -> readAppConfig confFile-    Nothing ->-      pure $ case find (\s -> styleName s == "gibiansky") styles of-        Just gibiansky -> defaultAppConfig { appStyle = gibiansky }-        Nothing        -> defaultAppConfig---- ---------------------------------------------------------------------
− plugins/hls-floskell-plugin/test/Main.hs
@@ -1,30 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Main-  ( main-  ) where--import qualified Ide.Plugin.Floskell as Floskell-import           System.FilePath-import           Test.Hls--main :: IO ()-main = defaultTestRunner tests--floskellPlugin :: PluginTestDescriptor ()-floskellPlugin = mkPluginTestDescriptor' Floskell.descriptor "floskell"--tests :: TestTree-tests = testGroup "floskell"-  [ goldenWithFloskell "formats a document" "Floskell" "formatted_document" $ \doc -> do-      formatDoc doc (FormattingOptions 4 True Nothing Nothing Nothing)--  , goldenWithFloskell "formats a range" "Floskell" "formatted_range" $ \doc -> do-      let range = Range (Position 1 0) (Position 4 22)-      formatRange doc (FormattingOptions 4 True Nothing Nothing Nothing) range-  ]--goldenWithFloskell :: TestName -> FilePath -> FilePath -> (TextDocumentIdentifier -> Session ()) -> TestTree-goldenWithFloskell title path desc = goldenWithHaskellDocFormatter def floskellPlugin "floskell" def title testDataDir path desc "hs"--testDataDir :: FilePath-testDataDir = "plugins" </> "hls-floskell-plugin" </> "test" </> "testdata"
− plugins/hls-floskell-plugin/test/testdata/Floskell.formatted_document.hs
@@ -1,17 +0,0 @@-module Floskell where--import           Data.List-import           Prelude-import           Data.Int--foo :: Int -> Int-foo 3 = 2-foo x = x--bar :: String -> IO String-bar s = do-  x <- return "hello"-  return "asdf"--data Baz = Baz { a :: Int, b :: String }-
− plugins/hls-floskell-plugin/test/testdata/Floskell.formatted_range.hs
@@ -1,14 +0,0 @@-module    Floskell where-import           Data.List-import           Prelude-import           Data.Int-foo   :: Int ->  Int-foo  3 = 2-foo    x  = x-bar   :: String ->   IO String-bar s =  do-      x <- return "hello"-      return "asdf"--data Baz = Baz { a :: Int, b :: String }-
− plugins/hls-floskell-plugin/test/testdata/Floskell.hs
@@ -1,15 +0,0 @@-module    Floskell where-import Data.List--import Prelude-import Data.Int-foo   :: Int ->  Int-foo  3 = 2-foo    x  = x-bar   :: String ->   IO String-bar s =  do-      x <- return "hello"-      return "asdf"--data Baz = Baz { a :: Int, b :: String }-
− plugins/hls-floskell-plugin/test/testdata/hie.yaml
@@ -1,3 +0,0 @@-cradle:-  direct:-    arguments: []
plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu.hs view
@@ -145,7 +145,12 @@                 pure $ InL $ makeDiffTextEdit contents out             ExitFailure n -> do                 logWith recorder Info $ StdErr err-                throwError $ PluginInternalError $ "Fourmolu failed with exit code " <> T.pack (show n)+                let cleanErr = T.stripEnd err+                let cliError = if T.null cleanErr+                               then ""+                               else "\n" <> cleanErr+                throwError $ PluginInternalError $+                    "Fourmolu failed with exit code " <> T.pack (show n) <> cliError  loadConfig ::     Recorder (WithPriority LogEvent) ->
plugins/hls-fourmolu-plugin/test/Main.hs view
@@ -3,11 +3,15 @@   ( main   ) where +import           Control.Lens                ((^.)) import           Data.Aeson import qualified Data.Aeson.KeyMap           as KM import           Data.Functor+import qualified Data.Map                    as M+import qualified Data.Text                   as T import           Ide.Plugin.Config import qualified Ide.Plugin.Fourmolu         as Fourmolu+import qualified Language.LSP.Protocol.Lens  as L import           Language.LSP.Protocol.Types import           Language.LSP.Test import           System.FilePath@@ -31,6 +35,28 @@             formatDoc doc (FormattingOptions 4 True Nothing Nothing Nothing)         , goldenWithFourmolu cli "uses correct operator fixities" "Fourmolu3" "formatted" $ \doc -> do             formatDoc doc (FormattingOptions 4 True Nothing Nothing Nothing)+        , testCase "error message contains stderr output" $ do+            let cliConfig = def {+                    formattingProvider = "fourmolu",+                    plugins = M.fromList [("fourmolu", def { plcConfig = KM.fromList ["external" .= True] })]+                }+            runSessionWithServer cliConfig fourmoluPlugin testDataDir $ do+                doc <- openDoc "FormatError.hs" "haskell"+                void waitForBuildQueue+                resp <- request SMethod_TextDocumentFormatting $+                    DocumentFormattingParams Nothing doc (FormattingOptions 4 True Nothing Nothing Nothing)+                liftIO $ case resp ^. L.result of+                    Left err -> do+                        let msg = err ^. L.message+                        -- Verify the error message structure:+                        -- 1. Contains the exit code prefix (base message intact)+                        assertBool ("Expected exit code prefix, got: " <> T.unpack msg)+                            ("failed with exit code" `T.isInfixOf` msg)+                        -- 2. Contains a stable parse-error phrase from formatter stderr+                        assertBool ("Expected parse error details from stderr, got: " <> T.unpack msg)+                            ("parse error on input" `T.isInfixOf` msg)+                    Right _ ->+                        assertFailure "Expected formatting to fail on unparsable file"         ]  goldenWithFourmolu :: Bool -> TestName -> FilePath -> FilePath -> (TextDocumentIdentifier -> Session ()) -> TestTree
+ plugins/hls-fourmolu-plugin/test/testdata/FormatError.hs view
@@ -0,0 +1,3 @@+module FormatError where++foo = let x = 1 in in x
plugins/hls-gadt-plugin/src/Ide/Plugin/GADT.hs view
@@ -86,7 +86,7 @@ codeActionHandler state plId (CodeActionParams _ _ doc range _) = withExceptT handleGhcidePluginError $ do     nfp <- withExceptT GhcidePluginErrors $ getNormalizedFilePathE (doc ^. L.uri)     (inRangeH98Decls, _) <- getInRangeH98DeclsAndExts state range nfp-    let actions = map (mkAction . printOutputable . tcdLName . unLoc) inRangeH98Decls+    let actions = map (mkAction . printOutputable . tyClDeclLName . unLoc) inRangeH98Decls     pure $ InL actions     where         mkAction :: T.Text -> Command |? CodeAction
plugins/hls-hlint-plugin/test/Main.hs view
@@ -218,8 +218,8 @@         doc <- openDoc "TwoHints.hs" "haskell"         _ <- hlintCaptureKick -        firstLine <- map fromAction <$> getCodeActions doc (mkRange 0 0 0 0)-        secondLine <- map fromAction <$> getCodeActions doc (mkRange 1 0 1 0)+        firstLine <- map fromAction <$> getCodeActions doc (mkRange 0 0 1 0)+        secondLine <- map fromAction <$> getCodeActions doc (mkRange 1 0 2 0)         thirdLine <- map fromAction <$> getCodeActions doc (mkRange 2 0 2 0)         multiLine <- map fromAction <$> getCodeActions doc (mkRange 0 0 2 0) 
− plugins/hls-module-name-plugin/src/Ide/Plugin/ModuleName.hs
@@ -1,196 +0,0 @@-{-# LANGUAGE DataKinds         #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE PatternSynonyms   #-}-{-# LANGUAGE RecordWildCards   #-}-{-# LANGUAGE ViewPatterns      #-}--{- | Keep the module name in sync with its file path.--Provide CodeLenses to:-* Add a module header ("module /moduleName/ where") to empty Haskell files-* Fix the module name if incorrect--}-module Ide.Plugin.ModuleName (-    descriptor,-    Log,-) where--import           Control.Monad                        (forM_, void)-import           Control.Monad.IO.Class               (liftIO)-import           Control.Monad.Trans.Class            (lift)-import           Control.Monad.Trans.Except-import           Control.Monad.Trans.Maybe-import           Data.Aeson                           (toJSON)-import           Data.Char                            (isLower, isUpper)-import           Data.List                            (intercalate, minimumBy,-                                                       stripPrefix)-import qualified Data.List.NonEmpty                   as NE-import qualified Data.Map                             as Map-import           Data.Maybe                           (mapMaybe)-import           Data.Ord                             (comparing)-import           Data.String                          (IsString)-import qualified Data.Text                            as T-import qualified Data.Text.Utf16.Rope.Mixed           as Rope-import           Development.IDE                      (GetParsedModule (GetParsedModule),-                                                       GhcSession (GhcSession),-                                                       IdeState, Pretty,-                                                       Priority (Debug),-                                                       Recorder, WithPriority,-                                                       colon, evalGhcEnv,-                                                       hscEnv, logWith,-                                                       realSrcSpanToRange,-                                                       rootDir, runAction,-                                                       useWithStale, (<+>))-import           Development.IDE.Core.FileStore       (getFileContents)-import           Development.IDE.Core.PluginUtils-import           Development.IDE.Core.PositionMapping (toCurrentRange)-import           Development.IDE.GHC.Compat           (GenLocated (L),-                                                       getSessionDynFlags,-                                                       hsmodName, importPaths,-                                                       locA, moduleNameString,-                                                       pattern RealSrcSpan,-                                                       pm_parsed_source, unLoc)-import           Ide.Logger                           (Pretty (..))-import           Ide.Plugin.Error-import           Ide.PluginUtils                      (toAbsolute)-import           Ide.Types-import           Language.LSP.Protocol.Message-import           Language.LSP.Protocol.Types-import           System.FilePath                      (dropExtension, normalise,-                                                       pathSeparator,-                                                       splitDirectories,-                                                       takeFileName)---- |Plugin descriptor-descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState-descriptor recorder plId =-    (defaultPluginDescriptor plId "Provides a code action to alter the module name if it is wrong")-        { pluginHandlers = mkPluginHandler SMethod_TextDocumentCodeLens (codeLens recorder)-        , pluginCommands = [PluginCommand updateModuleNameCommand "set name of module to match with file path" (command recorder)]-        }--updateModuleNameCommand :: IsString p => p-updateModuleNameCommand = "updateModuleName"---- | Generate code lenses-codeLens :: Recorder (WithPriority Log) -> PluginMethodHandler IdeState 'Method_TextDocumentCodeLens-codeLens recorder state pluginId CodeLensParams{_textDocument=TextDocumentIdentifier uri} = do-  res <- action recorder state uri-  pure $ InL (asCodeLens  <$> res)-  where-    asCodeLens :: Action -> CodeLens-    asCodeLens Replace{..} = CodeLens aRange (Just cmd) Nothing-      where-        cmd = mkLspCommand pluginId updateModuleNameCommand aTitle (Just [toJSON aUri])---- | (Quasi) Idempotent command execution: recalculate action to execute on command request-command :: Recorder (WithPriority Log) -> CommandFunction IdeState Uri-command recorder state _ uri = do-  actMaybe <- action recorder state uri-  forM_ actMaybe $ \Replace{..} ->-    let-      -- | Convert an Action to the corresponding edit operation-      edit = WorkspaceEdit (Just $ Map.singleton aUri [TextEdit aRange aCode]) Nothing Nothing-    in-      void $ lift $ pluginSendRequest SMethod_WorkspaceApplyEdit (ApplyWorkspaceEditParams Nothing edit) (const (pure ()))-  pure $ InR Null---- | A source code change-data Action = Replace-  { aUri   :: Uri-  , aRange :: Range-  , aTitle :: T.Text-  , aCode  :: T.Text-  }-  deriving (Show)---- | Required action (that can be converted to either CodeLenses or CodeActions)-action :: Recorder (WithPriority Log) -> IdeState -> Uri -> ExceptT PluginError (HandlerM c) [Action]-action recorder state uri = do-    nfp <- getNormalizedFilePathE  uri-    fp <- uriToFilePathE uri--    contents <- liftIO $ runAction "ModuleName.getFileContents" state $ getFileContents nfp-    let emptyModule = maybe True (T.null . T.strip . Rope.toText) contents--    correctNames <- mapExceptT liftIO $ pathModuleNames recorder state nfp fp-    logWith recorder Debug (CorrectNames correctNames)-    let bestName = minimumBy (comparing T.length) <$> NE.nonEmpty correctNames-    logWith recorder Debug (BestName bestName)--    statedNameMaybe <- liftIO $ codeModuleName state nfp-    logWith recorder Debug (ModuleName $ snd <$> statedNameMaybe)-    case (bestName, statedNameMaybe) of-      (Just bestName, Just (nameRange, statedName))-        | statedName `notElem` correctNames ->-            pure [Replace uri nameRange ("Set module name to " <> bestName) bestName]-      (Just bestName, Nothing)-        | emptyModule ->-            let code = "module " <> bestName <> " where\n"-            in pure [Replace uri (Range (Position 0 0) (Position 0 0)) code code]-      _ -> pure []---- | Possible module names, as derived by the position of the module in the--- source directories.  There may be more than one possible name, if the source--- directories are nested inside each other.-pathModuleNames :: Recorder (WithPriority Log) -> IdeState -> NormalizedFilePath -> FilePath -> ExceptT PluginError IO [T.Text]-pathModuleNames recorder state normFilePath filePath-  | firstLetter isLower $ takeFileName filePath = return ["Main"]-  | otherwise = do-      (session, _) <- runActionE "ModuleName.ghcSession" state $ useWithStaleE GhcSession normFilePath-      srcPaths <- liftIO $ evalGhcEnv (hscEnv session) $ importPaths <$> getSessionDynFlags-      logWith recorder Debug (SrcPaths srcPaths)--      -- Append a `pathSeparator` to make the path looks like a directory,-      --   and then we can drop it uniformly.-      -- See https://github.com/haskell/haskell-language-server/pull/3092 for details.-      let paths = map (normalise . (<> pure pathSeparator)) srcPaths-      logWith recorder Debug (NormalisedPaths paths)--      -- TODO, this can be avoid if the filePath is already absolute,-      -- we can avoid the toAbsolute call in the future.-      -- see Note [Root Directory]-      let mdlPath = (toAbsolute $ rootDir state) filePath-      logWith recorder Debug (AbsoluteFilePath mdlPath)--      let suffixes = mapMaybe (`stripPrefix` mdlPath) paths-      pure (map moduleNameFrom suffixes)-  where-    firstLetter :: (Char -> Bool) -> FilePath -> Bool-    firstLetter _ []       = False-    firstLetter pred (c:_) = pred c--    moduleNameFrom =-      T.pack-        . intercalate "."-        -- Do not suggest names whose components start from a lower-case char,-        -- they are guaranteed to be malformed.-        . filter (firstLetter isUpper)-        . splitDirectories-        . dropExtension---- | The module name, as stated in the module-codeModuleName :: IdeState -> NormalizedFilePath -> IO (Maybe (Range, T.Text))-codeModuleName state nfp = runMaybeT $ do-  (pm, mp) <- MaybeT . runAction "ModuleName.GetParsedModule" state $ useWithStale GetParsedModule nfp-  L (locA -> (RealSrcSpan l _)) m <- MaybeT . pure . hsmodName . unLoc $ pm_parsed_source pm-  range <- MaybeT . pure $ toCurrentRange mp (realSrcSpanToRange l)-  pure (range, T.pack $ moduleNameString m)--data Log =-    CorrectNames [T.Text]-  | BestName (Maybe T.Text)-  | ModuleName (Maybe T.Text)-  | SrcPaths [FilePath]-  | NormalisedPaths [FilePath]-  | AbsoluteFilePath FilePath-  deriving Show--instance Pretty Log where-  pretty log = "ModuleName." <> case log of-    CorrectNames log     -> "CorrectNames" <> colon <+> pretty log-    BestName log         -> "BestName" <> colon <+> pretty log-    ModuleName log       -> "StatedNameMaybe" <> colon <+> pretty log-    SrcPaths log         -> "SrcPaths" <> colon <+> pretty log-    NormalisedPaths log  -> "NormalisedPaths" <> colon <+> pretty log-    AbsoluteFilePath log -> "AbsoluteFilePath" <> colon <+> pretty log
− plugins/hls-module-name-plugin/test/Main.hs
@@ -1,66 +0,0 @@-{-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE OverloadedStrings     #-}-module Main-  ( main-  ) where--import           Control.Monad         (void)-import qualified Ide.Plugin.ModuleName as ModuleName-import           System.FilePath-import           Test.Hls--main :: IO ()-main = defaultTestRunner tests--moduleNamePlugin :: PluginTestDescriptor ModuleName.Log-moduleNamePlugin = mkPluginTestDescriptor ModuleName.descriptor "moduleName"--tests :: TestTree-tests =-  testGroup "moduleName"-  [ goldenWithModuleName "Add module header to empty module" "TEmptyModule" $ \doc -> do-      [CodeLens { _command = Just c }] <- getCodeLenses doc-      executeCommand c-      void $ skipManyTill anyMessage (message SMethod_WorkspaceApplyEdit)--  , goldenWithModuleName "Fix wrong module name" "TWrongModuleName" $ \doc -> do-      [CodeLens { _command = Just c }] <- getCodeLenses doc-      executeCommand c-      void $ skipManyTill anyMessage (message SMethod_WorkspaceApplyEdit)--  , goldenWithModuleName "Must infer module name as Main, if the file name starts with a lowercase" "mainlike" $ \doc -> do-      [CodeLens { _command = Just c }] <- getCodeLenses doc-      executeCommand c-      void $ skipManyTill anyMessage (message SMethod_WorkspaceApplyEdit)--  , goldenWithModuleName "Fix wrong module name in nested directory" "subdir/TWrongModuleName" $ \doc -> do-      [CodeLens { _command = Just c }] <- getCodeLenses doc-      executeCommand c-      void $ skipManyTill anyMessage (message SMethod_WorkspaceApplyEdit)-  , testCase "Should not show code lens if the module name is correct" $-      runSessionWithServer def moduleNamePlugin testDataDir $ do-        doc <- openDoc "CorrectName.hs" "haskell"-        lenses <- getCodeLenses doc-        liftIO $ lenses @?= []-        closeDoc doc-  -- https://github.com/haskell/haskell-language-server/issues/3047-  , goldenWithModuleName "Fix#3047" "canonicalize/Lib/A" $ \doc -> do-      [CodeLens { _command = Just c }] <- getCodeLenses doc-      executeCommand c-      void $ skipManyTill anyMessage (message SMethod_WorkspaceApplyEdit)-  , testCase "Keep stale lens even if parse failed" $ do-      runSessionWithServer def moduleNamePlugin testDataDir $ do-        doc <- openDoc "Stale.hs" "haskell"-        oldLens <- getCodeLenses doc-        let edit = TextEdit (mkRange 1 0 1 0) "f ="-        _ <- applyEdit doc edit-        newLens <- getCodeLenses doc-        liftIO $ newLens @?= oldLens-        closeDoc doc-  ]--goldenWithModuleName :: TestName -> FilePath -> (TextDocumentIdentifier -> Session ()) -> TestTree-goldenWithModuleName title path = goldenWithHaskellDoc def moduleNamePlugin title testDataDir path "expected" "hs"--testDataDir :: FilePath-testDataDir = "plugins" </> "hls-module-name-plugin" </> "test" </> "testdata"
− plugins/hls-module-name-plugin/test/testdata/CorrectName.hs
@@ -1,1 +0,0 @@-module CorrectName where
− plugins/hls-module-name-plugin/test/testdata/Stale.hs
@@ -1,1 +0,0 @@-module Foo where
− plugins/hls-module-name-plugin/test/testdata/TEmptyModule.expected.hs
@@ -1,3 +0,0 @@-module TEmptyModule where--
− plugins/hls-module-name-plugin/test/testdata/TEmptyModule.hs
@@ -1,2 +0,0 @@--
− plugins/hls-module-name-plugin/test/testdata/TWrongModuleName.expected.hs
@@ -1,7 +0,0 @@-module TWrongModuleName-  ( x-  )-where--x :: Integer-x = 11
− plugins/hls-module-name-plugin/test/testdata/TWrongModuleName.hs
@@ -1,7 +0,0 @@-module BadName-  ( x-  )-where--x :: Integer-x = 11
− plugins/hls-module-name-plugin/test/testdata/cabal.project
@@ -1,1 +0,0 @@-packages: ./canonicalize
− plugins/hls-module-name-plugin/test/testdata/canonicalize/Lib/A.expected.hs
@@ -1,1 +0,0 @@-module Lib.A where
− plugins/hls-module-name-plugin/test/testdata/canonicalize/Lib/A.hs
− plugins/hls-module-name-plugin/test/testdata/canonicalize/canonicalize.cabal
@@ -1,7 +0,0 @@-cabal-version: 2.4-name: canonicalize-version: 0.1.0.0--library-  build-depends: base-  hs-source-dirs: ./
− plugins/hls-module-name-plugin/test/testdata/hie.yaml
@@ -1,17 +0,0 @@-cradle:-  multi:-    - path: "./"-      config:-        cradle:-          direct:-            arguments:-              - "-isubdir"-              - "TEmptyModule"-              - "TWrongModuleName"-              - "CorrectName"-    - path: "./canonicalize"-      config:-        cradle:-          cabal:-            - path: "./"-              component: "lib:canonicalize"
− plugins/hls-module-name-plugin/test/testdata/mainlike.expected.hs
@@ -1,1 +0,0 @@-module Main where
− plugins/hls-module-name-plugin/test/testdata/mainlike.hs
− plugins/hls-module-name-plugin/test/testdata/subdir/TWrongModuleName.expected.hs
@@ -1,7 +0,0 @@-module TWrongModuleName-  ( x-  )-where--x :: Integer-x = 11
− plugins/hls-module-name-plugin/test/testdata/subdir/TWrongModuleName.hs
@@ -1,7 +0,0 @@-module BadName-  ( x-  )-where--x :: Integer-x = 11
plugins/hls-notes-plugin/src/Ide/Plugin/Notes.hs view
@@ -5,14 +5,13 @@                                                    throwError) import           Control.Monad.IO.Class           (liftIO) import qualified Data.Array                       as A-import           Data.Foldable                    (foldl') import           Data.HashMap.Strict              (HashMap) import qualified Data.HashMap.Strict              as HM import qualified Data.HashSet                     as HS-import           Data.List                        (uncons)-import           Data.Maybe                       (catMaybes, listToMaybe,-                                                   mapMaybe)-import           Data.Text                        (Text, intercalate)+import           Data.List                        as List+import           Data.Maybe                       (catMaybes, fromMaybe,+                                                   listToMaybe, mapMaybe)+import           Data.Text                        (Text) import qualified Data.Text                        as T import qualified Data.Text.Utf16.Rope.Mixed       as Rope import           Data.Traversable                 (for)@@ -25,8 +24,8 @@ import           Ide.Plugin.Error                 (PluginError (..)) import           Ide.Types import qualified Language.LSP.Protocol.Lens       as L-import           Language.LSP.Protocol.Message    (Method (Method_TextDocumentDefinition, Method_TextDocumentReferences),-                                                   SMethod (SMethod_TextDocumentDefinition, SMethod_TextDocumentReferences))+import           Language.LSP.Protocol.Message    (Method (Method_TextDocumentCompletion, Method_TextDocumentDefinition, Method_TextDocumentHover, Method_TextDocumentReferences),+                                                   SMethod (SMethod_TextDocumentCompletion, SMethod_TextDocumentDefinition, SMethod_TextDocumentHover, SMethod_TextDocumentReferences)) import           Language.LSP.Protocol.Types import           Text.Regex.TDFA                  (Regex, caseSensitive,                                                    defaultCompOpt,@@ -67,7 +66,7 @@             LogNoteReferencesFound file refs -> "Found note references in " <> prettyNotes file refs             LogNotesFound file notes -> "Found notes in " <> prettyNotes file notes         where prettyNotes file hm = pretty (show file) <> ": ["-                <> pretty (intercalate ", " (fmap (\(s, p) -> "\"" <> s <> "\" at " <> intercalate ", " (map (T.pack . show) p)) hm)) <> "]"+                <> pretty (T.intercalate ", " (fmap (\(s, p) -> "\"" <> s <> "\" at " <> T.intercalate ", " (map (T.pack . show) p)) hm)) <> "]"  {- The first time the user requests a jump-to-definition on a note reference, the@@ -80,6 +79,8 @@     , Ide.Types.pluginHandlers =         mkPluginHandler SMethod_TextDocumentDefinition jumpToNote         <> mkPluginHandler SMethod_TextDocumentReferences listReferences+        <> mkPluginHandler SMethod_TextDocumentHover hoverNote+        <> mkPluginHandler SMethod_TextDocumentCompletion autocomplete     }  findNotesRules :: Recorder (WithPriority Log) -> Rules ()@@ -98,7 +99,7 @@                 references <- fmap snd <$> use MkGetNotesInFile nfp                 pure $ fmap (HM.map (fmap (nfp,))) references             )-        pure $ Just $ foldl' (HM.unionWith (<>)) HM.empty definedReferences+        pure $ Just $ List.foldl' (HM.unionWith (<>)) HM.empty definedReferences  err :: MonadError PluginError m => Text -> Maybe a -> m a err s = maybe (throwError $ PluginInternalError s) pure@@ -130,9 +131,14 @@             Nothing -> pure (InR Null)             Just note -> do                 notes <- runActionE "notes.definedNoteReferencess" state $ useE MkGetNoteReferences nfp-                poss <- err ("Note reference (a comment of the form `{- Note [" <> note <> "] -}`) not found") (HM.lookup note notes)-                pure $ InL (mapMaybe (\(noteFp, pos@(Position l' _)) -> if l' == l then Nothing else Just (-                        Location (fromNormalizedUri $ normalizedFilePathToUri noteFp) (Range pos pos))) poss)+                case HM.lookup note notes of+                  Nothing -> pure (InL [])+                  Just poss -> pure $ InL $ mapMaybe (\(noteFp, pos@(Position l' _)) ->+                      if l' == l+                        then Nothing+                        else Just (Location (fromNormalizedUri $ normalizedFilePathToUri noteFp) (Range pos pos))+                    )+                    poss     where         uriOrig = toNormalizedUri $ param ^. (L.textDocument . L.uri) listReferences _ _ _ = throwError $ PluginInternalError "conversion to normalized file path failed"@@ -146,10 +152,10 @@             Nothing -> pure (InR (InR Null))             Just note -> do                 notes <- runActionE "notes.definedNotes" state $ useE MkGetNotes nfp-                (noteFp, pos) <- err ("Note definition (a comment of the form `{- Note [" <> note <> "]\\n~~~ ... -}`) not found") (HM.lookup note notes)-                pure $ InL (Definition (InL-                        (Location (fromNormalizedUri $ normalizedFilePathToUri noteFp) (Range pos pos))-                    ))+                case HM.lookup note notes of+                  Nothing -> pure (InR (InR Null))+                  Just (noteFp, pos) -> pure $ InL $ Definition $ InL $+                    Location (fromNormalizedUri $ normalizedFilePathToUri noteFp) (Range pos pos)     where         uriOrig = toNormalizedUri $ param ^. (L.textDocument . L.uri) jumpToNote _ _ _ = throwError $ PluginInternalError "conversion to normalized file path failed"@@ -168,7 +174,7 @@     let refMatches = (A.! 1) <$> matchAllText noteRefRegex content         refs = toPositions refMatches content     logWith recorder Debug $ LogNoteReferencesFound file (HM.toList refs)-    pure $ Just (HM.mapMaybe (fmap fst . uncons) notes, refs)+    pure $ Just (HM.mapMaybe (fmap fst . List.uncons) notes, refs)     where         uint = fromIntegral . toInteger         -- the regex library returns the character index of the match. However@@ -190,8 +196,203 @@  noteRefRegex, noteRegex :: Regex (noteRefRegex, noteRegex) =-    ( mkReg ("note \\[(.+)\\]" :: String)-    , mkReg ("note \\[([[:print:]]+)\\][[:blank:]]*\r?\n[[:blank:]]*(--)?[[:blank:]]*~~~" :: String)+    ( mkReg ("note[[:blank:]]?\\[(.+)\\]" :: String)+    , mkReg ("note[[:blank:]]?\\[([[:print:]]+)\\][[:blank:]]*\r?\n[[:blank:]]*(--)?[[:blank:]]*~~~" :: String)     )     where         mkReg = makeRegexOpts (defaultCompOpt { caseSensitive = False }) defaultExecOpt++-- | Find the precise range of `note[...]` or `note [...]` on a line.+findNoteRange+  :: Text   -- ^ Full line text+  -> Text   -- ^ Note title+  -> UInt   -- ^ Line number+  -> Maybe Range+findNoteRange line _note lineNo =+  case matchAllText noteRefRegex line of+    [] -> Nothing+    (arr:_) ->+      case arr A.! 0 of+        (_, (start, len)) ->+          let startCol = fromIntegral start+              endCol   = startCol + fromIntegral len+          in Just $+               Range+                 (Position lineNo startCol)+                 (Position lineNo endCol)++-- Given the path and position of a Note Declaration, finds Content in it.+-- ignores ~ as a seprator+extractNoteContent+  :: NormalizedFilePath+  -> Position+  -> IO (Maybe Text)+extractNoteContent nfp (Position startLine _) = do+  fileText <- readFileUtf8 $ fromNormalizedFilePath nfp++  let allLines = T.lines fileText+      headerLine = listToMaybe (drop (fromIntegral startLine) allLines)+      isBlockNote = maybe False ("{-" `T.isInfixOf`) headerLine+      isLineNote = maybe False (("--" `T.isPrefixOf`) . T.stripStart) headerLine+      afterDecl = drop (fromIntegral startLine + 1) allLines+      afterSeparator =+        case dropWhile (not . isSeparatorLine) afterDecl of+          []     -> []+          (_:xs) -> xs+      isStopLine :: Text -> Bool+      isStopLine t+        | isBlockNote = isSeparatorLine t || isNoteHeader t || isEndMarker t+        | isLineNote  = isSeparatorLine t || isNoteHeader t || isEndMarker t || not ("--" `T.isPrefixOf` T.stripStart t)+        | otherwise   = isEndMarker t || isNoteHeader t+      bodyLines = takeWhile (not . isStopLine) afterSeparator++  if null afterSeparator+     then pure Nothing+     else pure $ Just (T.unlines (map stripCommentPrefix bodyLines))++  where+    isSeparatorLine :: Text -> Bool+    isSeparatorLine t = "~~~" `T.isInfixOf` t++    isNoteHeader :: Text -> Bool+    isNoteHeader t =+      let stripped = T.toLower (T.stripStart (stripCommentPrefix t))+      in "note [" `T.isPrefixOf` stripped++    isEndMarker :: Text -> Bool+    isEndMarker t = "-}" `T.isInfixOf` t++    stripCommentPrefix :: Text -> Text+    stripCommentPrefix t =+      let s = T.stripStart t+      in if "--" `T.isPrefixOf` s+            then T.stripStart (T.drop 2 s)+            else s++normalizeNewlines :: Text -> Text+normalizeNewlines = T.replace "\r\n" "\n"++-- on hovering Note References, shows corresponding Declaration if it Exists+-- ignores Note Declaration+hoverNote :: PluginMethodHandler IdeState Method_TextDocumentHover+hoverNote state _ params+  | Just nfp <- uriToNormalizedFilePath uriOrig+  = do+      let pos@(Position line _) = params ^. L.position+      noteOpt <- getNote nfp state pos+      case noteOpt of+        Nothing -> pure (InR Null)++        Just note -> do+          mbRope <- liftIO $ runAction "notes.hoverLine" state (getFileContents nfp)++          -- compute precise hover range for highlighting corresponding Note Reference on Hover+          let lineText =+                case mbRope of+                  Nothing   -> ""+                  Just rope -> fromMaybe "" $ listToMaybe $ drop (fromIntegral line) $ Rope.lines rope++              mbRange = findNoteRange lineText note line++          notes <- runActionE "notes.hover" state $ useE MkGetNotes nfp+          case HM.lookup note notes of+            Nothing -> pure $ InL $ Hover (InL $ MarkupContent MarkupKind_Markdown "_No declaration available_") mbRange++            Just (defFile, defPos) ->+              if nfp == defFile && pos ^. L.line == defPos ^. L.line+                then pure (InR Null)+                else do+                  mbContent <- liftIO $ extractNoteContent defFile defPos++                  let contents =+                        case mbContent of+                          Nothing   -> "_No declaration available_"+                          Just body -> T.unlines [note, "", body]+                      normalizedContents = normalizeNewlines contents++                  pure $ InL $ Hover (InL $ MarkupContent MarkupKind_Markdown normalizedContents) mbRange+  where+    uriOrig = toNormalizedUri $ params ^. (L.textDocument . L.uri)++hoverNote _ _ _ = pure (InR Null)++-- Gives an autocomplete suggestion when 'note' prefix is detected+autocomplete :: PluginMethodHandler IdeState Method_TextDocumentCompletion+autocomplete state _ params = do+  let uri = params ^. (L.textDocument . L.uri)+      pos = params ^. L.position+      nuri = toNormalizedUri uri++  contents <-+    liftIO $+      runAction "Notes.GetUriContents" state $+        getUriContents nuri++  fmap InL $+    case contents of+      Nothing -> pure []++      Just rope -> do+        let linePrefix =  T.toLower $  T.stripEnd $  getLinePrefix rope pos++        -- Suggest NOTE DECLARATION snippit if "note" prefix detected+        if T.strip linePrefix == "note"+        then+          pure [CompletionItem "Note" Nothing (Just CompletionItemKind_Keyword) Nothing+              (Just "Note Declaration") Nothing Nothing Nothing Nothing+              Nothing (Just noteSnippet) (Just InsertTextFormat_Snippet) Nothing+              Nothing Nothing Nothing Nothing Nothing Nothing+          ]++        -- Suggest list of all NOTE DECLARATION if "note [" infix detected+        else if "note[" `T.isInfixOf` linePrefix || "note [" `T.isInfixOf` linePrefix+        then+          case uriToNormalizedFilePath nuri of+            Nothing -> pure []++            Just nfp -> do+              let typed =+                   case T.breakOnEnd "[" linePrefix of+                    (_, "")   -> ""+                    (_, rest)-> T.strip rest++              notesMap <-+                runActionE "notes.completion.notes" state $+                  useE MkGetNotes nfp++              let allNotes = HM.keys notesMap+                  matches =+                    filter+                      (\n -> T.toLower typed `T.isPrefixOf` T.toLower n)+                      allNotes++                  finalNotes =+                    if null matches then allNotes else matches+              pure $+                map+                  (\n ->+                     CompletionItem n Nothing (Just CompletionItemKind_Reference) Nothing (Just "Note reference")+                       Nothing Nothing (Just True) (Just "0") (Just n)+                       Nothing Nothing Nothing Nothing Nothing+                       Nothing Nothing Nothing Nothing+                  )+                  finalNotes+        else+          pure []++noteSnippet :: Text+noteSnippet =+  T.unlines+    [ "{- Note [${1:Declaration title}]"+    , "~~~"+    , "${2:Content}"+    , "-}"+    ]++getLinePrefix :: Rope.Rope -> Position -> Text+getLinePrefix rope (Position line col) =+  case Rope.splitAtLine (fromIntegral line) rope of+    (_, rest) ->+      case Rope.lines rest of+        (l:_) -> T.take (fromIntegral col) l+        _     -> ""
plugins/hls-notes-plugin/test/NotesTest.hs view
@@ -1,5 +1,6 @@ module Main (main) where +import           Data.List        (sort) import           Ide.Plugin.Notes (Log, descriptor) import           System.FilePath  ((</>)) import           Test.Hls@@ -12,6 +13,7 @@   testGroup "Notes"     [ gotoNoteTests     , noteReferenceTests+    , hoverNoteTests     ]  runSessionWithServer' :: FilePath -> (FilePath -> Session a) -> IO a@@ -28,13 +30,15 @@       testCase "multi_file" $ runSessionWithServer' testDataDir $ \dir -> do         doc <- openDoc "NoteDef.hs" "haskell"         waitForKickDone-        refs <- getReferences doc (Position 21 15) False-        let fp = dir </> "NoteDef.hs"-        liftIO $ refs @?= [-            Location (filePathToUri (dir </> "Other.hs")) (Range (Position 6 13) (Position 6 13)),-            Location (filePathToUri fp) (Range (Position 9 9) (Position 9 9)),-            Location (filePathToUri fp) (Range (Position 5 67) (Position 5 67))-          ]+        refs0 <- getReferences doc (Position 21 15) False+        let refs = sort refs0+            fp = dir </> "NoteDef.hs"++        liftIO $ sort refs @?= sort+            [ Location (filePathToUri (dir </> "Other.hs")) (Range (Position 6 13) (Position 6 13))+            , Location (filePathToUri fp) (Range (Position 9 9) (Position 9 9))+            , Location (filePathToUri fp) (Range (Position 5 67) (Position 5 67))+            ]     ]  gotoNoteTests :: TestTree@@ -75,3 +79,83 @@  testDataDir :: FilePath testDataDir = "plugins" </> "hls-notes-plugin" </> "test" </> "testdata"++hoverNoteTests :: TestTree+hoverNoteTests = testGroup "Hover Notes"+  [ testCase "hover normal-notes" $+      runSessionWithServer' testDataDir $ \_dir -> do+        let file = "HoverNote.hs"+            pos  = Position 24 10+        doc <- openDoc file "haskell"+        waitForKickDone+        hover <- getHover doc pos++        let expected =+              Just $ Hover (InL $ MarkupContent MarkupKind_Markdown+              "Normal-Notes\n\nContent of Normal Notes\n\n")+              (Just (Range (Position 24 3) (Position 24 22)))++        liftIO $ hover @?= expected+++  , testCase "hover multi-notes-one" $+      runSessionWithServer' testDataDir $ \_dir -> do+        let file = "HoverNote.hs"+            pos  = Position 25 10+        doc <- openDoc file "haskell"+        waitForKickDone+        hover <- getHover doc pos++        let expected =+              Just $ Hover (InL $ MarkupContent MarkupKind_Markdown+              "Multi-Notes-one\n\nContent of Multi-Notes-one\n\n\n")+              (Just (Range (Position 25 3) (Position 25 25)))++        liftIO $ hover @?= expected+++  , testCase "hover multi-notes-two" $+      runSessionWithServer' testDataDir $ \_dir -> do+        let file = "HoverNote.hs"+            pos  = Position 26 10+        doc <- openDoc file "haskell"+        waitForKickDone+        hover <- getHover doc pos++        let expected =+              Just $ Hover (InL $ MarkupContent MarkupKind_Markdown+              "Multi-Notes-two\n\nContent of Multi-Notes-two\n\n")+              (Just (Range (Position 26 3) (Position 26 25)))++        liftIO $ hover @?= expected+++  , testCase "hover single-comment-declaration" $+      runSessionWithServer' testDataDir $ \_dir -> do+        let file = "HoverNote.hs"+            pos  = Position 27 10+        doc <- openDoc file "haskell"+        waitForKickDone+        hover <- getHover doc pos++        let expected =+              Just $ Hover (InL $ MarkupContent MarkupKind_Markdown+              "Single Comment Declaration\n\nContent of Single Comment Declaration\n\n")+              (Just (Range (Position 27 3) (Position 27 36)))++        liftIO $ hover @?= expected++  , testCase "hover single-comment leak" $ runSessionWithServer' testDataDir $ \_dir -> do+        let file = "NoteDef.hs"+            pos  = Position 5 69+        doc <- openDoc file "haskell"+        waitForKickDone+        hover <- getHover doc pos++        let expected =+              Just $ Hover (InL $ MarkupContent MarkupKind_Markdown+              "Single line comments\n\nGHC's notes script only allows multiline comments to define notes, but in the\nHLS codebase this single line style can be found as well.\n\n")+              (Just (Range (Position 5 61) (Position 5 88)))++        liftIO $ hover @?= expected+  ]
+ plugins/hls-notes-plugin/test/testdata/HoverNote.hs view
@@ -0,0 +1,28 @@+module HoverNotes where++{- Note [Normal-Notes]+~~~+Content of Normal Notes+-}++{- Note [Multi-Notes-one]+~~~+Content of Multi-Notes-one++Note [Multi-Notes-two]+~~~+Content of Multi-Notes-two+-}++-- Note [Single Comment Declaration]+-- ~~~+-- Content of Single Comment Declaration+-- -}++foo :: Int+foo = 42++-- Note [Normal-Notes]+-- Note [Multi-Notes-one]+-- Note [Multi-Notes-two]+-- Note [Single Comment Declaration]
plugins/hls-ormolu-plugin/src/Ide/Plugin/Ormolu.hs view
@@ -170,7 +170,12 @@                pure $ InL $ makeDiffTextEdit contents out            ExitFailure n -> do                logWith recorder Info $ StdErr err-               throwError $ PluginInternalError $ "Ormolu failed with exit code " <> T.pack (show n)+               let cleanErr = T.stripEnd err+               let cliError = if T.null cleanErr+                              then ""+                              else "\n" <> cleanErr+               throwError $ PluginInternalError $+                   "Ormolu failed with exit code " <> T.pack (show n) <> cliError  newtype CLIVersionInfo =   CLIVersionInfo     { noCabal :: Bool
plugins/hls-ormolu-plugin/test/Main.hs view
@@ -4,11 +4,15 @@   ( main   ) where +import           Control.Lens                ((^.)) import           Data.Aeson import qualified Data.Aeson.KeyMap           as KM import           Data.Functor+import qualified Data.Map                    as M+import qualified Data.Text                   as T import           Ide.Plugin.Config import qualified Ide.Plugin.Ormolu           as Ormolu+import qualified Language.LSP.Protocol.Lens  as L import           Language.LSP.Protocol.Types import           System.FilePath import           Test.Hls@@ -31,6 +35,28 @@       , goldenWithOrmolu cli "formats operators correctly" "Ormolu3" "formatted" $ \doc -> do           formatDoc doc (FormattingOptions 4 True Nothing Nothing Nothing) #endif+      , testCase "error message contains stderr output" $ do+          let cliConfig = def {+                  formattingProvider = "ormolu",+                  plugins = M.fromList [("ormolu", def { plcConfig = KM.fromList ["external" .= True] })]+              }+          runSessionWithServer cliConfig ormoluPlugin testDataDir $ do+              doc <- openDoc "FormatError.hs" "haskell"+              void waitForBuildQueue+              resp <- request SMethod_TextDocumentFormatting $+                  DocumentFormattingParams Nothing doc (FormattingOptions 4 True Nothing Nothing Nothing)+              liftIO $ case resp ^. L.result of+                  Left err -> do+                      let msg = err ^. L.message+                      -- Verify the error message structure:+                      -- 1. Contains the exit code prefix (base message intact)+                      assertBool ("Expected exit code prefix, got: " <> T.unpack msg)+                          ("failed with exit code" `T.isInfixOf` msg)+                      -- 2. Contains a stable parse-error phrase from formatter stderr+                      assertBool ("Expected parse error details from stderr, got: " <> T.unpack msg)+                          ("parse error on input" `T.isInfixOf` msg)+                  Right _ ->+                      assertFailure "Expected formatting to fail on unparsable file"       ]  goldenWithOrmolu :: Bool -> TestName -> FilePath -> FilePath -> (TextDocumentIdentifier -> Session ()) -> TestTree
+ plugins/hls-ormolu-plugin/test/testdata/FormatError.hs view
@@ -0,0 +1,3 @@+module FormatError where++foo = let x = 1 in in x
plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs view
@@ -964,7 +964,7 @@         [modul, "(from", _] -> Just modul         _                   -> Nothing -suggestExtendImport :: ExportsMap -> ParsedSource -> Diagnostic -> [(T.Text, CodeActionKind, Rewrite)]+suggestExtendImport :: ExportsMap -> ParsedSource -> Diagnostic -> [(T.Text, CodeActionKind, CodeActionPreferred, Rewrite)] suggestExtendImport exportsMap (L _ HsModule {hsmodImports}) Diagnostic{_range=_range,..}     | Just [binding, mod, srcspan] <-       matchRegexUnifySpaces _message@@ -992,6 +992,7 @@             Just ident <- lookupExportMap binding mod           = [ ( "Add " <> renderImportStyle importStyle <> " to the import list of " <> mod               , quickFixImportKind' "extend" importStyle+              , True               , uncurry extendImport (unImportStyle importStyle) decl               )             | importStyle <- NE.toList $ importStyles ident@@ -1434,7 +1435,7 @@  ------------------------------------------------------------------------------------------------- -suggestNewOrExtendImportForClassMethod :: ExportsMap -> ParsedSource -> T.Text -> Diagnostic -> [(T.Text, CodeActionKind, [Either TextEdit Rewrite])]+suggestNewOrExtendImportForClassMethod :: ExportsMap -> ParsedSource -> T.Text -> Diagnostic -> [(T.Text, CodeActionKind, CodeActionPreferred, [Either TextEdit Rewrite])] suggestNewOrExtendImportForClassMethod packageExportsMap ps fileContents Diagnostic {_message}   | Just [methodName, className] <-       matchRegexUnifySpaces@@ -1454,6 +1455,7 @@           Just decl ->             [ ( "Add " <> renderImportStyle style <> " to the import list of " <> moduleText,                 quickFixImportKind' "extend" style,+                True,                 [Right $ uncurry extendImport (unImportStyle style) decl]               )               | style <- importStyle@@ -1462,7 +1464,7 @@           _             | Just (range, indent) <- newImportInsertRange ps fileContents             ->-             (\(kind, unNewImport -> x) -> (x, kind, [Left $ TextEdit range (x <> "\n" <> T.replicate indent " ")])) <$>+             (\(kind, unNewImport -> x) -> (x, kind, True, [Left $ TextEdit range (x <> "\n" <> T.replicate indent " ")])) <$>             [ (quickFixImportKind' "new" style, newUnqualImport moduleText rendered False)               | style <- importStyle,                 let rendered = renderImportStyle style@@ -1471,7 +1473,7 @@             | otherwise -> []         where moduleText = moduleNameText identInfo -suggestNewImport :: DynFlags -> ExportsMap -> ParsedSource -> T.Text -> Diagnostic -> [(T.Text, CodeActionKind, TextEdit)]+suggestNewImport :: DynFlags -> ExportsMap -> ParsedSource -> T.Text -> Diagnostic -> [(T.Text, CodeActionKind, CodeActionPreferred, TextEdit)] suggestNewImport df packageExportsMap ps fileContents Diagnostic{..}   | msg <- unifySpaces _message   , Just thingMissing <- extractNotInScopeName msg@@ -1491,7 +1493,7 @@   = let qis = qualifiedImportStyle df         suggestions = nubSortBy simpleCompareImportSuggestion           (constructNewImportSuggestions packageExportsMap (qual <|> qual', thingMissing) extendImportSuggestions qis) in-    map (\(ImportSuggestion _ kind (unNewImport -> imp)) -> (imp, kind, TextEdit range (imp <> "\n" <> T.replicate indent " "))) suggestions+    map (\(ImportSuggestion _ kind (unNewImport -> imp)) -> (imp, kind, True, TextEdit range (imp <> "\n" <> T.replicate indent " "))) suggestions   where     L _ HsModule {..} = ps suggestNewImport _ _ _ _ _ = []
plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs view
@@ -141,6 +141,13 @@ dropHsParTy (L _ (HsParTy _ ty)) = ty dropHsParTy other                = other +normalizeConstraintDP :: [LocatedAn AnnListItem (HsType GhcPs)] -> [LocatedAn AnnListItem (HsType GhcPs)]+normalizeConstraintDP [] = []+normalizeConstraintDP xs = normalizeFirst xs+  where+    normalizeFirst (x:rest) = setEntryDP x (SameLine 0) : rest+    normalizeFirst [] = []+ removeConstraint ::   -- | Predicate: Which context to drop.   (LHsType GhcPs -> Bool) ->@@ -154,15 +161,13 @@ #else     go (L l it@HsQualTy{hst_ctxt = L l' ctxt, hst_body}) = Rewrite (locA l) $ \_ -> do #endif-      let ctxt' = filter (not . toRemove) ctxt-          removeStuff = (toRemove <$> headMaybe ctxt) == Just True-      let hst_body' = if removeStuff then resetEntryDP hst_body else hst_body+      let ctxt' = normalizeConstraintDP $ filter (not . toRemove) ctxt       return $ case ctxt' of-          [] -> hst_body'+          [] -> hst_body           _ -> do             let ctxt'' = over _last (first removeComma) ctxt'             L l $ it{ hst_ctxt = L l' ctxt''-                    , hst_body = hst_body'+                    , hst_body = hst_body                     }     go (L _ (HsParTy _ ty)) = go ty     go (L _ HsForAllTy{hst_body}) = go hst_body@@ -243,11 +248,6 @@ liftParseAST df s = case parseAST df "" s of   Right x ->  pure (makeDeltaAst x)   Left _          -> TransformT $ lift $ Left $ "No parse: " <> s---headMaybe :: [a] -> Maybe a-headMaybe []      = Nothing-headMaybe (a : _) = Just a  lastMaybe :: [a] -> Maybe a lastMaybe []    = Nothing
plugins/hls-refactor-plugin/src/Development/IDE/Plugin/Plugins/AddArgument.hs view
@@ -4,6 +4,7 @@ import           Control.Monad                             (join) import           Control.Monad.Trans.Class                 (lift) import           Data.Bifunctor                            (Bifunctor (..))+import           Data.Char                                 (isUpper) import           Data.Either.Extra                         (maybeToEither) import qualified Data.Text                                 as T import           Development.IDE.GHC.Compat@@ -68,11 +69,20 @@ --      In this case a new argument would have to add its type between b and c in the signature. plugin :: ParsedModule -> Diagnostic -> Either PluginError [(T.Text, [TextEdit])] plugin parsedModule Diagnostic {_message, _range}-  | Just (name, typ) <- matchVariableNotInScope message = addArgumentAction parsedModule _range name typ+  | Just (name, typ) <- matchVariableNotInScope message+  , not (isQualifiedName name) = addArgumentAction parsedModule _range name typ   | Just (name, typ) <- matchFoundHoleIncludeUnderscore message = addArgumentAction parsedModule _range name (Just typ)   | otherwise = pure []   where     message = unifySpaces _message+    -- Qualified names (e.g. NE.toList) always start with an uppercase module+    -- qualifier. Since "Variable not in scope" only reports variables and+    -- operators, an unqualified name will never start with an uppercase letter.+    -- Therefore, checking for an uppercase first character reliably identifies+    -- qualified names, which can never be valid function argument patterns.+    isQualifiedName name = case T.uncons name of+      Just (c, _) -> isUpper c+      Nothing     -> False  -- Given a name for the new binding, add a new pattern to the match in the last position, -- returning how many patterns there were in this match prior to the transformation:
plugins/hls-refactor-plugin/test/Main.hs view
@@ -3288,7 +3288,7 @@     ]    typeSignatureLined3 = T.unlines $ header <>-    [ "foo :: ( Eq a"+    [ "foo :: (Eq a"     , "       , Show a"     , "       )"     , "    => a -> Bool"@@ -3296,7 +3296,7 @@     ]    typeSignatureLined3' = T.unlines $ header <>-    [ "foo :: ( Eq a"+    [ "foo :: (Eq a"     , "       )"     , "    => a -> Bool"     , "foo x = x == x"
plugins/hls-refactor-plugin/test/Test/AddArgument.hs view
@@ -3,10 +3,13 @@ {-# LANGUAGE DataKinds             #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE GADTs                 #-}+{-# LANGUAGE LambdaCase            #-} {-# LANGUAGE OverloadedStrings     #-}  module Test.AddArgument (tests) where +import           Data.List                         (find)+import           Data.Maybe                        (isJust) import qualified Data.Text                         as T import           Development.IDE.Types.Location import           Language.LSP.Protocol.Types       hiding@@ -42,7 +45,8 @@       mkGoldenAddArgTest "AddArgWithLambda" (r 1 0 1 50),       mkGoldenAddArgTest "MultiSigFirst" (r 2 0 2 50),       mkGoldenAddArgTest "MultiSigLast" (r 2 0 2 50),-      mkGoldenAddArgTest "MultiSigMiddle" (r 2 0 2 50)+      mkGoldenAddArgTest "MultiSigMiddle" (r 2 0 2 50),+      mkNoAddArgForQualifiedNameTest     ]   where     r x y x' y' = Range (Position x y) (Position x' y')@@ -71,3 +75,30 @@       "expected"       "hs"       action++-- | Verify that the "Add argument" code action is NOT offered for qualified names (e.g. NE.toList).+-- We also verify that an import suggestion IS offered, to confirm we're testing the right diagnostic.+mkNoAddArgForQualifiedNameTest :: TestTree+mkNoAddArgForQualifiedNameTest =+    testCase "No add argument for qualified names" $ runSessionWithServerInTmpDir def+      (  mkPluginTestDescriptor Refactor.iePluginDescriptor "ghcide-code-actions-imports-exports"+      <> mkPluginTestDescriptor Refactor.bindingsPluginDescriptor "ghcide-code-actions-bindings"+      )+      (FS.mkVirtualFileTree "plugins/hls-refactor-plugin/test/data/add-arg" (FS.directProject "QualifiedName.hs"))+      $ do+        doc <- openDoc "QualifiedName.hs" "haskell"+        _ <- waitForDiagnostics+        actions <- getCodeActions doc (Range (Position 5 0) (Position 5 50))+        -- Verify that an import suggestion exists, confirming the right diagnostic.+        let importAction = find (\case+              InR CodeAction {_title = t} -> "Data.List.NonEmpty" `T.isInfixOf` t+              _                           -> False) actions+        liftIO $ assertBool+          "Expected an import suggestion code action for Data.List.NonEmpty"+          (isJust importAction)+        -- Verify that the "Add argument" code action is NOT offered+        let addArgAction = find (\case+              InR CodeAction {_title = t} -> "Add argument" `T.isPrefixOf` t+              _                           -> False) actions+        liftIO $ addArgAction @?= Nothing+
+ plugins/hls-refactor-plugin/test/data/add-arg/QualifiedName.hs view
@@ -0,0 +1,6 @@+module QualifiedName where++import Data.List.NonEmpty (NonEmpty(..))++foo :: NonEmpty a -> [a]+foo xs = NE.toList xs
plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs view
@@ -5,8 +5,9 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards   #-} {-# OPTIONS_GHC -Wno-orphans #-}+{-# LANGUAGE LambdaCase        #-} -module Ide.Plugin.Rename (descriptor, E.Log) where+module Ide.Plugin.Rename (descriptor, Log) where  import           Control.Lens                          ((^.)) import           Control.Monad@@ -30,12 +31,12 @@ import           Development.IDE.Core.FileStore        (getVersionedTextDoc) import           Development.IDE.Core.PluginUtils import           Development.IDE.Core.RuleTypes-import           Development.IDE.Core.Service-import           Development.IDE.Core.Shake+import           Development.IDE.Core.Service          hiding (Log)+import           Development.IDE.Core.Shake            hiding (Log) import           Development.IDE.GHC.Compat import           Development.IDE.GHC.Compat.ExactPrint import           Development.IDE.GHC.Error-import           Development.IDE.GHC.ExactPrint+import           Development.IDE.GHC.ExactPrint        hiding (Log) import qualified Development.IDE.GHC.ExactPrint        as E import           Development.IDE.Plugin.CodeAction import           Development.IDE.Spans.AtPoint@@ -48,8 +49,11 @@ import           HieDb                                 ((:.) (..)) import           HieDb.Query import           HieDb.Types                           (RefRow (refIsGenerated))+import           Ide.Logger                            (Pretty (..),+                                                        cmapWithPrio) import           Ide.Plugin.Error import           Ide.Plugin.Properties+import qualified Ide.Plugin.Rename.ModuleName          as ModuleName import           Ide.PluginUtils import           Ide.Types import qualified Language.LSP.Protocol.Lens            as L@@ -58,30 +62,50 @@  instance Hashable (Mod a) where hash n = hash (unMod n) -descriptor :: Recorder (WithPriority E.Log) -> PluginId -> PluginDescriptor IdeState-descriptor recorder pluginId = mkExactprintPluginDescriptor recorder $-    (defaultPluginDescriptor pluginId "Provides renaming of Haskell identifiers")+data Log+    = LogExactPrint E.Log+    | LogModuleName ModuleName.Log++instance Pretty Log where+    pretty = \ case+        LogExactPrint msg -> pretty msg+        LogModuleName msg -> pretty msg++descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState+descriptor recorder pluginId = mkExactprintPluginDescriptor exactPrintRecorder $+    (defaultPluginDescriptor pluginId "Provides renaming of Haskell identifiers and module names")         { pluginHandlers = mconcat               [ mkPluginHandler SMethod_TextDocumentRename renameProvider               , mkPluginHandler SMethod_TextDocumentPrepareRename prepareRenameProvider+              , mkPluginHandler SMethod_TextDocumentCodeLens (ModuleName.codeLens moduleNameRecorder)               ]+        , pluginCommands = [PluginCommand ModuleName.updateModuleNameCommand "Set name of module to match with file path" (ModuleName.command moduleNameRecorder)]         , pluginConfigDescriptor = defaultConfigDescriptor             { configCustomConfig = mkCustomConfig properties }         }+    where+        exactPrintRecorder = cmapWithPrio LogExactPrint recorder+        moduleNameRecorder = cmapWithPrio LogModuleName recorder  prepareRenameProvider :: PluginMethodHandler IdeState Method_TextDocumentPrepareRename prepareRenameProvider state _pluginId (PrepareRenameParams (TextDocumentIdentifier uri) pos _progressToken) = do     nfp <- getNormalizedFilePathE uri-    namesUnderCursor <- getNamesAtPos state nfp pos+    HAR{hieAst} <- handleGetHieAst state nfp+    let spansWithNamesUnderCursor =+            [ srcSpan+            | (names, srcSpan) <- getNamesSpansAtPoint' hieAst pos+            , not (null names)]     -- When this handler says that rename is invalid, VSCode shows "The element can't be renamed"     -- and doesn't even allow you to create full rename request.     -- This handler deliberately approximates "things that definitely can't be renamed"-    -- to mean "there is no Name at given position".+    -- to mean "there is no Name at given position" (in which case+    -- `spansWithNamesUnderCursor` would be empty).     --     -- In particular it allows some cases through (e.g. cross-module renames),     -- so that the full rename handler can give more informative error about them.-    let renameValid = not $ null namesUnderCursor-    pure $ InL $ PrepareRenameResult $ InR $ InR $ PrepareRenameDefaultBehavior renameValid+    pure $ case spansWithNamesUnderCursor of+        [] -> InR Null+        srcSpan : _ -> InL $ PrepareRenameResult $ InL (realSrcSpanToRange srcSpan)  renameProvider :: PluginMethodHandler IdeState Method_TextDocumentRename renameProvider state pluginId (RenameParams _prog (TextDocumentIdentifier uri) pos newNameText) = do@@ -269,6 +293,12 @@ getNamesAtPoint' hf pos =   concat $ pointCommand hf pos (rights . M.keys . getNodeIds) +-- | A variant of `getNamesAtPoint'` that also returns source spans.+getNamesSpansAtPoint' :: HieASTs a -> Position -> [([Name], RealSrcSpan)]+getNamesSpansAtPoint' hf pos =+  pointCommand hf pos $+    \astNode -> (rights . M.keys . getNodeIds $ astNode, nodeSpan astNode)+ locToUri :: Location -> Uri locToUri (Location uri _) = uri @@ -291,4 +321,4 @@ properties :: Properties '[ 'PropertyKey "crossModule" 'TBoolean] properties = emptyProperties   & defineBooleanProperty #crossModule-    "Enable experimental cross-module renaming" False+    "Enable experimental cross-module renaming" True
+ plugins/hls-rename-plugin/src/Ide/Plugin/Rename/ModuleName.hs view
@@ -0,0 +1,190 @@+{-# LANGUAGE DataKinds         #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PatternSynonyms   #-}+{-# LANGUAGE RecordWildCards   #-}+{-# LANGUAGE ViewPatterns      #-}++{- | Keep the module name in sync with its file path.++Provide CodeLenses to:+* Add a module header ("module /moduleName/ where") to empty Haskell files+* Fix the module name if incorrect+-}+module Ide.Plugin.Rename.ModuleName (+    Log,+    codeLens,+    updateModuleNameCommand,+    command,+) where++import           Control.Monad                        (forM_, void)+import           Control.Monad.IO.Class               (liftIO)+import           Control.Monad.Trans.Class            (lift)+import           Control.Monad.Trans.Except+import           Control.Monad.Trans.Maybe+import           Data.Aeson                           (toJSON)+import           Data.Char                            (isLower, isUpper)+import           Data.List                            (intercalate, minimumBy,+                                                       stripPrefix)+import qualified Data.List.NonEmpty                   as NE+import qualified Data.Map                             as Map+import           Data.Maybe                           (mapMaybe)+import           Data.Ord                             (comparing)+import           Data.String                          (IsString)+import qualified Data.Text                            as T+import qualified Data.Text.Utf16.Rope.Mixed           as Rope+import           Development.IDE                      (GetParsedModule (GetParsedModule),+                                                       GhcSession (GhcSession),+                                                       IdeState, Pretty,+                                                       Priority (Debug),+                                                       Recorder, WithPriority,+                                                       colon, evalGhcEnv,+                                                       hscEnv, logWith,+                                                       realSrcSpanToRange,+                                                       rootDir, runAction,+                                                       useWithStale, (<+>))+import           Development.IDE.Core.FileStore       (getFileContents)+import           Development.IDE.Core.PluginUtils+import           Development.IDE.Core.PositionMapping (toCurrentRange)+import           Development.IDE.GHC.Compat           (GenLocated (L),+                                                       getSessionDynFlags,+                                                       hsmodName, importPaths,+                                                       locA, moduleNameString,+                                                       pattern RealSrcSpan,+                                                       pm_parsed_source, unLoc)+import           Ide.Logger                           (Pretty (..))+import           Ide.Plugin.Error+import           Ide.PluginUtils                      (toAbsolute)+import           Ide.Types+import           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types+import           System.FilePath                      (dropExtension, normalise,+                                                       pathSeparator,+                                                       splitDirectories,+                                                       takeFileName)++updateModuleNameCommand :: IsString p => p+updateModuleNameCommand = "updateModuleName"++-- | Generate code lenses+codeLens :: Recorder (WithPriority Log) -> PluginMethodHandler IdeState 'Method_TextDocumentCodeLens+codeLens recorder state pluginId CodeLensParams{_textDocument=TextDocumentIdentifier uri} = do+  res <- action recorder state uri+  pure $ InL (asCodeLens  <$> res)+  where+    asCodeLens :: Action -> CodeLens+    asCodeLens Replace{..} = CodeLens aRange (Just cmd) Nothing+      where+        cmd = mkLspCommand pluginId updateModuleNameCommand aTitle (Just [toJSON aUri])++-- | (Quasi) Idempotent command execution: recalculate action to execute on command request+command :: Recorder (WithPriority Log) -> CommandFunction IdeState Uri+command recorder state _ uri = do+  actMaybe <- action recorder state uri+  forM_ actMaybe $ \Replace{..} ->+    let+      -- | Convert an Action to the corresponding edit operation+      edit = WorkspaceEdit (Just $ Map.singleton aUri [TextEdit aRange aCode]) Nothing Nothing+    in+      void $ lift $ pluginSendRequest SMethod_WorkspaceApplyEdit (ApplyWorkspaceEditParams Nothing edit) (const (pure ()))+  pure $ InR Null++-- | A source code change+data Action = Replace+  { aUri   :: Uri+  , aRange :: Range+  , aTitle :: T.Text+  , aCode  :: T.Text+  }+  deriving (Show)++-- | Required action (that can be converted to either CodeLenses or CodeActions)+action :: Recorder (WithPriority Log) -> IdeState -> Uri -> ExceptT PluginError (HandlerM c) [Action]+action recorder state uri = do+    nfp <- getNormalizedFilePathE  uri+    fp <- uriToFilePathE uri++    contents <- liftIO $ runAction "ModuleName.getFileContents" state $ getFileContents nfp+    let emptyModule = maybe True (T.null . T.strip . Rope.toText) contents++    correctNames <- mapExceptT liftIO $ pathModuleNames recorder state nfp fp+    logWith recorder Debug (CorrectNames correctNames)+    let bestName = minimumBy (comparing T.length) <$> NE.nonEmpty correctNames+    logWith recorder Debug (BestName bestName)++    statedNameMaybe <- liftIO $ codeModuleName state nfp+    logWith recorder Debug (ModuleName $ snd <$> statedNameMaybe)+    case (bestName, statedNameMaybe) of+      (Just bestName, Just (nameRange, statedName))+        | statedName `notElem` correctNames ->+            pure [Replace uri nameRange ("Set module name to " <> bestName) bestName]+      (Just bestName, Nothing)+        | emptyModule ->+            let code = "module " <> bestName <> " where\n"+            in pure [Replace uri (Range (Position 0 0) (Position 0 0)) code code]+      _ -> pure []++-- | Possible module names, as derived by the position of the module in the+-- source directories.  There may be more than one possible name, if the source+-- directories are nested inside each other.+pathModuleNames :: Recorder (WithPriority Log) -> IdeState -> NormalizedFilePath -> FilePath -> ExceptT PluginError IO [T.Text]+pathModuleNames recorder state normFilePath filePath+  | firstLetter isLower $ takeFileName filePath = return ["Main"]+  | otherwise = do+      (session, _) <- runActionE "ModuleName.ghcSession" state $ useWithStaleE GhcSession normFilePath+      srcPaths <- liftIO $ evalGhcEnv (hscEnv session) $ importPaths <$> getSessionDynFlags+      logWith recorder Debug (SrcPaths srcPaths)++      -- Append a `pathSeparator` to make the path looks like a directory,+      --   and then we can drop it uniformly.+      -- See https://github.com/haskell/haskell-language-server/pull/3092 for details.+      let paths = map (normalise . (<> pure pathSeparator)) srcPaths+      logWith recorder Debug (NormalisedPaths paths)++      -- TODO, this can be avoid if the filePath is already absolute,+      -- we can avoid the toAbsolute call in the future.+      -- see Note [Root Directory]+      let mdlPath = (toAbsolute $ rootDir state) filePath+      logWith recorder Debug (AbsoluteFilePath mdlPath)++      let suffixes = mapMaybe (`stripPrefix` mdlPath) paths+      pure (map moduleNameFrom suffixes)+  where+    firstLetter :: (Char -> Bool) -> FilePath -> Bool+    firstLetter _ []       = False+    firstLetter pred (c:_) = pred c++    moduleNameFrom =+      T.pack+        . intercalate "."+        -- Do not suggest names whose components start from a lower-case char,+        -- they are guaranteed to be malformed.+        . filter (firstLetter isUpper)+        . splitDirectories+        . dropExtension++-- | The module name, as stated in the module+codeModuleName :: IdeState -> NormalizedFilePath -> IO (Maybe (Range, T.Text))+codeModuleName state nfp = runMaybeT $ do+  (pm, mp) <- MaybeT . runAction "ModuleName.GetParsedModule" state $ useWithStale GetParsedModule nfp+  L (locA -> (RealSrcSpan l _)) m <- MaybeT . pure . hsmodName . unLoc $ pm_parsed_source pm+  range <- MaybeT . pure $ toCurrentRange mp (realSrcSpanToRange l)+  pure (range, T.pack $ moduleNameString m)++data Log =+    CorrectNames [T.Text]+  | BestName (Maybe T.Text)+  | ModuleName (Maybe T.Text)+  | SrcPaths [FilePath]+  | NormalisedPaths [FilePath]+  | AbsoluteFilePath FilePath+  deriving Show++instance Pretty Log where+  pretty log = "ModuleName." <> case log of+    CorrectNames log     -> "CorrectNames" <> colon <+> pretty log+    BestName log         -> "BestName" <> colon <+> pretty log+    ModuleName log       -> "StatedNameMaybe" <> colon <+> pretty log+    SrcPaths log         -> "SrcPaths" <> colon <+> pretty log+    NormalisedPaths log  -> "NormalisedPaths" <> colon <+> pretty log+    AbsoluteFilePath log -> "AbsoluteFilePath" <> colon <+> pretty log
plugins/hls-rename-plugin/test/Main.hs view
@@ -4,13 +4,15 @@  module Main (main) where -import           Control.Lens               ((^.))-import           Data.Aeson-import qualified Data.Map                   as M-import           Data.Text                  (Text, pack)+import           Control.Lens                ((^.))+import           Data.Aeson                  (KeyValue ((.=)))+import           Data.Functor                (void)+import qualified Data.Map                    as M+import           Data.Text                   (Text, pack) import           Ide.Plugin.Config-import qualified Ide.Plugin.Rename          as Rename-import qualified Language.LSP.Protocol.Lens as L+import qualified Ide.Plugin.Rename           as Rename+import qualified Language.LSP.Protocol.Lens  as L+import           Language.LSP.Protocol.Types (Null (Null)) import           System.FilePath import           Test.Hls @@ -22,6 +24,56 @@  tests :: TestTree tests = testGroup "Rename"+    [ prepareRenameTests+    , renameTests+    , moduleNameTests+    ]++prepareRenameTests :: TestTree+prepareRenameTests = testGroup "PrepareRename"+    [ testCase "Module name (not yet renameable)" $ runRenameSession "" $ do+        doc <- openDoc "PrepareRename.hs" "haskell"+        void waitForBuildQueue+        result <- prepareRename doc (Position 0 9)+        liftIO $ result @?= InR Null++    , testCase "Function name" $ runRenameSession "" $ do+        doc <- openDoc "PrepareRename.hs" "haskell"+        void waitForBuildQueue+        result <- prepareRename doc (Position 8 1)+        liftIO $ result @?=+            InL (PrepareRenameResult (InL (Range (Position 8 0) (Position 8 3))))++    , testCase "Imported function name" $ runRenameSession "" $ do+        doc <- openDoc "PrepareRename.hs" "haskell"+        void waitForBuildQueue+        result <- prepareRename doc (Position 10 16)+        liftIO $ result @?=+            InL (PrepareRenameResult (InL (Range (Position 10 14) (Position 10 19))))++    , testCase "Non-renameable position" $ runRenameSession "" $ do+        doc <- openDoc "PrepareRename.hs" "haskell"+        void waitForBuildQueue+        result <- prepareRename doc (Position 6 23)+        liftIO $ result @?= InR Null++    , testCase "Operator" $ runRenameSession "" $ do+        doc <- openDoc "PrepareRename.hs" "haskell"+        void waitForBuildQueue+        result <- prepareRename doc (Position 10 7)+        liftIO $ result @?=+            InL (PrepareRenameResult (InL (Range (Position 10 6) (Position 10 9))))++    , testCase "Built-in operator" $ runRenameSession "" $ do+        doc <- openDoc "PrepareRename.hs" "haskell"+        void waitForBuildQueue+        result <- prepareRename doc (Position 13 7)+        liftIO $ result @?=+            InL (PrepareRenameResult (InL (Range (Position 13 7) (Position 13 8))))+    ]++renameTests :: TestTree+renameTests = testGroup "Identifier"     [ goldenWithRename "Data constructor" "DataConstructor" $ \doc ->         rename doc (Position 0 15) "Op"     , goldenWithRename "Data constructor with fields" "DataConstructorWithFields" $ \doc ->@@ -113,10 +165,70 @@         rename doc (Position 3 0) "foo'"     ] +moduleNameTests :: TestTree+moduleNameTests =+  testGroup "ModuleName"+  [ goldenWithModuleName "Add module header to empty module" "TEmptyModule" $ \doc -> do+      [CodeLens { _command = Just c }] <- getCodeLenses doc+      executeCommand c+      void $ skipManyTill anyMessage (message SMethod_WorkspaceApplyEdit)++  , goldenWithModuleName "Fix wrong module name" "TWrongModuleName" $ \doc -> do+      [CodeLens { _command = Just c }] <- getCodeLenses doc+      executeCommand c+      void $ skipManyTill anyMessage (message SMethod_WorkspaceApplyEdit)++  , goldenWithModuleName "Must infer module name as Main, if the file name starts with a lowercase" "mainlike" $ \doc -> do+      [CodeLens { _command = Just c }] <- getCodeLenses doc+      executeCommand c+      void $ skipManyTill anyMessage (message SMethod_WorkspaceApplyEdit)++  , goldenWithModuleName "Fix wrong module name in nested directory" "subdir/TWrongModuleName" $ \doc -> do+      [CodeLens { _command = Just c }] <- getCodeLenses doc+      executeCommand c+      void $ skipManyTill anyMessage (message SMethod_WorkspaceApplyEdit)+  , testCase "Should not show code lens if the module name is correct" $+      runSessionWithServer def renamePlugin modNameTestDataDir $ do+        doc <- openDoc "CorrectName.hs" "haskell"+        lenses <- getCodeLenses doc+        liftIO $ lenses @?= []+        closeDoc doc+  -- https://github.com/haskell/haskell-language-server/issues/3047+  , goldenWithModuleName "Fix#3047" "canonicalize/Lib/A" $ \doc -> do+      [CodeLens { _command = Just c }] <- getCodeLenses doc+      executeCommand c+      void $ skipManyTill anyMessage (message SMethod_WorkspaceApplyEdit)+  , testCase "Keep stale lens even if parse failed" $ do+      runSessionWithServer def renamePlugin modNameTestDataDir $ do+        doc <- openDoc "Stale.hs" "haskell"+        oldLens <- getCodeLenses doc+        let edit = TextEdit (mkRange 1 0 1 0) "f ="+        _ <- applyEdit doc edit+        newLens <- getCodeLenses doc+        liftIO $ newLens @?= oldLens+        closeDoc doc+  ]++goldenWithModuleName :: TestName -> FilePath -> (TextDocumentIdentifier -> Session ()) -> TestTree+goldenWithModuleName title path = goldenWithHaskellDoc def renamePlugin title modNameTestDataDir path "expected" "hs"++modNameTestDataDir :: FilePath+modNameTestDataDir = testDataDir </> "mod_name"+ goldenWithRename :: TestName-> FilePath -> (TextDocumentIdentifier -> Session ()) -> TestTree goldenWithRename title path act =     goldenWithHaskellDoc (def { plugins = M.fromList [("rename", def { plcConfig = "crossModule" .= True })] })        renamePlugin title testDataDir path "expected" "hs" act++-- NOTE: This should eventually be moved upstream to lsp-test (see+-- https://github.com/haskell/lsp/issues/636).+prepareRename :: TextDocumentIdentifier -> Position -> Session (PrepareRenameResult |? Null)+prepareRename doc pos = do+  let params = PrepareRenameParams doc pos Nothing+  rsp <- request SMethod_TextDocumentPrepareRename params+  case rsp ^. L.result of+    Left rspError -> liftIO $ assertFailure $ "prepareRename failed: " <> show rspError+    Right rspResult -> pure rspResult  renameExpectError :: TResponseError Method_TextDocumentRename -> TextDocumentIdentifier -> Position -> Text -> Session () renameExpectError expectedError doc pos newName = do
+ plugins/hls-rename-plugin/test/testdata/PrepareRename.hs view
@@ -0,0 +1,14 @@+module PrepareRename where++import qualified Foo as F++main :: IO Int+main = do+    x <- return $ foo 42+    return (foo x)+foo, bar :: Int -> Int+foo x = x + 1+bar = (+) 1 . F.foo . foo++baz :: a -> [a]+baz = (: [])
+ plugins/hls-rename-plugin/test/testdata/mod_name/CorrectName.hs view
@@ -0,0 +1,1 @@+module CorrectName where
+ plugins/hls-rename-plugin/test/testdata/mod_name/Stale.hs view
@@ -0,0 +1,1 @@+module Foo where
+ plugins/hls-rename-plugin/test/testdata/mod_name/TEmptyModule.expected.hs view
@@ -0,0 +1,3 @@+module TEmptyModule where++
+ plugins/hls-rename-plugin/test/testdata/mod_name/TEmptyModule.hs view
@@ -0,0 +1,2 @@++
+ plugins/hls-rename-plugin/test/testdata/mod_name/TWrongModuleName.expected.hs view
@@ -0,0 +1,7 @@+module TWrongModuleName+  ( x+  )+where++x :: Integer+x = 11
+ plugins/hls-rename-plugin/test/testdata/mod_name/TWrongModuleName.hs view
@@ -0,0 +1,7 @@+module BadName+  ( x+  )+where++x :: Integer+x = 11
+ plugins/hls-rename-plugin/test/testdata/mod_name/cabal.project view
@@ -0,0 +1,1 @@+packages: ./canonicalize
+ plugins/hls-rename-plugin/test/testdata/mod_name/canonicalize/Lib/A.expected.hs view
@@ -0,0 +1,1 @@+module Lib.A where
+ plugins/hls-rename-plugin/test/testdata/mod_name/canonicalize/Lib/A.hs view
+ plugins/hls-rename-plugin/test/testdata/mod_name/canonicalize/canonicalize.cabal view
@@ -0,0 +1,7 @@+cabal-version: 2.4+name: canonicalize+version: 0.1.0.0++library+  build-depends: base+  hs-source-dirs: ./
+ plugins/hls-rename-plugin/test/testdata/mod_name/hie.yaml view
@@ -0,0 +1,17 @@+cradle:+  multi:+    - path: "./"+      config:+        cradle:+          direct:+            arguments:+              - "-isubdir"+              - "TEmptyModule"+              - "TWrongModuleName"+              - "CorrectName"+    - path: "./canonicalize"+      config:+        cradle:+          cabal:+            - path: "./"+              component: "lib:canonicalize"
+ plugins/hls-rename-plugin/test/testdata/mod_name/mainlike.expected.hs view
@@ -0,0 +1,1 @@+module Main where
+ plugins/hls-rename-plugin/test/testdata/mod_name/mainlike.hs view
+ plugins/hls-rename-plugin/test/testdata/mod_name/subdir/TWrongModuleName.expected.hs view
@@ -0,0 +1,7 @@+module TWrongModuleName+  ( x+  )+where++x :: Integer+x = 11
+ plugins/hls-rename-plugin/test/testdata/mod_name/subdir/TWrongModuleName.hs view
@@ -0,0 +1,7 @@+module BadName+  ( x+  )+where++x :: Integer+x = 11
plugins/hls-semantic-tokens-plugin/test/SemanticTokensTest.hs view
@@ -22,6 +22,7 @@ import           Ide.Types import qualified Language.LSP.Protocol.Lens         as L import           Language.LSP.Protocol.Types+import qualified Language.LSP.Protocol.Types        as J import qualified Language.LSP.Test                  as Test import           Language.LSP.VFS                   (VirtualFile (..)) import           System.FilePath@@ -90,7 +91,7 @@ docLspSemanticTokensString doc = do   res <- Test.getSemanticTokens doc   textContent <- documentContents doc-  let vfs = VirtualFile 0 0 (Rope.fromText textContent)+  let vfs = VirtualFile 0 0 (Rope.fromText textContent) $ Just J.LanguageKind_Haskell   case res ^? Language.LSP.Protocol.Types._L of     Just tokens -> do       either (error . show) pure $ recoverLspSemanticTokens vfs tokens
plugins/hls-signature-help-plugin/test/Main.hs view
@@ -2,14 +2,10 @@ {-# LANGUAGE QuasiQuotes #-}  import           Control.Arrow                            ((>>>))-import           Control.Exception                        (throw)-import           Control.Lens                             ((^.))-import           Data.Maybe                               (fromJust) import           Data.Text                                (Text) import qualified Data.Text                                as T import           Development.IDE.Plugin.Completions.Types (PosPrefixInfo (PosPrefixInfo)) import           Ide.Plugin.SignatureHelp                 (descriptor)-import qualified Language.LSP.Protocol.Lens               as L import           Test.Hls import           Test.Hls.FileSystem                      (VirtualFileTree,                                                            directCradle, file,@@ -411,7 +407,7 @@       virtualFileTree = mkVirtualFileTreeWithSingleFile fileName sourceCode    in runSessionWithServerInTmpDir def plugin virtualFileTree $ do         doc <- openDoc fileName "haskell"-        (fmap . fmap) SimilarSignatureHelp (getSignatureHelp doc position)+        (fmap . fmap) SimilarSignatureHelp (getSignatureHelp doc position Nothing)  mkVirtualFileTreeWithSingleFile :: FilePath -> Text -> VirtualFileTree mkVirtualFileTreeWithSingleFile fileName sourceCode =@@ -499,16 +495,3 @@   InL x ~= InL y = x ~= y   InR x ~= InR y = x ~= y   _ ~= _         = False---- TODO use the one from lsp-test when we have https://github.com/haskell/lsp/pull/621---- | Returns the signature help at the specified position.-getSignatureHelp :: TextDocumentIdentifier -> Position -> Session (Maybe SignatureHelp)-getSignatureHelp doc pos =-  let params = SignatureHelpParams doc pos Nothing Nothing-   in nullToMaybe . getResponseResult <$> request SMethod_TextDocumentSignatureHelp params-  where-    getResponseResult rsp =-      case rsp ^. L.result of-        Right x  -> x-        Left err -> throw $ UnexpectedResponseError (fromJust $ rsp ^. L.id) err
plugins/hls-stylish-haskell-plugin/src/Ide/Plugin/StylishHaskell.hs view
@@ -25,8 +25,11 @@ import           Ide.Types                        hiding (Config) import           Language.Haskell.Stylish import           Language.LSP.Protocol.Types      as LSP-import           System.Directory import           System.FilePath++#if !MIN_VERSION_stylish_haskell(0,15,0)+import           System.Directory+#endif  data Log   = LogLanguageExtensionFromDynFlags
src/HlsPlugins.hs view
@@ -103,10 +103,6 @@  -- formatters -#if hls_floskell-import qualified Ide.Plugin.Floskell               as Floskell-#endif- #if hls_fourmolu import qualified Ide.Plugin.Fourmolu               as Fourmolu #endif@@ -162,9 +158,6 @@       Pragmas.suggestPragmaDescriptor  "pragmas-suggest" :       Pragmas.completionDescriptor  "pragmas-completion" :       Pragmas.suggestDisableWarningDescriptor  "pragmas-disable" :-#endif-#if hls_floskell-      Floskell.descriptor "floskell" : #endif #if hls_fourmolu       let pId = "fourmolu" in Fourmolu.descriptor (pluginRecorder pId) pId:
src/Ide/Arguments.hs view
@@ -22,6 +22,7 @@ import           Ide.Types                     (IdePlugins) import           Options.Applicative import           Paths_haskell_language_server+import           System.Directory              (getCurrentDirectory) import           System.Environment  -- ---------------------------------------------------------------------@@ -38,18 +39,19 @@   | PrintLibDir  data GhcideArguments = GhcideArguments-    { argsCommand           :: Command-    , argsCwd               :: Maybe FilePath-    , argsShakeProfiling    :: Maybe FilePath-    , argsTesting           :: Bool-    , argsExamplePlugin     :: Bool-    , argsLogLevel          :: Priority-    , argsLogFile           :: Maybe String+    { argsCommand                 :: Command+    , argsCwd                     :: Maybe FilePath+    , argsShakeProfiling          :: Maybe FilePath+    , argsTesting                 :: Bool+    , argsExamplePlugin           :: Bool+    , argsLogLevel                :: Priority+    , argsLogFile                 :: Maybe String     -- ^ the minimum log level to show-    , argsLogStderr         :: Bool-    , argsLogClient         :: Bool-    , argsThreads           :: Int-    , argsProjectGhcVersion :: Bool+    , argsLogStderr               :: Bool+    , argsLogClient               :: Bool+    , argsThreads                 :: Int+    , argsProjectGhcVersion       :: Bool+    , argsInitialWorkingDirectory :: FilePath     } deriving Show  data PrintVersion@@ -62,9 +64,11 @@   deriving (Show, Eq, Ord)  getArguments :: String -> IdePlugins IdeState -> IO Arguments-getArguments exeName plugins = execParser opts+getArguments exeName plugins = do+    cwd <- getCurrentDirectory+    execParser (opts cwd)   where-    opts = info ((+    opts cwd = info ((       VersionMode <$> printVersionParser exeName       <|> probeToolsParser exeName       <|> hsubparser@@ -74,7 +78,7 @@         )       <|> listPluginsParser       <|> BiosMode <$> biosParser-      <|> Ghcide <$> arguments plugins+      <|> Ghcide <$> arguments cwd plugins       <|> flag' PrintLibDir (long "print-libdir" <> help "Print project GHCs libdir")       )       <**> helper)@@ -115,8 +119,8 @@   flag' ListPluginsMode     (long "list-plugins" <> help "List all available plugins") -arguments :: IdePlugins IdeState -> Parser GhcideArguments-arguments plugins = GhcideArguments+arguments :: FilePath -> IdePlugins IdeState -> Parser GhcideArguments+arguments cwd plugins = GhcideArguments       <$> (commandP plugins <|> lspCommand <|> checkCommand)       <*> optional (strOption $ long "cwd" <> metavar "DIR"                   <> help "Change to this directory")@@ -186,6 +190,7 @@            )       <*> switch (long "project-ghc-version"                   <> help "Work out the project GHC version and print it")+      <*> pure cwd     where         lspCommand = LSP <$ flag' True (long "lsp" <> help "Start talking to an LSP server")         checkCommand = Check <$> many (argument str (metavar "FILES/DIRS..."))
test/functional/Format.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE CPP               #-} {-# LANGUAGE OverloadedStrings #-} module Format (tests) where @@ -13,7 +12,7 @@ import           Language.LSP.Test import           Test.Hls import           Test.Hls.Command-import           Test.Hls.Flags              (requiresFloskellPlugin,+import           Test.Hls.Flags              (requiresFourmoluPlugin,                                               requiresOrmoluPlugin)  tests :: TestTree@@ -34,11 +33,11 @@             _ -> assertFailure $ "strange response from formatting provider:" ++ show result           result -> assertFailure $ "strange response from formatting provider:" ++ show result -    , requiresOrmoluPlugin . requiresFloskellPlugin $ testCase "can change on the fly" $ runSessionWithConfig (formatConfig "none") hlsLspCommand fullLatestClientCaps "test/testdata/format" $ do+    , requiresOrmoluPlugin . requiresFourmoluPlugin $ testCase "can change on the fly" $ runSessionWithConfig (formatConfig "none") hlsLspCommand fullLatestClientCaps "test/testdata/format" $ do         void configurationRequest         formattedOrmolu <- liftIO $ T.readFile "test/testdata/format/Format.ormolu.formatted.hs"-        formattedFloskell <- liftIO $ T.readFile "test/testdata/format/Format.floskell.formatted.hs"-        formattedOrmoluPostFloskell <- liftIO $ T.readFile "test/testdata/format/Format.ormolu_post_floskell.formatted.hs"+        formattedFourmolu <- liftIO $ T.readFile "test/testdata/format/Format.fourmolu.formatted.hs"+        formattedOrmoluPostFourmolu <- liftIO $ T.readFile "test/testdata/format/Format.ormolu_post_fourmolu.formatted.hs"          doc <- openDoc "Format.hs" "haskell" @@ -46,13 +45,13 @@         formatDoc doc (FormattingOptions 2 True Nothing Nothing Nothing)         documentContents doc >>= liftIO . (@?= formattedOrmolu) -        setHlsConfig (formatLspConfig "floskell")-        formatDoc doc (FormattingOptions 2 True Nothing Nothing Nothing)-        documentContents doc >>= liftIO . (@?= formattedFloskell)+        setHlsConfig (formatLspConfig "fourmolu")+        formatDoc doc (FormattingOptions 4 True Nothing Nothing Nothing)+        documentContents doc >>= liftIO . (@?= formattedFourmolu)          setHlsConfig (formatLspConfig "ormolu")         formatDoc doc (FormattingOptions 2 True Nothing Nothing Nothing)-        documentContents doc >>= liftIO . (@?= formattedOrmoluPostFloskell)+        documentContents doc >>= liftIO . (@?= formattedOrmoluPostFourmolu)     ]  formatLspConfig :: T.Text -> Config
test/functional/Progress.hs view
@@ -13,6 +13,7 @@ import           Data.List                          (delete) import           Data.Maybe                         (fromJust) import           Data.Text                          (Text, pack)+import           GHC.Stack                          (withFrozenCallStack) import           Ide.Types import           Language.LSP.Protocol.Capabilities import qualified Language.LSP.Protocol.Lens         as L@@ -29,7 +30,11 @@             runSession hlsLspCommand progressCaps "test/testdata/diagnostics" $ do                 let path = "Foo.hs"                 _ <- openDoc path "haskell"-                expectProgressMessages [pack ("Setting up diagnostics (for " ++ path ++ ")"), "Processing", "Indexing"] [] []+                expectProgressMessages+                  ["Processing"]+                  [pack ("Setting up diagnostics (for " ++ path ++ ")"), "Indexing"]+                  []+                  []         , requiresEvalPlugin $ testCase "eval plugin sends progress reports" $             runSession hlsLspCommand progressCaps "plugins/hls-eval-plugin/test/testdata" $ do               doc <- openDoc "TIO.hs" "haskell"@@ -37,7 +42,8 @@                (codeLensResponse, createdProgressTokens, activeProgressTokens) <- expectProgressMessagesTill                 (responseForId SMethod_TextDocumentCodeLens lspId)-                ["Setting up testdata (for TIO.hs)", "Processing"]+                ["Setting up testdata (for TIO.hs)"]+                ["Processing", "Indexing"]                 []                 [] @@ -53,24 +59,24 @@                           (command ^. L.command)                           (decode $ encode $ fromJust $ command ^. L.arguments) -                      expectProgressMessages ["Evaluating"] createdProgressTokens activeProgressTokens+                      expectProgressMessages ["Evaluating"] ["Processing", "Indexing"] createdProgressTokens activeProgressTokens                   _ -> error $ "Unexpected response result: " ++ show response         , requiresOrmoluPlugin $ testCase "ormolu plugin sends progress notifications" $ do             runSessionWithConfig (def { ignoreConfigurationRequests = False }) hlsLspCommand progressCaps "test/testdata/format" $ do                 void configurationRequest                 setHlsConfig (formatLspConfig "ormolu")                 doc <- openDoc "Format.hs" "haskell"-                expectProgressMessages ["Setting up format (for Format.hs)", "Processing", "Indexing"] [] []+                expectProgressMessages ["Setting up format (for Format.hs)"] ["Processing", "Indexing"] [] []                 _ <- sendRequest SMethod_TextDocumentFormatting $ DocumentFormattingParams Nothing doc (FormattingOptions 2 True Nothing Nothing Nothing)-                expectProgressMessages ["Formatting Format.hs"] [] []+                expectProgressMessages ["Formatting Format.hs"] ["Processing", "Indexing"] [] []         , requiresFourmoluPlugin $ testCase "fourmolu plugin sends progress notifications" $ do             runSessionWithConfig (def { ignoreConfigurationRequests = False }) hlsLspCommand progressCaps "test/testdata/format" $ do                 void configurationRequest                 setHlsConfig (formatLspConfig "fourmolu")                 doc <- openDoc "Format.hs" "haskell"-                expectProgressMessages ["Setting up format (for Format.hs)", "Processing", "Indexing"] [] []+                expectProgressMessages ["Setting up format (for Format.hs)"] ["Processing", "Indexing"] [] []                 _ <- sendRequest SMethod_TextDocumentFormatting $ DocumentFormattingParams Nothing doc (FormattingOptions 2 True Nothing Nothing Nothing)-                expectProgressMessages ["Formatting Format.hs"] [] []+                expectProgressMessages ["Formatting Format.hs"] ["Processing", "Indexing"] [] []         ]  formatLspConfig :: Text -> Config@@ -114,58 +120,82 @@ interestingMessage theMessage =   fmap InterestingMessage theMessage <|> fmap ProgressMessage progressMessage -expectProgressMessagesTill :: Session a -> [Text] -> [ProgressToken] -> [ProgressToken] -> Session (a, [ProgressToken], [ProgressToken])-expectProgressMessagesTill stopMessage expectedTitles createdProgressTokens activeProgressTokens = do+expectProgressMessagesTill :: HasCallStack => Session a -> [Text] -> [Text] -> [ProgressToken] -> [ProgressToken] -> Session (a, [ProgressToken], [ProgressToken])+expectProgressMessagesTill stopMessage requiredTitles optionalTitles createdProgressTokens activeProgressTokens = do   message <- skipManyTill anyMessage (interestingMessage stopMessage)-  case message of-    InterestingMessage a -> do-      liftIO $ null expectedTitles @? "Expected titles not empty " <> show expectedTitles-      pure (a, createdProgressTokens, activeProgressTokens)-    ProgressMessage progressMessage ->-      updateExpectProgressStateAndRecurseWith-        (expectProgressMessagesTill stopMessage)-        progressMessage-        expectedTitles-        createdProgressTokens-        activeProgressTokens+  withFrozenCallStack $ do+    case message of+      InterestingMessage a -> do+        liftIO $ null requiredTitles @? "Required progress titles were not seen (consider moving to the optional list): " <> show requiredTitles+        pure (a, createdProgressTokens, activeProgressTokens)+      ProgressMessage progressMessage ->+        updateExpectProgressStateAndRecurseWith+          (expectProgressMessagesTill stopMessage)+          progressMessage+          requiredTitles+          optionalTitles+          createdProgressTokens+          activeProgressTokens  {- | Test that the server is correctly producing a sequence of progress related- messages. Creates can be dangling, but should be paired with a corresponding begin and end,- optionally with some progress in between. Tokens must match. The begin- messages have titles describing the work that is in-progress, we check that- the titles we see are those we expect.+ messages. `create` can be dangling, but `begin` cannot. Each `begin` should+ have a respective `end`, optionally with some progress in between. Tokens must+ match between these requests. The `begin` messages have titles describing the+ work that is in-progress.++ 'requiredTitles' must all be consumed before the loop terminates.+ 'optionalTitles' may appear (and if they do, their titles must match), but+ their absence is not an error. The LSP spec permits a server to create a+ progress token and then abandon it without ever sending a `begin` notification+ (e.g. when the underlying work completes before the progress-reporting thread+ starts). Any `begin` title that is neither required nor optional is treated as+ unexpected and fails the test.++ The loop terminates when all required titles have been seen and all active progress+ sessions have ended. -}-expectProgressMessages :: [Text] -> [ProgressToken] -> [ProgressToken] -> Session ()-expectProgressMessages [] _ [] = pure ()-expectProgressMessages expectedTitles createdProgressTokens activeProgressTokens = do+expectProgressMessages :: HasCallStack => [Text] -> [Text] -> [ProgressToken] -> [ProgressToken] -> Session ()+expectProgressMessages [] _ _ [] = pure ()+expectProgressMessages requiredTitles optionalTitles createdProgressTokens activeProgressTokens = do   message <- skipManyTill anyMessage progressMessage-  updateExpectProgressStateAndRecurseWith expectProgressMessages message expectedTitles createdProgressTokens activeProgressTokens+  withFrozenCallStack $+    updateExpectProgressStateAndRecurseWith expectProgressMessages message requiredTitles optionalTitles createdProgressTokens activeProgressTokens -updateExpectProgressStateAndRecurseWith :: ([Text] -> [ProgressToken] -> [ProgressToken] -> Session a)+updateExpectProgressStateAndRecurseWith :: HasCallStack+                                        => ([Text] -> [Text] -> [ProgressToken] -> [ProgressToken] -> Session a)                                         -> ProgressMessage                                         -> [Text]+                                        -> [Text]                                         -> [ProgressToken]                                         -> [ProgressToken]                                         -> Session a-updateExpectProgressStateAndRecurseWith f progressMessage expectedTitles createdProgressTokens activeProgressTokens = do+updateExpectProgressStateAndRecurseWith f progressMessage requiredTitles optionalTitles createdProgressTokens activeProgressTokens = do   case progressMessage of     ProgressCreate params -> do-      f expectedTitles ((params ^. L.token): createdProgressTokens) activeProgressTokens+      f requiredTitles optionalTitles ((params ^. L.token): createdProgressTokens) activeProgressTokens     ProgressBegin token params -> do       liftIO $ token `expectedIn` createdProgressTokens-      f (delete (params ^. L.title) expectedTitles) (delete token createdProgressTokens) (token:activeProgressTokens)+      let title = params ^. L.title+          (requiredTitles', optionalTitles')+            | title `elem` requiredTitles = (delete title requiredTitles, optionalTitles)+             -- Note that we do not delete from the optional titles list.+            | title `elem` optionalTitles = (requiredTitles, optionalTitles)+            | otherwise = error $ "Unexpected progress title: " ++ show title+                ++ "\n  required: " ++ show requiredTitles+                ++ "\n  optional: " ++ show optionalTitles+      f requiredTitles' optionalTitles' (delete token createdProgressTokens) (token:activeProgressTokens)     ProgressReport token _ -> do       liftIO $ token `expectedIn` activeProgressTokens-      f expectedTitles createdProgressTokens activeProgressTokens+      f requiredTitles optionalTitles createdProgressTokens activeProgressTokens     ProgressEnd token _ -> do       liftIO $ token `expectedIn` activeProgressTokens-      f expectedTitles createdProgressTokens (delete token activeProgressTokens)+      f requiredTitles optionalTitles createdProgressTokens (delete token activeProgressTokens)  -expectedIn :: (Foldable t, Eq a, Show a) => a -> t a -> Assertion+expectedIn :: (Foldable t, Eq a, Show a, HasCallStack) => a -> t a -> Assertion expectedIn a as = a `elem` as @? "Unexpected " ++ show a -getMessageResult :: Show (ErrorData m) => TResponseMessage m -> MessageResult m+getMessageResult :: (Show (ErrorData m), HasCallStack) => TResponseMessage m -> MessageResult m getMessageResult rsp =   case rsp ^. L.result of     Right x  -> x
− test/testdata/format/Format.floskell.formatted.hs
@@ -1,16 +0,0 @@-module Format where--import           Data.Int-import           Data.List-import           Prelude--foo :: Int -> Int-foo 3 = 2-foo x = x--bar :: String -> IO String-bar s = do-  x <- return "hello"-  return "asdf"--data Baz = Baz { a :: Int, b :: String }
+ test/testdata/format/Format.fourmolu.formatted.hs view
@@ -0,0 +1,16 @@+module Format where++import Data.Int+import Data.List+import Prelude++foo :: Int -> Int+foo 3 = 2+foo x = x++bar :: String -> IO String+bar s = do+    x <- return "hello"+    return "asdf"++data Baz = Baz {a :: Int, b :: String}
− test/testdata/format/Format.ormolu_post_floskell.formatted.hs
@@ -1,16 +0,0 @@-module Format where--import Data.Int-import Data.List-import Prelude--foo :: Int -> Int-foo 3 = 2-foo x = x--bar :: String -> IO String-bar s = do-  x <- return "hello"-  return "asdf"--data Baz = Baz {a :: Int, b :: String}
+ test/testdata/format/Format.ormolu_post_fourmolu.formatted.hs view
@@ -0,0 +1,16 @@+module Format where++import Data.Int+import Data.List+import Prelude++foo :: Int -> Int+foo 3 = 2+foo x = x++bar :: String -> IO String+bar s = do+  x <- return "hello"+  return "asdf"++data Baz = Baz {a :: Int, b :: String}
+ test/testdata/hieBiosMainIs/dist-newstyle/cache/plan.json view
@@ -0,0 +1,1 @@+{"cabal-version":"3.10.2.0","cabal-lib-version":"3.10.2.1","compiler-id":"ghc-9.8.1","os":"linux","arch":"x86_64","install-plan":[{"type":"pre-existing","id":"base-4.19.0.0-c1f2","pkg-name":"base","pkg-version":"4.19.0.0","depends":["ghc-bignum-1.3-d2d5","ghc-prim-0.11.0-b1d6","rts-1.0.2"]},{"type":"pre-existing","id":"ghc-bignum-1.3-d2d5","pkg-name":"ghc-bignum","pkg-version":"1.3","depends":["ghc-prim-0.11.0-b1d6"]},{"type":"pre-existing","id":"ghc-prim-0.11.0-b1d6","pkg-name":"ghc-prim","pkg-version":"0.11.0","depends":["rts-1.0.2"]},{"type":"configured","id":"hieBiosMainIs-0.1.0.0-inplace-hieBiosMainIs","pkg-name":"hieBiosMainIs","pkg-version":"0.1.0.0","flags":{},"style":"local","pkg-src":{"type":"local","path":"/home/zubin/haskell-language-server/test/testdata/hieBiosMainIs/."},"dist-dir":"/home/zubin/haskell-language-server/test/testdata/hieBiosMainIs/dist-newstyle/build/x86_64-linux/ghc-9.8.1/hieBiosMainIs-0.1.0.0/x/hieBiosMainIs","build-info":"/home/zubin/haskell-language-server/test/testdata/hieBiosMainIs/dist-newstyle/build/x86_64-linux/ghc-9.8.1/hieBiosMainIs-0.1.0.0/x/hieBiosMainIs/build-info.json","depends":["base-4.19.0.0-c1f2"],"exe-depends":[],"component-name":"exe:hieBiosMainIs","bin-file":"/home/zubin/haskell-language-server/test/testdata/hieBiosMainIs/dist-newstyle/build/x86_64-linux/ghc-9.8.1/hieBiosMainIs-0.1.0.0/x/hieBiosMainIs/build/hieBiosMainIs/hieBiosMainIs"},{"type":"pre-existing","id":"rts-1.0.2","pkg-name":"rts","pkg-version":"1.0.2","depends":[]}]}
+ test/testdata/missingModuleTest/missingModule/dist-newstyle/cache/plan.json view
@@ -0,0 +1,1 @@+{"cabal-version":"3.10.2.0","cabal-lib-version":"3.10.2.1","compiler-id":"ghc-9.8.1","os":"linux","arch":"x86_64","install-plan":[{"type":"pre-existing","id":"base-4.19.0.0-c1f2","pkg-name":"base","pkg-version":"4.19.0.0","depends":["ghc-bignum-1.3-d2d5","ghc-prim-0.11.0-b1d6","rts-1.0.2"]},{"type":"pre-existing","id":"ghc-bignum-1.3-d2d5","pkg-name":"ghc-bignum","pkg-version":"1.3","depends":["ghc-prim-0.11.0-b1d6"]},{"type":"pre-existing","id":"ghc-prim-0.11.0-b1d6","pkg-name":"ghc-prim","pkg-version":"0.11.0","depends":["rts-1.0.2"]},{"type":"configured","id":"missingModule-0.1.0.0-inplace","pkg-name":"missingModule","pkg-version":"0.1.0.0","flags":{},"style":"local","pkg-src":{"type":"local","path":"/home/zubin/haskell-language-server/test/testdata/missingModuleTest/missingModule/."},"dist-dir":"/home/zubin/haskell-language-server/test/testdata/missingModuleTest/missingModule/dist-newstyle/build/x86_64-linux/ghc-9.8.1/missingModule-0.1.0.0","build-info":"/home/zubin/haskell-language-server/test/testdata/missingModuleTest/missingModule/dist-newstyle/build/x86_64-linux/ghc-9.8.1/missingModule-0.1.0.0/build-info.json","depends":["base-4.19.0.0-c1f2"],"exe-depends":[],"component-name":"lib"},{"type":"pre-existing","id":"rts-1.0.2","pkg-name":"rts","pkg-version":"1.0.2","depends":[]}]}
+ test/testdata/missingModuleTest/noPrefixMatch/dist-newstyle/cache/plan.json view
@@ -0,0 +1,1 @@+{"cabal-version":"3.10.2.0","cabal-lib-version":"3.10.2.1","compiler-id":"ghc-9.8.1","os":"linux","arch":"x86_64","install-plan":[{"type":"pre-existing","id":"base-4.19.0.0-c1f2","pkg-name":"base","pkg-version":"4.19.0.0","depends":["ghc-bignum-1.3-d2d5","ghc-prim-0.11.0-b1d6","rts-1.0.2"]},{"type":"pre-existing","id":"ghc-bignum-1.3-d2d5","pkg-name":"ghc-bignum","pkg-version":"1.3","depends":["ghc-prim-0.11.0-b1d6"]},{"type":"pre-existing","id":"ghc-prim-0.11.0-b1d6","pkg-name":"ghc-prim","pkg-version":"0.11.0","depends":["rts-1.0.2"]},{"type":"configured","id":"noPrefixMatch-0.1.0.0-inplace-testExe","pkg-name":"noPrefixMatch","pkg-version":"0.1.0.0","flags":{},"style":"local","pkg-src":{"type":"local","path":"/home/zubin/haskell-language-server/test/testdata/missingModuleTest/noPrefixMatch/."},"dist-dir":"/home/zubin/haskell-language-server/test/testdata/missingModuleTest/noPrefixMatch/dist-newstyle/build/x86_64-linux/ghc-9.8.1/noPrefixMatch-0.1.0.0/x/testExe","build-info":"/home/zubin/haskell-language-server/test/testdata/missingModuleTest/noPrefixMatch/dist-newstyle/build/x86_64-linux/ghc-9.8.1/noPrefixMatch-0.1.0.0/x/testExe/build-info.json","depends":["base-4.19.0.0-c1f2"],"exe-depends":[],"component-name":"exe:testExe","bin-file":"/home/zubin/haskell-language-server/test/testdata/missingModuleTest/noPrefixMatch/dist-newstyle/build/x86_64-linux/ghc-9.8.1/noPrefixMatch-0.1.0.0/x/testExe/build/testExe/testExe"},{"type":"pre-existing","id":"rts-1.0.2","pkg-name":"rts","pkg-version":"1.0.2","depends":[]}]}
test/testdata/schema/ghc910/default-config.golden.json view
@@ -96,8 +96,9 @@             "codeLensOn": true,             "inlayHintsOn": true         },-        "moduleName": {-            "globalOn": true+        "notes": {+            "completionOn": true,+            "hoverOn": true         },         "ormolu": {             "config": {@@ -120,10 +121,11 @@             "globalOn": true         },         "rename": {+            "codeLensOn": true,             "config": {-                "crossModule": false+                "crossModule": true             },-            "globalOn": true+            "renameOn": true         },         "semanticTokens": {             "config": {
test/testdata/schema/ghc910/vscode-extension-schema.golden.json view
@@ -231,12 +231,18 @@         "scope": "resource",         "type": "boolean"     },-    "haskell.plugin.moduleName.globalOn": {+    "haskell.plugin.notes.completionOn": {         "default": true,-        "description": "Enables moduleName plugin",+        "description": "Enables notes completions",         "scope": "resource",         "type": "boolean"     },+    "haskell.plugin.notes.hoverOn": {+        "default": true,+        "description": "Enables notes hover",+        "scope": "resource",+        "type": "boolean"+    },     "haskell.plugin.ormolu.config.external": {         "default": false,         "markdownDescription": "Call out to an external \"ormolu\" executable, rather than using the bundled library",@@ -273,15 +279,21 @@         "scope": "resource",         "type": "boolean"     },+    "haskell.plugin.rename.codeLensOn": {+        "default": true,+        "description": "Enables rename code lenses",+        "scope": "resource",+        "type": "boolean"+    },     "haskell.plugin.rename.config.crossModule": {-        "default": false,+        "default": true,         "markdownDescription": "Enable experimental cross-module renaming",         "scope": "resource",         "type": "boolean"     },-    "haskell.plugin.rename.globalOn": {+    "haskell.plugin.rename.renameOn": {         "default": true,-        "description": "Enables rename plugin",+        "description": "Enables rename rename",         "scope": "resource",         "type": "boolean"     },
test/testdata/schema/ghc912/default-config.golden.json view
@@ -103,8 +103,9 @@             "codeLensOn": true,             "inlayHintsOn": true         },-        "moduleName": {-            "globalOn": true+        "notes": {+            "completionOn": true,+            "hoverOn": true         },         "ormolu": {             "config": {@@ -127,10 +128,11 @@             "globalOn": true         },         "rename": {+            "codeLensOn": true,             "config": {-                "crossModule": false+                "crossModule": true             },-            "globalOn": true+            "renameOn": true         },         "semanticTokens": {             "config": {
test/testdata/schema/ghc912/vscode-extension-schema.golden.json view
@@ -249,12 +249,18 @@         "scope": "resource",         "type": "boolean"     },-    "haskell.plugin.moduleName.globalOn": {+    "haskell.plugin.notes.completionOn": {         "default": true,-        "description": "Enables moduleName plugin",+        "description": "Enables notes completions",         "scope": "resource",         "type": "boolean"     },+    "haskell.plugin.notes.hoverOn": {+        "default": true,+        "description": "Enables notes hover",+        "scope": "resource",+        "type": "boolean"+    },     "haskell.plugin.ormolu.config.external": {         "default": false,         "markdownDescription": "Call out to an external \"ormolu\" executable, rather than using the bundled library",@@ -291,15 +297,21 @@         "scope": "resource",         "type": "boolean"     },+    "haskell.plugin.rename.codeLensOn": {+        "default": true,+        "description": "Enables rename code lenses",+        "scope": "resource",+        "type": "boolean"+    },     "haskell.plugin.rename.config.crossModule": {-        "default": false,+        "default": true,         "markdownDescription": "Enable experimental cross-module renaming",         "scope": "resource",         "type": "boolean"     },-    "haskell.plugin.rename.globalOn": {+    "haskell.plugin.rename.renameOn": {         "default": true,-        "description": "Enables rename plugin",+        "description": "Enables rename rename",         "scope": "resource",         "type": "boolean"     },
test/testdata/schema/ghc914/default-config.golden.json view
@@ -90,8 +90,9 @@             "codeLensOn": true,             "inlayHintsOn": true         },-        "moduleName": {-            "globalOn": true+        "notes": {+            "completionOn": true,+            "hoverOn": true         },         "overloaded-record-dot": {             "globalOn": true@@ -109,10 +110,11 @@             "globalOn": true         },         "rename": {+            "codeLensOn": true,             "config": {-                "crossModule": false+                "crossModule": true             },-            "globalOn": true+            "renameOn": true         },         "semanticTokens": {             "config": {
test/testdata/schema/ghc914/vscode-extension-schema.golden.json view
@@ -219,12 +219,18 @@         "scope": "resource",         "type": "boolean"     },-    "haskell.plugin.moduleName.globalOn": {+    "haskell.plugin.notes.completionOn": {         "default": true,-        "description": "Enables moduleName plugin",+        "description": "Enables notes completions",         "scope": "resource",         "type": "boolean"     },+    "haskell.plugin.notes.hoverOn": {+        "default": true,+        "description": "Enables notes hover",+        "scope": "resource",+        "type": "boolean"+    },     "haskell.plugin.overloaded-record-dot.globalOn": {         "default": true,         "description": "Enables overloaded-record-dot plugin",@@ -255,15 +261,21 @@         "scope": "resource",         "type": "boolean"     },+    "haskell.plugin.rename.codeLensOn": {+        "default": true,+        "description": "Enables rename code lenses",+        "scope": "resource",+        "type": "boolean"+    },     "haskell.plugin.rename.config.crossModule": {-        "default": false,+        "default": true,         "markdownDescription": "Enable experimental cross-module renaming",         "scope": "resource",         "type": "boolean"     },-    "haskell.plugin.rename.globalOn": {+    "haskell.plugin.rename.renameOn": {         "default": true,-        "description": "Enables rename plugin",+        "description": "Enables rename rename",         "scope": "resource",         "type": "boolean"     },
test/testdata/schema/ghc96/default-config.golden.json view
@@ -103,8 +103,9 @@             "codeLensOn": true,             "inlayHintsOn": true         },-        "moduleName": {-            "globalOn": true+        "notes": {+            "completionOn": true,+            "hoverOn": true         },         "ormolu": {             "config": {@@ -127,10 +128,11 @@             "globalOn": true         },         "rename": {+            "codeLensOn": true,             "config": {-                "crossModule": false+                "crossModule": true             },-            "globalOn": true+            "renameOn": true         },         "retrie": {             "globalOn": true
test/testdata/schema/ghc96/vscode-extension-schema.golden.json view
@@ -249,12 +249,18 @@         "scope": "resource",         "type": "boolean"     },-    "haskell.plugin.moduleName.globalOn": {+    "haskell.plugin.notes.completionOn": {         "default": true,-        "description": "Enables moduleName plugin",+        "description": "Enables notes completions",         "scope": "resource",         "type": "boolean"     },+    "haskell.plugin.notes.hoverOn": {+        "default": true,+        "description": "Enables notes hover",+        "scope": "resource",+        "type": "boolean"+    },     "haskell.plugin.ormolu.config.external": {         "default": false,         "markdownDescription": "Call out to an external \"ormolu\" executable, rather than using the bundled library",@@ -291,15 +297,21 @@         "scope": "resource",         "type": "boolean"     },+    "haskell.plugin.rename.codeLensOn": {+        "default": true,+        "description": "Enables rename code lenses",+        "scope": "resource",+        "type": "boolean"+    },     "haskell.plugin.rename.config.crossModule": {-        "default": false,+        "default": true,         "markdownDescription": "Enable experimental cross-module renaming",         "scope": "resource",         "type": "boolean"     },-    "haskell.plugin.rename.globalOn": {+    "haskell.plugin.rename.renameOn": {         "default": true,-        "description": "Enables rename plugin",+        "description": "Enables rename rename",         "scope": "resource",         "type": "boolean"     },
test/testdata/schema/ghc98/default-config.golden.json view
@@ -103,8 +103,9 @@             "codeLensOn": true,             "inlayHintsOn": true         },-        "moduleName": {-            "globalOn": true+        "notes": {+            "completionOn": true,+            "hoverOn": true         },         "ormolu": {             "config": {@@ -127,10 +128,11 @@             "globalOn": true         },         "rename": {+            "codeLensOn": true,             "config": {-                "crossModule": false+                "crossModule": true             },-            "globalOn": true+            "renameOn": true         },         "retrie": {             "globalOn": true
test/testdata/schema/ghc98/vscode-extension-schema.golden.json view
@@ -249,12 +249,18 @@         "scope": "resource",         "type": "boolean"     },-    "haskell.plugin.moduleName.globalOn": {+    "haskell.plugin.notes.completionOn": {         "default": true,-        "description": "Enables moduleName plugin",+        "description": "Enables notes completions",         "scope": "resource",         "type": "boolean"     },+    "haskell.plugin.notes.hoverOn": {+        "default": true,+        "description": "Enables notes hover",+        "scope": "resource",+        "type": "boolean"+    },     "haskell.plugin.ormolu.config.external": {         "default": false,         "markdownDescription": "Call out to an external \"ormolu\" executable, rather than using the bundled library",@@ -291,15 +297,21 @@         "scope": "resource",         "type": "boolean"     },+    "haskell.plugin.rename.codeLensOn": {+        "default": true,+        "description": "Enables rename code lenses",+        "scope": "resource",+        "type": "boolean"+    },     "haskell.plugin.rename.config.crossModule": {-        "default": false,+        "default": true,         "markdownDescription": "Enable experimental cross-module renaming",         "scope": "resource",         "type": "boolean"     },-    "haskell.plugin.rename.globalOn": {+    "haskell.plugin.rename.renameOn": {         "default": true,-        "description": "Enables rename plugin",+        "description": "Enables rename rename",         "scope": "resource",         "type": "boolean"     },
test/utils/Test/Hls/Flags.hs view
@@ -17,14 +17,6 @@ #endif  -- * Formatters--- | Disable test unless the floskell flag is set-requiresFloskellPlugin        :: TestTree -> TestTree-#if hls_floskell-requiresFloskellPlugin        = id-#else-requiresFloskellPlugin        = ignoreTestBecause "Floskell plugin disabled"-#endif- -- | Disable test unless the fourmolu flag is set requiresFourmoluPlugin        :: TestTree -> TestTree #if hls_fourmolu