Skip to content

Commit 595efc1

Browse files
committed
successful parsing of cabal.project file
1 parent 61e7d95 commit 595efc1

File tree

4 files changed

+143
-4
lines changed

4 files changed

+143
-4
lines changed

haskell-language-server.cabal

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,11 +333,12 @@ common cabalProject
333333

334334
library hls-cabal-project-plugin
335335
import: defaults, pedantic, warnings
336-
if !flag(cabal)
336+
if !flag(cabalProject)
337337
buildable: False
338338
exposed-modules:
339339
Ide.Plugin.CabalProject
340-
340+
Ide.Plugin.CabalProject.Parse
341+
Ide.Plugin.CabalProject.Diagnostics
341342

342343
build-depends:
343344
, bytestring
@@ -364,6 +365,7 @@ library hls-cabal-project-plugin
364365
, aeson
365366
, Cabal
366367
, pretty
368+
, cabal-install-parsers >= 0.6 && < 0.7
367369

368370
hs-source-dirs: plugins/hls-cabal-project-plugin/src
369371

plugins/hls-cabal-project-plugin/src/Ide/Plugin/CabalProject.hs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,17 @@ import Distribution.PackageDescription.Configuration (flattenPackageDe
4545
import Distribution.Parsec.Error
4646
import qualified Distribution.Parsec.Position as Syntax
4747
import GHC.Generics
48+
import Ide.Plugin.CabalProject.Parse (parseCabalProjectContents)
4849
import Ide.Plugin.Error
4950
import Ide.Types
5051
import qualified Language.LSP.Protocol.Lens as JL
5152
import qualified Language.LSP.Protocol.Message as LSP
5253
import Language.LSP.Protocol.Types
5354
import qualified Language.LSP.VFS as VFS
55+
import System.FilePath (takeFileName)
5456
import Text.Regex.TDFA
5557

58+
5659
data Log
5760
= LogModificationTime NormalizedFilePath FileVersion
5861
| LogShake Shake.Log
@@ -81,7 +84,7 @@ instance Pretty Log where
8184

8285
descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState
8386
descriptor recorder plId =
84-
(defaultCabalProjectPluginDescriptor plId "Provides a variety of IDE features in cabal files")
87+
(defaultCabalProjectPluginDescriptor plId "Provides a variety of IDE features in cabal.project files")
8588
{ pluginRules = cabalRules recorder plId
8689
, pluginHandlers =
8790
mconcat
@@ -92,11 +95,15 @@ descriptor recorder plId =
9295
\ide vfs _ (DidOpenTextDocumentParams TextDocumentItem{_uri, _version}) -> liftIO $ do
9396
whenUriFile _uri $ \file -> do
9497
log' Debug $ LogDocOpened _uri
98+
result <- parseCabalProjectContents (fromNormalizedFilePath file)
99+
case result of
100+
Left err -> putStrLn $ "Cabal project parse failed: " ++ err
101+
Right project -> putStrLn $ "Cabal project parsed successfully: " ++ show project
95102
restartCabalShakeSession (shakeExtras ide) vfs file "(opened)" $
96103
addFileOfInterest recorder ide file Modified{firstOpen = True}
97104
, mkPluginNotificationHandler LSP.SMethod_TextDocumentDidChange $
98105
\ide vfs _ (DidChangeTextDocumentParams VersionedTextDocumentIdentifier{_uri} _) -> liftIO $ do
99-
whenUriFile _uri $ \file -> do
106+
whenUriFile _uri $ \file-> do
100107
log' Debug $ LogDocModified _uri
101108
restartCabalShakeSession (shakeExtras ide) vfs file "(changed)" $
102109
addFileOfInterest recorder ide file Modified{firstOpen = False}
@@ -126,6 +133,7 @@ descriptor recorder plId =
126133
cabalRules :: Recorder (WithPriority Log) -> PluginId -> Rules ()
127134
cabalRules recorder _ = do
128135
ofInterestRules recorder
136+
-- cabalProjectParseRules recorder
129137

130138
{- | Helper function to restart the shake session, specifically for modifying .cabal files.
131139
No special logic, just group up a bunch of functions you need for the base
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
module Ide.Plugin.CabalProject.Diagnostics where
2+
3+
diagnostic = undefined
4+
5+
-- {-# LANGUAGE DuplicateRecordFields #-}
6+
-- {-# LANGUAGE OverloadedStrings #-}
7+
-- module Ide.Plugin.CabalProject.Diagnostics
8+
-- ( errorDiagnostic
9+
-- , warningDiagnostic
10+
-- , positionFromCabaProjectPosition
11+
-- , fatalParseErrorDiagnostic
12+
-- -- * Re-exports
13+
-- , FileDiagnostic
14+
-- , Diagnostic(..)
15+
-- )
16+
-- where
17+
18+
-- import Control.Lens ((&), (.~))
19+
-- import qualified Data.Text as T
20+
-- import Development.IDE (FileDiagnostic)
21+
-- import Development.IDE.Types.Diagnostics (fdLspDiagnosticL,
22+
-- ideErrorWithSource)
23+
-- import Distribution.Fields (showPError, showPWarning)
24+
-- import qualified Distribution.Parsec as Syntax
25+
-- import Ide.PluginUtils (extendNextLine)
26+
-- import Language.LSP.Protocol.Lens (range)
27+
-- import Language.LSP.Protocol.Types (Diagnostic (..),
28+
-- DiagnosticSeverity (..),
29+
-- NormalizedFilePath,
30+
-- Position (Position),
31+
-- Range (Range),
32+
-- fromNormalizedFilePath)
33+
34+
-- -- | Produce a diagnostic for a fatal Cabal parser error.
35+
-- fatalParseErrorDiagnostic :: NormalizedFilePath -> T.Text -> FileDiagnostic
36+
-- fatalParseErrorDiagnostic fp msg =
37+
-- mkDiag fp "cabal" DiagnosticSeverity_Error (toBeginningOfNextLine Syntax.zeroPos) msg
38+
39+
-- -- | Produce a diagnostic from a Cabal parser error
40+
-- errorDiagnostic :: NormalizedFilePath -> Syntax.PError -> FileDiagnostic
41+
-- errorDiagnostic fp err@(Syntax.PError pos _) =
42+
-- mkDiag fp "cabal" DiagnosticSeverity_Error (toBeginningOfNextLine pos) msg
43+
-- where
44+
-- msg = T.pack $ showPError (fromNormalizedFilePath fp) err
45+
46+
-- -- | Produce a diagnostic from a Cabal parser warning
47+
-- warningDiagnostic :: NormalizedFilePath -> Syntax.PWarning -> FileDiagnostic
48+
-- warningDiagnostic fp warning@(Syntax.PWarning _ pos _) =
49+
-- mkDiag fp "cabal" DiagnosticSeverity_Warning (toBeginningOfNextLine pos) msg
50+
-- where
51+
-- msg = T.pack $ showPWarning (fromNormalizedFilePath fp) warning
52+
53+
-- -- | The Cabal parser does not output a _range_ for a warning/error,
54+
-- -- only a single source code 'Lib.Position'.
55+
-- -- We define the range to be _from_ this position
56+
-- -- _to_ the first column of the next line.
57+
-- toBeginningOfNextLine :: Syntax.Position -> Range
58+
-- toBeginningOfNextLine cabalPos = extendNextLine $ Range pos pos
59+
-- where
60+
-- pos = positionFromCabalPosition cabalPos
61+
62+
-- -- | Convert a 'Lib.Position' from Cabal to a 'Range' that LSP understands.
63+
-- --
64+
-- -- Prefer this function over hand-rolled unpacking/packing, since LSP is zero-based,
65+
-- -- while Cabal is one-based.
66+
-- --
67+
-- -- >>> positionFromCabalPosition $ Lib.Position 1 1
68+
-- -- Position 0 0
69+
-- positionFromCabalPosition :: Syntax.Position -> Position
70+
-- positionFromCabalPosition (Syntax.Position line column) = Position (fromIntegral line') (fromIntegral col')
71+
-- where
72+
-- -- LSP is zero-based, Cabal is one-based
73+
-- -- Cabal can return line 0 for errors in the first line
74+
-- line' = if line <= 0 then 0 else line-1
75+
-- col' = if column <= 0 then 0 else column-1
76+
77+
-- -- | Create a 'FileDiagnostic'
78+
-- mkDiag
79+
-- :: NormalizedFilePath
80+
-- -- ^ Cabal file path
81+
-- -> T.Text
82+
-- -- ^ Where does the diagnostic come from?
83+
-- -> DiagnosticSeverity
84+
-- -- ^ Severity
85+
-- -> Range
86+
-- -- ^ Which source code range should the editor highlight?
87+
-- -> T.Text
88+
-- -- ^ The message displayed by the editor
89+
-- -> FileDiagnostic
90+
-- mkDiag file diagSource sev loc msg =
91+
-- ideErrorWithSource
92+
-- (Just diagSource)
93+
-- (Just sev)
94+
-- file
95+
-- msg
96+
-- Nothing
97+
-- & fdLspDiagnosticL . range .~ loc
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{-# LANGUAGE OverloadedStrings #-}
2+
3+
module Ide.Plugin.CabalProject.Parse
4+
( parseCabalProjectContents
5+
) where
6+
7+
import Data.Void (Void)
8+
9+
-- cabal-install-parsers 0.6 modules -----------------------------
10+
import Cabal.Parse (ParseError)
11+
import Cabal.Project (Project,
12+
parseProject)
13+
14+
-- error type lives in Cabal-syntax
15+
-- import Distribution.Parsec.Error (ParseError)
16+
17+
import Distribution.Types.GenericPackageDescription (GenericPackageDescription)
18+
19+
import qualified Data.ByteString as BS
20+
-- import Distribution.Parsec.Project (parseProject)
21+
-- import Distribution.Parsec.Common (ParseError)
22+
import Data.List.NonEmpty (NonEmpty)
23+
import Data.Text (pack)
24+
25+
parseCabalProjectContents :: FilePath -> IO (Either String (Project Void String String))
26+
parseCabalProjectContents file = do
27+
contents <- BS.readFile file
28+
case parseProject file contents of
29+
Left parseErr ->
30+
pure $ Left ("Parse error in " ++ file ++ ": " ++ show parseErr)
31+
Right project ->
32+
pure $ Right project

0 commit comments

Comments
 (0)