Skip to content
This repository was archived by the owner on Oct 7, 2020. It is now read-only.
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 16 additions & 64 deletions docs/Architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ class Typeable a => ModuleCache a where
cacheDataProducer :: CachedModule -> IdeM a

withCachedModuleAndData :: forall a b. ModuleCache a
=> Uri -> IdeM b -> (CachedModule -> a -> IdeM b) -> IdeM b
=> FilePath -> b
-> (CachedModule -> a -> IdeM b) -> IdeM b
withCachedModuleAndData uri noCache callback = ...
```

Expand All @@ -106,7 +107,7 @@ This data is used to find all references to a symbol, and to find the name corre
a particular position in the source.

```haskell
getReferencesInDoc :: Uri -> Position -> IdeM (IdeResponse [J.DocumentHighlight])
getReferencesInDoc :: Uri -> Position -> IdeM (IdeResult [J.DocumentHighlight])
getReferencesInDoc uri pos = do
let noCache = return $ nonExistentCacheErr "getReferencesInDoc"
withCachedModuleAndData uri noCache $
Expand Down Expand Up @@ -145,7 +146,7 @@ data GhcRequest m = forall a. GhcRequest
data IdeRequest m = forall a. IdeRequest
{ pureReqId :: J.LspId
, pureReqCallback :: RequestCallback m a
, pureReq :: IdeM (IdeResponse a)
, pureReq :: IdeM (IdeResult a)
}

```
Expand Down Expand Up @@ -176,75 +177,26 @@ for handling the "definition" request
...

-- HaRePlugin.hs
findDef :: Uri -> Position -> IdeM (IdeResponse Location)
findDef :: Uri -> Position -> IdeM (IdeResult Location)
```

The request uses the `findDef` function in the `HaRe` plugin to get the `Location`
of the definition of the symbol at the given position. The callback makes a LSP
response message out of the location, and forwards it to thread #4 which sends
it to the IDE via stdout
it to the IDE via stdout.

## Responses and results
## Deferred requests

While working in the `IdeGhcM` thread, you return results back to the dispatcher with
`IdeResult`:
Should you find yourself wanting to access a typechecked module from within `IdeM`,
use `withCachedModule` to get access to a cached version of that module.
If there is no cached module available, then it will automatically defer your result,
or return a default if that then fails to typecheck:

```haskell
runHareCommand :: String -> RefactGhc [ApplyRefacResult]
-> IdeGhcM (IdeResult WorkspaceEdit)
runHareCommand name cmd = do
eitherRes <- runHareCommand' cmd
case eitherRes of
Left err ->
pure (IdeResultFail
(IdeError PluginError
(T.pack $ name <> ": \"" <> err <> "\"")
Null))
Right res -> do
let changes = getRefactorResult res
refactRes <- makeRefactorResult changes
pure (IdeResultOk refactRes)
withCachedModule file (IdeResultOk []) $ \cm -> do
-- poke about with cm here
```

On `IdeM`, you must wrap any `IdeResult` in an `IdeResponse`:

```haskell
getDynFlags :: Uri -> IdeM (IdeResponse DynFlags)
getDynFlags uri =
pluginGetFileResponse "getDynFlags: " uri $ \fp -> do
mcm <- getCachedModule fp
case mcm of
ModuleCached cm _ -> return $
IdeResponseOk $ ms_hspp_opts $ pm_mod_summary $ tm_parsed_module $ tcMod cm
_ -> return $
IdeResponseFail $
IdeError PluginError ("getDynFlags: \"" <> "module not loaded" <> "\"") Null
```

Sometimes a request may need access to the typechecked module from ghc-mod, but
it is desirable to keep it on the `IdeM` thread. For this a deferred response can
be made:

```haskell
getDynFlags :: Uri -> IdeM (IdeResponse DynFlags)
getDynFlags uri =
pluginGetFileResponse "getDynFlags: " uri $ \fp -> do
mcm <- getCachedModule fp
return $ case mcm of
ModuleCached cm _ -> IdeResponseOk $ getFlags cm
_ -> IdeResponseDeferred fp getFlags
where getFlags = ms_hspp_opts $ pm_mod_summary $ tm_parsed_module $ tcMod cm
```

A deferred response takes a file path to a module, and a callback which will be executed
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this text should be moved up and kept, you simply say "will be deferred" now. We need to explain what it means.

as with the cached module passed as an argument as soon as the module is loaded.

This is wrapped with the helper function `withCachedModule` which will immediately return
the cached module if it is already available to use, and only defer it otherwise.

```haskell
getDynFlags :: Uri -> IdeM (IdeResponse DynFlags)
getDynFlags uri =
pluginGetFileResponse "getDynFlags: " uri $ \fp ->
withCachedModule fp (return . IdeResponseOk . ms_hspp_opts . pm_mod_summary . tm_parsed_module . tcMod)
```
Internally, a deferred response is represented by `IdeDefer`, which takes a file path
to a module, and a callback which will be executed with a `UriCache` passed as an
argument as soon as the module is loaded, or a `UriCacheFailed` if it failed.