Skip to content

Commit 1470977

Browse files
committed
Working on Plugin concept.
Tests do not pass, but want to rebase, so marking a checkpoint.
1 parent 5c4758e commit 1470977

File tree

9 files changed

+469
-41
lines changed

9 files changed

+469
-41
lines changed

.gitmodules

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@
1010
# rm -rf path_to_submodule
1111
[submodule "ghcide"]
1212
path = ghcide
13-
url = https://github.com/digital-asset/ghcide.git
14-
# url = https://github.com/alanz/ghcide.git
13+
# url = https://github.com/digital-asset/ghcide.git
14+
url = https://github.com/alanz/ghcide.git

exe/Main.hs

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ import Control.DeepSeq (NFData)
1616
import Control.Exception
1717
import Control.Monad.Extra
1818
import Control.Monad.IO.Class
19+
import qualified Crypto.Hash.SHA1 as H
1920
import Data.Binary (Binary)
21+
import Data.ByteString.Base16
22+
import qualified Data.ByteString.Char8 as B
2023
import Data.Default
2124
import Data.Dynamic (Typeable)
2225
import qualified Data.HashSet as HashSet
@@ -26,6 +29,8 @@ import qualified Data.Map.Strict as Map
2629
import Data.Maybe
2730
import qualified Data.Text as T
2831
import qualified Data.Text.IO as T
32+
-- import Data.Version
33+
-- import Development.GitRev
2934
import Development.IDE.Core.Debouncer
3035
import Development.IDE.Core.FileStore
3136
import Development.IDE.Core.OfInterest
@@ -42,18 +47,22 @@ import Development.IDE.Types.Location
4247
import Development.IDE.Types.Logger
4348
import Development.IDE.Types.Options
4449
import Development.Shake (Action, RuleResult, Rules, action, doesFileExist, need)
50+
import DynFlags
4551
import GHC hiding (def)
4652
import GHC.Generics (Generic)
4753
-- import qualified GHC.Paths
4854
import HIE.Bios
4955
import HIE.Bios.Cradle
56+
import HIE.Bios.Environment
5057
import HIE.Bios.Types
5158
import Ide.Plugin
5259
import Ide.Plugin.Config
5360
-- import Ide.Plugin.Formatter
5461
import Language.Haskell.LSP.Messages
5562
import Language.Haskell.LSP.Types (LspId(IdInt))
63+
import qualified Language.Haskell.LSP.Core as LSP
5664
import Linker
65+
-- import Paths_haskell_language_server
5766
import qualified System.Directory.Extra as IO
5867
-- import System.Environment
5968
import System.Exit
@@ -69,6 +78,7 @@ import Ide.Plugin.Example as Example
6978
import Ide.Plugin.Example2 as Example2
7079
import Ide.Plugin.Floskell as Floskell
7180
import Ide.Plugin.Ormolu as Ormolu
81+
import Ide.Plugin.Pragmas as Pragmas
7282

7383
-- ---------------------------------------------------------------------
7484

@@ -82,13 +92,29 @@ idePlugins includeExample
8292
CodeAction.plugin <>
8393
formatterPlugins [("ormolu", Ormolu.provider)
8494
,("floskell", Floskell.provider)] <>
85-
codeActionPlugins [("eg", Example.codeAction)
86-
,("eg2", Example2.codeAction)] <>
95+
codeActionPlugins [("eg", Example.codeAction)
96+
,("eg2", Example2.codeAction)
97+
,("pragmas", Pragmas.codeAction)] <>
98+
executeCommandPlugins [("pragmas", Pragmas.commands)] <>
8799
hoverPlugins [Example.hover, Example2.hover] <>
88100
if includeExample then Example.plugin <> Example2.plugin
89101
else mempty
90102

103+
commandIds :: T.Text -> [T.Text]
104+
commandIds pid = "typesignature.add" : allLspCmdIds pid [("pragmas", Pragmas.commands)]
105+
91106
-- ---------------------------------------------------------------------
107+
-- Prefix for the cache path
108+
cacheDir :: String
109+
cacheDir = "ghcide"
110+
111+
getCacheDir :: [String] -> IO FilePath
112+
getCacheDir opts = IO.getXdgDirectory IO.XdgCache (cacheDir </> opts_hash)
113+
where
114+
-- Create a unique folder per set of different GHC options, assuming that each different set of
115+
-- GHC options will create incompatible interface files.
116+
opts_hash = B.unpack $ encode $ H.finalize $ H.updates H.init (map B.pack opts)
117+
92118

93119
main :: IO ()
94120
main = do
@@ -108,13 +134,17 @@ main = do
108134

109135
dir <- IO.getCurrentDirectory
110136

137+
pid <- getPid
111138
let plugins = idePlugins argsExamplePlugin
139+
options = def { LSP.executeCommandCommands = Just (commandIds pid)
140+
, LSP.completionTriggerCharacters = Just "."
141+
}
112142

113143
if argLSP then do
114144
t <- offsetTime
115145
hPutStrLn stderr "Starting (haskell-language-server)LSP server..."
116146
hPutStrLn stderr "If you are seeing this in a terminal, you probably should have run ghcide WITHOUT the --lsp option!"
117-
runLanguageServer def (pluginHandler plugins) getInitialConfig getConfigFromNotification $ \getLspId event vfs caps -> do
147+
runLanguageServer options (pluginHandler plugins) getInitialConfig getConfigFromNotification $ \getLspId event vfs caps -> do
118148
t <- t
119149
hPutStrLn stderr $ "Started LSP server in " ++ showDuration t
120150
let options = (defaultIdeOptions $ loadSession dir)
@@ -235,14 +265,50 @@ getComponentOptions cradle = do
235265

236266

237267
createSession :: ComponentOptions -> IO HscEnvEq
238-
createSession opts = do
268+
createSession (ComponentOptions theOpts _) = do
239269
libdir <- getLibdir
270+
271+
cacheDir <- Main.getCacheDir theOpts
272+
240273
env <- runGhc (Just libdir) $ do
241-
_targets <- initSession opts
274+
dflags <- getSessionDynFlags
275+
(dflags', _targets) <- addCmdOpts theOpts dflags
276+
_ <- setSessionDynFlags $
277+
-- disabled, generated directly by ghcide instead
278+
flip gopt_unset Opt_WriteInterface $
279+
-- disabled, generated directly by ghcide instead
280+
-- also, it can confuse the interface stale check
281+
dontWriteHieFiles $
282+
setHiDir cacheDir $
283+
setDefaultHieDir cacheDir $
284+
setIgnoreInterfacePragmas $
285+
setLinkerOptions $
286+
disableOptimisation dflags'
242287
getSession
243288
initDynLinker env
244289
newHscEnvEq env
245290

291+
-- we don't want to generate object code so we compile to bytecode
292+
-- (HscInterpreted) which implies LinkInMemory
293+
-- HscInterpreted
294+
setLinkerOptions :: DynFlags -> DynFlags
295+
setLinkerOptions df = df {
296+
ghcLink = LinkInMemory
297+
, hscTarget = HscNothing
298+
, ghcMode = CompManager
299+
}
300+
301+
setIgnoreInterfacePragmas :: DynFlags -> DynFlags
302+
setIgnoreInterfacePragmas df =
303+
gopt_set (gopt_set df Opt_IgnoreInterfacePragmas) Opt_IgnoreOptimChanges
304+
305+
disableOptimisation :: DynFlags -> DynFlags
306+
disableOptimisation df = updOptLevel 0 df
307+
308+
setHiDir :: FilePath -> DynFlags -> DynFlags
309+
setHiDir f d =
310+
-- override user settings to avoid conflicts leading to recompilation
311+
d { hiDir = Just f}
246312

247313
cradleToSession :: Maybe FilePath -> Cradle a -> Action HscEnvEq
248314
cradleToSession mbYaml cradle = do

haskell-language-server.cabal

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ library
3434
Ide.Plugin.Example
3535
Ide.Plugin.Example2
3636
Ide.Plugin.Ormolu
37+
Ide.Plugin.Pragmas
3738
Ide.Plugin.Floskell
3839
Ide.Plugin.Formatter
3940
Ide.Types
@@ -109,7 +110,10 @@ executable haskell-language-server
109110

110111
build-depends:
111112
base >=4.7 && <5
113+
, base16-bytestring
112114
, binary
115+
, bytestring
116+
, cryptohash-sha1
113117
, containers
114118
, data-default
115119
, deepseq

0 commit comments

Comments
 (0)