on master help sign in
New diagnostics implementation (#737)

* Switch to haskell-lsp * Fix build of data-default on Windows * Use ghc environment files to avoid overflowing CLI length limits

commitc72b7344e2b3622c8fd8007c656850e553ce1465 authorDavidM-D <david.millar-durrant@daml.com> dateTue 30 Apr 2019, 22:51 (7 years ago) parent3f6eaba
7 files changed+255−209
BUILD.bazel+3 src/Development/IDE/Functions/AtPoint.hs+2−2 src/Development/IDE/Functions/GHCError.hs+18−14 src/Development/IDE/State/Rules.hs+9−6 src/Development/IDE/State/Shake.hs+19−16 src/Development/IDE/Types/Diagnostics.hs+190−93 src/Development/IDE/Types/Location.hs+14−78
BUILD.bazel+3
@@ -20,6 +20,9 @@ da_haskell_library(
2020 "ghc-lib",
2121 "ghc-lib-parser",
2222 "hashable",
23+ "haskell-lsp",
24+ "haskell-lsp-types",
25+ "lens",
2326 "mtl",
2427 "pretty",
2528 "safe-exceptions",
src/Development/IDE/Functions/AtPoint.hs+2−2
@@ -80,8 +80,8 @@ locationsAtPoint pos = map srcSpanToLocation
8080
8181spansAtPoint :: Position -> [SpanInfo] -> [SpanInfo]
8282spansAtPoint pos = filter atp where
83− line = positionLine pos + 1
84− cha = positionCharacter pos + 1
83+ line = _line pos + 1
84+ cha = _character pos + 1
8585 atp SpanInfo{..} = spaninfoStartLine <= line
8686 && spaninfoEndLine >= line
8787 && spaninfoStartCol <= cha
src/Development/IDE/Functions/GHCError.hs+18−14
@@ -1,6 +1,6 @@
11-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
22-- SPDX-License-Identifier: Apache-2.0
3−
3+{-# LANGUAGE DuplicateRecordFields #-}
44module Development.IDE.Functions.GHCError
55 ( mkDiag
66 , toDiagnostics
@@ -26,6 +26,7 @@ module Development.IDE.Functions.GHCError
2626 , noSpan
2727 ) where
2828
29+import Control.Lens
2930import Development.IDE.Types.Diagnostics as D
3031import qualified Data.Text as T
3132import Development.IDE.UtilGHC()
@@ -36,6 +37,7 @@ import Data.Maybe
3637import "ghc-lib-parser" ErrUtils
3738import "ghc-lib-parser" SrcLoc
3839import qualified "ghc-lib-parser" Outputable as Out
40+import qualified Language.Haskell.LSP.Types as LSP
3941
4042
4143
@@ -48,18 +50,19 @@ mkDiag dflags src e =
4850 case toDSeverity $ errMsgSeverity e of
4951 Nothing -> Nothing
5052 Just bSeverity ->
51− Just
53+ Just $ set dLocation (Just $ srcSpanToLocation $ errMsgSpan e)
5254 Diagnostic
53− { dFilePath = srcSpanToFilename $ errMsgSpan e
54− , dRange = srcSpanToRange $ errMsgSpan e
55− , dSeverity = bSeverity
56− , dSource = src
57− , dMessage = T.pack $ Out.showSDoc dflags (ErrUtils.pprLocErrMsg e)
55+ { _range = srcSpanToRange $ errMsgSpan e
56+ , _severity = Just bSeverity
57+ , _source = Just src
58+ , _message = T.pack $ Out.showSDoc dflags (ErrUtils.pprLocErrMsg e)
59+ , _code = Nothing
60+ , _relatedInformation = Nothing
5861 }
5962
6063-- | Convert a GHC SrcSpan to a DAML compiler Range
6164srcSpanToRange :: SrcSpan -> Range
62−srcSpanToRange (UnhelpfulSpan _) = lRange noLocation
65+srcSpanToRange (UnhelpfulSpan _) = noRange
6366srcSpanToRange (RealSrcSpan real) = realSrcSpanToRange real
6467
6568realSrcSpanToRange :: RealSrcSpan -> Range
@@ -74,18 +77,19 @@ srcSpanToFilename (UnhelpfulSpan fs) = FS.unpackFS fs
7477srcSpanToFilename (RealSrcSpan real) = FS.unpackFS $ srcSpanFile real
7578
7679srcSpanToLocation :: SrcSpan -> Location
77−srcSpanToLocation src = Location (srcSpanToFilename src) (srcSpanToRange src)
80+srcSpanToLocation src =
81+ Location (LSP.filePathToUri $ srcSpanToFilename src) (srcSpanToRange src)
7882
7983-- | Convert a GHC severity to a DAML compiler Severity. Severities below
8084-- "Warning" level are dropped (returning Nothing).
81−toDSeverity :: GHC.Severity -> Maybe D.Severity
85+toDSeverity :: GHC.Severity -> Maybe D.DiagnosticSeverity
8286toDSeverity SevOutput = Nothing
8387toDSeverity SevInteractive = Nothing
8488toDSeverity SevDump = Nothing
85−toDSeverity SevInfo = Nothing
86−toDSeverity SevWarning = Just D.Warning
87−toDSeverity SevError = Just Error
88−toDSeverity SevFatal = Just Error
89+toDSeverity SevInfo = Just DsInfo
90+toDSeverity SevWarning = Just DsWarning
91+toDSeverity SevError = Just DsError
92+toDSeverity SevFatal = Just DsError
8993
9094
9195-- | Produce a bag of GHC-style errors (@ErrorMessages@) from the given
src/Development/IDE/State/Rules.hs+9−6
@@ -4,6 +4,7 @@
44{-# LANGUAGE TypeFamilies #-}
55{-# LANGUAGE OverloadedStrings #-}
66{-# LANGUAGE FlexibleInstances #-}
7+{-# LANGUAGE DuplicateRecordFields #-}
78
89-- | A Shake implementation of the compiler service, built
910-- using the "Shaker" abstraction layer for in-memory use.
@@ -24,6 +25,7 @@ module Development.IDE.State.Rules(
2425
2526import Control.Concurrent.Extra
2627import Control.Exception (evaluate)
28+import Control.Lens (set)
2729import Control.Monad.Except
2830import Control.Monad.Extra (whenJust)
2931import qualified Development.IDE.Functions.Compile as Compile
@@ -239,12 +241,13 @@ reportImportCyclesRule =
239241 where cycleErrorInFile f (PartOfCycle imp fs)
240242 | f `elem` fs = Just (imp, fs)
241243 cycleErrorInFile _ _ = Nothing
242− toDiag imp mods = Diagnostic
243− { dFilePath = lFilePath loc
244− , dRange = lRange loc
245− , dSeverity = Error
246− , dSource = "Import cycle detection"
247− , dMessage = "Cyclic module dependency between " <> showCycle mods
244+ toDiag imp mods = set dLocation (Just loc) $ Diagnostic
245+ { _range = (_range :: Location -> Range) loc
246+ , _severity = Just DsError
247+ , _source = Just "Import cycle detection"
248+ , _message = "Cyclic module dependency between " <> showCycle mods
249+ , _code = Nothing
250+ , _relatedInformation = Nothing
248251 }
249252 where loc = srcSpanToLocation (getLoc imp)
250253 getModuleName file = do
src/Development/IDE/State/Shake.hs+19−16
@@ -4,6 +4,8 @@
44{-# LANGUAGE ExistentialQuantification #-}
55{-# LANGUAGE TypeFamilies #-}
66{-# LANGUAGE ConstraintKinds #-}
7+{-# LANGUAGE DuplicateRecordFields #-}
8+{-# LANGUAGE OverloadedStrings #-}
79
810-- | A Shake implementation of the compiler service.
911--
@@ -59,9 +61,11 @@ import Development.IDE.Types.Diagnostics
5961import Control.Concurrent.Extra
6062import Control.Exception
6163import Control.DeepSeq
64+import Control.Lens (view, set)
6265import System.Time.Extra
6366import Data.Typeable
6467import Data.Tuple.Extra
68+import System.Directory
6569import System.FilePath
6670import qualified Development.Shake as Shake
6771import Control.Monad.Extra
@@ -358,11 +362,7 @@ defineEarlyCutoff op = addBuiltinRule noLint noIdentity $ \(Q (key, file)) old m
358362 (bs, res) <- actionCatch
359363 (do v <- op key file; liftIO $ evaluate $ force v) $
360364 \(e :: SomeException) -> pure (Nothing, ([ideErrorText file $ T.pack $ show e | not $ isBadDependency e],Nothing))
361− res <- return $ first (map $ fixDiagnostic file) res
362−
363− let badErrors = filter (\d -> null file || dRange d == noRange) $ fst res
364− when (badErrors /= []) $
365− reportSeriousError $ "Bad errors found for " ++ show (key, file) ++ " got " ++ show badErrors
365+ res <- return $ first (map $ set dFilePath $ Just file) res
366366
367367 (before, after) <- liftIO $ setValues state key file res
368368 updateFileDiagnostics file before after
@@ -378,24 +378,27 @@ defineEarlyCutoff op = addBuiltinRule noLint noIdentity $ \(Q (key, file)) old m
378378 unwrap x = if BS.null x then Nothing else Just $ BS.tail x
379379
380380
381−-- | If any diagnostic has the wrong filename, generate a new diagnostic with the right file name
382−fixDiagnostic :: FilePath -> Diagnostic -> Diagnostic
383−fixDiagnostic x d
384− | dFilePath d == x = d
385− | otherwise = d{dFilePath = x, dRange = noRange, dMessage = T.pack ("Originally reported at " ++ dFilePath d ++ "\n") <> dMessage d}
386−
387−
388381updateFileDiagnostics ::
389382 FilePath
390383 -> Maybe [Diagnostic] -- ^ previous results for this file
391384 -> [Diagnostic] -- ^ current results
392385 -> Action ()
393386updateFileDiagnostics afp previousAll currentAll = do
394− let filt = Set.fromList . filter (\x -> dFilePath x == afp)
395− previous = fmap filt previousAll
396− current = filt currentAll
387+ -- TODO (MK) We canonicalize to make sure that the two files agree on use of
388+ -- / and \ and other shenanigans.
389+ -- Once we have finished the migration to haskell-lsp we should make sure that
390+ -- this is no longer necessary.
391+ afp' <- liftIO $ canonicalizePath afp
392+ let filtM diags = do
393+ diags' <-
394+ filterM
395+ (\x -> fmap (== Just afp') (traverse canonicalizePath $ view dFilePath x))
396+ diags
397+ pure (Set.fromList diags')
398+ previous <- liftIO $ traverse filtM previousAll
399+ current <- liftIO $ filtM currentAll
397400 when (Just current /= previous) $
398− sendEvent $ EventFileDiagnostics $ FileDiagnostics afp $ Set.toList current
401+ sendEvent $ EventFileDiagnostics $ (filePathToUri afp, Set.toList current)
399402
400403
401404setPriority :: (Enum a) => a -> Action ()
src/Development/IDE/Types/Diagnostics.hs+190−93
@@ -3,137 +3,234 @@
33
44{-# LANGUAGE FlexibleInstances #-}
55{-# LANGUAGE OverloadedStrings #-}
6+{-# LANGUAGE DuplicateRecordFields #-}
7+{-# LANGUAGE BlockArguments #-}
68module Development.IDE.Types.Diagnostics (
7− Diagnostic(..),
8− FileDiagnostics(..),
9+ LSP.Diagnostic(..),
10+ FileDiagnostics,
911 Location(..),
1012 Range(..),
11− Severity(..),
13+ LSP.DiagnosticSeverity(..),
1214 Position(..),
15+ DiagnosticStore,
16+ DiagnosticRelatedInformation(..),
17+ List(..),
18+ StoreItem(..),
19+ Uri(..),
1320 noLocation,
1421 noRange,
22+ noFilePath,
1523 ideErrorText,
1624 ideErrorPretty,
1725 errorDiag,
1826 ideTryIOException,
1927 prettyFileDiagnostics,
20− prettyDiagnostic
28+ prettyDiagnostic,
29+ prettyDiagnosticStore,
30+ defDiagnostic,
31+ addDiagnostics,
32+ filterSeriousErrors,
33+ dLocation,
34+ dFilePath,
35+ filePathToUri,
36+ getDiagnosticsFromStore
2137 ) where
2238
23−import Control.DeepSeq
2439import Control.Exception
25−import Data.Aeson (FromJSON, ToJSON)
40+import Control.Lens (Lens', lens, set, view)
2641import Data.Either.Combinators
27−import Data.List.Extra
42+import Data.Maybe as Maybe
43+import Data.Foldable
44+import qualified Data.Map as Map
2845import qualified Data.Text as T
2946import Data.Text.Prettyprint.Doc.Syntax
30−import GHC.Generics
31−import qualified Network.URI.Encode
47+import Data.String (IsString(..))
3248import qualified Text.PrettyPrint.Annotated.HughesPJClass as Pretty
49+import Language.Haskell.LSP.Types as LSP (
50+ DiagnosticSeverity(..)
51+ , Diagnostic(..)
52+ , filePathToUri
53+ , uriToFilePath
54+ , List(..)
55+ , DiagnosticRelatedInformation(..)
56+ , Uri(..)
57+ )
58+import Language.Haskell.LSP.Diagnostics
3359
3460import Development.IDE.Types.Location
3561
36−ideErrorText :: FilePath -> T.Text -> Diagnostic
37−ideErrorText absFile = errorDiag absFile "Ide Error"
38−
39−ideErrorPretty :: Pretty.Pretty e => FilePath -> e -> Diagnostic
40−ideErrorPretty absFile = ideErrorText absFile . T.pack . Pretty.prettyShow
41−
42−errorDiag :: FilePath -> T.Text -> T.Text -> Diagnostic
43−errorDiag fp src msg =
44− Diagnostic
45− { dFilePath = fp
46− , dRange = noRange
47− , dSeverity = Error
48− , dSource = src
49− , dMessage = msg
62+ideErrorText :: FilePath -> T.Text -> LSP.Diagnostic
63+ideErrorText fp = errorDiag fp "Ide Error"
64+
65+ideErrorPretty :: Pretty.Pretty e => FilePath -> e -> LSP.Diagnostic
66+ideErrorPretty fp = ideErrorText fp . T.pack . Pretty.prettyShow
67+
68+errorDiag :: FilePath -> T.Text -> T.Text -> LSP.Diagnostic
69+errorDiag fp src =
70+ set dFilePath (Just fp) . diagnostic noRange LSP.DsError src
71+
72+-- | This is for compatibility with our old diagnostic type
73+diagnostic :: Range
74+ -> LSP.DiagnosticSeverity
75+ -> T.Text -- ^ source
76+ -> T.Text -- ^ message
77+ -> LSP.Diagnostic
78+diagnostic rng sev src msg
79+ = LSP.Diagnostic {
80+ _range = rng,
81+ _severity = Just sev,
82+ _code = Nothing,
83+ _source = Just src,
84+ _message = msg,
85+ _relatedInformation = Nothing
86+ }
87+
88+-- | Any optional field is instantiated to Nothing
89+defDiagnostic ::
90+ Range ->
91+ T.Text -> -- ^ error message
92+ LSP.Diagnostic
93+defDiagnostic _range _message = LSP.Diagnostic {
94+ _range
95+ , _message
96+ , _severity = Nothing
97+ , _code = Nothing
98+ , _source = Nothing
99+ , _relatedInformation = Nothing
50100 }
51101
52−ideTryIOException :: FilePath -> IO a -> IO (Either Diagnostic a)
102+-- | setLocation but with no range information
103+dFilePath ::
104+ Lens' LSP.Diagnostic (Maybe FilePath)
105+dFilePath = lens g s where
106+ g :: LSP.Diagnostic -> Maybe FilePath
107+ g d = (uriToFilePath . _uri) =<< view dLocation d
108+ s :: LSP.Diagnostic -> Maybe FilePath -> LSP.Diagnostic
109+ s d@Diagnostic{..} fp = set dLocation
110+ (Location <$> (filePathToUri <$> fp) <*> pure _range) d
111+
112+-- | This adds location information to the diagnostics but this is only used in
113+-- the case of serious errors to give some context to what went wrong
114+dLocation ::
115+ Lens' LSP.Diagnostic (Maybe Location)
116+dLocation = lens g s where
117+ s :: LSP.Diagnostic -> Maybe Location -> LSP.Diagnostic
118+ s d = \case
119+ Just loc ->
120+ d {LSP._range=(_range :: Location -> Range) loc
121+ , LSP._relatedInformation = Just $ LSP.List [DiagnosticRelatedInformation loc "dLocation: Unknown error"]}
122+ Nothing -> d {LSP._range = noRange, LSP._relatedInformation = Nothing}
123+ g :: LSP.Diagnostic -> Maybe Location
124+ g Diagnostic{..} = case _relatedInformation of
125+ Just (List [DiagnosticRelatedInformation loc _]) -> Just loc
126+ Just (List xs) -> error $ "Diagnostic created, expected 1 related information but got" <> show xs
127+ Nothing -> Nothing
128+
129+filterSeriousErrors ::
130+ FilePath ->
131+ [LSP.Diagnostic] ->
132+ [LSP.Diagnostic]
133+filterSeriousErrors fp =
134+ filter (maybe False hasSeriousErrors . LSP._relatedInformation)
135+ where
136+ hasSeriousErrors :: List DiagnosticRelatedInformation -> Bool
137+ hasSeriousErrors (List a) = any ((/=) uri . _uri . _location) a
138+ uri = LSP.filePathToUri fp
139+
140+addDiagnostics ::
141+ FilePath ->
142+ [LSP.Diagnostic] ->
143+ DiagnosticStore -> DiagnosticStore
144+addDiagnostics fp diags ds =
145+ updateDiagnostics
146+ ds
147+ (LSP.filePathToUri fp)
148+ Nothing $
149+ partitionBySource diags
150+
151+ideTryIOException :: FilePath -> IO a -> IO (Either LSP.Diagnostic a)
53152ideTryIOException fp act =
54153 mapLeft (\(e :: IOException) -> ideErrorText fp $ T.pack $ show e) <$> try act
55154
56−data Diagnostic = Diagnostic
57− { dFilePath :: !FilePath
58− -- ^ Specific file that the diagnostic refers to.
59− , dRange :: !Range
60− -- ^ The range to which the diagnostic applies.
61− , dSeverity :: !Severity
62− -- ^ The severity of the diagnostic, such as 'SError' or 'SWarning'.
63− , dSource :: !T.Text
64− -- ^ Human-readable description for the source of the diagnostic,
65− -- for example 'parser'.
66− , dMessage :: !T.Text
67− -- ^ The diagnostic's message.
68− }
69− deriving (Eq, Ord, Show, Generic)
70−
71−instance NFData Diagnostic
72−
73−-- | The diagnostic severity.
74−data Severity
75− = Error | Warning
76− deriving (Eq, Ord, Show, Generic)
77−
78−instance NFData Severity
79−
80155-- | Human readable diagnostics for a specific file.
81156--
82157-- This type packages a pretty printed, human readable error message
83158-- along with the related source location so that we can display the error
84159-- on either the console or in the IDE at the right source location.
85160--
86−data FileDiagnostics = FileDiagnostics
87− { fdFilePath :: !FilePath
88− -- ^ Path of the module that we were trying to process.
89− -- In a multi-module program this is the file that we started
90− -- trying to compile, not necessarily the one in which we found the
91− -- reported errors or warnings.
92− , fdDiagnostics :: ![Diagnostic]
93− -- ^ Diagnostics for the desired module,
94− -- as well as any transitively imported modules.
95− }
96− deriving (Eq, Ord, Show, Generic)
97−
98−instance FromJSON Diagnostic
99−instance ToJSON Diagnostic
100−
101−instance FromJSON Severity
102−instance ToJSON Severity
103−
104−instance FromJSON FileDiagnostics
105−instance ToJSON FileDiagnostics
161+type FileDiagnostics = (Uri, [Diagnostic])
106162
107−prettyFileDiagnostics :: FileDiagnostics -> Doc SyntaxClass
108−prettyFileDiagnostics (FileDiagnostics filePath diagnostics) =
109− label_ "Compiler error in" $ vcat
110− [ label_ "File:" $ pretty filePath
111− , label_ "Errors:" $ vcat $ map prettyDiagnostic $ nubOrd diagnostics
112− ]
163+prettyRange :: Range -> Doc SyntaxClass
164+prettyRange Range{..} =
165+ label_ "Range" $ vcat
166+ [ label_ "Start:" $ prettyPosition _start
167+ , label_ "End: " $ prettyPosition _end
168+ ]
169+
170+prettyPosition :: Position -> Doc SyntaxClass
171+prettyPosition Position{..} = label_ "Position" $ vcat
172+ [ label_ "Line:" $ pretty _line
173+ , label_ "Character:" $ pretty _character
174+ ]
113175
114176stringParagraphs :: T.Text -> Doc a
115177stringParagraphs = vcat . map (fillSep . map pretty . T.words) . T.lines
116178
117−prettyDiagnostic :: Diagnostic -> Doc SyntaxClass
118−prettyDiagnostic (Diagnostic filePath range severity source msg) =
179+prettyDiagnostic :: LSP.Diagnostic -> Doc SyntaxClass
180+prettyDiagnostic LSP.Diagnostic{..} =
119181 vcat
120− [ label_ "File: " $ pretty filePath
121− , label_ "Range: "
122− $ annotate (LinkSC uri title)
123− $ pretty range
124− , label_ "Source: " $ pretty source
125− , label_ "Severity:" $ pretty $ show severity
182+ [label_ "Range: "
183+ $ prettyRange _range
184+ , label_ "Source: " $ pretty _source
185+ , label_ "Severity:" $ pretty $ show sev
126186 , label_ "Message: "
127− $ case severity of
128− Error -> annotate ErrorSC
129− Warning -> annotate WarningSC
130− $ stringParagraphs msg
187+ $ case sev of
188+ LSP.DsError -> annotate ErrorSC
189+ LSP.DsWarning -> annotate WarningSC
190+ LSP.DsInfo -> annotate InfoSC
191+ LSP.DsHint -> annotate HintSC
192+ $ stringParagraphs _message
193+ , label_ "Code:" $ pretty _code
131194 ]
132195 where
133− -- FIXME(JM): Move uri construction to DA.Pretty?
134− Position sline _ = rangeStart range
135− Position eline _ = rangeEnd range
136− uri = "command:daml.revealLocation?"
137− <> Network.URI.Encode.encodeText ("[\"file://" <> T.pack filePath <> "\","
138− <> T.pack (show sline) <> ", " <> T.pack (show eline) <> "]")
139− title = T.pack filePath
196+ sev = fromMaybe LSP.DsError _severity
197+
198+prettyDiagnosticStore :: DiagnosticStore -> Doc SyntaxClass
199+prettyDiagnosticStore ds =
200+ vcat $
201+ map prettyFileDiagnostics $
202+ Map.assocs $
203+ Map.map getDiagnosticsFromStore ds
204+
205+prettyFileDiagnostics :: FileDiagnostics -> Doc SyntaxClass
206+prettyFileDiagnostics (uri, diags) =
207+ label_ "Compiler error in" $ vcat
208+ [ label_ "File:" $ pretty filePath
209+ , label_ "Errors:" $ vcat $ map prettyDiagnostic diags
210+ ] where
211+
212+ -- prettyFileDiags :: (FilePath, [(T.Text, [LSP.Diagnostic])]) -> Doc SyntaxClass
213+ -- prettyFileDiags (fp,stages) =
214+ -- label_ ("File: "<>fp) $ vcat $ map prettyStage stages
215+
216+ -- prettyStage :: (T.Text, [LSP.Diagnostic]) -> Doc SyntaxClass
217+ -- prettyStage (stage,diags) =
218+ -- label_ ("Stage: "<>T.unpack stage) $ vcat $ map prettyDiagnostic diags
219+
220+ filePath :: FilePath
221+ filePath = fromMaybe dontKnow $ uriToFilePath uri
222+
223+ -- storeContents ::
224+ -- (FilePath, [(T.Text, [LSP.Diagnostic])])
225+ -- -- ^ Source File, Stage Source, Diags
226+ -- storeContents = (fromMaybe dontKnow $ uriToFilePath uri, getDiags diags)
227+
228+ dontKnow :: IsString s => s
229+ dontKnow = "<unknown>"
230+
231+ -- getDiags :: DiagnosticsBySource -> [(T.Text, [LSP.Diagnostic])]
232+ -- getDiags = map (\(ds, diag) -> (fromMaybe dontKnow ds, toList diag)) . Map.assocs
233+
234+getDiagnosticsFromStore :: StoreItem -> [Diagnostic]
235+getDiagnosticsFromStore (StoreItem _ diags) =
236+ toList =<< Map.elems diags
src/Development/IDE/Types/Location.hs+14−78
@@ -12,75 +12,25 @@ module Development.IDE.Types.Location
1212 , Location(..)
1313 , appendLocation
1414 , noLocation
15+ , noFilePath
1516 , noRange
1617 , Position(..)
1718 , Range(..)
1819 , appendRange
1920 ) where
2021
21−import Control.DeepSeq (NFData (..))
22−import Data.Aeson.Types (FromJSON, FromJSONKey, ToJSON, ToJSONKey)
23−import Data.Binary (Binary)
24−import Data.Data
25−import Data.Text.Prettyprint.Doc.Syntax
26−import GHC.Generics
27−
28−------------------------------------------------------------------------------
29−--- Types
30−------------------------------------------------------------------------------
31−
32−-- | Position in a text document expressed as zero-based line and
33−-- character offset.
34−data Position = Position
35− { positionLine :: {-# UNPACK #-} !Int
36− -- ^ Zero-based line position in the document.
37− , positionCharacter :: {-# UNPACK #-} !Int
38− -- ^ Zero-based character offset on the line.
39− } deriving (Eq, Ord, Read, Show, Generic, Data)
40−
41−instance NFData Position
42−
43−instance Pretty Position where
44− pretty pos =
45− pretty (positionLine pos + 1) <> colon <> pretty (positionCharacter pos + 1)
46−
47−
48−-- | A range in a text document expressed as inclusive start-position and an
49−-- exclusive end-position.
50−data Range = Range
51− { rangeStart :: {-# UNPACK #-} !Position
52− -- ^ The start position of the range, which is considered to be part of
53− -- the range.
54− , rangeEnd :: {-# UNPACK #-} !Position
55− -- ^ The end position of the range, which is not considered to be part
56− -- of the range.
57− } deriving (Eq, Ord, Read, Show, Generic, Data)
58−
59−instance NFData Range
60−
61−instance Pretty Range where
62− pretty range =
63− pretty (rangeStart range) <> "-" <> pretty (rangeEnd range)
64−
65−
66−-- | Represents a location inside a resource, such as a line inside a text file.
67−data Location = Location
68− { lFilePath :: !FilePath
69− -- ^ The uri of the document.
70− , lRange :: !Range
71− -- ^ The range within the document.
72− } deriving (Eq, Ord, Read, Show, Generic, Data)
73−
74−instance NFData Location
75−
22+import Language.Haskell.LSP.Types (Location(..), Range(..), Position(..), Uri(..), filePathToUri)
7623
7724-- | A dummy location to use when location information is missing.
7825noLocation :: Location
7926noLocation = Location
80− { lFilePath = "<unknown>"
81− , lRange = noRange
27+ { _uri = filePathToUri noFilePath
28+ , _range = noRange
8229 }
8330
31+noFilePath :: FilePath
32+noFilePath = "<unknown>"
33+
8434-- A dummy range to use when range is unknown
8535noRange :: Range
8636noRange = Range (Position 0 0) (Position 100000 0)
@@ -90,14 +40,14 @@ noRange = Range (Position 0 0) (Position 100000 0)
9040-- the code was generated.
9141genLocation :: Location
9242genLocation = Location
93− { lFilePath = "<generated>"
94− , lRange = Range (Position 0 0) (Position 0 0)
43+ { _uri = Uri "<generated>"
44+ , _range = Range (Position 0 0) (Position 0 0)
9545 }
9646
9747
9848-- | Is a location generated.
9949isGenLocation :: Location -> Bool
100−isGenLocation x = lFilePath x == "<generated>"
50+isGenLocation x = _uri x == Uri "<generated>"
10151
10252
10353-- | Check if a position is inside a range.
@@ -116,27 +66,13 @@ inRangeClosed pos (Range start end) = start <= pos && pos <= end
11666-- and the maximum position is the max of both.
11767appendRange :: Range -> Range -> Range
11868appendRange r1 r2
119− = Range { rangeStart = min (rangeStart r1) (rangeStart r2)
120− , rangeEnd = max (rangeEnd r1) (rangeEnd r2) }
69+ = Range { _start = min (_start r1) (_start r2)
70+ , _end = max (_end r1) (_end r2) }
12171
12272
12373-- | Produce a new location where the ranges are the appended and we choose
12474-- the file path of the second.
12575appendLocation :: Location -> Location -> Location
12676appendLocation l1 l2
127− = Location { lFilePath = lFilePath l2
128− , lRange = appendRange (lRange l1) (lRange l2) }
129−
130−instance ToJSON Position
131−instance FromJSON Position
132−instance ToJSONKey Position
133−instance FromJSONKey Position
134−instance Binary Position
135−
136−instance ToJSON Range
137−instance FromJSON Range
138−instance Binary Range
139−
140−instance ToJSON Location
141−instance FromJSON Location
142−instance Binary Location
77+ = Location { _uri = _uri l2
78+ , _range = appendRange (_range l1) (_range l2) }