Skip to content

Commit 12ca71f

Browse files
EggBaconAndSpammergify[bot]
authored andcommitted
dhall-lsp-server: Add option to only use ASCII when formatting and linting (#1533)
* Add config support The config so far consists of a single `asciiOnly` flag (whose intended behaviour is not yet implemented). * Implement 'ascii-only' option when formatting and linting This commit adds functionality to the 'asciiOnly' flag, i.e. when turned on we don't output fancy non-ascii characters. Needs a recent version of the client to function -- the version on the marketplace does not relay configuration data to the server yet!
1 parent bd0b213 commit 12ca71f

File tree

5 files changed

+81
-28
lines changed

5 files changed

+81
-28
lines changed

dhall-lsp-server/src/Dhall/LSP/Backend/Formatting.hs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module Dhall.LSP.Backend.Formatting (formatExpr, formatExprWithHeader) where
22

33
import Dhall.Core (Expr)
44
import Dhall.Parser (Header(..))
5-
import Dhall.Pretty (CharacterSet(..), prettyCharacterSet)
5+
import Dhall.Pretty (CharacterSet, prettyCharacterSet)
66
import qualified Dhall.Pretty
77
import Dhall.Src (Src)
88

@@ -12,17 +12,18 @@ import qualified Data.Text.Prettyprint.Doc as Pretty
1212
import qualified Data.Text.Prettyprint.Doc.Render.Text as Pretty
1313

1414
-- | Pretty-print the given Dhall expression.
15-
formatExpr :: Pretty.Pretty b => Expr Src b -> Text
16-
formatExpr expr = formatExprWithHeader expr (Header "")
15+
formatExpr :: Pretty.Pretty b => CharacterSet -> Expr Src b -> Text
16+
formatExpr charSet expr = Pretty.renderStrict . Dhall.Pretty.layout
17+
. Pretty.unAnnotate $ prettyCharacterSet charSet expr
1718

1819
-- | Pretty-print the given Dhall expression, prepending the given a "header"
1920
-- (usually consisting of comments and whitespace).
20-
formatExprWithHeader :: Pretty.Pretty b => Expr Src b -> Header -> Text
21-
formatExprWithHeader expr (Header header) = Pretty.renderStrict
21+
formatExprWithHeader :: Pretty.Pretty b => CharacterSet -> Expr Src b -> Header -> Text
22+
formatExprWithHeader charSet expr (Header header) = Pretty.renderStrict
2223
(Dhall.Pretty.layout doc)
2324
where
2425
doc =
2526
Pretty.pretty header
26-
<> Pretty.unAnnotate (prettyCharacterSet Unicode expr)
27+
<> Pretty.unAnnotate (prettyCharacterSet charSet expr)
2728
<> "\n"
2829

dhall-lsp-server/src/Dhall/LSP/Backend/Typing.hs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
module Dhall.LSP.Backend.Typing (annotateLet, exprAt, srcAt, typeAt) where
22

33
import Dhall.Context (Context, insert, empty)
4-
import Dhall.Core (Binding(..), Expr(..), subExpressions, normalize, shift, subst, Var(..), pretty)
4+
import Dhall.Core (Binding(..), Expr(..), subExpressions, normalize, shift, subst, Var(..))
55
import Dhall.TypeCheck (typeWithA, TypeError(..))
66
import Dhall.Parser (Src(..))
77

8-
import Data.Monoid ((<>))
98
import Control.Lens (toListOf)
10-
import Data.Text (Text)
119
import Control.Applicative ((<|>))
1210
import Data.Bifunctor (first)
1311
import Data.Void (absurd, Void)
@@ -89,14 +87,15 @@ srcAt pos expr = do Note src _ <- exprAt pos expr
8987

9088

9189
-- | Given a well-typed expression and a position find the let binder at that
92-
-- position (if there is one) and return a textual update to the source code
93-
-- that inserts the type annotation (or replaces the existing one). If
94-
-- something goes wrong returns a textual error message.
95-
annotateLet :: Position -> WellTyped -> Either String (Src, Text)
90+
-- position (if there is one) and return the type annotation to be inserted
91+
-- (potentially replacing the existing one). If something goes wrong returns a
92+
-- textual error message.
93+
annotateLet :: Position -> WellTyped -> Either String (Src, Expr Src Void)
9694
annotateLet pos expr = do
9795
annotateLet' pos empty (fromWellTyped expr)
9896

99-
annotateLet' :: Position -> Context (Expr Src Void) -> Expr Src Void -> Either String (Src, Text)
97+
annotateLet' :: Position -> Context (Expr Src Void) -> Expr Src Void
98+
-> Either String (Src, Expr Src Void)
10099
-- the input only contains singleton lets
101100
annotateLet' pos ctx (Note src e@(Let (Binding { value = a }) _))
102101
| not $ any (pos `inside`) [ src' | Note src' _ <- toListOf subExpressions e ]
@@ -105,7 +104,7 @@ annotateLet' pos ctx (Note src e@(Let (Binding { value = a }) _))
105104
Just x -> return x
106105
Nothing -> Left "The impossible happened: failed\
107106
\ to re-parse a Let expression."
108-
return (srcAnnot, ": " <> pretty (normalize _A) <> " ")
107+
return (srcAnnot, normalize _A)
109108

110109
-- binders, see typeAt'
111110
annotateLet' pos ctx (Let (Binding { variable = x, value = a }) e@(Note src _)) | pos `inside` src = do

dhall-lsp-server/src/Dhall/LSP/Handlers.hs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@ import Data.Void (Void)
1212
import Dhall.Core (Expr(Note, Embed), pretty, Import(..), ImportHashed(..), ImportType(..), headers)
1313
import Dhall.Import (localToPath)
1414
import Dhall.Parser (Src(..))
15+
import Dhall.Pretty (CharacterSet(..))
1516

1617
import Dhall.LSP.Backend.Completion (Completion(..), completionQueryAt,
1718
completeEnvironmentImport, completeLocalImport, buildCompletionContext, completeProjections, completeFromContext)
1819
import Dhall.LSP.Backend.Dhall (FileIdentifier, parse, load, typecheck,
1920
fileIdentifierFromFilePath, fileIdentifierFromURI, invalidate, parseWithHeader)
2021
import Dhall.LSP.Backend.Diagnostics (Range(..), Diagnosis(..), explain,
2122
rangeFromDhall, diagnose, embedsWithRanges)
22-
import Dhall.LSP.Backend.Formatting (formatExprWithHeader)
23+
import Dhall.LSP.Backend.Formatting (formatExpr, formatExprWithHeader)
2324
import Dhall.LSP.Backend.Freezing (computeSemanticHash, getImportHashPosition,
2425
stripHash, getAllImportsWithHashPositions)
2526
import Dhall.LSP.Backend.Linting (Suggestion(..), suggest, lint)
@@ -34,6 +35,7 @@ import Control.Monad (guard, forM)
3435
import Control.Monad.Trans (liftIO)
3536
import Control.Monad.Trans.Except (throwE, catchE, runExceptT)
3637
import Control.Monad.Trans.State.Strict (execStateT)
38+
import Data.Default (def)
3739
import qualified Data.HashMap.Strict as HashMap
3840
import qualified Data.Map.Strict as Map
3941
import Data.Maybe (maybeToList)
@@ -57,6 +59,14 @@ wrapHandler vstate handle message =
5759
execStateT . runExceptT $
5860
catchE (handle message) lspUserMessage
5961

62+
getServerConfig :: HandlerM ServerConfig
63+
getServerConfig = do
64+
lsp <- use lspFuncs
65+
mConfig <- liftIO (LSP.config lsp)
66+
case mConfig of
67+
Just config -> return config
68+
Nothing -> return def
69+
6070
lspUserMessage :: (Severity, Text) -> HandlerM ()
6171
lspUserMessage (Log, text) =
6272
lspSendNotification LSP.NotLogMessage J.WindowLogMessage
@@ -289,7 +299,11 @@ documentFormattingHandler request = do
289299
Right res -> return res
290300
_ -> throwE (Warning, "Failed to format dhall code; parse error.")
291301

292-
let formatted = formatExprWithHeader expr header
302+
ServerConfig {..} <- getServerConfig
303+
let charSet | asciiOnly = ASCII
304+
| otherwise = Unicode
305+
306+
let formatted = formatExprWithHeader charSet expr header
293307
numLines = Text.length txt
294308
range = J.Range (J.Position 0 0) (J.Position numLines 0)
295309
edits = J.List [J.TextEdit range formatted]
@@ -327,7 +341,11 @@ executeLintAndFormat request = do
327341
Right res -> return res
328342
_ -> throwE (Warning, "Failed to lint dhall code; parse error.")
329343

330-
let linted = formatExprWithHeader (lint expr) header
344+
ServerConfig {..} <- getServerConfig
345+
let charSet | asciiOnly = ASCII
346+
| otherwise = Unicode
347+
348+
let linted = formatExprWithHeader charSet (lint expr) header
331349
numLines = Text.length txt
332350
range = J.Range (J.Position 0 0) (J.Position numLines 0)
333351
edit = J.WorkspaceEdit
@@ -350,13 +368,18 @@ executeAnnotateLet request = do
350368
Left _ -> throwE (Warning, "Failed to annotate let binding; not well-typed.")
351369
Right e -> return e
352370

353-
(Src (SourcePos _ x1 y1) (SourcePos _ x2 y2) _, txt)
371+
ServerConfig {..} <- getServerConfig
372+
let charSet | asciiOnly = ASCII
373+
| otherwise = Unicode
374+
375+
(Src (SourcePos _ x1 y1) (SourcePos _ x2 y2) _, annotExpr)
354376
<- case annotateLet (line, col) welltyped of
355377
Right x -> return x
356378
Left msg -> throwE (Warning, Text.pack msg)
357379

358380
let range = J.Range (J.Position (unPos x1 - 1) (unPos y1 - 1))
359381
(J.Position (unPos x2 - 1) (unPos y2 - 1))
382+
txt = formatExpr charSet annotExpr
360383
edit = J.WorkspaceEdit
361384
(Just (HashMap.singleton uri (J.List [J.TextEdit range txt]))) Nothing
362385

dhall-lsp-server/src/Dhall/LSP/Server.hs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22
module Dhall.LSP.Server(run) where
33

44
import Control.Concurrent.MVar
5+
import Control.Lens ((^.))
6+
import Data.Aeson (fromJSON, Result(Success))
57
import Data.Default
68
import qualified Language.Haskell.LSP.Control as LSP.Control
79
import qualified Language.Haskell.LSP.Core as LSP.Core
810

911
import qualified Language.Haskell.LSP.Types as J
12+
import qualified Language.Haskell.LSP.Types.Lens as J
1013

1114
import Data.Text (Text)
1215
import qualified System.Log.Logger
@@ -23,15 +26,23 @@ run mlog = do
2326
setupLogger mlog
2427
state <- newEmptyMVar
2528

26-
-- these two are stubs since we do not use a config
27-
let onInitialConfiguration :: J.InitializeRequest -> Either Text ()
28-
onInitialConfiguration _ = Right ()
29-
let onConfigurationChange :: J.DidChangeConfigurationNotification -> Either Text ()
30-
onConfigurationChange _ = Right ()
29+
let onInitialConfiguration :: J.InitializeRequest -> Either Text ServerConfig
30+
onInitialConfiguration req
31+
| Just initOpts <- req ^. J.params . J.initializationOptions
32+
, Success config <- fromJSON initOpts
33+
= Right config
34+
onInitialConfiguration _ = Right def
35+
36+
let onConfigurationChange :: J.DidChangeConfigurationNotification -> Either Text ServerConfig
37+
onConfigurationChange notification
38+
| preConfig <- notification ^. J.params . J.settings
39+
, Success config <- fromJSON preConfig
40+
= Right config
41+
onConfigurationChange _ = Right def
3142

3243
-- Callback that is called when the LSP server is started; makes the lsp
3344
-- state (LspFuncs) available to the message handlers through the `state` MVar.
34-
let onStartup :: LSP.Core.LspFuncs () -> IO (Maybe J.ResponseError)
45+
let onStartup :: LSP.Core.LspFuncs ServerConfig -> IO (Maybe J.ResponseError)
3546
onStartup lsp = do
3647
putMVar state (initialState lsp)
3748
return Nothing

dhall-lsp-server/src/Dhall/LSP/State.hs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import qualified Language.Haskell.LSP.Types as J
77

88
import Control.Lens.TH (makeLenses)
99
import Lens.Family (LensLike')
10+
import Data.Aeson (FromJSON(..), withObject, (.:), (.:?), (.!=))
1011
import Data.Map.Strict (Map, empty)
12+
import Data.Default (Default(def))
1113
import Data.Dynamic (Dynamic)
1214
import Dhall.LSP.Backend.Dhall (DhallError, Cache, emptyCache)
1315
import Data.Text (Text)
@@ -28,22 +30,39 @@ data Severity = Error
2830
| Log
2931
-- ^ Log message, not displayed by default.
3032

33+
data ServerConfig = ServerConfig
34+
{ asciiOnly :: Bool
35+
-- ^ Use ASCII symbols rather than fancy unicode when formatting and linting
36+
-- code.
37+
} deriving Show
38+
39+
instance Default ServerConfig where
40+
def = ServerConfig { asciiOnly = False }
41+
42+
-- We need to derive the FromJSON instance manually in order to provide defaults
43+
-- for absent fields.
44+
instance FromJSON ServerConfig where
45+
parseJSON = withObject "settings" $ \v -> do
46+
s <- v .: "vscode-dhall-lsp-server"
47+
flip (withObject "vscode-dhall-lsp-server") s $ \o -> ServerConfig
48+
<$> o .:? "asciiOnly" .!= asciiOnly def
49+
3150
data ServerState = ServerState
3251
{ _importCache :: Cache -- ^ The dhall import cache
3352
, _errors :: Map J.Uri DhallError -- ^ Map from dhall files to their errors
3453
, _httpManager :: Maybe Dynamic
3554
-- ^ The http manager used by dhall's import infrastructure
36-
, _lspFuncs :: LSP.LspFuncs ()
55+
, _lspFuncs :: LSP.LspFuncs ServerConfig
3756
-- ^ Access to the lsp functions supplied by haskell-lsp
3857
}
3958

4059
makeLenses ''ServerState
4160

4261
sendFunc :: Functor f =>
43-
LensLike' f (LSP.LspFuncs ()) (LSP.FromServerMessage -> IO ())
62+
LensLike' f (LSP.LspFuncs ServerConfig) (LSP.FromServerMessage -> IO ())
4463
sendFunc k s = fmap (\x -> s {LSP.sendFunc = x}) (k (LSP.sendFunc s))
4564

46-
initialState :: LSP.LspFuncs () -> ServerState
65+
initialState :: LSP.LspFuncs ServerConfig -> ServerState
4766
initialState lsp = ServerState {..}
4867
where
4968
_importCache = emptyCache

0 commit comments

Comments
 (0)