1
1
-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2
2
-- SPDX-License-Identifier: Apache-2.0
3
- {-# LANGUAGE NamedFieldPuns #-}
4
- {-# LANGUAGE OverloadedStrings #-}
3
+ {-# LANGUAGE LambdaCase #-}
4
+ {-# LANGUAGE NamedFieldPuns #-}
5
+ {-# LANGUAGE OverloadedStrings #-}
6
+ {-# LANGUAGE ScopedTypeVariables #-}
5
7
module Main (main ) where
6
8
7
- import Control.Arrow ( (&&&) )
9
+ import Control.Exception ( displayException )
8
10
import Control.Monad.IO.Class (liftIO )
11
+ import Data.Bifunctor (first )
9
12
import Data.Function ((&) )
13
+ import Data.Functor ((<&>) )
14
+ import Data.Maybe (catMaybes )
10
15
import Data.Text (Text )
11
- import qualified Development.IDE.Main as GhcideMain
12
16
import Development.IDE.Types.Logger (Doc , Priority (Error , Info ),
17
+ Recorder ,
13
18
WithPriority (WithPriority , priority ),
14
19
cfilter , cmapWithPrio ,
15
20
defaultLayoutOptions ,
16
- layoutPretty ,
21
+ layoutPretty , logWith ,
17
22
makeDefaultStderrRecorder ,
18
- payload , renderStrict ,
19
- withDefaultRecorder )
23
+ renderStrict , withFileRecorder )
20
24
import qualified Development.IDE.Types.Logger as Logger
21
25
import qualified HlsPlugins as Plugins
22
26
import Ide.Arguments (Arguments (.. ),
@@ -30,7 +34,7 @@ import Ide.Types (PluginDescriptor (pluginNotifica
30
34
mkPluginNotificationHandler )
31
35
import Language.LSP.Protocol.Message as LSP
32
36
import Language.LSP.Server as LSP
33
- import Prettyprinter (Pretty (pretty ), vsep )
37
+ import Prettyprinter (Pretty (pretty ), vcat , vsep )
34
38
35
39
data Log
36
40
= LogIdeMain IdeMain. Log
@@ -43,13 +47,27 @@ instance Pretty Log where
43
47
44
48
main :: IO ()
45
49
main = do
50
+ stderrRecorder <- makeDefaultStderrRecorder Nothing
46
51
-- plugin cli commands use stderr logger for now unless we change the args
47
52
-- parser to get logging arguments first or do more complicated things
48
- pluginCliRecorder <- cmapWithPrio pretty <$> makeDefaultStderrRecorder Nothing
53
+ let pluginCliRecorder = cmapWithPrio pretty stderrRecorder
49
54
args <- getArguments " haskell-language-server" (Plugins. idePlugins (cmapWithPrio LogPlugins pluginCliRecorder))
50
55
51
- (lspLogRecorder, cb1) <- Logger. withBacklog Logger. lspClientLogRecorder
52
- (lspMessageRecorder, cb2) <- Logger. withBacklog Logger. lspClientMessageRecorder
56
+ -- Recorder that logs to the LSP client with logMessage
57
+ (lspLogRecorder, cb1) <-
58
+ Logger. withBacklog Logger. lspClientLogRecorder
59
+ <&> first (cmapWithPrio renderDoc)
60
+ -- Recorder that logs to the LSP client with showMessage
61
+ (lspMessageRecorder, cb2) <-
62
+ Logger. withBacklog Logger. lspClientMessageRecorder
63
+ <&> first (cmapWithPrio renderDoc)
64
+ -- Recorder that logs Error severity logs to the client with showMessage and some extra text
65
+ let lspErrorMessageRecorder = lspMessageRecorder
66
+ & cfilter (\ WithPriority { priority } -> priority >= Error )
67
+ & cmapWithPrio (\ msg -> vsep
68
+ [" Error condition, please check your setup and/or the [issue tracker](" <> issueTrackerUrl <> " ): "
69
+ , msg
70
+ ])
53
71
-- This plugin just installs a handler for the `initialized` notification, which then
54
72
-- picks up the LSP environment and feeds it to our recorders
55
73
let lspRecorderPlugin = (defaultPluginDescriptor " LSPRecorderCallback" )
@@ -58,28 +76,35 @@ main = do
58
76
liftIO $ (cb1 <> cb2) env
59
77
}
60
78
61
- let (argsTesting, minPriority, logFilePath) =
79
+ let (minPriority, logFilePath, logStderr, logClient ) =
62
80
case args of
63
- Ghcide GhcideArguments { argsTesting, argsLogLevel, argsLogFile} ->
64
- (argsTesting, argsLogLevel, argsLogFile)
65
- _ -> (False , Info , Nothing )
81
+ Ghcide GhcideArguments { argsLogLevel, argsLogFile, argsLogStderr, argsLogClient } ->
82
+ (argsLogLevel, argsLogFile, argsLogStderr, argsLogClient )
83
+ _ -> (Info , Nothing , True , False )
66
84
67
- withDefaultRecorder logFilePath Nothing $ \ textWithPriorityRecorder -> do
85
+ -- Adapter for withFileRecorder to handle the case where we don't want to log to a file
86
+ let withLogFileRecorder action = case logFilePath of
87
+ Just p -> withFileRecorder p Nothing $ \ case
88
+ Left e -> do
89
+ let exceptionMessage = pretty $ displayException e
90
+ let message = vcat [exceptionMessage, " Couldn't open log file; not logging to it." ]
91
+ logWith stderrRecorder Error message
92
+ action Nothing
93
+ Right r -> action (Just r)
94
+ Nothing -> action Nothing
95
+
96
+ withLogFileRecorder $ \ logFileRecorder -> do
68
97
let
69
- recorder = cmapWithPrio (pretty &&& id ) $ mconcat
70
- [textWithPriorityRecorder
71
- & cfilter (\ WithPriority { priority } -> priority >= minPriority)
72
- & cmapWithPrio fst
73
- , lspMessageRecorder
74
- & cfilter (\ WithPriority { priority } -> priority >= Error )
75
- & cmapWithPrio (renderDoc . fst )
76
- , lspLogRecorder
77
- & cfilter (\ WithPriority { priority } -> priority >= minPriority)
78
- & cmapWithPrio (renderStrict . layoutPretty defaultLayoutOptions . fst )
79
- -- do not log heap stats to the LSP log as they interfere with the
80
- -- ability of lsp-test to detect a stuck server in tests and benchmarks
81
- & if argsTesting then cfilter (not . heapStats . snd . payload) else id
82
- ]
98
+ lfr = logFileRecorder
99
+ ser = if logStderr then Just stderrRecorder else Nothing
100
+ lemr = Just lspErrorMessageRecorder
101
+ llr = if logClient then Just lspLogRecorder else Nothing
102
+ recorder :: Recorder (WithPriority Log ) =
103
+ [lfr, ser, lemr, llr]
104
+ & catMaybes
105
+ & mconcat
106
+ & cmapWithPrio pretty
107
+ & cfilter (\ WithPriority { priority } -> priority >= minPriority)
83
108
plugins = Plugins. idePlugins (cmapWithPrio LogPlugins recorder)
84
109
85
110
defaultMain
@@ -88,14 +113,7 @@ main = do
88
113
(plugins <> pluginDescToIdePlugins [lspRecorderPlugin])
89
114
90
115
renderDoc :: Doc a -> Text
91
- renderDoc d = renderStrict $ layoutPretty defaultLayoutOptions $ vsep
92
- [" Error condition, please check your setup and/or the [issue tracker](" <> issueTrackerUrl <> " ): "
93
- ,d
94
- ]
116
+ renderDoc d = renderStrict $ layoutPretty defaultLayoutOptions d
95
117
96
118
issueTrackerUrl :: Doc a
97
119
issueTrackerUrl = " https://github.com/haskell/haskell-language-server/issues"
98
-
99
- heapStats :: Log -> Bool
100
- heapStats (LogIdeMain (IdeMain. LogIDEMain (GhcideMain. LogHeapStats _))) = True
101
- heapStats _ = False
0 commit comments