11-- Copyright (c) 2019 The DAML Authors. All rights reserved.
22-- SPDX-License-Identifier: Apache-2.0
3- {-# LANGUAGE NamedFieldPuns #-}
4- {-# LANGUAGE OverloadedStrings #-}
3+ {-# LANGUAGE LambdaCase #-}
4+ {-# LANGUAGE NamedFieldPuns #-}
5+ {-# LANGUAGE OverloadedStrings #-}
6+ {-# LANGUAGE ScopedTypeVariables #-}
57module Main (main ) where
68
7- import Control.Arrow ( (&&&) )
9+ import Control.Exception ( displayException )
810import Control.Monad.IO.Class (liftIO )
11+ import Data.Bifunctor (first )
912import Data.Function ((&) )
13+ import Data.Functor ((<&>) )
14+ import Data.Maybe (catMaybes )
1015import Data.Text (Text )
11- import qualified Development.IDE.Main as GhcideMain
1216import Development.IDE.Types.Logger (Doc , Priority (Error , Info ),
17+ Recorder ,
1318 WithPriority (WithPriority , priority ),
1419 cfilter , cmapWithPrio ,
1520 defaultLayoutOptions ,
16- layoutPretty ,
21+ layoutPretty , logWith ,
1722 makeDefaultStderrRecorder ,
18- payload , renderStrict ,
19- withDefaultRecorder )
23+ renderStrict , withFileRecorder )
2024import qualified Development.IDE.Types.Logger as Logger
2125import qualified HlsPlugins as Plugins
2226import Ide.Arguments (Arguments (.. ),
@@ -30,7 +34,7 @@ import Ide.Types (PluginDescriptor (pluginNotifica
3034 mkPluginNotificationHandler )
3135import Language.LSP.Protocol.Message as LSP
3236import Language.LSP.Server as LSP
33- import Prettyprinter (Pretty (pretty ), vsep )
37+ import Prettyprinter (Pretty (pretty ), vcat , vsep )
3438
3539data Log
3640 = LogIdeMain IdeMain. Log
@@ -43,13 +47,27 @@ instance Pretty Log where
4347
4448main :: IO ()
4549main = do
50+ stderrRecorder <- makeDefaultStderrRecorder Nothing
4651 -- plugin cli commands use stderr logger for now unless we change the args
4752 -- parser to get logging arguments first or do more complicated things
48- pluginCliRecorder <- cmapWithPrio pretty <$> makeDefaultStderrRecorder Nothing
53+ let pluginCliRecorder = cmapWithPrio pretty stderrRecorder
4954 args <- getArguments " haskell-language-server" (Plugins. idePlugins (cmapWithPrio LogPlugins pluginCliRecorder))
5055
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+ ])
5371 -- This plugin just installs a handler for the `initialized` notification, which then
5472 -- picks up the LSP environment and feeds it to our recorders
5573 let lspRecorderPlugin = (defaultPluginDescriptor " LSPRecorderCallback" )
@@ -58,28 +76,35 @@ main = do
5876 liftIO $ (cb1 <> cb2) env
5977 }
6078
61- let (argsTesting, minPriority, logFilePath) =
79+ let (minPriority, logFilePath, logStderr, logClient ) =
6280 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 )
6684
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
6897 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)
83108 plugins = Plugins. idePlugins (cmapWithPrio LogPlugins recorder)
84109
85110 defaultMain
@@ -88,14 +113,7 @@ main = do
88113 (plugins <> pluginDescToIdePlugins [lspRecorderPlugin])
89114
90115renderDoc :: 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
95117
96118issueTrackerUrl :: Doc a
97119issueTrackerUrl = " 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