Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement code action to add module to cabal file #3778

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions cabal-parser/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Revision history for cabal-parser

## 0.1.0.0 -- YYYY-mm-dd

* First version. Released on an unsuspecting world.
20 changes: 20 additions & 0 deletions cabal-parser/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Copyright (c) 2023 Jana Chadt

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
71 changes: 71 additions & 0 deletions cabal-parser/cabal-parser.cabal
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
cabal-version: 3.0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd probably just make this an internal library of the cabal plugin for now. We can split it out later, but additional packages do mean we have to do more releases etc.

name: cabal-parser
version: 0.1.0.0
synopsis: Parses cabal files to AST, annotated with source positions
license: MIT
license-file: LICENSE
author: VeryMilkyJoe
maintainer: [email protected]
category: Parser
build-type: Simple
extra-doc-files: CHANGELOG.md

common warnings
ghc-options: -Wall

library
import: warnings
exposed-modules:
Text.Cabal.Data
Text.Cabal.Parser
Text.Cabal.Types
Text.Cabal.Value

build-depends:
, base
, containers
, lsp
, lsp-types
, megaparsec >= 8 && < 10
, prettyprinter >= 1.7
, text
hs-source-dirs: src
default-language: Haskell2010

executable cabal-parser-cli
import: warnings
default-language: Haskell2010
hs-source-dirs: exe
main-is: Main.hs
other-modules: Options
build-depends:
, base
, cabal-parser
, optparse-applicative
, filepath
, directory
, text
, transformers
, megaparsec
, prettyprinter

test-suite tests
import: warnings
default-language: Haskell2010
type: exitcode-stdio-1.0
hs-source-dirs: test
main-is: Main.hs
other-modules: Parser
build-depends:
, base
, Cabal-syntax
, cabal-parser
, directory
, filepath
, lsp-types
, megaparsec
, prettyprinter
, tasty
, tasty-expected-failure
, tasty-hunit
, text
92 changes: 92 additions & 0 deletions cabal-parser/exe/Main.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
module Main where

import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.Trans.State.Strict
import qualified Data.Text.IO as T
import Options
import Options.Applicative
import Prettyprinter
import System.Directory (canonicalizePath,
doesDirectoryExist,
listDirectory)
import System.FilePath
import System.IO
import Text.Cabal.Parser
import Text.Megaparsec (errorBundlePretty)

main :: IO ()
main = do
options <- execParser opts
case optCommand options of
Parse parseOpts -> do
analytics <- flip execStateT defParseAnalytics $ forM_ (parseFiles parseOpts) $ \fileTarget' -> do
fileTarget <- liftIO $ canonicalizePath fileTarget'
isDirectory <- liftIO $ doesDirectoryExist fileTarget

if isDirectory
then do
targets <- liftIO $ listDirectory fileTarget
forM_ targets $ \target ->
parseFileAndReport parseOpts (fileTarget </> target)
else do
parseFileAndReport parseOpts fileTarget

putStrLn $ showAnalytics analytics

parseFileAndReport :: MonadIO m => ParseOptions -> FilePath -> StateT ParseAnalytics m ()
parseFileAndReport parseOpts cabalFile = do
cabalFileContents <- liftIO $ T.readFile cabalFile
liftIO $ putStr $ "Parsing \"" <> cabalFile <> "\""
liftIO $ hFlush stdout
case parseCabalFile cabalFile cabalFileContents of
Left err -> do
addFailure
liftIO $ putStrLn $ " failed"
when (not $ silent parseOpts) $ do
liftIO $ putStrLn $ errorBundlePretty err
Right ast -> do
addSuccess
liftIO $ putStrLn $ " succeeded"
when (showAst parseOpts) $ do
liftIO $ putStrLn $ show $ pretty ast

data ParseAnalytics = ParseAnalytics
{ parsedSuccessfully :: !Int
, parsedFailed :: !Int
, parsedNum :: !Int
}
deriving (Show, Eq, Ord)

defParseAnalytics :: ParseAnalytics
defParseAnalytics =
ParseAnalytics
{ parsedSuccessfully = 0
, parsedFailed = 0
, parsedNum = 0
}

addSuccess :: (Monad m) => StateT ParseAnalytics m ()
addSuccess = do
modify'
( \pa ->
pa
{ parsedSuccessfully = parsedSuccessfully pa + 1
, parsedNum = parsedNum pa + 1
}
)

addFailure :: (Monad m) => StateT ParseAnalytics m ()
addFailure = do
modify'
( \pa ->
pa
{ parsedFailed = parsedFailed pa + 1
, parsedNum = parsedNum pa + 1
}
)

showAnalytics :: ParseAnalytics -> String
showAnalytics a
| parsedFailed a == 0 && parsedSuccessfully a > 0 = "Successfully parsed all cabal files (" <> show (parsedNum a) <> ")"
| otherwise = "Parsed " <> show (parsedSuccessfully a) <> " out of " <> show (parsedNum a) <> " cabal files"
55 changes: 55 additions & 0 deletions cabal-parser/exe/Options.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
module Options
( Options(..)
, Command(..)
, ParseOptions(..)
, opts
) where

import Options.Applicative

data Options = Options
{ optCommand :: Command
}
deriving (Show, Eq, Ord)

data Command
= Parse ParseOptions
deriving (Show, Eq, Ord)

data ParseOptions = ParseOptions
{ parseFiles :: [FilePath]
, silent :: Bool
, showAst :: Bool
}
deriving (Show, Eq, Ord)

opts :: ParserInfo Options
opts = info (options <**> helper)
( fullDesc
<> progDesc "Command Line Interface for cabal-parse"
<> header "Parse your cabal files"
)

options :: Parser Options
options =
Options
<$> parseCommands

parseCommands :: Parser Command
parseCommands =
subparser
( command "parse" (info (Parse <$> parseCommand) $ progDesc "Check cabal-parse can parse your cabal file")
)

parseCommand :: Parser ParseOptions
parseCommand =
ParseOptions
<$> some (argument str (metavar "FILES"))
<*> flag False True
( long "silent"
<> help "Don't show parse errors on stdout"
)
<*> flag False True
( long "ast"
<> help "Show the parsed AST in a human readable form"
)
Loading