Skip to content

Commit e52ceae

Browse files
committed
References Use db for findDef
save source file location to db Find source for boot files Use DynFlags from HieDb instead of unsafeGlobalDynFlags Return multiple definitions don't typecheck files on load Add support for persistent stale values Add persistent hie file rule docs wip better typedef defs for deps update hiedb Fix for files with errors Fix build integrate hiedb commands and set dynflags on boot workspace symbol tweaks, cabal.project Write ifaces on save use real mtime for saved files safe indexing bump hiedb Proper refs for FOIs hlint Update exe/Main.hs Co-authored-by: Pepe Iborra <pepeiborra@me.com> Review comments update hiedb Update src/Development/IDE/Core/Shake.hs Co-authored-by: Pepe Iborra <pepeiborra@me.com> Update src/Development/IDE/Core/Rules.hs Co-authored-by: Pepe Iborra <pepeiborra@me.com> Update src/Development/IDE/Core/Rules.hs Co-authored-by: Pepe Iborra <pepeiborra@me.com> Update src/Development/IDE/Spans/AtPoint.hs Co-authored-by: Pepe Iborra <pepeiborra@me.com> Update src/Development/IDE/Core/Rules.hs Co-authored-by: Pepe Iborra <pepeiborra@me.com> Apply suggestions from code review Co-authored-by: Pepe Iborra <pepeiborra@me.com> more careful re-indexing update for hiedb-0.1.0.0 Remove cached-deps stuff for now explicit showSDoc docs in AtPoint add doc comment about database consistency add TODO for better position mapping from diff Make HLS compile with (GHC 8.10.2) and ghcide+hiedb. Hacks to tactics plugic, class plugin, cabal file, and main. HLS unit tests for the find references functionality. Mark failing test. Hlint fromRight hint. Hlint nubOrd. Update ignore annotation to point to correct function. This block of code that uses nub used to be part of main. But it got split off into a new function called runIde, and the hlint annotation was not updated to match. Update tests to document that we now have references and workspace symbol providers. Fix line number in broken test. We have this code: 20: ddd :: Num a => a -> a -> a 21: ddd vv ww = vv +! ww The intention was to ask for the type definition of symbol "a" in line 20, and then assert that no type definitions were found. The reality is that the test was asking for the definition of the symbol at (20, 15) in 0-based indexing, which is the "!" in "+!". Until recently, ghcide could not find type definitions for "+!", so no type definitions were found and the test passed. But now, ghcide can find type definitions for "+!", and this test has begun to fail. The solution is to change (20, 15) to (19, 15), so that we ask for the type definitions of the symbol "a", which will not be found. Update testcase. Getting a type definition can produce more than one result. E.g. the type of the symbol "pid" in: 1: data Parameter a = Parameter a 2: f :: forall a. Parameter a -> Parameter a 3: f pid = pid is (Parameter a), and the definition of this type is two part: the definition of Parameter on line 1, and the definition of a on line 2. Kludge compilation for <=8.8.3. FunTy takes a third argument in 8.10.1 and on. Indexing improvments, persistent rules, diff mapping fix position mapping for persistent position mapping improvements local type references Move hiedb initialization stuff to session-loader Document indexHieFile Update HLS formatting hiedb from hackage Unboxed vector for position mapping tests rebase fixes
1 parent 0403dbf commit e52ceae

40 files changed

+1491
-401
lines changed

ghcide/exe/Arguments.hs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,52 @@
11
-- Copyright (c) 2019 The DAML Authors. All rights reserved.
22
-- SPDX-License-Identifier: Apache-2.0
33

4-
module Arguments(Arguments(..), getArguments) where
4+
module Arguments(Arguments, Arguments'(..), getArguments, IdeCmd(..)) where
55

66
import Options.Applicative
7+
import HieDb.Run
78

9+
type Arguments = Arguments' IdeCmd
810

9-
data Arguments = Arguments
11+
data IdeCmd = Typecheck [FilePath] | DbCmd Command | LSP
12+
13+
data Arguments' a = Arguments
1014
{argLSP :: Bool
1115
,argsCwd :: Maybe FilePath
12-
,argFiles :: [FilePath]
1316
,argsVersion :: Bool
1417
,argsShakeProfiling :: Maybe FilePath
1518
,argsOTMemoryProfiling :: Bool
1619
,argsTesting :: Bool
1720
,argsDisableKick :: Bool
1821
,argsThreads :: Int
1922
,argsVerbose :: Bool
23+
,argFilesOrCmd :: a
2024
}
2125

2226
getArguments :: IO Arguments
2327
getArguments = execParser opts
2428
where
2529
opts = info (arguments <**> helper)
2630
( fullDesc
27-
<> progDesc "Used as a test bed to check your IDE will work"
2831
<> header "ghcide - the core of a Haskell IDE")
2932

3033
arguments :: Parser Arguments
3134
arguments = Arguments
3235
<$> switch (long "lsp" <> help "Start talking to an LSP server")
3336
<*> optional (strOption $ long "cwd" <> metavar "DIR" <> help "Change to this directory")
34-
<*> many (argument str (metavar "FILES/DIRS..."))
3537
<*> switch (long "version" <> help "Show ghcide and GHC versions")
3638
<*> optional (strOption $ long "shake-profiling" <> metavar "DIR" <> help "Dump profiling reports to this directory")
3739
<*> switch (long "ot-memory-profiling" <> help "Record OpenTelemetry info to the eventlog. Needs the -l RTS flag to have an effect")
3840
<*> switch (long "test" <> help "Enable additional lsp messages used by the testsuite")
3941
<*> switch (long "test-no-kick" <> help "Disable kick. Useful for testing cancellation")
4042
<*> option auto (short 'j' <> help "Number of threads (0: automatic)" <> metavar "NUM" <> value 0 <> showDefault)
4143
<*> switch (long "verbose" <> help "Include internal events in logging output")
44+
<*> ( hsubparser (command "typecheck" (info (Typecheck <$> fileCmd) fileInfo)
45+
<> command "hiedb" (info (DbCmd <$> cmdParser <**> helper) hieInfo)
46+
<> command "lsp" (info (pure LSP <**> helper) lspInfo) )
47+
<|> Typecheck <$> fileCmd )
48+
where
49+
fileCmd = many (argument str (metavar "FILES/DIRS..."))
50+
lspInfo = fullDesc <> progDesc "Start talking to an LSP server"
51+
fileInfo = fullDesc <> progDesc "Used as a test bed to check your IDE will work"
52+
hieInfo = fullDesc <> progDesc "Query .hie files"

ghcide/exe/Main.hs

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import Development.IDE.Types.Options
2929
import Development.IDE.Types.Logger
3030
import Development.IDE.Plugin
3131
import Development.IDE.Plugin.Test as Test
32-
import Development.IDE.Session (loadSession)
32+
import Development.IDE.Session (loadSession, setInitialDynFlags, getHieDbLoc, runWithDb)
3333
import Development.Shake (ShakeOptions (shakeThreads))
3434
import qualified Language.Haskell.LSP.Core as LSP
3535
import Language.Haskell.LSP.Messages
@@ -58,6 +58,9 @@ import Development.IDE.Plugin.HLS.GhcIde as GhcIde
5858
import Ide.Plugin.Config
5959
import Ide.PluginUtils (allLspCmdIds', getProcessID, pluginDescToIdePlugins)
6060

61+
import HieDb.Types (LibDir(..))
62+
import HieDb.Run (Options(..), runCommand)
63+
6164
ghcideVersion :: IO String
6265
ghcideVersion = do
6366
path <- getExecutablePath
@@ -78,6 +81,31 @@ main = do
7881
if argsVersion then ghcideVersion >>= putStrLn >> exitSuccess
7982
else hPutStrLn stderr {- see WARNING above -} =<< ghcideVersion
8083

84+
whenJust argsCwd IO.setCurrentDirectory
85+
86+
-- We want to set the global DynFlags right now, so that we can use
87+
-- `unsafeGlobalDynFlags` even before the project is configured
88+
libdir <- setInitialDynFlags
89+
90+
dir <- IO.getCurrentDirectory
91+
dbLoc <- getHieDbLoc dir
92+
93+
case argFilesOrCmd of
94+
DbCmd cmd -> do
95+
let opts :: Options
96+
opts = Options
97+
{ database = dbLoc
98+
, trace = False
99+
, quiet = False
100+
, virtualFile = False
101+
}
102+
runCommand (LibDir $ fromJust libdir) opts cmd
103+
Typecheck (Just -> argFilesOrCmd) | not argLSP -> runWithDb dbLoc $ runIde Arguments{..}
104+
_ -> let argFilesOrCmd = Nothing in runWithDb dbLoc $ runIde Arguments{..}
105+
106+
107+
runIde :: Arguments' (Maybe [FilePath]) -> HieDb -> IndexQueue -> IO ()
108+
runIde Arguments{..} hiedb hiechan = do
81109
-- lock to avoid overlapping output on stdout
82110
lock <- newLock
83111
let logger p = Logger $ \pri msg -> when (pri >= p) $ withLock lock $
@@ -105,8 +133,8 @@ main = do
105133
options = def { LSP.executeCommandCommands = Just hlsCommands
106134
, LSP.completionTriggerCharacters = Just "."
107135
}
108-
109-
if argLSP then do
136+
case argFilesOrCmd of
137+
Nothing -> do
110138
t <- offsetTime
111139
hPutStrLn stderr "Starting LSP server..."
112140
hPutStrLn stderr "If you are seeing this in a terminal, you probably should have run ghcide WITHOUT the --lsp option!"
@@ -136,8 +164,8 @@ main = do
136164
unless argsDisableKick $
137165
action kick
138166
initialise caps rules
139-
getLspId event wProg wIndefProg (logger logLevel) debouncer options vfs
140-
else do
167+
getLspId event wProg wIndefProg (logger logLevel) debouncer options vfs hiedb hiechan
168+
Just argFiles -> do
141169
-- GHC produces messages with UTF8 in them, so make sure the terminal doesn't error
142170
hSetEncoding stdout utf8
143171
hSetEncoding stderr utf8
@@ -172,7 +200,7 @@ main = do
172200
}
173201
defOptions = defaultIdeOptions sessionLoader
174202
logLevel = if argsVerbose then minBound else Info
175-
ide <- initialise def mainRule (pure $ IdInt 0) (showEvent lock) dummyWithProg (const (const id)) (logger logLevel) debouncer options vfs
203+
ide <- initialise def mainRule (pure $ IdInt 0) (showEvent lock) dummyWithProg (const (const id)) (logger logLevel) debouncer options vfs hiedb hiechan
176204

177205
putStrLn "\nStep 4/4: Type checking the files"
178206
setFilesOfInterest ide $ HashMap.fromList $ map ((, OnDisk) . toNormalizedFilePath') files
@@ -201,7 +229,7 @@ main = do
201229

202230
unless (null failed) (exitWith $ ExitFailure (length failed))
203231

204-
{-# ANN main ("HLint: ignore Use nubOrd" :: String) #-}
232+
{-# ANN runIde ("HLint: ignore Use nubOrd" :: String) #-}
205233

206234
expandFiles :: [FilePath] -> IO [FilePath]
207235
expandFiles = concatMapM $ \x -> do

ghcide/ghcide.cabal

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ library
6060
hie-compat,
6161
hls-plugin-api >= 0.6,
6262
lens,
63+
hiedb == 0.2.0.*,
6364
mtl,
6465
network-uri,
6566
parallel,
@@ -73,6 +74,7 @@ library
7374
safe-exceptions,
7475
shake >= 0.18.4,
7576
sorted-list,
77+
sqlite-simple,
7678
stm,
7779
syb,
7880
text,
@@ -82,6 +84,9 @@ library
8284
utf8-string,
8385
vector,
8486
hslogger,
87+
Diff,
88+
vector,
89+
bytestring-encoding,
8590
opentelemetry >=0.6.1,
8691
heapsize ==0.3.*
8792
if flag(ghc-lib)
@@ -249,6 +254,8 @@ executable ghcide
249254
if flag(ghc-lib)
250255
buildable: False
251256
default-language: Haskell2010
257+
include-dirs:
258+
include
252259
hs-source-dirs: exe
253260
ghc-options:
254261
-threaded
@@ -262,13 +269,15 @@ executable ghcide
262269
"-with-rtsopts=-I0 -A128M"
263270
main-is: Main.hs
264271
build-depends:
272+
hiedb,
265273
aeson,
266274
base == 4.*,
267275
data-default,
268276
directory,
269277
extra,
270278
filepath,
271279
gitrev,
280+
ghc,
272281
hashable,
273282
haskell-lsp,
274283
haskell-lsp-types,

ghcide/session-loader/Development/IDE/Session.hs

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
{-# LANGUAGE TypeFamilies #-}
2+
{-# LANGUAGE CPP #-}
3+
#include "ghc-api-version.h"
24

35
{-|
46
The logic for setting up a ghcide session by tapping into hie-bios.
@@ -8,6 +10,9 @@ module Development.IDE.Session
810
,defaultLoadingOptions
911
,loadSession
1012
,loadSessionWithOptions
13+
,setInitialDynFlags
14+
,getHieDbLoc
15+
,runWithDb
1116
) where
1217

1318
-- Unfortunately, we cannot use loadSession with ghc-lib since hie-bios uses
@@ -71,6 +76,15 @@ import Packages
7176
import Control.Exception (evaluate)
7277
import Data.Void
7378

79+
import HieDb.Create
80+
import HieDb.Types
81+
import HieDb.Utils
82+
import Database.SQLite.Simple
83+
import Control.Concurrent.STM.TQueue
84+
import Control.Concurrent.STM (atomically)
85+
import Maybes (MaybeT(runMaybeT))
86+
import HIE.Bios.Cradle (yamlConfig)
87+
7488

7589
data CacheDirs = CacheDirs
7690
{ hiCacheDir, hieCacheDir, oCacheDir :: Maybe FilePath}
@@ -91,6 +105,47 @@ defaultLoadingOptions = SessionLoadingOptions
91105
,getCacheDirs = getCacheDirsDefault
92106
}
93107

108+
-- | Sets `unsafeGlobalDynFlags` on using the hie-bios cradle and returns the GHC libdir
109+
setInitialDynFlags :: IO (Maybe FilePath)
110+
setInitialDynFlags = do
111+
dir <- IO.getCurrentDirectory
112+
hieYaml <- runMaybeT $ yamlConfig dir
113+
cradle <- maybe (HieBios.loadImplicitCradle $ addTrailingPathSeparator dir) HieBios.loadCradle hieYaml
114+
libDirRes <- getRuntimeGhcLibDir cradle
115+
libdir <- case libDirRes of
116+
CradleSuccess libdir -> pure $ Just libdir
117+
CradleFail err -> do
118+
hPutStrLn stderr $ "Couldn't load cradle for libdir: " ++ show err
119+
return Nothing
120+
CradleNone -> return Nothing
121+
dynFlags <- mapM (dynFlagsForPrinting . LibDir) libdir
122+
mapM_ setUnsafeGlobalDynFlags dynFlags
123+
pure libdir
124+
125+
-- | Wraps `withHieDb` to provide a database connection for reading, and a `HieWriterChan` for
126+
-- writing. Actions are picked off one by one from the `HieWriterChan` and executed in serial
127+
-- by a worker thread using a dedicated database connection.
128+
-- This is done in order to serialize writes to the database, or else SQLite becomes unhappy
129+
runWithDb :: FilePath -> (HieDb -> IndexQueue -> IO ()) -> IO ()
130+
runWithDb fp k =
131+
withHieDb fp $ \writedb -> do
132+
initConn writedb
133+
chan <- newTQueueIO
134+
race_ (writerThread writedb chan) (withHieDb fp (flip k chan))
135+
where
136+
writerThread db chan = forever $ do
137+
k <- atomically $ readTQueue chan
138+
k db `catch` \e@SQLError{} -> do
139+
hPutStrLn stderr $ "Error in worker, ignoring: " ++ show e
140+
141+
getHieDbLoc :: FilePath -> IO FilePath
142+
getHieDbLoc dir = do
143+
let db = dirHash++"-"++takeBaseName dir++"-"++VERSION_ghc <.> "hiedb"
144+
dirHash = B.unpack $ B16.encode $ H.hash $ B.pack dir
145+
cDir <- IO.getXdgDirectory IO.XdgCache cacheDir
146+
createDirectoryIfMissing True cDir
147+
pure (cDir </> db)
148+
94149
-- | Given a root directory, return a Shake 'Action' which setups an
95150
-- 'IdeGhcSession' given a file.
96151
-- Some of the many things this does:
@@ -715,8 +770,8 @@ notifyUserImplicitCradle fp =
715770
NotificationMessage "2.0" WindowShowMessage $ ShowMessageParams MtInfo $
716771
"No [cradle](https://github.com/mpickering/hie-bios#hie-bios) found for "
717772
<> T.pack fp <>
718-
".\n Proceeding with [implicit cradle](https://hackage.haskell.org/package/implicit-hie).\n\
719-
\You should ignore this message, unless you see a 'Multi Cradle: No prefixes matched' error."
773+
".\n Proceeding with [implicit cradle](https://hackage.haskell.org/package/implicit-hie).\n" <>
774+
"You should ignore this message, unless you see a 'Multi Cradle: No prefixes matched' error."
720775

721776
notifyCradleLoaded :: FilePath -> FromServerMessage
722777
notifyCradleLoaded fp =

0 commit comments

Comments
 (0)