Skip to content
This repository was archived by the owner on Oct 7, 2020. It is now read-only.

Commit c4190b2

Browse files
authored
Merge pull request #629 from Bubba/command-uuids
Use UUIDs for commands
2 parents 92d3e8a + 356e750 commit c4190b2

File tree

12 files changed

+108
-27
lines changed

12 files changed

+108
-27
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,4 @@ dist-newstyle/
5353
dist/
5454
tags
5555
test-logs/
56+
.DS_Store

haskell-ide-engine.cabal

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ library
3535
Haskell.Ide.Engine.Transport.JsonStdio
3636
Haskell.Ide.Engine.Transport.LspStdio
3737
Haskell.Ide.Engine.Types
38+
Haskell.Ide.Engine.Compat
3839
other-modules: Paths_haskell_ide_engine
3940
build-depends: Cabal >= 1.22
4041
, Diff
@@ -43,6 +44,7 @@ library
4344
, apply-refact
4445
, async
4546
, base >= 4.9 && < 5
47+
, bimap
4648
, brittany
4749
, bytestring
4850
, cabal-helper >= 0.8.0.2
@@ -69,15 +71,20 @@ library
6971
, mtl
7072
, optparse-simple >= 0.0.3
7173
, process
74+
, random
7275
, sorted-list >= 0.2.1.0
7376
, stm
7477
, tagsoup
7578
, text
7679
, transformers
80+
, uuid
7781
, vector
7882
, yaml
7983
, yi-rope
80-
84+
if os(windows)
85+
build-depends: win32
86+
else
87+
build-depends: unix
8188
ghc-options: -Wall -Wredundant-constraints
8289
if flag(pedantic)
8390
ghc-options: -Werror

src/Haskell/Ide/Engine/Compat.hs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{-# LANGUAGE CPP #-}
2+
module Haskell.Ide.Engine.Compat where
3+
4+
#ifdef mingw32_HOST_OS
5+
6+
import qualified System.Win32.Process as P (getCurrentProcessId)
7+
getProcessID :: IO Int
8+
getProcessID = fromIntegral <$> P.getCurrentProcessId
9+
10+
#else
11+
12+
import qualified System.Posix.Process as P (getProcessID)
13+
getProcessID :: IO Int
14+
getProcessID = fromIntegral <$> P.getProcessID
15+
16+
#endif

src/Haskell/Ide/Engine/Plugin/ApplyRefact.hs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,11 @@ lintCmd' uri = pluginGetFile "lintCmd: " uri $ \fp -> do
106106
$ List (map hintToDiagnostic $ stripIgnores fs)
107107

108108
runLintCmd :: FilePath -> [String] -> ExceptT [Diagnostic] IO [Idea]
109-
runLintCmd fp args =
110-
do (flags,classify,hint) <- liftIO $ argsSettings args
111-
let myflags = flags { hseFlags = (hseFlags flags) { extensions = (EnableExtension TypeApplications:extensions (hseFlags flags))}}
112-
res <- bimapExceptT parseErrorToDiagnostic id $ ExceptT $ parseModuleEx myflags fp Nothing
113-
pure $ applyHints classify hint [res]
109+
runLintCmd fp args = do
110+
(flags,classify,hint) <- liftIO $ argsSettings args
111+
let myflags = flags { hseFlags = (hseFlags flags) { extensions = EnableExtension TypeApplications:extensions (hseFlags flags)}}
112+
res <- bimapExceptT parseErrorToDiagnostic id $ ExceptT $ parseModuleEx myflags fp Nothing
113+
pure $ applyHints classify hint [res]
114114

115115
parseErrorToDiagnostic :: Hlint.ParseError -> [Diagnostic]
116116
parseErrorToDiagnostic (Hlint.ParseError l msg contents) =

src/Haskell/Ide/Engine/Transport/LspStdio.hs

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import Control.Monad.STM
2626
import Control.Monad.Reader
2727
import qualified Data.Aeson as J
2828
import Data.Aeson ( (.=), (.:), (.:?), (.!=) )
29+
import qualified Data.Bimap as BM
2930
import qualified Data.ByteString.Lazy as BL
3031
import Data.Char (isUpper, isAlphaNum)
3132
import Data.Default
@@ -38,6 +39,7 @@ import qualified Data.Set as S
3839
import qualified Data.SortedList as SL
3940
import qualified Data.Text as T
4041
import Data.Text.Encoding
42+
import Data.UUID
4143
import qualified Data.Vector as V
4244
import qualified GhcModCore as GM
4345
import qualified GhcMod.Monad.Types as GM
@@ -47,6 +49,7 @@ import Haskell.Ide.Engine.MonadTypes
4749
import Haskell.Ide.Engine.Dispatcher
4850
import Haskell.Ide.Engine.PluginUtils
4951
import Haskell.Ide.Engine.Types
52+
import Haskell.Ide.Engine.Compat
5053
import qualified Haskell.Ide.Engine.Plugin.HaRe as HaRe
5154
import qualified Haskell.Ide.Engine.Plugin.GhcMod as GhcMod
5255
import qualified Haskell.Ide.Engine.Plugin.ApplyRefact as ApplyRefact
@@ -64,6 +67,7 @@ import qualified Language.Haskell.LSP.Types as J
6467
import qualified Language.Haskell.LSP.Utility as U
6568
import System.Exit
6669
import qualified System.Log.Logger as L
70+
import System.Random
6771
import qualified Yi.Rope as Yi
6872

6973
import Name
@@ -95,7 +99,11 @@ run
9599
-> IO Int
96100
run dispatcherProc cin _origDir captureFp = flip E.catches handlers $ do
97101

102+
-- TODO: Figure out how to test with random seeds
103+
commandUUIDs <- getCommandUUIDs
104+
98105
rin <- atomically newTChan :: IO (TChan ReactorInput)
106+
99107
let dp lf = do
100108
cancelTVar <- atomically $ newTVar S.empty
101109
wipTVar <- atomically $ newTVar S.empty
@@ -105,7 +113,7 @@ run dispatcherProc cin _origDir captureFp = flip E.catches handlers $ do
105113
, wipReqsTVar = wipTVar
106114
, docVersionTVar = versionTVar
107115
}
108-
let reactorFunc = flip runReaderT lf $ reactor dispatcherEnv cin rin
116+
let reactorFunc = flip runReaderT lf $ reactor dispatcherEnv cin rin commandUUIDs
109117

110118
let errorHandler :: ErrorHandler
111119
errorHandler lid code e =
@@ -120,12 +128,17 @@ run dispatcherProc cin _origDir captureFp = flip E.catches handlers $ do
120128
return Nothing
121129

122130
flip E.finally finalProc $ do
123-
CTRL.run (getConfig, dp) (hieHandlers rin) hieOptions captureFp
131+
CTRL.run (getConfig, dp) (hieHandlers rin) (hieOptions (BM.elems commandUUIDs)) captureFp
124132
where
125133
handlers = [E.Handler ioExcept, E.Handler someExcept]
126134
finalProc = L.removeAllHandlers
127135
ioExcept (e :: E.IOException) = print e >> return 1
128136
someExcept (e :: E.SomeException) = print e >> return 1
137+
getCommandUUIDs = do
138+
uuid <- toText . fst . random . mkStdGen <$> getProcessID
139+
let commands = ["hare:demote", "applyrefact:applyOne"]
140+
uuids = map (T.append uuid . T.append ":") commands
141+
return $ BM.fromList (zip commands uuids)
129142

130143
-- ---------------------------------------------------------------------
131144

@@ -135,7 +148,6 @@ type ReactorInput
135148

136149
-- ---------------------------------------------------------------------
137150

138-
139151
-- ---------------------------------------------------------------------
140152

141153
-- | Callback from haskell-lsp core to convert the generic message to the
@@ -333,8 +345,8 @@ sendErrorLog msg = reactorSend' (`Core.sendErrorLogS` msg)
333345
-- | The single point that all events flow through, allowing management of state
334346
-- to stitch replies and requests together from the two asynchronous sides: lsp
335347
-- server and hie dispatcher
336-
reactor :: forall void. DispatcherEnv -> TChan (PluginRequest R) -> TChan ReactorInput -> R void
337-
reactor (DispatcherEnv cancelReqTVar wipTVar versionTVar) cin inp = do
348+
reactor :: forall void. DispatcherEnv -> TChan (PluginRequest R) -> TChan ReactorInput -> BM.Bimap T.Text T.Text -> R void
349+
reactor (DispatcherEnv cancelReqTVar wipTVar versionTVar) cin inp commandUUIDs = do
338350
let
339351
makeRequest req@(GReq _ _ Nothing (Just lid) _ _) = liftIO $ atomically $ do
340352
modifyTVar wipTVar (S.insert lid)
@@ -387,7 +399,7 @@ reactor (DispatcherEnv cancelReqTVar wipTVar versionTVar) cin inp = do
387399
let
388400
options = J.object ["documentSelector" .= J.object [ "language" .= J.String "haskell"]]
389401
registrationsList =
390-
[ J.Registration "hare:demote" J.WorkspaceExecuteCommand (Just options)
402+
[ J.Registration (commandUUIDs BM.! "hare:demote") J.WorkspaceExecuteCommand (Just options)
391403
]
392404
let registrations = J.RegistrationParams (J.List registrationsList)
393405

@@ -573,7 +585,7 @@ reactor (DispatcherEnv cancelReqTVar wipTVar versionTVar) cin inp = do
573585
title :: T.Text
574586
title = "Apply hint:" <> head (T.lines m)
575587
-- NOTE: the cmd needs to be registered via the InitializeResponse message. See hieOptions above
576-
cmd = "applyrefact:applyOne"
588+
cmd = commandUUIDs BM.! "applyrefact:applyOne"
577589
-- need 'file', 'start_pos' and hint title (to distinguish between alternative suggestions at the same location)
578590
args = J.Array $ V.singleton $ J.toJSON $ ApplyRefact.AOP doc start code
579591
cmdparams = Just args
@@ -589,9 +601,12 @@ reactor (DispatcherEnv cancelReqTVar wipTVar versionTVar) cin inp = do
589601
ReqExecuteCommand req -> do
590602
liftIO $ U.logs $ "reactor:got ExecuteCommandRequest:" ++ show req
591603
let params = req ^. J.params
592-
command = params ^. J.command
604+
command' = params ^. J.command
605+
-- if this is a UUID then use the mapping for it
606+
command = fromMaybe command' (BM.lookupR command' commandUUIDs)
593607
margs = params ^. J.arguments
594608

609+
liftIO $ U.logs $ "ExecuteCommand mapped command " ++ show command' ++ " to " ++ show command
595610

596611
--liftIO $ U.logs $ "reactor:ExecuteCommandRequest:margs=" ++ show margs
597612
let cmdparams = case margs of
@@ -804,7 +819,6 @@ requestDiagnostics tn cin file ver = do
804819
sendEmpty = publishDiagnostics maxToSend file Nothing (Map.fromList [(Just "ghcmod",SL.toSortedList [])])
805820
maxToSend = maybe 50 maxNumberOfProblems mc
806821

807-
-- mc <- asks Core.config
808822
let sendHlint = maybe True hlintOn mc
809823
when sendHlint $ do
810824
-- get hlint diagnostics
@@ -840,8 +854,8 @@ syncOptions = J.TextDocumentSyncOptions
840854
, J._save = Just $ J.SaveOptions $ Just False
841855
}
842856

843-
hieOptions :: Core.Options
844-
hieOptions =
857+
hieOptions :: [T.Text] -> Core.Options
858+
hieOptions commandUUIDs =
845859
def { Core.textDocumentSync = Just syncOptions
846860
, Core.completionProvider = Just (J.CompletionOptions (Just True) (Just ["."]))
847861
-- As of 2018-05-24, vscode needs the commands to be registered
@@ -850,7 +864,7 @@ hieOptions =
850864
--
851865
-- Hopefully the end May 2018 vscode release will stabilise
852866
-- this, it is a major rework of the machinery anyway.
853-
, Core.executeCommandProvider = Just (J.ExecuteCommandOptions (J.List ["applyrefact:applyOne","hare:demote"]))
867+
, Core.executeCommandProvider = Just (J.ExecuteCommandOptions (J.List commandUUIDs))
854868
}
855869

856870

stack-8.2.1.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ extra-deps:
3939
- haddock-api-2.18.1
4040
- haddock-library-1.4.4
4141
- hlint-2.0.11
42+
- process-1.6.3.0
4243
- sorted-list-0.2.1.0
4344
- syz-0.2.0.0
4445
- yi-rope-0.11

stack-8.2.2.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ extra-deps:
4141
- syz-0.2.0.0
4242
- yi-rope-0.11
4343
- conduit-parse-0.2.1.0
44+
- process-1.6.3.0
4445

4546
flags:
4647
haskell-ide-engine:

submodules/haskell-lsp-test

submodules/yi-rope

Submodule yi-rope added at 7867909

test/Functional.hs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ module Main where
44

55
import Control.Monad.IO.Class
66
import Control.Lens hiding (List)
7+
import Control.Monad
78
import Data.Aeson
89
import qualified Data.HashMap.Strict as H
10+
import Data.Maybe
911
import Language.Haskell.LSP.Test
1012
import Language.Haskell.LSP.Types
1113
import qualified Language.Haskell.LSP.Types as LSP (error, id)
@@ -23,7 +25,7 @@ main = do
2325
cdAndDo "./test/testdata" $ hspec dispatchSpec
2426

2527
spec :: Spec
26-
spec =
28+
spec = do
2729
describe "deferred responses" $ do
2830
it "do not affect hover requests" $ runSession hieCommand "test/testdata" $ do
2931
doc <- openDoc "FuncTest.hs" "haskell"
@@ -107,7 +109,7 @@ spec =
107109

108110
it "returns hints as diagnostics" $ runSession hieCommand "test/testdata" $ do
109111
_ <- openDoc "FuncTest.hs" "haskell"
110-
112+
111113
cwd <- liftIO getCurrentDirectory
112114
let testUri = filePathToUri $ cwd </> "test/testdata/FuncTest.hs"
113115

@@ -143,3 +145,39 @@ spec =
143145
]
144146
)
145147
Nothing
148+
149+
describe "multi-server setup" $
150+
it "doesn't have clashing commands on two servers" $ do
151+
let getCommands = runSession hieCommand "test/testdata" $ do
152+
rsp <- getInitializeResponse
153+
let uuids = rsp ^? result . _Just . capabilities . executeCommandProvider . _Just . commands
154+
return $ fromJust uuids
155+
List uuids1 <- getCommands
156+
List uuids2 <- getCommands
157+
liftIO $ forM_ (zip uuids1 uuids2) (uncurry shouldNotBe)
158+
159+
describe "code actions" $
160+
it "provide hlint suggestions" $ runSession hieCommand "test/testdata" $ do
161+
doc <- openDoc "ApplyRefact2.hs" "haskell"
162+
diagsRsp <- skipManyTill anyNotification notification :: Session PublishDiagnosticsNotification
163+
let (List diags) = diagsRsp ^. params . diagnostics
164+
reduceDiag = head diags
165+
166+
liftIO $ do
167+
length diags `shouldBe` 2
168+
reduceDiag ^. range `shouldBe` Range (Position 1 0) (Position 1 12)
169+
reduceDiag ^. severity `shouldBe` Just DsInfo
170+
reduceDiag ^. code `shouldBe` Just "Eta reduce"
171+
reduceDiag ^. source `shouldBe` Just "hlint"
172+
173+
let r = Range (Position 0 0) (Position 99 99)
174+
c = CodeActionContext (diagsRsp ^. params . diagnostics)
175+
_ <- sendRequest TextDocumentCodeAction (CodeActionParams doc r c)
176+
177+
rsp <- response :: Session CodeActionResponse
178+
let (List cmds) = fromJust $ rsp ^. result
179+
evaluateCmd = head cmds
180+
liftIO $ do
181+
length cmds `shouldBe` 1
182+
evaluateCmd ^. title `shouldBe` "Apply hint:Evaluate"
183+

test/TestUtils.hs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ testOptions = GM.defaultOptions {
4848
cdAndDo :: FilePath -> IO a -> IO a
4949
cdAndDo path fn = do
5050
old <- getCurrentDirectory
51-
r <- bracket (setCurrentDirectory path) (\_ -> setCurrentDirectory old)
52-
$ \_ -> fn
53-
return r
51+
bracket (setCurrentDirectory path) (\_ -> setCurrentDirectory old)
52+
$ const fn
53+
5454

5555
testCommand :: (ToJSON a, Typeable b, ToJSON b, Show b, Eq b) => IdePlugins -> IdeGhcM (IdeResult b) -> PluginId -> CommandName -> a -> (IdeResult b) -> IO ()
5656
testCommand testPlugins act plugin cmd arg res = do
@@ -82,7 +82,7 @@ withFileLogging logFile f = do
8282
when exists $ removeFile logPath
8383

8484
Core.setupLogger (Just logPath) ["hie"] L.DEBUG
85-
85+
8686
f
8787

8888
-- ---------------------------------------------------------------------
@@ -103,7 +103,7 @@ files =
103103
]
104104

105105
stackYaml :: String
106-
stackYaml =
106+
stackYaml =
107107
#if (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(8,4,3,0)))
108108
"stack.yaml"
109109
#elif (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(8,4,2,0)))
@@ -118,7 +118,7 @@ stackYaml =
118118

119119
-- | The command to execute the version of hie for the current compiler.
120120
hieCommand :: String
121-
hieCommand = "stack exec --stack-yaml=" ++ stackYaml ++ " hie -- --lsp"
121+
hieCommand = "stack exec --stack-yaml=" ++ stackYaml ++ " hie -- --lsp -d -l test-logs/functional-hie.log"
122122

123123
-- |Choose a resolver based on the current compiler, otherwise HaRe/ghc-mod will
124124
-- not be able to load the files

test/testdata/ApplyRefact2.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
main = undefined
2+
foo x = id x

0 commit comments

Comments
 (0)