@@ -14,7 +14,8 @@ import Data.Maybe (catMaybes, fromMaybe,
14
14
isJust )
15
15
import qualified Data.Text as T
16
16
import qualified Data.Text.IO as T
17
- import Development.IDE (Action , Rules )
17
+ import Development.IDE (Action , Rules ,
18
+ hDuplicateTo' )
18
19
import Development.IDE.Core.Debouncer (Debouncer ,
19
20
newAsyncDebouncer )
20
21
import Development.IDE.Core.FileStore (makeVFSHandle )
@@ -54,6 +55,7 @@ import Development.IDE.Types.Options (IdeGhcSession,
54
55
import Development.IDE.Types.Shake (Key (Key ))
55
56
import Development.Shake (action )
56
57
import GHC.IO.Encoding (setLocaleEncoding )
58
+ import GHC.IO.Handle (hDuplicate )
57
59
import HIE.Bios.Cradle (findCradle )
58
60
import Ide.Plugin.Config (CheckParents (NeverCheck ),
59
61
Config ,
@@ -68,11 +70,12 @@ import System.Exit (ExitCode (ExitFailure),
68
70
exitWith )
69
71
import System.FilePath (takeExtension ,
70
72
takeFileName )
71
- import System.IO (BufferMode (LineBuffering ),
73
+ import System.IO (BufferMode (LineBuffering , NoBuffering ),
74
+ Handle , hFlush ,
72
75
hPutStrLn ,
73
76
hSetBuffering ,
74
77
hSetEncoding , stderr ,
75
- stdout , utf8 )
78
+ stdin , stdout , utf8 )
76
79
import System.Time.Extra (offsetTime ,
77
80
showDuration )
78
81
import Text.Printf (printf )
@@ -90,6 +93,8 @@ data Arguments = Arguments
90
93
, argsDefaultHlsConfig :: Config
91
94
, argsGetHieDbLoc :: FilePath -> IO FilePath -- ^ Map project roots to the location of the hiedb for the project
92
95
, argsDebouncer :: IO (Debouncer NormalizedUri ) -- ^ Debouncer used for diagnostics
96
+ , argsHandleIn :: IO Handle
97
+ , argsHandleOut :: IO Handle
93
98
}
94
99
95
100
instance Default Arguments where
@@ -106,6 +111,21 @@ instance Default Arguments where
106
111
, argsDefaultHlsConfig = def
107
112
, argsGetHieDbLoc = getHieDbLoc
108
113
, argsDebouncer = newAsyncDebouncer
114
+ , argsHandleIn = pure stdin
115
+ , argsHandleOut = do
116
+ -- Move stdout to another file descriptor and duplicate stderr
117
+ -- to stdout. This guards against stray prints from corrupting the JSON-RPC
118
+ -- message stream.
119
+ newStdout <- hDuplicate stdout
120
+ stderr `hDuplicateTo'` stdout
121
+ hSetBuffering stdout NoBuffering
122
+
123
+ -- Print out a single space to assert that the above redirection works.
124
+ -- This is interleaved with the logger, hence we just print a space here in
125
+ -- order not to mess up the output too much. Verified that this breaks
126
+ -- the language server tests without the redirection.
127
+ putStr " " >> hFlush stdout
128
+ return newStdout
109
129
}
110
130
111
131
-- | Cheap stderr logger that relies on LineBuffering
@@ -130,13 +150,15 @@ defaultMain Arguments{..} = do
130
150
rules = argsRules >> pluginRules plugins
131
151
132
152
debouncer <- argsDebouncer
153
+ inH <- argsHandleIn
154
+ outH <- argsHandleOut
133
155
134
156
case argFiles of
135
157
Nothing -> do
136
158
t <- offsetTime
137
159
hPutStrLn stderr " Starting LSP server..."
138
160
hPutStrLn stderr " If you are seeing this in a terminal, you probably should have run ghcide WITHOUT the --lsp option!"
139
- runLanguageServer options argsGetHieDbLoc argsOnConfigChange (pluginHandlers plugins) $ \ env vfs rootPath hiedb hieChan -> do
161
+ runLanguageServer options inH outH argsGetHieDbLoc argsOnConfigChange (pluginHandlers plugins) $ \ env vfs rootPath hiedb hieChan -> do
140
162
t <- t
141
163
hPutStrLn stderr $ " Started LSP server in " ++ showDuration t
142
164
0 commit comments