mirror of
https://github.com/github/semantic.git
synced 2024-12-23 23:11:50 +03:00
Merge branch 'master' into bump-tree-sitter-python
This commit is contained in:
commit
1c72b23cea
12
HLint.hs
12
HLint.hs
@ -19,3 +19,15 @@ error "use wrap" = free . Free ==> wrap
|
||||
|
||||
error "use extract" = headF . runCofree ==> extract
|
||||
error "use unwrap" = tailF . runCofree ==> unwrap
|
||||
|
||||
error "avoid head" = head
|
||||
where note = "head is partial; consider using Data.Maybe.listToMaybe"
|
||||
|
||||
error "avoid tail" = tail
|
||||
where note = "tail is partial; consider pattern-matching"
|
||||
|
||||
error "avoid init" = init
|
||||
where note = "init is partial; consider pattern-matching"
|
||||
|
||||
error "avoid last" = last
|
||||
where note = "last is partial; consider pattern-matching"
|
||||
|
@ -63,7 +63,6 @@ library
|
||||
, Parser
|
||||
, Patch
|
||||
, Paths_semantic_diff
|
||||
, Prologue
|
||||
, Renderer
|
||||
, Renderer.JSON
|
||||
, Renderer.Patch
|
||||
@ -87,9 +86,10 @@ library
|
||||
, async
|
||||
, bifunctors
|
||||
, bytestring
|
||||
, cmark
|
||||
, cmark-gfm
|
||||
, comonad
|
||||
, containers
|
||||
, deepseq
|
||||
, directory
|
||||
, effects
|
||||
, filepath
|
||||
@ -104,7 +104,6 @@ library
|
||||
, mtl
|
||||
, optparse-applicative
|
||||
, parallel
|
||||
, protolude
|
||||
, recursion-schemes
|
||||
, semigroups
|
||||
, split
|
||||
@ -121,7 +120,7 @@ library
|
||||
, python
|
||||
, json
|
||||
default-language: Haskell2010
|
||||
default-extensions: DeriveFunctor, DeriveFoldable, DeriveTraversable, DeriveGeneric, FlexibleContexts, FlexibleInstances, OverloadedStrings, NoImplicitPrelude, RecordWildCards, StrictData
|
||||
default-extensions: DeriveFunctor, DeriveFoldable, DeriveTraversable, DeriveGeneric, FlexibleContexts, FlexibleInstances, OverloadedStrings, RecordWildCards, StrictData
|
||||
ghc-options: -Wall -fno-warn-name-shadowing -O -j
|
||||
ghc-prof-options: -fprof-auto
|
||||
|
||||
@ -134,7 +133,7 @@ executable semantic
|
||||
build-depends: base
|
||||
, semantic-diff
|
||||
default-language: Haskell2010
|
||||
default-extensions: FlexibleInstances, OverloadedStrings, NoImplicitPrelude, RecordWildCards
|
||||
default-extensions: FlexibleInstances, OverloadedStrings, RecordWildCards
|
||||
|
||||
test-suite test
|
||||
type: exitcode-stdio-1.0
|
||||
@ -162,7 +161,10 @@ test-suite test
|
||||
, base
|
||||
, bifunctors
|
||||
, bytestring
|
||||
, comonad
|
||||
, deepseq
|
||||
, filepath
|
||||
, free
|
||||
, Glob
|
||||
, haskell-tree-sitter
|
||||
, hspec >= 2.4.1
|
||||
@ -171,7 +173,6 @@ test-suite test
|
||||
, HUnit
|
||||
, leancheck
|
||||
, mtl
|
||||
, protolude
|
||||
, containers
|
||||
, recursion-schemes >= 4.1
|
||||
, semantic-diff
|
||||
@ -179,7 +180,7 @@ test-suite test
|
||||
, these
|
||||
ghc-options: -threaded -rtsopts -with-rtsopts=-N -j
|
||||
default-language: Haskell2010
|
||||
default-extensions: DeriveFunctor, DeriveGeneric, FlexibleContexts, FlexibleInstances, OverloadedStrings, NoImplicitPrelude, RecordWildCards
|
||||
default-extensions: DeriveFunctor, DeriveGeneric, FlexibleContexts, FlexibleInstances, OverloadedStrings, RecordWildCards
|
||||
|
||||
custom-setup
|
||||
setup-depends: base >= 4.8 && < 5
|
||||
|
@ -1,16 +1,18 @@
|
||||
{-# LANGUAGE DataKinds, DefaultSignatures, GADTs, RankNTypes, TypeOperators #-}
|
||||
module Algorithm where
|
||||
|
||||
import Control.Applicative (liftA2)
|
||||
import Control.Monad (guard)
|
||||
import Control.Monad.Free.Freer
|
||||
import Data.Function (on)
|
||||
import Data.Functor.Both
|
||||
import Data.Functor.Classes
|
||||
import Data.Maybe
|
||||
import Data.These
|
||||
import Data.Union
|
||||
import Diff
|
||||
import GHC.Generics
|
||||
import Prologue hiding (liftF)
|
||||
import Term
|
||||
import Text.Show
|
||||
|
||||
-- | A single step in a diffing algorithm, parameterized by the types of terms, diffs, and the result of the applicable algorithm.
|
||||
data AlgorithmF term diff result where
|
||||
|
@ -8,19 +8,29 @@ module Alignment
|
||||
, modifyJoin
|
||||
) where
|
||||
|
||||
import Prologue hiding (fst, snd)
|
||||
import Data.Bifunctor (bimap, first, second)
|
||||
import Control.Arrow ((***))
|
||||
import Control.Comonad (extract)
|
||||
import Control.Monad (join)
|
||||
import Control.Monad.Free
|
||||
import Data.Align
|
||||
import Data.Bifunctor.Join
|
||||
import Data.Foldable (toList)
|
||||
import Data.Function (on)
|
||||
import Data.Functor.Both
|
||||
import Data.List (partition)
|
||||
import Data.Maybe (fromJust)
|
||||
import Data.Functor.Foldable
|
||||
import Data.Functor.Identity
|
||||
import Data.List (partition, sortBy)
|
||||
import Data.Maybe (catMaybes, fromJust, listToMaybe)
|
||||
import Data.Range
|
||||
import Data.Semigroup ((<>))
|
||||
import Data.Source
|
||||
import Data.Record
|
||||
import Data.These
|
||||
import Diff
|
||||
import Info
|
||||
import Patch
|
||||
import Prelude hiding (fst, snd)
|
||||
import SplitDiff
|
||||
import Term
|
||||
|
||||
@ -30,7 +40,7 @@ numberedRows = countUp (both 1 1)
|
||||
where countUp _ [] = []
|
||||
countUp from (row : rows) = numberedLine from row : countUp (nextLineNumbers from row) rows
|
||||
numberedLine from row = fromJust ((,) <$> modifyJoin (uncurry These) from `applyThese` row)
|
||||
nextLineNumbers from row = modifyJoin (fromThese identity identity) (succ <$ row) <*> from
|
||||
nextLineNumbers from row = modifyJoin (fromThese id id) (succ <$ row) <*> from
|
||||
|
||||
-- | Determine whether a line contains any patches.
|
||||
hasChanges :: (Foldable f, Functor f) => SplitDiff f annotation -> Bool
|
||||
@ -45,7 +55,7 @@ alignPatch :: forall fields f. (Traversable f, HasField fields Range) => Both So
|
||||
alignPatch sources patch = case patch of
|
||||
Delete term -> fmap (pure . SplitDelete) <$> alignSyntax' this (fst sources) term
|
||||
Insert term -> fmap (pure . SplitInsert) <$> alignSyntax' that (snd sources) term
|
||||
Replace term1 term2 -> fmap (pure . SplitReplace) <$> alignWith (fmap (these identity identity const . runJoin) . Join)
|
||||
Replace term1 term2 -> fmap (pure . SplitReplace) <$> alignWith (fmap (these id id const . runJoin) . Join)
|
||||
(alignSyntax' this (fst sources) term1)
|
||||
(alignSyntax' that (snd sources) term2)
|
||||
where getRange = byteRange . extract
|
||||
@ -93,7 +103,7 @@ alignBranch getRange children ranges = case intersectingChildren of
|
||||
(leftRange, rightRange) = splitThese headRanges
|
||||
alignAsymmetrically range advanceBy = let (line, remaining) = lineAndRemaining asymmetricalChildren range in
|
||||
line $ alignBranch getRange (remaining <> symmetricalChildren <> nonIntersectingChildren) (modifyJoin (advanceBy (drop 1)) ranges)
|
||||
lineAndRemaining _ Nothing = (identity, [])
|
||||
lineAndRemaining _ Nothing = (id, [])
|
||||
lineAndRemaining children (Just ranges) = let (intersections, remaining) = alignChildren getRange children ranges in
|
||||
((:) $ (,) <$> ranges `applyToBoth` (sortBy (compare `on` getRange) <$> intersections), remaining)
|
||||
|
||||
@ -106,9 +116,9 @@ alignChildren getRange (first:rest) headRanges
|
||||
-- It intersects on both sides, so we can just take the first line whole.
|
||||
(True, True) -> ((<>) <$> toTerms first <*> firstRemaining, restRemaining)
|
||||
-- It only intersects on the left, so split it up.
|
||||
(True, False) -> ((<>) <$> toTerms (fromJust l) <*> firstRemaining, maybe identity (:) r restRemaining)
|
||||
(True, False) -> ((<>) <$> toTerms (fromJust l) <*> firstRemaining, maybe id (:) r restRemaining)
|
||||
-- It only intersects on the right, so split it up.
|
||||
(False, True) -> ((<>) <$> toTerms (fromJust r) <*> firstRemaining, maybe identity (:) l restRemaining)
|
||||
(False, True) -> ((<>) <$> toTerms (fromJust r) <*> firstRemaining, maybe id (:) l restRemaining)
|
||||
-- It doesn’t intersect at all, so skip it and move along.
|
||||
(False, False) -> (firstRemaining, first:restRemaining)
|
||||
| otherwise = alignChildren getRange rest headRanges
|
||||
|
@ -3,10 +3,11 @@
|
||||
{-# OPTIONS_GHC -funbox-strict-fields #-}
|
||||
module Category where
|
||||
|
||||
import Prologue
|
||||
import Control.DeepSeq
|
||||
import Data.Functor.Listable
|
||||
import Data.Text (pack)
|
||||
import Data.Text.Encoding (encodeUtf8)
|
||||
import Data.Hashable
|
||||
import Data.Text (Text)
|
||||
import GHC.Generics
|
||||
|
||||
-- | A standardized category of AST node. Used to determine the semantics for
|
||||
-- | semantic diffing and define comparability of nodes.
|
||||
@ -243,12 +244,6 @@ data Category
|
||||
|
||||
instance Hashable Category
|
||||
|
||||
instance (StringConv Category Text) where
|
||||
strConv _ = pack . show
|
||||
|
||||
instance (StringConv Category ByteString) where
|
||||
strConv _ = encodeUtf8 . show
|
||||
|
||||
instance Listable Category where
|
||||
tiers = cons0 Program
|
||||
\/ cons0 ParseError
|
||||
|
@ -3,10 +3,10 @@ module Data.Align.Generic where
|
||||
|
||||
import Control.Monad
|
||||
import Data.Align
|
||||
import Data.Functor.Identity
|
||||
import Data.These
|
||||
import Data.Union
|
||||
import GHC.Generics
|
||||
import Prologue
|
||||
|
||||
-- | Functors which can be aligned (structure-unioning-ly zipped). The default implementation will operate generically over the constructors in the aligning type.
|
||||
class GAlign f where
|
||||
@ -16,7 +16,7 @@ class GAlign f where
|
||||
galignWith f a b = to1 <$> galignWith f (from1 a) (from1 b)
|
||||
|
||||
galign :: GAlign f => f a -> f b -> Maybe (f (These a b))
|
||||
galign = galignWith identity
|
||||
galign = galignWith id
|
||||
|
||||
-- 'Data.Align.Align' instances
|
||||
|
||||
|
@ -10,10 +10,12 @@ module Data.Blob
|
||||
, nullOid
|
||||
) where
|
||||
|
||||
import Data.ByteString.Char8 (ByteString, pack)
|
||||
import Data.Maybe (isJust)
|
||||
import Data.Source as Source
|
||||
import Data.Word
|
||||
import Language
|
||||
import Numeric
|
||||
import Prologue
|
||||
|
||||
-- | The source, oid, path, and Maybe BlobKind of a blob.
|
||||
data Blob = Blob
|
||||
@ -30,9 +32,9 @@ data BlobKind = PlainBlob Word32 | ExecutableBlob Word32 | SymlinkBlob Word32
|
||||
deriving (Show, Eq)
|
||||
|
||||
modeToDigits :: BlobKind -> ByteString
|
||||
modeToDigits (PlainBlob mode) = toS $ showOct mode ""
|
||||
modeToDigits (ExecutableBlob mode) = toS $ showOct mode ""
|
||||
modeToDigits (SymlinkBlob mode) = toS $ showOct mode ""
|
||||
modeToDigits (PlainBlob mode) = pack $ showOct mode ""
|
||||
modeToDigits (ExecutableBlob mode) = pack $ showOct mode ""
|
||||
modeToDigits (SymlinkBlob mode) = pack $ showOct mode ""
|
||||
|
||||
-- | The default plain blob mode
|
||||
defaultPlainBlob :: BlobKind
|
||||
|
@ -1,9 +1,11 @@
|
||||
{-# OPTIONS_GHC -fno-warn-orphans -funbox-strict-fields #-}
|
||||
module Data.Functor.Both (Both, both, runBothWith, fst, snd, module X) where
|
||||
|
||||
import Control.DeepSeq
|
||||
import Data.Bifunctor.Join as X
|
||||
import Prologue hiding (fst, snd)
|
||||
import qualified Prologue
|
||||
import Data.Semigroup
|
||||
import Prelude hiding (fst, snd)
|
||||
import qualified Prelude
|
||||
|
||||
-- | A computation over both sides of a pair.
|
||||
type Both a = Join (,) a
|
||||
@ -18,11 +20,11 @@ runBothWith f = uncurry f . runJoin
|
||||
|
||||
-- | Runs the left side of a `Both`.
|
||||
fst :: Both a -> a
|
||||
fst = Prologue.fst . runJoin
|
||||
fst = Prelude.fst . runJoin
|
||||
|
||||
-- | Runs the right side of a `Both`.
|
||||
snd :: Both a -> a
|
||||
snd = Prologue.snd . runJoin
|
||||
snd = Prelude.snd . runJoin
|
||||
|
||||
instance (Semigroup a, Monoid a) => Monoid (Join (,) a) where
|
||||
mempty = pure mempty
|
||||
|
@ -8,7 +8,6 @@ module Data.Functor.Classes.Eq.Generic
|
||||
import Control.Comonad.Cofree as Cofree
|
||||
import Data.Functor.Classes
|
||||
import GHC.Generics
|
||||
import Prologue
|
||||
|
||||
-- | Generically-derivable lifting of the 'Eq' class to unary type constructors.
|
||||
class GEq1 f where
|
||||
|
@ -9,7 +9,6 @@ module Data.Functor.Classes.Show.Generic
|
||||
|
||||
import Data.Functor.Classes
|
||||
import GHC.Generics
|
||||
import Prologue
|
||||
import Text.Show
|
||||
|
||||
-- | Generically-derivable lifting of the 'Show' class to unary type constructors.
|
||||
@ -41,7 +40,7 @@ instance Show a => GShow1 (Either a) where gliftShowsPrec = liftShowsPrec
|
||||
-- Generics
|
||||
|
||||
instance GShow1 U1 where
|
||||
gliftShowsPrec _ _ _ _ = identity
|
||||
gliftShowsPrec _ _ _ _ = id
|
||||
|
||||
instance GShow1 Par1 where
|
||||
gliftShowsPrec sp _ d (Par1 a) = sp d a
|
||||
|
@ -24,9 +24,12 @@ module Data.Functor.Listable
|
||||
, ofWeight
|
||||
) where
|
||||
|
||||
import Control.Comonad.Cofree as Cofree
|
||||
import Control.Comonad.Trans.Cofree as CofreeF
|
||||
import Control.Monad.Free as Free
|
||||
import Control.Monad.Trans.Free as FreeF
|
||||
import Data.Bifunctor.Join
|
||||
import Data.These
|
||||
import Prologue
|
||||
import Test.LeanCheck
|
||||
|
||||
type Tier a = [a]
|
||||
@ -114,24 +117,27 @@ instance Listable2 These where
|
||||
liftTiers2 this that = liftCons1 this This \/ liftCons1 that That \/ liftCons2 this that These
|
||||
|
||||
instance Listable1 f => Listable2 (CofreeF f) where
|
||||
liftTiers2 annotationTiers recurTiers = liftCons2 annotationTiers (liftTiers recurTiers) (:<)
|
||||
liftTiers2 annotationTiers recurTiers = liftCons2 annotationTiers (liftTiers recurTiers) (CofreeF.:<)
|
||||
|
||||
instance (Listable1 f, Listable a) => Listable1 (CofreeF f a) where
|
||||
liftTiers = liftTiers2 tiers
|
||||
|
||||
instance (Functor f, Listable1 f) => Listable1 (Cofree f) where
|
||||
instance (Functor f, Listable1 f) => Listable1 (Cofree.Cofree f) where
|
||||
liftTiers annotationTiers = go
|
||||
where go = liftCons1 (liftTiers2 annotationTiers go) cofree
|
||||
cofree (a CofreeF.:< f) = a Cofree.:< f
|
||||
|
||||
instance Listable1 f => Listable2 (FreeF f) where
|
||||
liftTiers2 pureTiers recurTiers = liftCons1 pureTiers Pure \/ liftCons1 (liftTiers recurTiers) Free
|
||||
liftTiers2 pureTiers recurTiers = liftCons1 pureTiers FreeF.Pure \/ liftCons1 (liftTiers recurTiers) FreeF.Free
|
||||
|
||||
instance (Listable1 f, Listable a) => Listable1 (FreeF f a) where
|
||||
liftTiers = liftTiers2 tiers
|
||||
|
||||
instance (Functor f, Listable1 f) => Listable1 (Free f) where
|
||||
instance (Functor f, Listable1 f) => Listable1 (Free.Free f) where
|
||||
liftTiers pureTiers = go
|
||||
where go = liftCons1 (liftTiers2 pureTiers go) free
|
||||
free (FreeF.Free f) = Free.Free f
|
||||
free (FreeF.Pure a) = Free.Pure a
|
||||
|
||||
instance (Listable1 f, Listable a) => Listable (ListableF f a) where
|
||||
tiers = ListableF `mapT` tiers1
|
||||
|
@ -1,10 +1,10 @@
|
||||
{-# LANGUAGE DefaultSignatures #-}
|
||||
module Data.Mergeable where
|
||||
|
||||
import Control.Applicative
|
||||
import Data.Functor.Identity
|
||||
import Data.Mergeable.Generic
|
||||
import GHC.Generics
|
||||
import Prologue
|
||||
|
||||
-- Classes
|
||||
|
||||
@ -25,7 +25,7 @@ class Functor t => Mergeable t where
|
||||
|
||||
-- | Sequnce a 'Mergeable' functor by 'merge'ing the 'Alternative' values.
|
||||
sequenceAlt :: Alternative f => t (f a) -> f (t a)
|
||||
sequenceAlt = merge identity
|
||||
sequenceAlt = merge id
|
||||
|
||||
|
||||
-- Instances
|
||||
|
@ -1,8 +1,8 @@
|
||||
{-# LANGUAGE TypeOperators #-}
|
||||
module Data.Mergeable.Generic where
|
||||
|
||||
import Control.Applicative
|
||||
import GHC.Generics
|
||||
import Prologue
|
||||
|
||||
-- Classes
|
||||
|
||||
@ -38,7 +38,7 @@ instance (GMergeable f, GMergeable g) => GMergeable (f :*: g) where
|
||||
gmerge f (a :*: b) = (:*:) <$> gmerge f a <*> gmerge f b
|
||||
|
||||
instance GMergeable [] where
|
||||
gmerge f (x:xs) = ((:) <$> f x <|> pure identity) <*> gmerge f xs
|
||||
gmerge f (x:xs) = ((:) <$> f x <|> pure id) <*> gmerge f xs
|
||||
gmerge _ [] = pure []
|
||||
|
||||
instance GMergeable Maybe where
|
||||
|
@ -1,6 +1,6 @@
|
||||
module Data.Output where
|
||||
|
||||
import Prologue
|
||||
import Data.ByteString (ByteString)
|
||||
|
||||
class Monoid o => Output o where
|
||||
toOutput :: o -> ByteString
|
||||
|
@ -6,8 +6,9 @@ module Data.Range
|
||||
, intersectsRange
|
||||
) where
|
||||
|
||||
import Control.DeepSeq
|
||||
import Data.Semigroup
|
||||
import Prologue
|
||||
import GHC.Generics
|
||||
import Test.LeanCheck
|
||||
|
||||
-- | A half-open interval of integers, defined by start & end indices.
|
||||
|
@ -1,9 +1,10 @@
|
||||
{-# LANGUAGE ConstraintKinds, DataKinds, GADTs, KindSignatures, MultiParamTypeClasses, TypeFamilies, TypeOperators #-}
|
||||
module Data.Record where
|
||||
|
||||
import Control.DeepSeq
|
||||
import Data.Kind
|
||||
import Data.Functor.Listable
|
||||
import GHC.Show
|
||||
import Prologue
|
||||
import Data.Semigroup
|
||||
|
||||
-- | A type-safe, extensible record structure.
|
||||
-- |
|
||||
|
@ -27,22 +27,25 @@ module Data.Source
|
||||
, ListableByteString(..)
|
||||
) where
|
||||
|
||||
import Control.Arrow ((&&&))
|
||||
import Data.Array
|
||||
import qualified Data.ByteString as B
|
||||
import Data.Char (ord)
|
||||
import Data.Char (chr, ord)
|
||||
import Data.List (span)
|
||||
import Data.Monoid (First(..), Last(..))
|
||||
import Data.Range
|
||||
import Data.Semigroup hiding (First(..), Last(..))
|
||||
import Data.Span
|
||||
import Data.String (IsString(..))
|
||||
import qualified Data.Text as T
|
||||
import Prologue
|
||||
import qualified Data.Text.Encoding as T
|
||||
import Test.LeanCheck
|
||||
|
||||
-- | The contents of a source file, represented as a 'ByteString'.
|
||||
newtype Source = Source { sourceBytes :: B.ByteString }
|
||||
deriving (Eq, IsString, Show)
|
||||
|
||||
fromBytes :: ByteString -> Source
|
||||
fromBytes :: B.ByteString -> Source
|
||||
fromBytes = Source
|
||||
|
||||
|
||||
@ -67,13 +70,13 @@ totalSpan source = Span (Pos 1 1) (Pos (length ranges) (succ (end lastRange - st
|
||||
|
||||
-- En/decoding
|
||||
|
||||
-- | Return a 'Source' from a 'ByteString'.
|
||||
-- | Return a 'Source' from a 'Text'.
|
||||
fromText :: T.Text -> Source
|
||||
fromText = Source . encodeUtf8
|
||||
fromText = Source . T.encodeUtf8
|
||||
|
||||
-- | Return the ByteString contained in the 'Source'.
|
||||
toText :: Source -> Text
|
||||
toText = decodeUtf8 . sourceBytes
|
||||
-- | Return the Text contained in the 'Source'.
|
||||
toText :: Source -> T.Text
|
||||
toText = T.decodeUtf8 . sourceBytes
|
||||
|
||||
|
||||
-- | Return a 'Source' that contains a slice of the given 'Source'.
|
||||
@ -148,12 +151,9 @@ instance Listable Source where
|
||||
newtype ListableByteString = ListableByteString { unListableByteString :: B.ByteString }
|
||||
|
||||
instance Listable ListableByteString where
|
||||
tiers = (ListableByteString . encodeUtf8 . T.pack) `mapT` strings
|
||||
tiers = (ListableByteString . T.encodeUtf8 . T.pack) `mapT` strings
|
||||
where strings = foldr ((\\//) . listsOf . toTiers) []
|
||||
[ ['a'..'z'] <> ['A'..'Z'] <> ['0'..'9']
|
||||
, [' '..'/'] <> [':'..'@'] <> ['['..'`'] <> ['{'..'~']
|
||||
, [chr 0x00..chr 0x1f] <> [chr 127] -- Control characters.
|
||||
, [chr 0xa0..chr 0x24f] ] -- Non-ASCII.
|
||||
|
||||
instance StringConv Source ByteString where
|
||||
strConv _ = sourceBytes
|
||||
|
@ -9,10 +9,12 @@ module Data.Span
|
||||
, emptySpan
|
||||
) where
|
||||
|
||||
import Control.DeepSeq
|
||||
import Data.Aeson ((.=), (.:))
|
||||
import qualified Data.Aeson as A
|
||||
import Data.Hashable (Hashable)
|
||||
import Data.Semigroup
|
||||
import Prologue
|
||||
import GHC.Generics
|
||||
import Test.LeanCheck
|
||||
|
||||
-- | Source position information
|
||||
|
@ -3,11 +3,10 @@ module Data.Syntax where
|
||||
|
||||
import Algorithm
|
||||
import Data.Align.Generic
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.Functor.Classes.Eq.Generic
|
||||
import Data.Functor.Classes.Show.Generic
|
||||
import GHC.Generics
|
||||
import Prologue
|
||||
import Text.Show
|
||||
|
||||
-- Undifferentiated
|
||||
|
||||
|
@ -8,13 +8,15 @@ module Data.Syntax.Algebra
|
||||
, cyclomaticComplexityAlgebra
|
||||
) where
|
||||
|
||||
import Control.Comonad (extract)
|
||||
import Data.Bifunctor (second)
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.Functor.Foldable
|
||||
import Data.Record
|
||||
import qualified Data.Syntax as Syntax
|
||||
import qualified Data.Syntax.Declaration as Declaration
|
||||
import qualified Data.Syntax.Statement as Statement
|
||||
import Data.Union
|
||||
import Prologue
|
||||
import Term
|
||||
|
||||
-- | An F-algebra on some carrier functor 'f'.
|
||||
|
@ -67,6 +67,9 @@ module Data.Syntax.Assignment
|
||||
, Node(..)
|
||||
, nodeLocation
|
||||
-- Combinators
|
||||
, Alternative(..)
|
||||
, optional
|
||||
, MonadError(..)
|
||||
, location
|
||||
, Data.Syntax.Assignment.project
|
||||
, symbol
|
||||
@ -85,23 +88,30 @@ module Data.Syntax.Assignment
|
||||
, makeState
|
||||
) where
|
||||
|
||||
import Control.Arrow
|
||||
import Control.Applicative
|
||||
import Control.Comonad.Cofree
|
||||
import Control.Monad (guard)
|
||||
import Control.Monad.Error.Class hiding (Error)
|
||||
import Control.Monad.Free.Freer
|
||||
import Data.Blob
|
||||
import Data.ByteString (isSuffixOf)
|
||||
import Data.ByteString.Char8 (ByteString, pack, unpack)
|
||||
import Data.Foldable
|
||||
import Data.Function
|
||||
import Data.Functor.Classes
|
||||
import Data.Functor.Foldable as F hiding (Nil)
|
||||
import qualified Data.IntMap.Lazy as IntMap
|
||||
import Data.Ix (inRange)
|
||||
import Data.List.NonEmpty (nonEmpty)
|
||||
import Data.Maybe
|
||||
import Data.Record
|
||||
import Data.Semigroup
|
||||
import qualified Data.Source as Source (Source, fromBytes, slice, sourceBytes, sourceLines)
|
||||
import Data.String
|
||||
import GHC.Stack
|
||||
import qualified Info
|
||||
import Prologue hiding (Alt, get, Location, State, state)
|
||||
import System.Console.ANSI
|
||||
import Text.Parser.TreeSitter.Language
|
||||
import Text.Show hiding (show)
|
||||
|
||||
-- | Assignment from an AST with some set of 'symbol's onto some other value.
|
||||
--
|
||||
@ -192,11 +202,11 @@ formatErrorWithOptions includeSource colourize Blob{..} Error{..}
|
||||
$ withSGRCode colourize [SetConsoleIntensity BoldIntensity] (showPos (maybe Nothing (const (Just blobPath)) blobKind) errorPos . showString ": ")
|
||||
. withSGRCode colourize [SetColor Foreground Vivid Red] (showString "error" . showString ": " . showExpectation errorExpected errorActual . showChar '\n')
|
||||
. (if includeSource
|
||||
then showString (toS context) . (if "\n" `isSuffixOf` context then identity else showChar '\n')
|
||||
then showString (unpack context) . (if "\n" `isSuffixOf` context then id else showChar '\n')
|
||||
. showString (replicate (succ (Info.posColumn errorPos + lineNumberDigits)) ' ') . withSGRCode colourize [SetColor Foreground Vivid Green] (showChar '^' . showChar '\n')
|
||||
else identity)
|
||||
else id)
|
||||
. showString (prettyCallStack callStack) . showChar '\n'
|
||||
where context = maybe "\n" (Source.sourceBytes . sconcat) (nonEmpty [ Source.fromBytes (toS (showLineNumber i)) <> Source.fromBytes ": " <> l | (i, l) <- zip [1..] (Source.sourceLines blobSource), inRange (Info.posLine errorPos - 2, Info.posLine errorPos) i ])
|
||||
where context = maybe "\n" (Source.sourceBytes . sconcat) (nonEmpty [ Source.fromBytes (pack (showLineNumber i)) <> Source.fromBytes ": " <> l | (i, l) <- zip [1..] (Source.sourceLines blobSource), inRange (Info.posLine errorPos - 2, Info.posLine errorPos) i ])
|
||||
showLineNumber n = let s = show n in replicate (lineNumberDigits - length s) ' ' <> s
|
||||
lineNumberDigits = succ (floor (logBase 10 (fromIntegral (Info.posLine errorPos) :: Double)))
|
||||
|
||||
@ -326,9 +336,9 @@ instance Enum grammar => Alternative (Assignment ast grammar) where
|
||||
(Children l `Then` continueL) <|> (Children r `Then` continueR) = Children (Left <$> l <|> Right <$> r) `Then` either continueL continueR
|
||||
(Location `Then` continueL) <|> (Location `Then` continueR) = Location `Then` uncurry (<|>) . (continueL &&& continueR)
|
||||
(Source `Then` continueL) <|> (Source `Then` continueR) = Source `Then` uncurry (<|>) . (continueL &&& continueR)
|
||||
l <|> r | Just c <- (liftA2 (IntMap.unionWith (<|>)) `on` choices) l r = Choose c (atEnd l <|> atEnd r) `Then` identity
|
||||
l <|> r | Just c <- (liftA2 (IntMap.unionWith (<|>)) `on` choices) l r = Choose c (atEnd l <|> atEnd r) `Then` id
|
||||
| otherwise = wrap $ Alt l r
|
||||
where choices :: Assignment ast grammar a -> Maybe (IntMap (Assignment ast grammar a))
|
||||
where choices :: Assignment ast grammar a -> Maybe (IntMap.IntMap (Assignment ast grammar a))
|
||||
choices (Choose choices _ `Then` continue) = Just (continue <$> choices)
|
||||
choices (Many rule `Then` continue) = ((Many rule `Then` continue) <$) <$> choices rule
|
||||
choices (Catch during handler `Then` continue) = ((Catch during handler `Then` continue) <$) <$> choices during
|
||||
|
@ -3,10 +3,10 @@ module Data.Syntax.Comment where
|
||||
|
||||
import Algorithm
|
||||
import Data.Align.Generic
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.Functor.Classes.Eq.Generic
|
||||
import Data.Functor.Classes.Show.Generic
|
||||
import GHC.Generics
|
||||
import Prologue
|
||||
|
||||
-- | An unnested comment (line or block).
|
||||
newtype Comment a = Comment { commentContent :: ByteString }
|
||||
|
@ -6,7 +6,6 @@ import Data.Align.Generic
|
||||
import Data.Functor.Classes.Eq.Generic
|
||||
import Data.Functor.Classes.Show.Generic
|
||||
import GHC.Generics
|
||||
import Prologue
|
||||
|
||||
data Function a = Function { functionName :: !a, functionParameters :: ![a], functionBody :: !a }
|
||||
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Show, Traversable)
|
||||
|
@ -6,7 +6,6 @@ import Data.Align.Generic
|
||||
import Data.Functor.Classes.Eq.Generic
|
||||
import Data.Functor.Classes.Show.Generic
|
||||
import GHC.Generics
|
||||
import Prologue
|
||||
|
||||
-- | Typical prefix function application, like `f(x)` in many languages, or `f x` in Haskell.
|
||||
data Call a = Call { callFunction :: !a, callParams :: ![a], callBlock :: !a }
|
||||
|
@ -3,10 +3,11 @@ module Data.Syntax.Literal where
|
||||
|
||||
import Algorithm
|
||||
import Data.Align.Generic
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.Functor.Classes.Eq.Generic
|
||||
import Data.Functor.Classes.Show.Generic
|
||||
import GHC.Generics
|
||||
import Prologue hiding (Set)
|
||||
import Prelude hiding (String)
|
||||
|
||||
-- Boolean
|
||||
|
||||
|
@ -3,10 +3,10 @@ module Data.Syntax.Markup where
|
||||
|
||||
import Algorithm
|
||||
import Data.Align.Generic
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.Functor.Classes.Eq.Generic
|
||||
import Data.Functor.Classes.Show.Generic
|
||||
import GHC.Generics
|
||||
import Prologue hiding (Text)
|
||||
|
||||
|
||||
newtype Document a = Document [a]
|
||||
|
@ -6,7 +6,6 @@ import Data.Align.Generic
|
||||
import Data.Functor.Classes.Eq.Generic
|
||||
import Data.Functor.Classes.Show.Generic
|
||||
import GHC.Generics
|
||||
import Prologue
|
||||
|
||||
-- | Conditional. This must have an else block, which can be filled with some default value when omitted in the source, e.g. 'pure ()' for C-style if-without-else or 'pure Nothing' for Ruby-style, in both cases assuming some appropriate Applicative context into which the If will be lifted.
|
||||
data If a = If { ifCondition :: !a, ifThenBody :: !a, ifElseBody :: !a }
|
||||
|
@ -6,7 +6,6 @@ import Data.Align.Generic
|
||||
import Data.Functor.Classes.Eq.Generic
|
||||
import Data.Functor.Classes.Show.Generic
|
||||
import GHC.Generics
|
||||
import Prologue hiding (Product)
|
||||
|
||||
data Annotation a = Annotation { annotationSubject :: !a, annotationType :: !a }
|
||||
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Show, Traversable)
|
||||
|
@ -2,7 +2,6 @@ module Data.Text.Listable where
|
||||
|
||||
import Data.Functor.Listable
|
||||
import Data.Text
|
||||
import Prologue
|
||||
|
||||
newtype ListableText = ListableText { unListableText :: Text }
|
||||
|
||||
|
@ -6,35 +6,33 @@ module Decorators
|
||||
) where
|
||||
|
||||
import Data.Aeson
|
||||
import Data.ByteString.Char8 (ByteString, pack, unpack)
|
||||
import Data.Functor.Classes (Show1 (liftShowsPrec))
|
||||
import Data.Text.Encoding (decodeUtf8)
|
||||
import Data.Union
|
||||
import Data.String
|
||||
import GHC.Generics
|
||||
import Prologue
|
||||
import Renderer.JSON
|
||||
import Term
|
||||
import Text.Show
|
||||
|
||||
|
||||
-- | Compute a 'ByteString' label for a 'Show1'able 'Term'.
|
||||
--
|
||||
-- This uses 'liftShowsPrec' to produce the 'ByteString', with the effect that
|
||||
-- constant fields will be included and parametric fields will not be.
|
||||
constructorNameAndConstantFields :: Show1 f => TermF f a b -> ByteString
|
||||
constructorNameAndConstantFields (_ :< f) = toS (liftShowsPrec (const (const identity)) (const identity) 0 f "")
|
||||
constructorNameAndConstantFields (_ :< f) = pack (liftShowsPrec (const (const id)) (const id) 0 f "")
|
||||
|
||||
-- | Compute a 'ConstructorLabel' label for a 'Union' of syntax 'Term's.
|
||||
constructorLabel :: ConstructorName f => TermF f a b -> ConstructorLabel
|
||||
constructorLabel (_ :< f) = ConstructorLabel $ toS (constructorName f)
|
||||
constructorLabel (_ :< f) = ConstructorLabel $ pack (constructorName f)
|
||||
|
||||
|
||||
newtype ConstructorLabel = ConstructorLabel ByteString
|
||||
|
||||
instance Show ConstructorLabel where
|
||||
showsPrec _ (ConstructorLabel s) = showString (toS s)
|
||||
showsPrec _ (ConstructorLabel s) = showString (unpack s)
|
||||
|
||||
instance ToJSONFields ConstructorLabel where
|
||||
toJSONFields (ConstructorLabel s) = [ "category" .= (toS s :: Text) ]
|
||||
toJSONFields (ConstructorLabel s) = [ "category" .= decodeUtf8 s ]
|
||||
|
||||
|
||||
class ConstructorName f where
|
||||
|
35
src/Diff.hs
35
src/Diff.hs
@ -2,7 +2,11 @@
|
||||
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
||||
module Diff where
|
||||
|
||||
import Prologue
|
||||
import qualified Control.Comonad.Trans.Cofree as CofreeF
|
||||
import Control.DeepSeq
|
||||
import qualified Control.Monad.Free as Free
|
||||
import qualified Control.Monad.Trans.Free as FreeF
|
||||
import Data.Bifunctor
|
||||
import Data.Functor.Both as Both
|
||||
import Data.Mergeable
|
||||
import Data.Record
|
||||
@ -11,8 +15,8 @@ import Syntax
|
||||
import Term
|
||||
|
||||
-- | An annotated series of patches of terms.
|
||||
type DiffF f annotation = FreeF (TermF f (Both annotation)) (Patch (Term f annotation))
|
||||
type Diff f annotation = Free (TermF f (Both annotation)) (Patch (Term f annotation))
|
||||
type DiffF f annotation = FreeF.FreeF (TermF f (Both annotation)) (Patch (Term f annotation))
|
||||
type Diff f annotation = Free.Free (TermF f (Both annotation)) (Patch (Term f annotation))
|
||||
|
||||
type SyntaxDiff fields = Diff Syntax (Record fields)
|
||||
|
||||
@ -25,8 +29,8 @@ diffCost = diffSum $ patchSum termSize
|
||||
|
||||
-- | Merge a diff using a function to provide the Term (in Maybe, to simplify recovery of the before/after state) for every Patch.
|
||||
mergeMaybe :: Mergeable f => (Patch (Term f annotation) -> Maybe (Term f annotation)) -> (Both annotation -> annotation) -> Diff f annotation -> Maybe (Term f annotation)
|
||||
mergeMaybe transform extractAnnotation = iter algebra . fmap transform
|
||||
where algebra (annotations :< syntax) = cofree . (extractAnnotation annotations :<) <$> sequenceAlt syntax
|
||||
mergeMaybe transform extractAnnotation = Free.iter algebra . fmap transform
|
||||
where algebra (annotations CofreeF.:< syntax) = cofree . (extractAnnotation annotations CofreeF.:<) <$> sequenceAlt syntax
|
||||
|
||||
-- | Recover the before state of a diff.
|
||||
beforeTerm :: Mergeable f => Diff f annotation -> Maybe (Term f annotation)
|
||||
@ -41,12 +45,21 @@ afterTerm = mergeMaybe after Both.snd
|
||||
-- Typed using Free so as to accommodate Free structures derived from diffs that don’t fit into the Diff type synonym.
|
||||
mapAnnotations :: (Functor f, Functor g)
|
||||
=> (annotation -> annotation')
|
||||
-> Free (TermF f (g annotation)) (Patch (Term f annotation))
|
||||
-> Free (TermF f (g annotation')) (Patch (Term f annotation'))
|
||||
mapAnnotations f = hoistFree (first (fmap f)) . fmap (fmap (fmap f))
|
||||
-> Free.Free (TermF f (g annotation)) (Patch (Term f annotation))
|
||||
-> Free.Free (TermF f (g annotation')) (Patch (Term f annotation'))
|
||||
mapAnnotations f = Free.hoistFree (first (fmap f)) . fmap (fmap (fmap f))
|
||||
|
||||
|
||||
instance (NFData (f (Diff f a)), NFData (Cofree f a), NFData a, Functor f) => NFData (Diff f a) where
|
||||
instance (NFData (f (Diff f a)), NFData (Term f a), NFData a, Functor f) => NFData (Diff f a) where
|
||||
rnf fa = case runFree fa of
|
||||
Free f -> rnf f `seq` ()
|
||||
Pure a -> rnf a `seq` ()
|
||||
FreeF.Free f -> rnf f `seq` ()
|
||||
FreeF.Pure a -> rnf a `seq` ()
|
||||
|
||||
|
||||
free :: FreeF.FreeF f a (Free.Free f a) -> Free.Free f a
|
||||
free (FreeF.Free f) = Free.Free f
|
||||
free (FreeF.Pure a) = Free.Pure a
|
||||
|
||||
runFree :: Free.Free f a -> FreeF.FreeF f a (Free.Free f a)
|
||||
runFree (Free.Free f) = FreeF.Free f
|
||||
runFree (Free.Pure a) = FreeF.Pure a
|
||||
|
18
src/Files.hs
18
src/Files.hs
@ -9,27 +9,33 @@ module Files
|
||||
import Control.Exception (catch, IOException)
|
||||
import Control.Monad.IO.Class
|
||||
import Data.Aeson
|
||||
import Data.These
|
||||
import Data.Functor.Both
|
||||
import qualified Data.Blob as Blob
|
||||
import Data.Functor.Both
|
||||
import Data.Maybe
|
||||
import Data.Semigroup
|
||||
import Data.Source
|
||||
import Data.String
|
||||
import Data.Text
|
||||
import Data.These
|
||||
import GHC.Generics
|
||||
import Language
|
||||
import Prologue hiding (readFile)
|
||||
import qualified Data.ByteString as B
|
||||
import qualified Data.ByteString.Lazy as BL
|
||||
import Prelude (fail)
|
||||
import Prelude hiding (readFile)
|
||||
import System.Exit
|
||||
import System.FilePath
|
||||
import System.IO (Handle)
|
||||
import Text.Read
|
||||
|
||||
-- | Read a utf8-encoded file to a 'Blob'.
|
||||
readFile :: forall m. MonadIO m => FilePath -> Maybe Language -> m Blob.Blob
|
||||
readFile path language = do
|
||||
raw <- liftIO $ (Just <$> B.readFile path) `catch` (const (pure Nothing) :: IOException -> IO (Maybe ByteString))
|
||||
raw <- liftIO $ (Just <$> B.readFile path) `catch` (const (pure Nothing) :: IOException -> IO (Maybe B.ByteString))
|
||||
pure $ fromMaybe (Blob.emptyBlob path) (Blob.sourceBlob path language . fromBytes <$> raw)
|
||||
|
||||
-- | Return a language based on a FilePath's extension, or Nothing if extension is not found or not supported.
|
||||
languageForFilePath :: FilePath -> Maybe Language
|
||||
languageForFilePath = languageForType . toS . takeExtension
|
||||
languageForFilePath = languageForType . takeExtension
|
||||
|
||||
-- | Read JSON encoded blob pairs from a handle.
|
||||
readBlobPairsFromHandle :: MonadIO m => Handle -> m [Both Blob.Blob]
|
||||
|
@ -7,17 +7,22 @@ module Interpreter
|
||||
) where
|
||||
|
||||
import Algorithm
|
||||
import Control.Comonad (extract)
|
||||
import Control.Comonad.Cofree (unwrap)
|
||||
import Control.Monad.Free (cutoff)
|
||||
import Control.Monad.Free.Freer hiding (cutoff)
|
||||
import Data.Align.Generic
|
||||
import Data.Functor.Both
|
||||
import Data.Functor.Classes (Eq1)
|
||||
import RWS
|
||||
import Data.Hashable (Hashable)
|
||||
import Data.Maybe (isJust)
|
||||
import Data.Record
|
||||
import Data.Text (Text)
|
||||
import Data.These
|
||||
import Diff
|
||||
import Info hiding (Return)
|
||||
import Patch (inserting, deleting, replacing, patchSum)
|
||||
import Prologue hiding (lookup)
|
||||
import RWS
|
||||
import Syntax as S hiding (Return)
|
||||
import Term
|
||||
|
||||
@ -107,7 +112,7 @@ algorithmWithTerms t1 t2 = case (unwrap t1, unwrap t2) of
|
||||
|
||||
-- | Test whether two terms are comparable by their Category.
|
||||
comparableByCategory :: HasField fields Category => ComparabilityRelation f fields
|
||||
comparableByCategory a b = category (headF a) == category (headF b)
|
||||
comparableByCategory (a :< _) (b :< _) = category a == category b
|
||||
|
||||
-- | Test whether two terms are comparable by their constructor.
|
||||
comparableByConstructor :: GAlign f => ComparabilityRelation f fields
|
||||
|
@ -1,11 +1,14 @@
|
||||
{-# LANGUAGE DataKinds, DeriveGeneric, DeriveAnyClass #-}
|
||||
module Language where
|
||||
|
||||
import Control.Comonad
|
||||
import Control.Comonad.Trans.Cofree hiding (cofree)
|
||||
import Control.DeepSeq
|
||||
import Data.Aeson
|
||||
import Data.Foldable
|
||||
import Data.Record
|
||||
import Data.String
|
||||
import GHC.Generics
|
||||
import Info
|
||||
import Prologue
|
||||
import qualified Syntax as S
|
||||
import Term
|
||||
|
||||
|
@ -1,9 +1,13 @@
|
||||
{-# LANGUAGE DataKinds #-}
|
||||
module Language.Go where
|
||||
|
||||
import Control.Comonad
|
||||
import Control.Comonad.Cofree
|
||||
import Data.Foldable (toList)
|
||||
import Data.Maybe
|
||||
import Data.Source
|
||||
import Data.Text
|
||||
import Info
|
||||
import Prologue
|
||||
import qualified Syntax as S
|
||||
import Term
|
||||
|
||||
@ -131,4 +135,4 @@ categoryForGoName name = case name of
|
||||
"method_declaration" -> Method
|
||||
"import_spec" -> Import
|
||||
"block" -> ExpressionStatements
|
||||
s -> Other (toS s)
|
||||
s -> Other s
|
||||
|
@ -6,6 +6,7 @@ module Language.JSON.Syntax
|
||||
, Term)
|
||||
where
|
||||
|
||||
import Control.Comonad.Cofree (Cofree(..))
|
||||
import qualified Data.Syntax as Syntax
|
||||
import qualified Data.Syntax.Literal as Literal
|
||||
import Data.Syntax.Assignment hiding (Assignment, Error)
|
||||
@ -15,7 +16,6 @@ import qualified Term
|
||||
import Data.Record
|
||||
import Data.Union
|
||||
import GHC.Stack
|
||||
import Prologue hiding (Location)
|
||||
|
||||
type Syntax =
|
||||
[ Literal.Array
|
||||
@ -35,7 +35,7 @@ type Assignment = HasCallStack => Assignment.Assignment (AST Grammar) Grammar Te
|
||||
|
||||
|
||||
makeTerm :: (HasCallStack, f :< fs) => a -> f (Term.Term (Union fs) a) -> Term.Term (Union fs) a
|
||||
makeTerm a f = cofree (a :< inj f)
|
||||
makeTerm a f = a :< inj f
|
||||
|
||||
parseError :: Assignment
|
||||
parseError = makeTerm <$> symbol ParseError <*> (Syntax.Error [] <$ source)
|
||||
@ -65,4 +65,3 @@ boolean = makeTerm <$> symbol Grammar.True <*> (Literal.true <$ source)
|
||||
|
||||
none :: Assignment
|
||||
none = makeTerm <$> symbol Null <*> (Literal.Null <$ source)
|
||||
|
||||
|
@ -5,11 +5,11 @@ module Language.Markdown
|
||||
, toGrammar
|
||||
) where
|
||||
|
||||
import CMark
|
||||
import Control.Comonad.Cofree
|
||||
import CMarkGFM
|
||||
import Data.Source
|
||||
import qualified Data.Syntax.Assignment as A (AST, Node(..))
|
||||
import Info
|
||||
import Prologue hiding (Location)
|
||||
import Text.Parser.TreeSitter.Language (Symbol(..), SymbolType(..))
|
||||
|
||||
data Grammar
|
||||
@ -33,15 +33,27 @@ data Grammar
|
||||
| Strong
|
||||
| Link
|
||||
| Image
|
||||
| Strikethrough
|
||||
| Table
|
||||
| TableRow
|
||||
| TableCell
|
||||
deriving (Bounded, Enum, Eq, Ord, Show)
|
||||
|
||||
exts :: [CMarkExtension]
|
||||
exts = [
|
||||
extStrikethrough
|
||||
, extTable
|
||||
, extAutolink
|
||||
, extTagfilter
|
||||
]
|
||||
|
||||
cmarkParser :: Source -> A.AST NodeType
|
||||
cmarkParser source = toTerm (totalRange source) (totalSpan source) $ commonmarkToNode [ optSourcePos, optSafe ] (toText source)
|
||||
cmarkParser source = toTerm (totalRange source) (totalSpan source) $ commonmarkToNode [ optSourcePos, optSafe ] exts (toText source)
|
||||
where toTerm :: Range -> Span -> Node -> A.AST NodeType
|
||||
toTerm within withinSpan (Node position t children) =
|
||||
let range = maybe within (spanToRangeInLineRanges lineRanges . toSpan) position
|
||||
span = maybe withinSpan toSpan position
|
||||
in cofree $ (A.Node t range span) :< (toTerm range span <$> children)
|
||||
in (A.Node t range span) :< (toTerm range span <$> children)
|
||||
|
||||
toSpan PosInfo{..} = Span (Pos startLine startColumn) (Pos (max startLine endLine) (succ (if endLine <= startLine then max startColumn endColumn else endColumn)))
|
||||
|
||||
@ -68,6 +80,10 @@ toGrammar EMPH{} = Emphasis
|
||||
toGrammar STRONG{} = Strong
|
||||
toGrammar LINK{} = Link
|
||||
toGrammar IMAGE{} = Image
|
||||
toGrammar STRIKETHROUGH{} = Strikethrough
|
||||
toGrammar TABLE{} = Table
|
||||
toGrammar TABLE_ROW{} = TableRow
|
||||
toGrammar TABLE_CELL{} = TableCell
|
||||
|
||||
|
||||
instance Symbol Grammar where
|
||||
|
@ -6,17 +6,20 @@ module Language.Markdown.Syntax
|
||||
, Term
|
||||
) where
|
||||
|
||||
import qualified CMark
|
||||
import Control.Comonad.Cofree (Cofree(..), unwrap)
|
||||
import qualified CMarkGFM
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.Function (on)
|
||||
import Data.Record
|
||||
import Data.Syntax.Assignment hiding (Assignment, Error)
|
||||
import qualified Data.Syntax.Assignment as Assignment
|
||||
import qualified Data.Syntax.Markup as Markup
|
||||
import qualified Data.Syntax as Syntax
|
||||
import qualified Data.Text as Text
|
||||
import Data.Text.Encoding (encodeUtf8)
|
||||
import Data.Union
|
||||
import GHC.Stack
|
||||
import Language.Markdown as Grammar (Grammar(..))
|
||||
import Prologue hiding (Location, link, list, section)
|
||||
import qualified Term
|
||||
|
||||
type Syntax =
|
||||
@ -44,7 +47,7 @@ type Syntax =
|
||||
]
|
||||
|
||||
type Term = Term.Term (Union Syntax) (Record Location)
|
||||
type Assignment = HasCallStack => Assignment.Assignment (AST CMark.NodeType) Grammar Term
|
||||
type Assignment = HasCallStack => Assignment.Assignment (AST CMarkGFM.NodeType) Grammar Term
|
||||
|
||||
|
||||
assignment :: Assignment
|
||||
@ -60,16 +63,16 @@ paragraph :: Assignment
|
||||
paragraph = makeTerm <$> symbol Paragraph <*> children (Markup.Paragraph <$> many inlineElement)
|
||||
|
||||
list :: Assignment
|
||||
list = (cofree .) . (:<) <$> symbol List <*> (project (\ (Node (CMark.LIST CMark.ListAttributes{..}) _ _ :< _) -> case listType of
|
||||
CMark.BULLET_LIST -> inj . Markup.UnorderedList
|
||||
CMark.ORDERED_LIST -> inj . Markup.OrderedList) <*> children (many item))
|
||||
list = (:<) <$> symbol List <*> (project (\ (Node (CMarkGFM.LIST CMarkGFM.ListAttributes{..}) _ _ Term.:< _) -> case listType of
|
||||
CMarkGFM.BULLET_LIST -> inj . Markup.UnorderedList
|
||||
CMarkGFM.ORDERED_LIST -> inj . Markup.OrderedList) <*> children (many item))
|
||||
|
||||
item :: Assignment
|
||||
item = makeTerm <$> symbol Item <*> children (many blockElement)
|
||||
|
||||
section :: Assignment
|
||||
section = makeTerm <$> symbol Heading <*> (heading >>= \ headingTerm -> Markup.Section (level headingTerm) headingTerm <$> while (((<) `on` level) headingTerm) blockElement)
|
||||
where heading = makeTerm <$> symbol Heading <*> (project (\ (Node (CMark.HEADING level) _ _ :< _) -> Markup.Heading level) <*> children (many inlineElement))
|
||||
where heading = makeTerm <$> symbol Heading <*> (project (\ (Node (CMarkGFM.HEADING level) _ _ Term.:< _) -> Markup.Heading level) <*> children (many inlineElement))
|
||||
level term = case term of
|
||||
_ | Just section <- prj (unwrap term) -> level (Markup.sectionHeading section)
|
||||
_ | Just heading <- prj (unwrap term) -> Markup.headingLevel heading
|
||||
@ -79,7 +82,7 @@ blockQuote :: Assignment
|
||||
blockQuote = makeTerm <$> symbol BlockQuote <*> children (Markup.BlockQuote <$> many blockElement)
|
||||
|
||||
codeBlock :: Assignment
|
||||
codeBlock = makeTerm <$> symbol CodeBlock <*> (project (\ (Node (CMark.CODE_BLOCK language _) _ _ :< _) -> Markup.Code (nullText language)) <*> source)
|
||||
codeBlock = makeTerm <$> symbol CodeBlock <*> (project (\ (Node (CMarkGFM.CODE_BLOCK language _) _ _ Term.:< _) -> Markup.Code (nullText language)) <*> source)
|
||||
|
||||
thematicBreak :: Assignment
|
||||
thematicBreak = makeTerm <$> symbol ThematicBreak <*> pure Markup.ThematicBreak <* source
|
||||
@ -106,10 +109,10 @@ htmlInline :: Assignment
|
||||
htmlInline = makeTerm <$> symbol HTMLInline <*> (Markup.HTMLBlock <$> source)
|
||||
|
||||
link :: Assignment
|
||||
link = makeTerm <$> symbol Link <*> project (\ (Node (CMark.LINK url title) _ _ :< _) -> Markup.Link (toS url) (nullText title)) <* source
|
||||
link = makeTerm <$> symbol Link <*> project (\ (Node (CMarkGFM.LINK url title) _ _ Term.:< _) -> Markup.Link (encodeUtf8 url) (nullText title)) <* source
|
||||
|
||||
image :: Assignment
|
||||
image = makeTerm <$> symbol Image <*> project (\ (Node (CMark.IMAGE url title) _ _ :< _) -> Markup.Image (toS url) (nullText title)) <* source
|
||||
image = makeTerm <$> symbol Image <*> project (\ (Node (CMarkGFM.IMAGE url title) _ _ Term.:< _) -> Markup.Image (encodeUtf8 url) (nullText title)) <* source
|
||||
|
||||
code :: Assignment
|
||||
code = makeTerm <$> symbol Code <*> (Markup.Code Nothing <$> source)
|
||||
@ -124,7 +127,7 @@ softBreak = makeTerm <$> symbol SoftBreak <*> pure Markup.LineBreak <* source
|
||||
-- Implementation details
|
||||
|
||||
makeTerm :: (f :< fs, HasCallStack) => a -> f (Term.Term (Union fs) a) -> Term.Term (Union fs) a
|
||||
makeTerm a f = cofree $ a :< inj f
|
||||
makeTerm a f = a :< inj f
|
||||
|
||||
nullText :: Text.Text -> Maybe ByteString
|
||||
nullText text = if Text.null text then Nothing else Just (toS text)
|
||||
nullText text = if Text.null text then Nothing else Just (encodeUtf8 text)
|
||||
|
@ -7,9 +7,11 @@ module Language.Python.Syntax
|
||||
) where
|
||||
|
||||
import Algorithm
|
||||
import Control.Comonad.Cofree (Cofree(..))
|
||||
import Data.Align.Generic
|
||||
import Data.Functor.Classes.Eq.Generic
|
||||
import Data.Functor.Classes.Show.Generic
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Record
|
||||
import qualified Data.Syntax as Syntax
|
||||
import Data.Syntax.Assignment hiding (Assignment, Error)
|
||||
@ -24,7 +26,6 @@ import Data.Union
|
||||
import GHC.Generics
|
||||
import GHC.Stack
|
||||
import Language.Python.Grammar as Grammar
|
||||
import Prologue hiding (Location)
|
||||
import qualified Term
|
||||
|
||||
type Syntax =
|
||||
@ -236,7 +237,7 @@ tryStatement = makeTerm <$> symbol TryStatement <*> children (Statement.Try <$>
|
||||
|
||||
exceptClause :: Assignment
|
||||
exceptClause = makeTerm <$> symbol ExceptClause <*> children
|
||||
(Statement.Catch <$> ((makeTerm <$> location <*> (uncurry Statement.Let . swap <$> ((,) <$> expression <* symbol AnonAs <*> expression) <*> emptyTerm))
|
||||
(Statement.Catch <$> ((makeTerm <$> location <*> (uncurry (flip Statement.Let) <$> ((,) <$> expression <* symbol AnonAs <*> expression) <*> emptyTerm))
|
||||
<|> makeTerm <$> location <*> many expression)
|
||||
<*> expressions)
|
||||
|
||||
@ -245,8 +246,8 @@ functionDefinition = (symbol FunctionDefinition >>= \ loc -> children (makeFunc
|
||||
<|> (symbol AsyncFunctionDefinition >>= \ loc -> children (makeAsyncFunctionDeclaration loc <$> async' <*> expression <*> (symbol Parameters *> children (many expression)) <*> optional (symbol Type *> children expression) <*> expressions))
|
||||
<|> (symbol Lambda >>= \ loc -> children (makeFunctionDeclaration loc <$> (makeTerm <$> symbol AnonLambda <*> (Syntax.Identifier <$> source)) <*> ((symbol LambdaParameters *> children (many expression)) <|> pure []) <*> optional (symbol Type *> children expression) <*> expressions))
|
||||
where
|
||||
makeFunctionDeclaration loc functionName' functionParameters ty functionBody = makeTerm loc $ Type.Annotation (makeTerm loc $ Declaration.Function functionName' functionParameters functionBody) (maybe (makeTerm loc Syntax.Empty) identity ty)
|
||||
makeAsyncFunctionDeclaration loc async' functionName' functionParameters ty functionBody = makeTerm loc $ Type.Annotation (makeTerm loc $ Type.Annotation (makeTerm loc $ Declaration.Function functionName' functionParameters functionBody) (maybe (makeTerm loc Syntax.Empty) identity ty)) async'
|
||||
makeFunctionDeclaration loc functionName' functionParameters ty functionBody = makeTerm loc $ Type.Annotation (makeTerm loc $ Declaration.Function functionName' functionParameters functionBody) (maybe (makeTerm loc Syntax.Empty) id ty)
|
||||
makeAsyncFunctionDeclaration loc async' functionName' functionParameters ty functionBody = makeTerm loc $ Type.Annotation (makeTerm loc $ Type.Annotation (makeTerm loc $ Declaration.Function functionName' functionParameters functionBody) (maybe (makeTerm loc Syntax.Empty) id ty)) async'
|
||||
|
||||
async' :: Assignment
|
||||
async' = makeTerm <$> symbol AnonAsync <*> (Syntax.Identifier <$> source)
|
||||
@ -476,7 +477,7 @@ conditionalExpression = makeTerm <$> symbol ConditionalExpression <*> children (
|
||||
Statement.If conditional thenBranch <$> expressions)
|
||||
|
||||
makeTerm :: (HasCallStack, f :< fs) => a -> f (Term.Term (Union fs) a) -> Term.Term (Union fs) a
|
||||
makeTerm a f = cofree (a :< inj f)
|
||||
makeTerm a f = a :< inj f
|
||||
|
||||
emptyTerm :: Assignment
|
||||
emptyTerm = makeTerm <$> location <*> pure Syntax.Empty
|
||||
|
@ -1,13 +1,17 @@
|
||||
{-# LANGUAGE DataKinds #-}
|
||||
module Language.Ruby where
|
||||
|
||||
import Control.Comonad
|
||||
import Control.Comonad.Cofree
|
||||
import Data.Foldable (toList)
|
||||
import Data.List (partition)
|
||||
import Data.Semigroup
|
||||
import Data.Source
|
||||
import Data.Text (Text)
|
||||
import Info
|
||||
import Prologue
|
||||
import Language
|
||||
import qualified Syntax as S
|
||||
import Term
|
||||
import Term hiding ((:<))
|
||||
|
||||
termAssignment
|
||||
:: Source -- ^ The source of the term.
|
||||
@ -44,7 +48,7 @@ termAssignment _ category children
|
||||
-> Just $ S.Class constant [superclass] body
|
||||
(Class, constant : rest) -> Just $ S.Class constant [] rest
|
||||
(SingletonClass, identifier : rest) -> Just $ S.Class identifier [] rest
|
||||
(Case, _) -> Just $ uncurry S.Switch (Prologue.break ((== When) . Info.category . extract) children)
|
||||
(Case, _) -> Just $ uncurry S.Switch (break ((== When) . Info.category . extract) children)
|
||||
(When, expr : body) -> Just $ S.Case expr body
|
||||
(Ternary, condition : cases) -> Just $ S.Ternary condition cases
|
||||
(MethodCall, fn : args)
|
||||
@ -55,10 +59,10 @@ termAssignment _ category children
|
||||
-> Just $ S.FunctionCall fn [] (toList . unwrap =<< args)
|
||||
(Object, _ ) -> Just . S.Object Nothing $ foldMap toTuple children
|
||||
(Modifier If, [ lhs, condition ]) -> Just $ S.If condition [lhs]
|
||||
(Modifier Unless, [lhs, rhs]) -> Just $ S.If (withRecord (setCategory (extract rhs) Negate) (S.Negate rhs)) [lhs]
|
||||
(Unless, expr : rest) -> Just $ S.If (withRecord (setCategory (extract expr) Negate) (S.Negate expr)) rest
|
||||
(Modifier Until, [ lhs, rhs ]) -> Just $ S.While (withRecord (setCategory (extract rhs) Negate) (S.Negate rhs)) [lhs]
|
||||
(Until, expr : rest) -> Just $ S.While (withRecord (setCategory (extract expr) Negate) (S.Negate expr)) rest
|
||||
(Modifier Unless, [lhs, rhs]) -> Just $ S.If (setCategory (extract rhs) Negate :< S.Negate rhs) [lhs]
|
||||
(Unless, expr : rest) -> Just $ S.If ((setCategory (extract expr) Negate) :< S.Negate expr) rest
|
||||
(Modifier Until, [ lhs, rhs ]) -> Just $ S.While (setCategory (extract rhs) Negate :< S.Negate rhs) [lhs]
|
||||
(Until, expr : rest) -> Just $ S.While (setCategory (extract expr) Negate :< S.Negate expr) rest
|
||||
(Elsif, condition : body ) -> Just $ S.If condition body
|
||||
(SubscriptAccess, [ base, element ]) -> Just $ S.SubscriptAccess base element
|
||||
(For, lhs : expr : rest ) -> Just $ S.For [lhs, expr] rest
|
||||
@ -92,8 +96,6 @@ termAssignment _ category children
|
||||
(Modifier While, [ lhs, condition ]) -> Just $ S.While condition [lhs]
|
||||
_ | category `elem` [ BeginBlock, EndBlock ] -> Just $ S.BlockStatement children
|
||||
_ -> Nothing
|
||||
where
|
||||
withRecord record syntax = cofree (record :< syntax)
|
||||
|
||||
categoryForRubyName :: Text -> Category
|
||||
categoryForRubyName name = case name of
|
||||
|
@ -6,6 +6,8 @@ module Language.Ruby.Syntax
|
||||
, Term
|
||||
) where
|
||||
|
||||
import Control.Comonad.Cofree (Cofree(..))
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Record
|
||||
import qualified Data.Syntax as Syntax
|
||||
import Data.Syntax.Assignment hiding (Assignment, Error)
|
||||
@ -18,7 +20,6 @@ import qualified Data.Syntax.Statement as Statement
|
||||
import Data.Union
|
||||
import GHC.Stack
|
||||
import Language.Ruby.Grammar as Grammar
|
||||
import Prologue hiding (for, get, Location, state, unless)
|
||||
import qualified Term
|
||||
|
||||
-- | The type of Ruby syntax.
|
||||
@ -388,7 +389,7 @@ emptyStatement = makeTerm <$> symbol EmptyStatement <*> (Syntax.Empty <$ source
|
||||
-- Helper functions
|
||||
|
||||
makeTerm :: (f :< fs, HasCallStack) => a -> f (Term.Term (Union fs) a) -> Term.Term (Union fs) a
|
||||
makeTerm a f = cofree $ a :< inj f
|
||||
makeTerm a f = a :< inj f
|
||||
|
||||
emptyTerm :: Assignment
|
||||
emptyTerm = makeTerm <$> location <*> pure Syntax.Empty
|
||||
|
@ -1,10 +1,13 @@
|
||||
{-# LANGUAGE DataKinds #-}
|
||||
module Language.TypeScript where
|
||||
|
||||
import Control.Comonad (extract)
|
||||
import Control.Comonad.Cofree (unwrap)
|
||||
import Data.Foldable (toList)
|
||||
import Data.Source
|
||||
import Data.Text (Text)
|
||||
import Info
|
||||
import Language
|
||||
import Prologue
|
||||
import qualified Syntax as S
|
||||
import Term
|
||||
|
||||
@ -22,7 +25,7 @@ termAssignment _ category children =
|
||||
(CommaOperator, [ a, b ])
|
||||
| S.Indexed rest <- unwrap b
|
||||
-> Just $ S.Indexed $ a : rest
|
||||
(FunctionCall, id : rest) -> case Prologue.break ((== Args) . Info.category . extract) rest of
|
||||
(FunctionCall, id : rest) -> case break ((== Args) . Info.category . extract) rest of
|
||||
(typeArgs, [ args ]) -> let flatArgs = toList (unwrap args) in
|
||||
Just $ case unwrap id of
|
||||
S.MemberAccess target method -> S.MethodCall target method typeArgs flatArgs
|
||||
@ -49,12 +52,12 @@ termAssignment _ category children =
|
||||
, Finally <- Info.category (extract finally)
|
||||
-> Just $ S.Try [body] [catch] Nothing (Just finally)
|
||||
(ArrayLiteral, _) -> Just $ S.Array Nothing children
|
||||
(Method, children) -> case Prologue.break ((== ExpressionStatements) . Info.category . extract) children of
|
||||
(prev, [body]) -> case Prologue.break ((== Identifier) . Info.category . extract) prev of
|
||||
(Method, children) -> case break ((== ExpressionStatements) . Info.category . extract) children of
|
||||
(prev, [body]) -> case break ((== Identifier) . Info.category . extract) prev of
|
||||
(prev, [id, callSignature]) -> Just $ S.Method prev id Nothing (toList (unwrap callSignature)) (toList (unwrap body))
|
||||
_ -> Nothing -- No identifier found or callSignature found.
|
||||
_ -> Nothing -- No body found.``
|
||||
(Class, identifier : rest) -> case Prologue.break ((== Other "class_body") . Info.category . extract) rest of
|
||||
(Class, identifier : rest) -> case break ((== Other "class_body") . Info.category . extract) rest of
|
||||
(clauses, [ definitions ]) -> Just $ S.Class identifier clauses (toList (unwrap definitions))
|
||||
_ -> Nothing
|
||||
(Module, [ identifier, definitions ]) -> Just $ S.Module identifier (toList (unwrap definitions))
|
||||
@ -66,10 +69,8 @@ termAssignment _ category children =
|
||||
| S.Indexed _ <- unwrap statements
|
||||
-> Just $ S.Export Nothing (toList (unwrap statements))
|
||||
| otherwise -> Just $ S.Export (Just statements) []
|
||||
(For, _)
|
||||
| Just (exprs, body) <- unsnoc children
|
||||
-> Just $ S.For exprs [body]
|
||||
(Function, children) -> case Prologue.break ((== ExpressionStatements) . Info.category . extract) children of
|
||||
(For, _:_) -> Just $ S.For (init children) [last children]
|
||||
(Function, children) -> case break ((== ExpressionStatements) . Info.category . extract) children of
|
||||
(inits, [body]) -> case inits of
|
||||
[id, callSignature] -> Just $ S.Function id (toList (unwrap callSignature)) (toList (unwrap body))
|
||||
[callSignature] -> Just $ S.AnonymousFunction (toList (unwrap callSignature)) (toList (unwrap body))
|
||||
|
@ -11,13 +11,15 @@ module Parser
|
||||
, rubyParser
|
||||
) where
|
||||
|
||||
import qualified CMark
|
||||
import Control.Comonad.Trans.Cofree (headF)
|
||||
import qualified CMarkGFM
|
||||
import Data.Functor.Foldable hiding (fold, Nil)
|
||||
import Data.Record
|
||||
import Data.Source as Source
|
||||
import qualified Data.Syntax as Syntax
|
||||
import Data.Syntax.Assignment
|
||||
import Data.Union
|
||||
import Foreign.Ptr
|
||||
import Info hiding (Empty, Go)
|
||||
import Language
|
||||
import Language.Markdown
|
||||
@ -25,7 +27,6 @@ import qualified Language.JSON.Syntax as JSON
|
||||
import qualified Language.Markdown.Syntax as Markdown
|
||||
import qualified Language.Python.Syntax as Python
|
||||
import qualified Language.Ruby.Syntax as Ruby
|
||||
import Prologue hiding (Location)
|
||||
import Syntax hiding (Go)
|
||||
import Term
|
||||
import qualified Text.Parser.TreeSitter as TS
|
||||
@ -49,7 +50,7 @@ data Parser term where
|
||||
-- | A tree-sitter parser.
|
||||
TreeSitterParser :: Ptr TS.Language -> Parser (SyntaxTerm DefaultFields)
|
||||
-- | A parser for 'Markdown' using cmark.
|
||||
MarkdownParser :: Parser (AST CMark.NodeType)
|
||||
MarkdownParser :: Parser (AST CMarkGFM.NodeType)
|
||||
-- | A parser which will parse any input 'Source' into a top-level 'Term' whose children are leaves consisting of the 'Source's lines.
|
||||
LineByLineParser :: Parser (SyntaxTerm DefaultFields)
|
||||
|
||||
|
@ -14,10 +14,11 @@ module Patch
|
||||
, mapPatch
|
||||
) where
|
||||
|
||||
import Control.DeepSeq
|
||||
import Data.Align
|
||||
import Data.Functor.Listable
|
||||
import Data.These
|
||||
import Prologue
|
||||
import GHC.Generics
|
||||
|
||||
-- | An operation to replace, insert, or delete an item.
|
||||
data Patch a
|
||||
|
@ -1,41 +0,0 @@
|
||||
module Prologue
|
||||
( module X
|
||||
, lookup
|
||||
, (&&&)
|
||||
, (***)
|
||||
, hylo, cata, para, ana
|
||||
, cofree, runCofree, free, runFree
|
||||
, module Data.Hashable
|
||||
) where
|
||||
|
||||
import Protolude as X
|
||||
import Data.List (lookup)
|
||||
|
||||
import Control.Comonad.Cofree as X hiding ((:<), unfold, unfoldM)
|
||||
import Control.Monad.Free as X (Free())
|
||||
import Control.Monad.Free as X hiding (Free(Free, Pure), unfold, unfoldM)
|
||||
import Control.Comonad.Trans.Cofree as X (CofreeF(..), headF, tailF)
|
||||
import Control.Monad.Trans.Free as X (FreeF(..))
|
||||
import Control.Comonad as X
|
||||
import qualified Control.Comonad.Cofree as Cofree
|
||||
import qualified Control.Monad.Free as Free
|
||||
|
||||
import Control.Arrow ((&&&), (***))
|
||||
|
||||
import Data.Functor.Foldable (hylo, cata, para, ana)
|
||||
|
||||
import Data.Hashable
|
||||
|
||||
cofree :: CofreeF f a (Cofree f a) -> Cofree f a
|
||||
cofree (a :< f) = a Cofree.:< f
|
||||
|
||||
runCofree :: Cofree f a -> CofreeF f a (Cofree f a)
|
||||
runCofree (a Cofree.:< f) = a :< f
|
||||
|
||||
free :: FreeF f a (Free f a) -> Free f a
|
||||
free (Free f) = Free.Free f
|
||||
free (Pure a) = Free.Pure a
|
||||
|
||||
runFree :: Free f a -> FreeF f a (Free f a)
|
||||
runFree (Free.Free f) = Free f
|
||||
runFree (Free.Pure a) = Pure a
|
28
src/RWS.hs
28
src/RWS.hs
@ -12,10 +12,23 @@ module RWS (
|
||||
, defaultD
|
||||
) where
|
||||
|
||||
import Prologue hiding (State, evalState, runState)
|
||||
import Control.Applicative (empty)
|
||||
import Control.Arrow ((&&&))
|
||||
import Control.Comonad
|
||||
import Control.Comonad.Trans.Cofree hiding (cofree, runCofree)
|
||||
import Control.Monad.Free
|
||||
import Control.Monad.State.Strict
|
||||
import Data.Foldable
|
||||
import Data.Function ((&), on)
|
||||
import Data.Functor.Foldable
|
||||
import Data.Hashable
|
||||
import Data.List (sortOn)
|
||||
import Data.Maybe
|
||||
import Data.Monoid (First(..))
|
||||
import Data.Record
|
||||
import Data.Semigroup hiding (First(..))
|
||||
import Data.These
|
||||
import Data.Traversable
|
||||
import Patch
|
||||
import Term
|
||||
import Data.Array.Unboxed
|
||||
@ -23,9 +36,8 @@ import Data.Functor.Classes
|
||||
import SES
|
||||
import qualified Data.Functor.Both as Both
|
||||
import Data.Functor.Listable
|
||||
import Data.KdTree.Static hiding (toList)
|
||||
import Data.KdTree.Static hiding (empty, toList)
|
||||
import qualified Data.IntMap as IntMap
|
||||
import Data.Semigroup (Min(..), Option(..))
|
||||
|
||||
import Control.Monad.Random
|
||||
import System.Random.Mersenne.Pure64
|
||||
@ -68,7 +80,7 @@ rws editDistance canCompare as bs =
|
||||
in fmap snd rwsDiffs
|
||||
|
||||
-- | An IntMap of unmapped terms keyed by their position in a list of terms.
|
||||
type UnmappedTerms f fields = IntMap (UnmappedTerm f fields)
|
||||
type UnmappedTerms f fields = IntMap.IntMap (UnmappedTerm f fields)
|
||||
|
||||
type Diff f fields = These (Term f (Record fields)) (Term f (Record fields))
|
||||
|
||||
@ -228,8 +240,8 @@ setFeatureVector = setField
|
||||
minimumTermIndex :: [RWS.UnmappedTerm f fields] -> Int
|
||||
minimumTermIndex = pred . maybe 0 getMin . getOption . foldMap (Option . Just . Min . termIndex)
|
||||
|
||||
toMap :: [UnmappedTerm f fields] -> IntMap (UnmappedTerm f fields)
|
||||
toMap = IntMap.fromList . fmap (termIndex &&& identity)
|
||||
toMap :: [UnmappedTerm f fields] -> IntMap.IntMap (UnmappedTerm f fields)
|
||||
toMap = IntMap.fromList . fmap (termIndex &&& id)
|
||||
|
||||
toKdTree :: [UnmappedTerm f fields] -> KdTree Double (UnmappedTerm f fields)
|
||||
toKdTree = build (elems . feature)
|
||||
@ -284,13 +296,13 @@ pqGramDecorator getLabel p q = cata algebra
|
||||
pure $! cofree ((gram { stem = padToSize p (Just label : stem gram), base = padToSize q labels } :. rest) :< functor)
|
||||
siblingLabels :: Traversable f => f (Term f (Record (Gram label ': fields))) -> [Maybe label]
|
||||
siblingLabels = foldMap (base . rhead . extract)
|
||||
padToSize n list = take n (list <> repeat Prologue.empty)
|
||||
padToSize n list = take n (list <> repeat empty)
|
||||
|
||||
-- | Computes a unit vector of the specified dimension from a hash.
|
||||
unitVector :: Int -> Int -> FeatureVector
|
||||
unitVector d hash = listArray (0, d - 1) ((* invMagnitude) <$> components)
|
||||
where
|
||||
invMagnitude = 1 / sqrtDouble (sum (fmap (** 2) components))
|
||||
invMagnitude = 1 / sqrt (sum (fmap (** 2) components))
|
||||
components = evalRand (sequenceA (replicate d (liftRand randomDouble))) (pureMT (fromIntegral hash))
|
||||
|
||||
-- | Test the comparability of two root 'Term's in O(1).
|
||||
|
@ -18,13 +18,18 @@ module Renderer
|
||||
, File(..)
|
||||
) where
|
||||
|
||||
import Control.Comonad.Cofree (Cofree, unwrap)
|
||||
import Control.Comonad.Trans.Cofree (CofreeF(..))
|
||||
import Control.DeepSeq
|
||||
import Data.Aeson (Value, (.=))
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.Foldable (asum)
|
||||
import qualified Data.Map as Map
|
||||
import Data.Output
|
||||
import Data.Syntax.Algebra (RAlgebra)
|
||||
import Data.Text (Text)
|
||||
import Diff (SyntaxDiff)
|
||||
import Info (DefaultFields)
|
||||
import Prologue
|
||||
import Renderer.JSON as R
|
||||
import Renderer.Patch as R
|
||||
import Renderer.SExpression as R
|
||||
|
@ -6,20 +6,28 @@ module Renderer.JSON
|
||||
, ToJSONFields(..)
|
||||
) where
|
||||
|
||||
import Control.Comonad.Cofree
|
||||
import qualified Control.Comonad.Trans.Cofree as CofreeF
|
||||
import Control.Monad.Free
|
||||
import qualified Control.Monad.Trans.Free as FreeF
|
||||
import Data.Aeson (ToJSON, toJSON, encode, object, (.=))
|
||||
import Data.Aeson as A hiding (json)
|
||||
import Data.Bifunctor.Join
|
||||
import Data.Blob
|
||||
import Data.ByteString.Lazy (toStrict)
|
||||
import Data.Foldable (toList)
|
||||
import Data.Functor.Both (Both)
|
||||
import qualified Data.Map as Map
|
||||
import Data.Output
|
||||
import Data.Record
|
||||
import Data.Semigroup ((<>))
|
||||
import Data.Text (pack, Text)
|
||||
import Data.Text.Encoding (decodeUtf8)
|
||||
import Data.Union
|
||||
import GHC.Generics
|
||||
import Info
|
||||
import Language
|
||||
import Patch
|
||||
import Prologue hiding ((++), toStrict)
|
||||
import Syntax as S
|
||||
|
||||
--
|
||||
@ -34,7 +42,7 @@ renderJSONDiff blobs diff = Map.fromList
|
||||
, ("paths", toJSON (blobPath <$> toList blobs))
|
||||
]
|
||||
|
||||
instance Output (Map Text Value) where
|
||||
instance Output (Map.Map Text Value) where
|
||||
toOutput = toStrict . (<> "\n") . encode
|
||||
|
||||
instance ToJSON a => ToJSONFields (Join (,) a) where
|
||||
@ -45,16 +53,12 @@ instance ToJSON a => ToJSON (Join (,) a) where
|
||||
toEncoding = foldable
|
||||
|
||||
instance (ToJSONFields a, ToJSONFields (f (Free f a))) => ToJSON (Free f a) where
|
||||
toJSON splitDiff = case runFree splitDiff of
|
||||
(Free f) -> object (toJSONFields f)
|
||||
(Pure p) -> object (toJSONFields p)
|
||||
toEncoding splitDiff = case runFree splitDiff of
|
||||
(Free f) -> pairs $ mconcat (toJSONFields f)
|
||||
(Pure p) -> pairs $ mconcat (toJSONFields p)
|
||||
toJSON = object . toJSONFields
|
||||
toEncoding = pairs . mconcat . toJSONFields
|
||||
|
||||
instance ToJSONFields (CofreeF f a (Cofree f a)) => ToJSON (Cofree f a) where
|
||||
toJSON = object . toJSONFields . runCofree
|
||||
toEncoding = pairs . mconcat . toJSONFields . runCofree
|
||||
instance (ToJSONFields a, ToJSONFields (f (Cofree f a))) => ToJSON (Cofree f a) where
|
||||
toJSON (a :< f) = object (toJSONFields a <> toJSONFields f)
|
||||
toEncoding (a :< f) = pairs (mconcat (toJSONFields a <> toJSONFields f))
|
||||
|
||||
class ToJSONFields a where
|
||||
toJSONFields :: KeyValue kv => a -> [kv]
|
||||
@ -73,7 +77,7 @@ instance ToJSONFields Range where
|
||||
toJSONFields Range{..} = ["sourceRange" .= [ start, end ]]
|
||||
|
||||
instance ToJSONFields Category where
|
||||
toJSONFields c = ["category" .= case c of { Other s -> s ; _ -> toS c }]
|
||||
toJSONFields c = ["category" .= case c of { Other s -> s ; _ -> pack (show c) }]
|
||||
|
||||
instance ToJSONFields Span where
|
||||
toJSONFields sourceSpan = [ "sourceSpan" .= sourceSpan ]
|
||||
@ -82,18 +86,19 @@ instance ToJSONFields a => ToJSONFields (Maybe a) where
|
||||
toJSONFields = maybe [] toJSONFields
|
||||
|
||||
instance (ToJSONFields a, ToJSONFields (f (Cofree f a))) => ToJSONFields (Cofree f a) where
|
||||
toJSONFields = toJSONFields . runCofree
|
||||
|
||||
instance (ToJSONFields a, ToJSONFields (f b)) => ToJSONFields (CofreeF f a b) where
|
||||
toJSONFields (a :< f) = toJSONFields a <> toJSONFields f
|
||||
|
||||
instance (ToJSONFields a, ToJSONFields (f (Free f a))) => ToJSONFields (Free f a) where
|
||||
toJSONFields = toJSONFields . runFree
|
||||
instance (ToJSONFields a, ToJSONFields (f b)) => ToJSONFields (CofreeF.CofreeF f a b) where
|
||||
toJSONFields (a CofreeF.:< f) = toJSONFields a <> toJSONFields f
|
||||
|
||||
instance (ToJSONFields a, ToJSONFields (f b)) => ToJSONFields (FreeF f a b) where
|
||||
instance (ToJSONFields a, ToJSONFields (f (Free f a))) => ToJSONFields (Free f a) where
|
||||
toJSONFields (Free f) = toJSONFields f
|
||||
toJSONFields (Pure a) = toJSONFields a
|
||||
|
||||
instance (ToJSONFields a, ToJSONFields (f b)) => ToJSONFields (FreeF.FreeF f a b) where
|
||||
toJSONFields (FreeF.Free f) = toJSONFields f
|
||||
toJSONFields (FreeF.Pure a) = toJSONFields a
|
||||
|
||||
instance ToJSON a => ToJSONFields (Patch a) where
|
||||
toJSONFields (Insert a) = [ "insert" .= a ]
|
||||
toJSONFields (Delete a) = [ "delete" .= a ]
|
||||
|
@ -10,17 +10,21 @@ module Renderer.Patch
|
||||
import Alignment
|
||||
import Data.Bifunctor.Join
|
||||
import Data.Blob
|
||||
import Data.ByteString.Char8 (ByteString, pack)
|
||||
import qualified Data.ByteString.Char8 as ByteString
|
||||
import Data.Functor.Both as Both
|
||||
import Data.List (span, unzip)
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Monoid (Sum(..))
|
||||
import Data.Output
|
||||
import Data.Range
|
||||
import Data.Record
|
||||
import Data.Semigroup ((<>))
|
||||
import Data.Source
|
||||
import Data.These
|
||||
import Diff
|
||||
import Patch
|
||||
import Prologue hiding (fst, snd)
|
||||
import Prelude hiding (fst, snd)
|
||||
import SplitDiff
|
||||
|
||||
-- | Render a timed out file as a truncated diff.
|
||||
@ -74,9 +78,9 @@ showHunk blobs hunk = maybeOffsetHeader <>
|
||||
maybeOffsetHeader = if lengthA > 0 && lengthB > 0
|
||||
then offsetHeader
|
||||
else mempty
|
||||
offsetHeader = "@@ -" <> offsetA <> "," <> show lengthA <> " +" <> offsetB <> "," <> show lengthB <> " @@" <> "\n"
|
||||
offsetHeader = "@@ -" <> offsetA <> "," <> pack (show lengthA) <> " +" <> offsetB <> "," <> pack (show lengthB) <> " @@" <> "\n"
|
||||
(lengthA, lengthB) = runJoin . fmap getSum $ hunkLength hunk
|
||||
(offsetA, offsetB) = runJoin . fmap (show . getSum) $ offset hunk
|
||||
(offsetA, offsetB) = runJoin . fmap (pack . show . getSum) $ offset hunk
|
||||
|
||||
-- | Given the before and after sources, render a change to a string.
|
||||
showChange :: Functor f => HasField fields Range => Both Source -> Change (SplitDiff f (Record fields)) -> ByteString
|
||||
@ -117,7 +121,7 @@ header blobs = ByteString.intercalate "\n" ([filepathHeader, fileModeHeader] <>
|
||||
beforeFilepath = "--- " <> modeHeader "a" modeA pathA
|
||||
afterFilepath = "+++ " <> modeHeader "b" modeB pathB
|
||||
sources = blobSource <$> blobs
|
||||
(pathA, pathB) = case runJoin $ toS . blobPath <$> blobs of
|
||||
(pathA, pathB) = case runJoin $ pack . blobPath <$> blobs of
|
||||
("", path) -> (path, path)
|
||||
(path, "") -> (path, path)
|
||||
paths -> paths
|
||||
|
@ -4,12 +4,15 @@ module Renderer.SExpression
|
||||
, renderSExpressionTerm
|
||||
) where
|
||||
|
||||
import Control.Comonad.Trans.Cofree hiding (runCofree)
|
||||
import Control.Monad.Trans.Free hiding (runFree)
|
||||
import Data.Bifunctor.Join
|
||||
import Data.ByteString hiding (foldr, spanEnd)
|
||||
import Data.ByteString.Char8 hiding (foldr, spanEnd)
|
||||
import Data.Record
|
||||
import Prologue hiding (replicate, encodeUtf8)
|
||||
import Data.Semigroup
|
||||
import Diff
|
||||
import Patch
|
||||
import Prelude hiding (replicate)
|
||||
import Term
|
||||
|
||||
-- | Returns a ByteString SExpression formatted diff.
|
||||
@ -33,22 +36,19 @@ printDiff diff level = case runFree diff of
|
||||
pad :: Int -> ByteString
|
||||
pad n | n < 0 = ""
|
||||
| n < 1 = "\n"
|
||||
| otherwise = "\n" <> replicate (2 * n) space
|
||||
| otherwise = "\n" <> replicate (2 * n) ' '
|
||||
|
||||
printTerm :: (ConstrainAll Show fields, Foldable f) => Term f (Record fields) -> Int -> ByteString
|
||||
printTerm term level = go term level 0
|
||||
where
|
||||
pad :: Int -> Int -> ByteString
|
||||
pad p n | n < 1 = ""
|
||||
| otherwise = "\n" <> replicate (2 * (p + n)) space
|
||||
| otherwise = "\n" <> replicate (2 * (p + n)) ' '
|
||||
go :: (ConstrainAll Show fields, Foldable f) => Term f (Record fields) -> Int -> Int -> ByteString
|
||||
go term parentLevel level = case runCofree term of
|
||||
(annotation :< syntax) -> pad parentLevel level <> "(" <> showAnnotation annotation <> foldr (\t acc -> go t parentLevel (level + 1) <> acc) "" syntax <> ")"
|
||||
|
||||
showAnnotation :: ConstrainAll Show fields => Record fields -> ByteString
|
||||
showAnnotation Nil = ""
|
||||
showAnnotation (only :. Nil) = show only
|
||||
showAnnotation (first :. rest) = show first <> " " <> showAnnotation rest
|
||||
|
||||
space :: Word8
|
||||
space = fromIntegral $ ord ' '
|
||||
showAnnotation (only :. Nil) = pack (show only)
|
||||
showAnnotation (first :. rest) = pack (show first) <> " " <> showAnnotation rest
|
||||
|
@ -17,16 +17,26 @@ module Renderer.TOC
|
||||
, entrySummary
|
||||
) where
|
||||
|
||||
import Control.Comonad (extract)
|
||||
import Control.Comonad.Cofree (unwrap)
|
||||
import Control.DeepSeq
|
||||
import Control.Monad.Free (iter)
|
||||
import Data.Aeson
|
||||
import Data.Align (crosswalk)
|
||||
import Data.Bifunctor (bimap, first)
|
||||
import Data.Blob
|
||||
import Data.ByteString.Lazy (toStrict)
|
||||
import Data.Foldable (fold, foldl', toList)
|
||||
import Data.Functor.Both hiding (fst, snd)
|
||||
import qualified Data.Functor.Both as Both
|
||||
import Data.Functor.Foldable (cata)
|
||||
import Data.Functor.Listable
|
||||
import Data.Function (on)
|
||||
import Data.List.NonEmpty (nonEmpty)
|
||||
import Data.Maybe (fromMaybe, mapMaybe)
|
||||
import Data.Output
|
||||
import Data.Record
|
||||
import Data.Semigroup ((<>), sconcat)
|
||||
import Data.Source as Source
|
||||
import Data.Text (toLower)
|
||||
import qualified Data.Text as T
|
||||
@ -34,10 +44,10 @@ import Data.Text.Listable
|
||||
import Data.These
|
||||
import Data.Union
|
||||
import Diff
|
||||
import GHC.Generics
|
||||
import Info
|
||||
import Language
|
||||
import Patch
|
||||
import Prologue hiding (toStrict)
|
||||
import qualified Data.List as List
|
||||
import qualified Data.Map as Map hiding (null)
|
||||
import Syntax as S
|
||||
@ -47,7 +57,7 @@ import qualified Data.Syntax.Declaration as Declaration
|
||||
import qualified Data.Syntax.Markup as Markup
|
||||
import Term
|
||||
|
||||
data Summaries = Summaries { changes, errors :: !(Map Text [Value]) }
|
||||
data Summaries = Summaries { changes, errors :: !(Map.Map T.Text [Value]) }
|
||||
deriving (Eq, Show)
|
||||
|
||||
instance Monoid Summaries where
|
||||
@ -62,12 +72,12 @@ instance ToJSON Summaries where
|
||||
|
||||
data JSONSummary
|
||||
= JSONSummary
|
||||
{ summaryCategoryName :: Text
|
||||
, summaryTermName :: Text
|
||||
{ summaryCategoryName :: T.Text
|
||||
, summaryTermName :: T.Text
|
||||
, summarySpan :: Span
|
||||
, summaryChangeType :: Text
|
||||
, summaryChangeType :: T.Text
|
||||
}
|
||||
| ErrorSummary { error :: Text, errorSpan :: Span, errorLanguage :: Maybe Language }
|
||||
| ErrorSummary { error :: T.Text, errorSpan :: Span, errorLanguage :: Maybe Language }
|
||||
deriving (Generic, Eq, Show)
|
||||
|
||||
instance ToJSON JSONSummary where
|
||||
@ -80,10 +90,10 @@ isValidSummary _ = True
|
||||
|
||||
-- | A declaration’s identifier and type.
|
||||
data Declaration
|
||||
= MethodDeclaration { declarationIdentifier :: Text }
|
||||
| FunctionDeclaration { declarationIdentifier :: Text }
|
||||
| SectionDeclaration { declarationIdentifier :: Text, declarationLevel :: Int }
|
||||
| ErrorDeclaration { declarationIdentifier :: Text, declarationLanguage :: Maybe Language }
|
||||
= MethodDeclaration { declarationIdentifier :: T.Text }
|
||||
| FunctionDeclaration { declarationIdentifier :: T.Text }
|
||||
| SectionDeclaration { declarationIdentifier :: T.Text, declarationLevel :: Int }
|
||||
| ErrorDeclaration { declarationIdentifier :: T.Text, declarationLanguage :: Maybe Language }
|
||||
deriving (Eq, Generic, NFData, Show)
|
||||
|
||||
getDeclaration :: HasField fields (Maybe Declaration) => Record fields -> Maybe Declaration
|
||||
@ -96,14 +106,14 @@ declaration (annotation :< _) = annotation <$ (getField annotation :: Maybe Decl
|
||||
|
||||
-- | Compute 'Declaration's for methods and functions in 'Syntax'.
|
||||
syntaxDeclarationAlgebra :: HasField fields Range => Blob -> RAlgebra (SyntaxTermF fields) (SyntaxTerm fields) (Maybe Declaration)
|
||||
syntaxDeclarationAlgebra Blob{..} r = case tailF r of
|
||||
syntaxDeclarationAlgebra Blob{..} (a :< r) = case r of
|
||||
S.Function (identifier, _) _ _ -> Just $ FunctionDeclaration (getSource identifier)
|
||||
S.Method _ (identifier, _) Nothing _ _ -> Just $ MethodDeclaration (getSource identifier)
|
||||
S.Method _ (identifier, _) (Just (receiver, _)) _ _
|
||||
| S.Indexed [receiverParams] <- unwrap receiver
|
||||
, S.ParameterDecl (Just ty) _ <- unwrap receiverParams -> Just $ MethodDeclaration ("(" <> getSource ty <> ") " <> getSource identifier)
|
||||
| otherwise -> Just $ MethodDeclaration (getSource receiver <> "." <> getSource identifier)
|
||||
S.ParseError{} -> Just $ ErrorDeclaration (toText (Source.slice (byteRange (headF r)) blobSource)) blobLanguage
|
||||
S.ParseError{} -> Just $ ErrorDeclaration (toText (Source.slice (byteRange a) blobSource)) blobLanguage
|
||||
_ -> Nothing
|
||||
where getSource = toText . flip Source.slice blobSource . byteRange . extract
|
||||
|
||||
@ -111,10 +121,10 @@ syntaxDeclarationAlgebra Blob{..} r = case tailF r of
|
||||
declarationAlgebra :: (Declaration.Function :< fs, Declaration.Method :< fs, Syntax.Error :< fs, Functor (Union fs), HasField fields Range)
|
||||
=> Blob
|
||||
-> RAlgebra (TermF (Union fs) (Record fields)) (Term (Union fs) (Record fields)) (Maybe Declaration)
|
||||
declarationAlgebra Blob{..} r
|
||||
| Just (Declaration.Function (identifier, _) _ _) <- prj (tailF r) = Just $ FunctionDeclaration (getSource (extract identifier))
|
||||
| Just (Declaration.Method _ (identifier, _) _ _) <- prj (tailF r) = Just $ MethodDeclaration (getSource (extract identifier))
|
||||
| Just Syntax.Error{} <- prj (tailF r) = Just $ ErrorDeclaration (getSource (headF r)) blobLanguage
|
||||
declarationAlgebra Blob{..} (a :< r)
|
||||
| Just (Declaration.Function (identifier, _) _ _) <- prj r = Just $ FunctionDeclaration (getSource (extract identifier))
|
||||
| Just (Declaration.Method _ (identifier, _) _ _) <- prj r = Just $ MethodDeclaration (getSource (extract identifier))
|
||||
| Just Syntax.Error{} <- prj r = Just $ ErrorDeclaration (getSource a) blobLanguage
|
||||
| otherwise = Nothing
|
||||
where getSource = toText . flip Source.slice blobSource . byteRange
|
||||
|
||||
@ -122,9 +132,9 @@ declarationAlgebra Blob{..} r
|
||||
markupSectionAlgebra :: (Markup.Section :< fs, Syntax.Error :< fs, HasField fields Range, Functor (Union fs), Foldable (Union fs))
|
||||
=> Blob
|
||||
-> RAlgebra (TermF (Union fs) (Record fields)) (Term (Union fs) (Record fields)) (Maybe Declaration)
|
||||
markupSectionAlgebra Blob{..} r
|
||||
| Just (Markup.Section level (heading, _) _) <- prj (tailF r) = Just $ SectionDeclaration (maybe (getSource (extract heading)) (firstLine . toText . flip Source.slice blobSource . sconcat) (nonEmpty (byteRange . extract <$> toList (unwrap heading)))) level
|
||||
| Just Syntax.Error{} <- prj (tailF r) = Just $ ErrorDeclaration (getSource (headF r)) blobLanguage
|
||||
markupSectionAlgebra Blob{..} (a :< r)
|
||||
| Just (Markup.Section level (heading, _) _) <- prj r = Just $ SectionDeclaration (maybe (getSource (extract heading)) (firstLine . toText . flip Source.slice blobSource . sconcat) (nonEmpty (byteRange . extract <$> toList (unwrap heading)))) level
|
||||
| Just Syntax.Error{} <- prj r = Just $ ErrorDeclaration (getSource a) blobLanguage
|
||||
| otherwise = Nothing
|
||||
where getSource = firstLine . toText . flip Source.slice blobSource . byteRange
|
||||
firstLine = T.takeWhile (/= '\n')
|
||||
@ -183,7 +193,7 @@ entrySummary entry = case entry of
|
||||
Replaced a -> recordSummary a "modified"
|
||||
|
||||
-- | Construct a 'JSONSummary' from a node annotation and a change type label.
|
||||
recordSummary :: (HasField fields (Maybe Declaration), HasField fields Span) => Record fields -> Text -> Maybe JSONSummary
|
||||
recordSummary :: (HasField fields (Maybe Declaration), HasField fields Span) => Record fields -> T.Text -> Maybe JSONSummary
|
||||
recordSummary record = case getDeclaration record of
|
||||
Just (ErrorDeclaration text language) -> Just . const (ErrorSummary text (sourceSpan record) language)
|
||||
Just declaration -> Just . JSONSummary (toCategoryName declaration) (declarationIdentifier declaration) (sourceSpan record)
|
||||
@ -193,7 +203,7 @@ renderToCDiff :: (HasField fields (Maybe Declaration), HasField fields Span, Tra
|
||||
renderToCDiff blobs = uncurry Summaries . bimap toMap toMap . List.partition isValidSummary . diffTOC
|
||||
where toMap [] = mempty
|
||||
toMap as = Map.singleton summaryKey (toJSON <$> as)
|
||||
summaryKey = toS $ case runJoin (blobPath <$> blobs) of
|
||||
summaryKey = T.pack $ case runJoin (blobPath <$> blobs) of
|
||||
(before, after) | null before -> after
|
||||
| null after -> before
|
||||
| before == after -> after
|
||||
@ -202,7 +212,7 @@ renderToCDiff blobs = uncurry Summaries . bimap toMap toMap . List.partition isV
|
||||
renderToCTerm :: (HasField fields (Maybe Declaration), HasField fields Span, Traversable f) => Blob -> Term f (Record fields) -> Summaries
|
||||
renderToCTerm Blob{..} = uncurry Summaries . bimap toMap toMap . List.partition isValidSummary . termToC
|
||||
where toMap [] = mempty
|
||||
toMap as = Map.singleton (toS blobPath) (toJSON <$> as)
|
||||
toMap as = Map.singleton (T.pack blobPath) (toJSON <$> as)
|
||||
|
||||
diffTOC :: (HasField fields (Maybe Declaration), HasField fields Span, Traversable f) => Diff f (Record fields) -> [JSONSummary]
|
||||
diffTOC = mapMaybe entrySummary . dedupe . tableOfContentsBy declaration
|
||||
@ -211,11 +221,11 @@ termToC :: (HasField fields (Maybe Declaration), HasField fields Span, Traversab
|
||||
termToC = mapMaybe (flip recordSummary "unchanged") . termTableOfContentsBy declaration
|
||||
|
||||
-- The user-facing category name
|
||||
toCategoryName :: Declaration -> Text
|
||||
toCategoryName :: Declaration -> T.Text
|
||||
toCategoryName declaration = case declaration of
|
||||
FunctionDeclaration _ -> "Function"
|
||||
MethodDeclaration _ -> "Method"
|
||||
SectionDeclaration _ l -> "Heading " <> show l
|
||||
SectionDeclaration _ l -> "Heading " <> T.pack (show l)
|
||||
ErrorDeclaration{} -> "ParseError"
|
||||
|
||||
instance Listable Declaration where
|
||||
|
@ -4,7 +4,6 @@ module SES
|
||||
, Myers.ses
|
||||
) where
|
||||
|
||||
import Prologue
|
||||
import qualified SES.Myers as Myers
|
||||
|
||||
-- | Edit constructor for two terms, if comparable. Otherwise returns Nothing.
|
||||
|
@ -6,10 +6,9 @@ module SES.Myers
|
||||
|
||||
import Data.Array ((!))
|
||||
import qualified Data.Array as Array
|
||||
import Data.Foldable (find, toList)
|
||||
import Data.Ix
|
||||
import Data.These
|
||||
import GHC.Show hiding (show)
|
||||
import Prologue hiding (error)
|
||||
|
||||
-- | An edit script, i.e. a sequence of changes/copies of elements.
|
||||
type EditScript a b = [These a b]
|
||||
|
@ -8,8 +8,12 @@ module Semantic
|
||||
) where
|
||||
|
||||
import Algorithm hiding (diff)
|
||||
import Control.Applicative ((<|>))
|
||||
import Control.Comonad.Cofree (hoistCofree)
|
||||
import Control.Monad ((<=<))
|
||||
import Data.Align.Generic (GAlign)
|
||||
import Data.Blob
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.Functor.Both as Both
|
||||
import Data.Functor.Classes (Eq1, Show1)
|
||||
import Data.Output
|
||||
@ -23,7 +27,6 @@ import Interpreter
|
||||
import qualified Language
|
||||
import Patch
|
||||
import Parser
|
||||
import Prologue
|
||||
import Renderer
|
||||
import Semantic.Task as Task
|
||||
import Term
|
||||
|
@ -1,14 +1,15 @@
|
||||
module Semantic.Log where
|
||||
|
||||
import Data.String
|
||||
import Prologue hiding (Location, show)
|
||||
import Data.Bifunctor (second)
|
||||
import Data.Foldable (toList)
|
||||
import Data.List (intersperse)
|
||||
import Data.Semigroup ((<>))
|
||||
import qualified Data.Time.Format as Time
|
||||
import qualified Data.Time.LocalTime as LocalTime
|
||||
import System.Console.ANSI
|
||||
import System.IO (hIsTerminalDevice)
|
||||
import System.IO (Handle, hIsTerminalDevice)
|
||||
import System.Posix.Process
|
||||
import System.Posix.Types
|
||||
import Text.Show
|
||||
import Text.Printf
|
||||
|
||||
-- | A log message at a specific level.
|
||||
@ -42,7 +43,7 @@ logfmtFormatter Options{..} (Message level message pairs time) =
|
||||
. showChar '\n' $ ""
|
||||
where
|
||||
kv k v = showString k . showChar '=' . v
|
||||
showPairs = foldr (.) identity . intersperse (showChar ' ')
|
||||
showPairs = foldr (.) id . intersperse (showChar ' ')
|
||||
showTime = showString . Time.formatTime Time.defaultTimeLocale "%FT%XZ%z"
|
||||
|
||||
-- | Format log messages to a terminal. Suitable for local development.
|
||||
@ -62,7 +63,7 @@ terminalFormatter Options{..} (Message level message pairs time) =
|
||||
showLevel Warning = withSGRCode colourize [SetColor Foreground Vivid Yellow, SetConsoleIntensity BoldIntensity] (showString " WARN")
|
||||
showLevel Info = withSGRCode colourize [SetColor Foreground Vivid Cyan, SetConsoleIntensity BoldIntensity] (showString " INFO")
|
||||
showLevel Debug = withSGRCode colourize [SetColor Foreground Vivid White, SetConsoleIntensity BoldIntensity] (showString "DEBUG")
|
||||
showPairs pairs = foldr (.) identity $ intersperse (showChar ' ') (showPair <$> pairs)
|
||||
showPairs pairs = foldr (.) id $ intersperse (showChar ' ') (showPair <$> pairs)
|
||||
showPair (k, v) = showString k . showChar '=' . withSGRCode colourize [SetConsoleIntensity BoldIntensity] (showString v)
|
||||
showTime = showString . Time.formatTime Time.defaultTimeLocale "%X"
|
||||
|
||||
|
@ -26,15 +26,19 @@ module Semantic.Task
|
||||
|
||||
import Control.Concurrent.STM.TMQueue
|
||||
import Control.Exception
|
||||
import Control.Monad (join, when)
|
||||
import Control.Monad.Error.Class
|
||||
import Control.Monad.IO.Class
|
||||
import Control.Parallel.Strategies
|
||||
import qualified Control.Concurrent.Async as Async
|
||||
import Control.Monad.Free.Freer
|
||||
import Data.Blob
|
||||
import qualified Data.ByteString as B
|
||||
import Data.Foldable (fold)
|
||||
import Data.Functor.Both as Both
|
||||
import Data.Functor.Foldable (cata)
|
||||
import Data.Record
|
||||
import Data.String
|
||||
import Data.Semigroup ((<>))
|
||||
import qualified Data.Syntax as Syntax
|
||||
import Data.Syntax.Algebra (RAlgebra, decoratorWithAlgebra)
|
||||
import qualified Data.Syntax.Assignment as Assignment
|
||||
@ -44,20 +48,20 @@ import qualified Data.Time.LocalTime as LocalTime
|
||||
import Data.Union
|
||||
import Diff
|
||||
import qualified Files
|
||||
import GHC.Conc (atomically)
|
||||
import Language
|
||||
import Language.Markdown
|
||||
import Parser
|
||||
import Prologue hiding (Location, show)
|
||||
import System.IO (hPutStr)
|
||||
import System.Exit (die)
|
||||
import System.IO (Handle, hPutStr, stderr)
|
||||
import Term
|
||||
import Text.Show
|
||||
import TreeSitter
|
||||
import Semantic.Log
|
||||
|
||||
data TaskF output where
|
||||
ReadBlobs :: Either Handle [(FilePath, Maybe Language)] -> TaskF [Blob]
|
||||
ReadBlobPairs :: Either Handle [Both (FilePath, Maybe Language)] -> TaskF [Both Blob]
|
||||
WriteToOutput :: Either Handle FilePath -> ByteString -> TaskF ()
|
||||
WriteToOutput :: Either Handle FilePath -> B.ByteString -> TaskF ()
|
||||
WriteLog :: Level -> String -> [(String, String)] -> TaskF ()
|
||||
Time :: String -> [(String, String)] -> Task output -> TaskF output
|
||||
Parse :: Parser term -> Blob -> TaskF term
|
||||
@ -84,8 +88,8 @@ readBlobs from = ReadBlobs from `Then` return
|
||||
readBlobPairs :: Either Handle [Both (FilePath, Maybe Language)] -> Task [Both Blob]
|
||||
readBlobPairs from = ReadBlobPairs from `Then` return
|
||||
|
||||
-- | A 'Task' which writes a 'ByteString' to a 'Handle' or a 'FilePath'.
|
||||
writeToOutput :: Either Handle FilePath -> ByteString -> Task ()
|
||||
-- | A 'Task' which writes a 'B.ByteString' to a 'Handle' or a 'FilePath'.
|
||||
writeToOutput :: Either Handle FilePath -> B.ByteString -> Task ()
|
||||
writeToOutput path contents = WriteToOutput path contents `Then` return
|
||||
|
||||
|
||||
@ -142,11 +146,11 @@ runTaskWithOptions :: Options -> Task a -> IO a
|
||||
runTaskWithOptions options task = do
|
||||
options <- configureOptionsForHandle stderr options
|
||||
logQueue <- newTMQueueIO
|
||||
logging <- async (logSink options logQueue)
|
||||
logging <- Async.async (logSink options logQueue)
|
||||
|
||||
result <- run options logQueue task
|
||||
atomically (closeTMQueue logQueue)
|
||||
wait logging
|
||||
Async.wait logging
|
||||
either die pure result
|
||||
where logSink options@Options{..} queue = do
|
||||
message <- atomically (readTMQueue queue)
|
||||
|
@ -6,20 +6,25 @@ module SemanticCmdLine
|
||||
, runParse
|
||||
) where
|
||||
|
||||
import Control.Monad ((<=<))
|
||||
import Files (languageForFilePath)
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.Foldable (find)
|
||||
import Data.Functor.Both hiding (fst, snd)
|
||||
import Data.List (intercalate)
|
||||
import Data.List.Split (splitWhen)
|
||||
import Data.Semigroup ((<>))
|
||||
import Data.Version (showVersion)
|
||||
import Development.GitRev
|
||||
import Language
|
||||
import Options.Applicative hiding (action)
|
||||
import Prologue hiding (concurrently, readFile)
|
||||
import Renderer
|
||||
import qualified Paths_semantic_diff as Library (version)
|
||||
import qualified Semantic.Task as Task
|
||||
import qualified Semantic.Log as Log
|
||||
import System.IO (stdin)
|
||||
import System.IO (Handle, stdin, stdout)
|
||||
import qualified Semantic (parseBlobs, diffBlobPairs)
|
||||
import Text.Read
|
||||
|
||||
main :: IO ()
|
||||
main = customExecParser (prefs showHelpOnEmpty) arguments >>= uncurry Task.runTaskWithOptions
|
||||
|
@ -1,25 +1,24 @@
|
||||
module SplitDiff where
|
||||
|
||||
import Control.Comonad
|
||||
import Control.Comonad.Trans.Cofree
|
||||
import Control.Monad.Free
|
||||
import Data.Record
|
||||
import Info
|
||||
import Prologue
|
||||
import Term (Term, TermF)
|
||||
|
||||
-- | A patch to only one side of a diff.
|
||||
data SplitPatch a = SplitInsert a | SplitDelete a | SplitReplace a
|
||||
data SplitPatch a
|
||||
= SplitInsert { splitTerm :: a }
|
||||
| SplitDelete { splitTerm :: a }
|
||||
| SplitReplace { splitTerm :: a }
|
||||
deriving (Show, Eq, Functor)
|
||||
|
||||
-- | Get the term from a split patch.
|
||||
getSplitTerm :: SplitPatch a -> a
|
||||
getSplitTerm (SplitInsert a) = a
|
||||
getSplitTerm (SplitDelete a) = a
|
||||
getSplitTerm (SplitReplace a) = a
|
||||
|
||||
-- | Get the range of a SplitDiff.
|
||||
getRange :: Functor f => HasField fields Range => SplitDiff f (Record fields) -> Range
|
||||
getRange diff = byteRange $ case runFree diff of
|
||||
getRange diff = byteRange $ case diff of
|
||||
Free annotated -> headF annotated
|
||||
Pure patch -> extract (getSplitTerm patch)
|
||||
Pure patch -> extract (splitTerm patch)
|
||||
|
||||
-- | A diff with only one side’s annotations.
|
||||
type SplitDiff f annotation = Free (TermF f annotation) (SplitPatch (Term f annotation))
|
||||
|
@ -1,15 +1,15 @@
|
||||
{-# LANGUAGE DeriveAnyClass #-}
|
||||
module Syntax where
|
||||
|
||||
import Control.DeepSeq
|
||||
import Data.Aeson
|
||||
import Data.Align.Generic
|
||||
import Data.Functor.Classes
|
||||
import Data.Functor.Classes.Eq.Generic
|
||||
import Data.Functor.Listable
|
||||
import Data.Mergeable
|
||||
import Data.Text (pack)
|
||||
import Data.Text (pack, Text)
|
||||
import GHC.Generics
|
||||
import Prologue
|
||||
|
||||
-- | A node in an abstract syntax tree.
|
||||
--
|
||||
|
43
src/Term.hs
43
src/Term.hs
@ -1,38 +1,54 @@
|
||||
{-# LANGUAGE RankNTypes, TypeFamilies, TypeSynonymInstances, UndecidableInstances #-}
|
||||
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
||||
module Term where
|
||||
module Term
|
||||
( Term
|
||||
, TermF
|
||||
, SyntaxTerm
|
||||
, SyntaxTermF
|
||||
, zipTerms
|
||||
, termSize
|
||||
, alignCofreeWith
|
||||
, cofree
|
||||
, runCofree
|
||||
, CofreeF.CofreeF(..)
|
||||
) where
|
||||
|
||||
import Prologue
|
||||
import qualified Control.Comonad.Cofree as Cofree
|
||||
import qualified Control.Comonad.Trans.Cofree as CofreeF
|
||||
import Control.DeepSeq
|
||||
import Control.Monad.Free
|
||||
import Data.Align.Generic
|
||||
import Data.Functor.Both
|
||||
import Data.Functor.Foldable
|
||||
import Data.Maybe
|
||||
import Data.Record
|
||||
import Data.These
|
||||
import Syntax
|
||||
|
||||
-- | A Term with an abstract syntax tree and an annotation.
|
||||
type Term f = Cofree f
|
||||
type TermF = CofreeF
|
||||
type Term f = Cofree.Cofree f
|
||||
type TermF = CofreeF.CofreeF
|
||||
|
||||
-- | A Term with a Syntax leaf and a record of fields.
|
||||
type SyntaxTerm fields = Term Syntax (Record fields)
|
||||
type SyntaxTermF fields = TermF Syntax (Record fields)
|
||||
|
||||
instance (NFData (f (Cofree f a)), NFData a, Functor f) => NFData (Cofree f a) where
|
||||
instance (NFData (f (Cofree.Cofree f a)), NFData a, Functor f) => NFData (Cofree.Cofree f a) where
|
||||
rnf = rnf . runCofree
|
||||
|
||||
instance (NFData a, NFData (f b)) => NFData (CofreeF f a b) where
|
||||
rnf (a :< s) = rnf a `seq` rnf s `seq` ()
|
||||
instance (NFData a, NFData (f b)) => NFData (CofreeF.CofreeF f a b) where
|
||||
rnf (a CofreeF.:< s) = rnf a `seq` rnf s `seq` ()
|
||||
|
||||
-- | Zip two terms by combining their annotations into a pair of annotations.
|
||||
-- | If the structure of the two terms don't match, then Nothing will be returned.
|
||||
zipTerms :: (Traversable f, GAlign f) => Term f annotation -> Term f annotation -> Maybe (Term f (Both annotation))
|
||||
zipTerms t1 t2 = iter go (alignCofreeWith galign (const Nothing) both (These t1 t2))
|
||||
where go (a :< s) = cofree . (a :<) <$> sequenceA s
|
||||
where go (a CofreeF.:< s) = cofree . (a CofreeF.:<) <$> sequenceA s
|
||||
|
||||
-- | Return the node count of a term.
|
||||
termSize :: (Foldable f, Functor f) => Term f annotation -> Int
|
||||
termSize = cata size where
|
||||
size (_ :< syntax) = 1 + sum syntax
|
||||
size (_ CofreeF.:< syntax) = 1 + sum syntax
|
||||
|
||||
-- | Aligns (zips, retaining non-overlapping portions of the structure) a pair of terms.
|
||||
alignCofreeWith :: Functor f
|
||||
@ -43,5 +59,12 @@ alignCofreeWith :: Functor f
|
||||
-> Free (TermF f combined) contrasted
|
||||
alignCofreeWith compare contrast combine = go
|
||||
where go terms = fromMaybe (pure (contrast terms)) $ case terms of
|
||||
These t1 t2 -> wrap . (combine (extract t1) (extract t2) :<) . fmap go <$> compare (unwrap t1) (unwrap t2)
|
||||
These (a1 Cofree.:< f1) (a2 Cofree.:< f2) -> wrap . (combine a1 a2 CofreeF.:<) . fmap go <$> compare f1 f2
|
||||
_ -> Nothing
|
||||
|
||||
|
||||
cofree :: CofreeF.CofreeF f a (Cofree.Cofree f a) -> Cofree.Cofree f a
|
||||
cofree (a CofreeF.:< f) = a Cofree.:< f
|
||||
|
||||
runCofree :: Cofree.Cofree f a -> CofreeF.CofreeF f a (Cofree.Cofree f a)
|
||||
runCofree (a Cofree.:< f) = a CofreeF.:< f
|
||||
|
@ -5,8 +5,10 @@ module TreeSitter
|
||||
, defaultTermAssignment
|
||||
) where
|
||||
|
||||
import Prologue hiding (Constructor)
|
||||
import Category
|
||||
import Control.Comonad (extract)
|
||||
import Control.Exception
|
||||
import Control.Monad ((<=<))
|
||||
import Data.Blob
|
||||
import Data.ByteString.Unsafe (unsafeUseAsCStringLen)
|
||||
import Data.Functor.Foldable hiding (Nil)
|
||||
@ -15,6 +17,7 @@ import Data.Record
|
||||
import Data.Source
|
||||
import Data.Span
|
||||
import qualified Data.Syntax.Assignment as A
|
||||
import Data.Text (Text, pack)
|
||||
import Language
|
||||
import qualified Language.Go as Go
|
||||
import qualified Language.TypeScript as TS
|
||||
@ -82,7 +85,7 @@ documentToTerm language document Blob{..} = do
|
||||
let allChildren = getChildren (fromIntegral (nodeChildCount node)) copyAll
|
||||
|
||||
let source = slice (nodeRange node) blobSource
|
||||
assignTerm language source (range :. categoryForLanguageProductionName language (toS name) :. nodeSpan node :. Nil) children allChildren
|
||||
assignTerm language source (range :. categoryForLanguageProductionName language (pack name) :. nodeSpan node :. Nil) children allChildren
|
||||
where getChildren count copy = do
|
||||
nodes <- allocaArray count $ \ childNodesPtr -> do
|
||||
_ <- with (nodeTSNode node) (\ nodePtr -> copy nodePtr childNodesPtr (fromIntegral count))
|
||||
@ -127,7 +130,7 @@ defaultTermAssignment source category children allChildren
|
||||
|
||||
-- Control flow statements
|
||||
(If, condition : body) -> S.If condition body
|
||||
(Switch, _) -> uncurry S.Switch (Prologue.break ((== Case) . Info.category . extract) children)
|
||||
(Switch, _) -> uncurry S.Switch (break ((== Case) . Info.category . extract) children)
|
||||
(Case, expr : body) -> S.Case expr body
|
||||
(While, expr : rest) -> S.While expr rest
|
||||
|
||||
|
@ -2,22 +2,26 @@
|
||||
module AlignmentSpec where
|
||||
|
||||
import Alignment
|
||||
import Control.Arrow ((&&&))
|
||||
import Control.Comonad.Cofree (Cofree, hoistCofree)
|
||||
import Control.Monad.Free (Free, wrap)
|
||||
import Control.Monad.State
|
||||
import Data.Align hiding (align)
|
||||
import Data.Bifunctor
|
||||
import Data.Bifunctor.Join
|
||||
import Data.Functor.Both as Both
|
||||
import Data.Foldable (toList)
|
||||
import Data.Functor.Both as Both hiding (fst, snd)
|
||||
import Data.Functor.Listable
|
||||
import Data.List (nub)
|
||||
import Data.List (nub, sort)
|
||||
import Data.Maybe (catMaybes, fromMaybe)
|
||||
import Data.Monoid hiding ((<>))
|
||||
import Data.Range
|
||||
import Data.Record
|
||||
import Data.Semigroup ((<>))
|
||||
import qualified Data.Source as Source
|
||||
import qualified Data.Text as Text
|
||||
import Data.These
|
||||
import Patch
|
||||
import Prologue hiding (fst, snd)
|
||||
import qualified Prologue
|
||||
import SplitDiff
|
||||
import Syntax
|
||||
import Term
|
||||
@ -47,17 +51,17 @@ spec = parallel $ do
|
||||
|
||||
prop "covers every input line" $
|
||||
\ elements -> let (_, children, ranges) = toAlignBranchInputs elements in
|
||||
join <$> traverse (modifyJoin (fromThese [] []) . fmap (pure . Prologue.fst)) (alignBranch Prologue.snd children ranges) `shouldBe` ranges
|
||||
join <$> traverse (modifyJoin (fromThese [] []) . fmap (pure . fst)) (alignBranch snd children ranges) `shouldBe` ranges
|
||||
|
||||
prop "covers every input child" $
|
||||
\ elements -> let (_, children, ranges) = toAlignBranchInputs elements in
|
||||
sort (nub (keysOfAlignedChildren (alignBranch Prologue.snd children ranges))) `shouldBe` sort (nub (catMaybes (branchElementKey <$> elements)))
|
||||
sort (nub (keysOfAlignedChildren (alignBranch snd children ranges))) `shouldBe` sort (nub (catMaybes (branchElementKey <$> elements)))
|
||||
|
||||
prop "covers every line of every input child" $
|
||||
\ elements -> let (_, children, ranges) = toAlignBranchInputs elements in
|
||||
sort (keysOfAlignedChildren (alignBranch Prologue.snd children ranges)) `shouldBe` sort (do
|
||||
sort (keysOfAlignedChildren (alignBranch snd children ranges)) `shouldBe` sort (do
|
||||
line <- children
|
||||
these (pure . Prologue.fst) (pure . Prologue.fst) (\ (k1, _) (k2, _) -> [ k1, k2 ]) . runJoin $ line)
|
||||
these (pure . fst) (pure . fst) (\ (k1, _) (k2, _) -> [ k1, k2 ]) . runJoin $ line)
|
||||
|
||||
describe "alignDiff" $ do
|
||||
it "aligns identical branches on a single line" $
|
||||
@ -197,17 +201,17 @@ spec = parallel $ do
|
||||
\ xs -> counts (numberedRows (unListableF <$> xs :: [Join These Char])) `shouldBe` length . catMaybes <$> Join (unalign (runJoin . unListableF <$> xs))
|
||||
|
||||
data BranchElement
|
||||
= Child Text (Join These Text)
|
||||
| Margin (Join These Text)
|
||||
= Child Text.Text (Join These Text.Text)
|
||||
| Margin (Join These Text.Text)
|
||||
deriving Show
|
||||
|
||||
branchElementKey :: BranchElement -> Maybe Text
|
||||
branchElementKey :: BranchElement -> Maybe Text.Text
|
||||
branchElementKey (Child key _) = Just key
|
||||
branchElementKey _ = Nothing
|
||||
|
||||
toAlignBranchInputs :: [BranchElement] -> (Both Source.Source, [Join These (Text, Range)], Both [Range])
|
||||
toAlignBranchInputs :: [BranchElement] -> (Both Source.Source, [Join These (Text.Text, Range)], Both [Range])
|
||||
toAlignBranchInputs elements = (sources, join . (`evalState` both 0 0) . traverse go $ elements, ranges)
|
||||
where go :: BranchElement -> State (Both Int) [Join These (Text, Range)]
|
||||
where go :: BranchElement -> State (Both Int) [Join These (Text.Text, Range)]
|
||||
go child@(Child key _) = do
|
||||
lines <- traverse (\ (Child _ contents) -> do
|
||||
prev <- get
|
||||
@ -229,8 +233,8 @@ toAlignBranchInputs elements = (sources, join . (`evalState` both 0 0) . travers
|
||||
branchElementContents (Child _ contents) = contents
|
||||
branchElementContents (Margin contents) = contents
|
||||
|
||||
keysOfAlignedChildren :: [Join These (Range, [(Text, Range)])] -> [Text]
|
||||
keysOfAlignedChildren lines = lines >>= these identity identity (<>) . runJoin . fmap (fmap Prologue.fst . Prologue.snd)
|
||||
keysOfAlignedChildren :: [Join These (Range, [(Text.Text, Range)])] -> [Text.Text]
|
||||
keysOfAlignedChildren lines = lines >>= these id id (<>) . runJoin . fmap (fmap fst . snd)
|
||||
|
||||
joinCrosswalk :: Bicrosswalk p => Align f => (a -> f b) -> Join p a -> f (Join p b)
|
||||
joinCrosswalk f = fmap Join . bicrosswalk f f . runJoin
|
||||
@ -240,7 +244,7 @@ instance Listable BranchElement where
|
||||
, Margin `mapT` joinTheseOf (Text.singleton `mapT` padding '-') ]
|
||||
where key = Text.singleton `mapT` [['a'..'z'] <> ['A'..'Z'] <> ['0'..'9']]
|
||||
contents key = (wrap key . Text.singleton) `mapT` padding '*'
|
||||
wrap key contents = "(" <> key <> contents <> ")" :: Text
|
||||
wrap key contents = "(" <> key <> contents <> ")" :: Text.Text
|
||||
padding :: Char -> [Tier Char]
|
||||
padding char = frequency [ (10, [[char]])
|
||||
, (1, [['\n']]) ]
|
||||
@ -254,16 +258,16 @@ instance Listable BranchElement where
|
||||
|
||||
|
||||
counts :: [Join These (Int, a)] -> Both Int
|
||||
counts numbered = fromMaybe 0 . getLast . mconcat . fmap Last <$> Join (unalign (runJoin . fmap Prologue.fst <$> numbered))
|
||||
counts numbered = fromMaybe 0 . getLast . mconcat . fmap Last <$> Join (unalign (runJoin . fmap fst <$> numbered))
|
||||
|
||||
align :: Both Source.Source -> ConstructibleFree Syntax (Patch (Term Syntax (Record '[Range]))) (Both (Record '[Range])) -> PrettyDiff (SplitDiff [] (Record '[Range]))
|
||||
align sources = PrettyDiff sources . fmap (fmap (getRange &&& identity)) . alignDiff sources . deconstruct
|
||||
align sources = PrettyDiff sources . fmap (fmap (getRange &&& id)) . alignDiff sources . deconstruct
|
||||
|
||||
info :: Int -> Int -> Record '[Range]
|
||||
info start end = Range start end :. Nil
|
||||
|
||||
prettyDiff :: Both Source.Source -> [Join These (ConstructibleFree [] (SplitPatch (Term [] (Record '[Range]))) (Record '[Range]))] -> PrettyDiff (SplitDiff [] (Record '[Range]))
|
||||
prettyDiff sources = PrettyDiff sources . fmap (fmap ((getRange &&& identity) . deconstruct))
|
||||
prettyDiff sources = PrettyDiff sources . fmap (fmap ((getRange &&& id) . deconstruct))
|
||||
|
||||
data PrettyDiff a = PrettyDiff { unPrettySources :: Both Source.Source, unPrettyLines :: [Join These (Range, a)] }
|
||||
deriving Eq
|
||||
@ -301,16 +305,16 @@ instance (Functor f, PatchConstructible patch) => PatchConstructible (Constructi
|
||||
delete = ConstructibleFree . pure . delete
|
||||
|
||||
class SyntaxConstructible s where
|
||||
leaf :: annotation -> Text -> s annotation
|
||||
leaf :: annotation -> Text.Text -> s annotation
|
||||
branch :: annotation -> [s annotation] -> s annotation
|
||||
|
||||
instance SyntaxConstructible (ConstructibleFree Syntax patch) where
|
||||
leaf info = ConstructibleFree . free . Free . (info :<) . Leaf
|
||||
branch info = ConstructibleFree . free . Free . (info :<) . Indexed . fmap deconstruct
|
||||
leaf info = ConstructibleFree . wrap . (info :<) . Leaf
|
||||
branch info = ConstructibleFree . wrap . (info :<) . Indexed . fmap deconstruct
|
||||
|
||||
instance SyntaxConstructible (ConstructibleFree [] patch) where
|
||||
leaf info = ConstructibleFree . free . Free . (info :<) . const []
|
||||
branch info = ConstructibleFree . free . Free . (info :<) . fmap deconstruct
|
||||
leaf info = ConstructibleFree . wrap . (info :<) . const []
|
||||
branch info = ConstructibleFree . wrap . (info :<) . fmap deconstruct
|
||||
|
||||
instance SyntaxConstructible (Cofree Syntax) where
|
||||
info `leaf` value = cofree $ info :< Leaf value
|
||||
|
@ -6,7 +6,9 @@ import Data.Functor.Both as Both
|
||||
import Data.Maybe
|
||||
import Data.String
|
||||
import Language
|
||||
import Prologue hiding (readFile, toList)
|
||||
import Prelude hiding (readFile)
|
||||
import System.Exit (ExitCode(..))
|
||||
import System.IO (IOMode(..), openFile)
|
||||
import Test.Hspec hiding (shouldBe, shouldNotBe, shouldThrow, errorCall)
|
||||
import Test.Hspec.Expectations.Pretty
|
||||
|
||||
|
@ -1,12 +1,11 @@
|
||||
{-# LANGUAGE RankNTypes, ScopedTypeVariables #-}
|
||||
module Data.Mergeable.Spec where
|
||||
|
||||
import Control.Applicative (Alternative(..))
|
||||
import Data.Functor.Identity
|
||||
import Data.Functor.Listable
|
||||
import Data.Maybe (catMaybes)
|
||||
import Data.Mergeable
|
||||
import Data.String (String)
|
||||
import GHC.Show
|
||||
import Prologue
|
||||
import Syntax
|
||||
import Test.Hspec
|
||||
import Test.Hspec.LeanCheck
|
||||
@ -44,7 +43,7 @@ sequenceAltLaws value function = describe "sequenceAlt" $ do
|
||||
\ a -> sequenceAlt (pure <$> a) `shouldNotBe` (empty :: g (f a))
|
||||
|
||||
prop "relationship with merge" . forAll (productWith ((Blind .) . fmap . getBlind) function value :: [Tier (Blind (f (g a)))]) $
|
||||
\ a -> sequenceAlt (getBlind a) `shouldBe` merge identity (getBlind a)
|
||||
\ a -> sequenceAlt (getBlind a) `shouldBe` merge id (getBlind a)
|
||||
|
||||
|
||||
withAlternativeInstances :: forall f a. (Listable a, Eq (f a), Show (f a)) => (forall g. (Alternative g, Eq (g (f a)), Show (g (f a))) => [Tier (f a)] -> [Tier (Blind (a -> g a))] -> Spec) -> [Tier (f a)] -> Spec
|
||||
|
@ -2,16 +2,17 @@
|
||||
module Data.RandomWalkSimilarity.Spec where
|
||||
|
||||
import Category
|
||||
import Control.Comonad.Trans.Cofree (headF)
|
||||
import Control.Monad.Free (wrap)
|
||||
import Data.Array.IArray
|
||||
import Data.Bifunctor
|
||||
import Data.Functor.Listable
|
||||
import RWS
|
||||
import Data.Record
|
||||
import Data.These
|
||||
import Diff
|
||||
import Info
|
||||
import Patch
|
||||
import Prologue
|
||||
import RWS
|
||||
import Syntax
|
||||
import Term
|
||||
import Test.Hspec
|
||||
|
@ -1,11 +1,16 @@
|
||||
{-# LANGUAGE DataKinds #-}
|
||||
module Data.Syntax.Assignment.Spec where
|
||||
|
||||
import Data.ByteString.Char8 as B (words, length)
|
||||
import Control.Comonad.Cofree (Cofree(..))
|
||||
import Control.Comonad.Trans.Cofree (headF)
|
||||
import Data.Bifunctor (first)
|
||||
import Data.ByteString.Char8 as B (ByteString, length, words)
|
||||
import Data.Semigroup ((<>))
|
||||
import Data.Source
|
||||
import Data.Syntax.Assignment
|
||||
import GHC.Stack (getCallStack)
|
||||
import Info
|
||||
import Prologue hiding (State)
|
||||
import Prelude hiding (words)
|
||||
import Test.Hspec
|
||||
import Text.Parser.TreeSitter.Language (Symbol(..), SymbolType(..))
|
||||
|
||||
@ -276,7 +281,7 @@ spec = do
|
||||
Left [ "symbol", "red" ]
|
||||
|
||||
node :: symbol -> Int -> Int -> [AST symbol] -> AST symbol
|
||||
node symbol start end children = cofree $ Node symbol (Range start end) (Info.Span (Info.Pos 1 (succ start)) (Info.Pos 1 (succ end))) :< children
|
||||
node symbol start end children = Node symbol (Range start end) (Info.Span (Info.Pos 1 (succ start)) (Info.Pos 1 (succ end))) :< children
|
||||
|
||||
data Grammar = Palette | Red | Green | Blue | Magenta
|
||||
deriving (Enum, Eq, Show)
|
||||
@ -285,7 +290,7 @@ instance Symbol Grammar where
|
||||
symbolType Magenta = Anonymous
|
||||
symbolType _ = Regular
|
||||
|
||||
data Out = Out ByteString | OutError ByteString
|
||||
data Out = Out B.ByteString | OutError B.ByteString
|
||||
deriving (Eq, Show)
|
||||
|
||||
red :: HasCallStack => Assignment (AST Grammar) Grammar Out
|
||||
|
@ -2,13 +2,13 @@
|
||||
module DiffSpec where
|
||||
|
||||
import Category
|
||||
import Control.Comonad.Trans.Cofree (headF)
|
||||
import Data.Functor.Both
|
||||
import Data.Functor.Listable
|
||||
import RWS
|
||||
import Diff
|
||||
import Info
|
||||
import Interpreter
|
||||
import Prologue
|
||||
import SpecHelpers
|
||||
import Term
|
||||
import Test.Hspec
|
||||
|
@ -1,13 +1,15 @@
|
||||
{-# LANGUAGE DataKinds, GeneralizedNewtypeDeriving, OverloadedStrings #-}
|
||||
module IntegrationSpec where
|
||||
|
||||
import Control.DeepSeq
|
||||
import qualified Data.ByteString as B
|
||||
import Data.Foldable (find, traverse_)
|
||||
import Data.Functor.Both
|
||||
import Data.List (union, concat, transpose)
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Semigroup ((<>))
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.ByteString as B
|
||||
import Data.Text.Encoding (decodeUtf8)
|
||||
import GHC.Show (Show(..))
|
||||
import Prologue hiding (fst, snd, readFile)
|
||||
import System.FilePath
|
||||
import System.FilePath.Glob
|
||||
import SpecHelpers
|
||||
@ -87,7 +89,7 @@ examples directory = do
|
||||
|
||||
lookupNormalized :: FilePath -> [FilePath] -> FilePath
|
||||
lookupNormalized name xs = fromMaybe
|
||||
(panic ("cannot find " <> T.pack name <> " make sure .A, .B and exist." :: Text))
|
||||
(error ("cannot find " <> name <> " make sure .A, .B and exist."))
|
||||
(lookupNormalized' name xs)
|
||||
|
||||
lookupNormalized' :: FilePath -> [FilePath] -> Maybe FilePath
|
||||
@ -112,17 +114,17 @@ testDiff paths expectedOutput = do
|
||||
expected <- verbatim <$> B.readFile expectedOutput
|
||||
actual `shouldBe` expected
|
||||
|
||||
stripWhitespace :: ByteString -> ByteString
|
||||
stripWhitespace :: B.ByteString -> B.ByteString
|
||||
stripWhitespace = B.foldl' go B.empty
|
||||
where go acc x | x `B.elem` " \t\n" = acc
|
||||
| otherwise = B.snoc acc x
|
||||
|
||||
-- | A wrapper around `ByteString` with a more readable `Show` instance.
|
||||
newtype Verbatim = Verbatim ByteString
|
||||
-- | A wrapper around 'B.ByteString' with a more readable 'Show' instance.
|
||||
newtype Verbatim = Verbatim B.ByteString
|
||||
deriving (Eq, NFData)
|
||||
|
||||
instance Show Verbatim where
|
||||
showsPrec _ (Verbatim byteString) = ('\n':) . (T.unpack (decodeUtf8 byteString) ++)
|
||||
|
||||
verbatim :: ByteString -> Verbatim
|
||||
verbatim :: B.ByteString -> Verbatim
|
||||
verbatim = Verbatim . stripWhitespace
|
||||
|
@ -2,6 +2,7 @@
|
||||
module InterpreterSpec where
|
||||
|
||||
import Category
|
||||
import Control.Monad.Free (wrap)
|
||||
import Data.Functor.Both
|
||||
import Data.Functor.Foldable hiding (Nil)
|
||||
import Data.Functor.Listable
|
||||
@ -9,7 +10,6 @@ import Data.Record
|
||||
import Diff
|
||||
import Interpreter
|
||||
import Patch
|
||||
import Prologue
|
||||
import Syntax
|
||||
import Term
|
||||
import Test.Hspec (Spec, describe, it, parallel)
|
||||
|
@ -1,6 +1,7 @@
|
||||
module PatchOutputSpec where
|
||||
|
||||
import Prologue
|
||||
import Control.Comonad.Trans.Cofree (CofreeF(..))
|
||||
import Control.Monad.Free (wrap)
|
||||
import Data.Blob
|
||||
import Data.Functor.Both
|
||||
import Data.Range
|
||||
@ -14,4 +15,4 @@ spec :: Spec
|
||||
spec = parallel $ do
|
||||
describe "hunks" $ do
|
||||
it "empty diffs have empty hunks" $
|
||||
hunks (wrap $ pure (Range 0 0 :. Nil) :< Leaf ("" :: Text)) (both (Blob mempty "abcde" "path2.txt" (Just defaultPlainBlob) Nothing) (Blob mempty "xyz" "path2.txt" (Just defaultPlainBlob) Nothing)) `shouldBe` [Hunk {offset = pure 0, changes = [], trailingContext = []}]
|
||||
hunks (wrap $ pure (Range 0 0 :. Nil) :< Leaf "") (both (Blob mempty "abcde" "path2.txt" (Just defaultPlainBlob) Nothing) (Blob mempty "xyz" "path2.txt" (Just defaultPlainBlob) Nothing)) `shouldBe` [Hunk {offset = pure 0, changes = [], trailingContext = []}]
|
||||
|
@ -1,7 +1,6 @@
|
||||
module SES.Myers.Spec where
|
||||
|
||||
import Data.These
|
||||
import Prologue
|
||||
import SES.Myers
|
||||
import Test.Hspec
|
||||
import Test.Hspec.LeanCheck
|
||||
|
@ -1,12 +1,16 @@
|
||||
{-# LANGUAGE DuplicateRecordFields #-}
|
||||
module SemanticCmdLineSpec where
|
||||
|
||||
import Control.Monad (when)
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.Foldable (for_)
|
||||
import Data.Functor.Both
|
||||
import Data.Semigroup ((<>))
|
||||
import Language
|
||||
import Prologue
|
||||
import Renderer
|
||||
import Semantic.Task
|
||||
import SemanticCmdLine
|
||||
import System.IO (Handle)
|
||||
import Test.Hspec hiding (shouldBe, shouldNotBe, shouldThrow, errorCall)
|
||||
import Test.Hspec.Expectations.Pretty
|
||||
|
||||
|
@ -1,10 +1,11 @@
|
||||
module SemanticSpec where
|
||||
|
||||
import Control.Comonad.Cofree (Cofree(..))
|
||||
import Data.Blob
|
||||
import Data.Functor (void)
|
||||
import Data.Functor.Both as Both
|
||||
import Language
|
||||
import Patch
|
||||
import Prologue
|
||||
import Renderer
|
||||
import Semantic
|
||||
import Semantic.Task
|
||||
@ -17,11 +18,11 @@ spec = parallel $ do
|
||||
describe "parseBlob" $ do
|
||||
it "parses in the specified language" $ do
|
||||
Just term <- runTask $ parseBlob IdentityTermRenderer methodsBlob
|
||||
void term `shouldBe` cofree (() :< Indexed [ cofree (() :< Method [] (cofree (() :< Leaf "foo")) Nothing [] []) ])
|
||||
void term `shouldBe` (() :< Indexed [ () :< Method [] (() :< Leaf "foo") Nothing [] [] ])
|
||||
|
||||
it "parses line by line if not given a language" $ do
|
||||
Just term <- runTask $ parseBlob IdentityTermRenderer methodsBlob { blobLanguage = Nothing }
|
||||
void term `shouldBe` cofree (() :< Indexed [ cofree (() :< Leaf "def foo\n"), cofree (() :< Leaf "end\n"), cofree (() :< Leaf "") ])
|
||||
void term `shouldBe` (() :< Indexed [ () :< Leaf "def foo\n", () :< Leaf "end\n", () :< Leaf "" ])
|
||||
|
||||
it "renders with the specified renderer" $ do
|
||||
output <- runTask $ parseBlob SExpressionTermRenderer methodsBlob
|
||||
@ -29,11 +30,11 @@ spec = parallel $ do
|
||||
|
||||
describe "diffTermPair" $ do
|
||||
it "produces an Insert when the first blob is missing" $ do
|
||||
result <- runTask (diffTermPair (both (emptyBlob "/foo") (sourceBlob "/foo" Nothing "")) (runBothWith replacing) (pure (cofree (() :< []))))
|
||||
result <- runTask (diffTermPair (both (emptyBlob "/foo") (sourceBlob "/foo" Nothing "")) (runBothWith replacing) (pure (() :< [])))
|
||||
(() <$) <$> result `shouldBe` pure (Insert ())
|
||||
|
||||
it "produces a Delete when the second blob is missing" $ do
|
||||
result <- runTask (diffTermPair (both (sourceBlob "/foo" Nothing "") (emptyBlob "/foo")) (runBothWith replacing) (pure (cofree (() :< []))))
|
||||
result <- runTask (diffTermPair (both (sourceBlob "/foo" Nothing "") (emptyBlob "/foo")) (runBothWith replacing) (pure (() :< [])))
|
||||
(() <$) <$> result `shouldBe` pure (Delete ())
|
||||
|
||||
where
|
||||
|
@ -1,10 +1,11 @@
|
||||
module SourceSpec where
|
||||
|
||||
import Data.Char (chr)
|
||||
import Data.Range
|
||||
import Data.Semigroup
|
||||
import Data.Source
|
||||
import Data.Span
|
||||
import qualified Data.Text as Text
|
||||
import Prologue hiding (list)
|
||||
import Test.Hspec
|
||||
import Test.Hspec.LeanCheck
|
||||
import Test.LeanCheck
|
||||
|
@ -1,6 +1,5 @@
|
||||
module Main where
|
||||
|
||||
import Prologue
|
||||
import qualified AlignmentSpec
|
||||
import qualified CommandSpec
|
||||
import qualified Data.Mergeable.Spec
|
||||
|
@ -7,15 +7,19 @@ module SpecHelpers
|
||||
, unListableDiff
|
||||
) where
|
||||
|
||||
import Control.Exception
|
||||
import Control.Monad.Free (Free, hoistFree)
|
||||
import Data.Bifunctor (first)
|
||||
import Data.Blob
|
||||
import qualified Data.ByteString as B
|
||||
import Data.Functor.Both
|
||||
import Data.Functor.Listable
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Source
|
||||
import Diff
|
||||
import Language
|
||||
import Patch
|
||||
import Prologue hiding (readFile)
|
||||
import Prelude hiding (readFile)
|
||||
import Renderer
|
||||
import Semantic
|
||||
import Semantic.Task
|
||||
@ -23,13 +27,13 @@ import System.FilePath
|
||||
import Term
|
||||
|
||||
-- | Returns an s-expression formatted diff for the specified FilePath pair.
|
||||
diffFilePaths :: Both FilePath -> IO ByteString
|
||||
diffFilePaths :: Both FilePath -> IO B.ByteString
|
||||
diffFilePaths paths = do
|
||||
blobs <- traverse readFile paths
|
||||
runTask (diffBlobPair SExpressionDiffRenderer blobs)
|
||||
|
||||
-- | Returns an s-expression parse tree for the specified FilePath.
|
||||
parseFilePath :: FilePath -> IO ByteString
|
||||
parseFilePath :: FilePath -> IO B.ByteString
|
||||
parseFilePath path = do
|
||||
blob <- readFile path
|
||||
runTask (parseBlob SExpressionTermRenderer blob)
|
||||
@ -46,7 +50,7 @@ readFile path = do
|
||||
|
||||
-- | Returns a Maybe Language based on the FilePath's extension.
|
||||
languageForFilePath :: FilePath -> Maybe Language
|
||||
languageForFilePath = languageForType . toS . takeExtension
|
||||
languageForFilePath = languageForType . takeExtension
|
||||
|
||||
-- | Extract a 'Diff' from a 'ListableF' enumerated by a property test.
|
||||
unListableDiff :: Functor f => ListableF (Free (TermF f (ListableF (Join (,)) annotation))) (Patch (ListableF (Term f) annotation)) -> Diff f annotation
|
||||
|
@ -2,14 +2,23 @@
|
||||
{-# LANGUAGE DataKinds, TypeOperators #-}
|
||||
module TOCSpec where
|
||||
|
||||
import Data.Aeson
|
||||
import Category as C
|
||||
import Control.Comonad (extract)
|
||||
import Control.Comonad.Trans.Cofree (headF)
|
||||
import Control.Monad.Free (wrap)
|
||||
import Data.Aeson
|
||||
import Data.Blob
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.Function (on)
|
||||
import Data.Functor.Both
|
||||
import Data.Functor.Listable
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Monoid (Last(..))
|
||||
import Data.Output
|
||||
import Data.Record
|
||||
import Data.Semigroup ((<>))
|
||||
import Data.Source
|
||||
import Data.Text (Text)
|
||||
import Data.Text.Listable
|
||||
import Data.These
|
||||
import Diff
|
||||
@ -17,7 +26,7 @@ import Info
|
||||
import Interpreter
|
||||
import Language
|
||||
import Patch
|
||||
import Prologue hiding (fst, snd, readFile)
|
||||
import Prelude hiding (readFile)
|
||||
import Renderer
|
||||
import Renderer.TOC
|
||||
import RWS
|
||||
@ -51,7 +60,7 @@ spec = parallel $ do
|
||||
prop "produces changed entries for relevant nodes containing irrelevant patches" $
|
||||
\ diff -> let diff' = fmap (1 <$) <$> mapAnnotations (const (0 :: Int)) (wrap (pure 0 :< Indexed [unListableDiff diff :: Diff Syntax Int])) in
|
||||
tableOfContentsBy (\ (n :< _) -> if n == 0 then Just n else Nothing) diff' `shouldBe`
|
||||
if Prologue.null diff' then [Unchanged 0]
|
||||
if null diff' then [Unchanged 0]
|
||||
else replicate (length diff') (Changed 0)
|
||||
|
||||
describe "diffTOC" $ do
|
||||
|
@ -3,7 +3,6 @@ module TermSpec where
|
||||
|
||||
import Category
|
||||
import Data.Functor.Listable
|
||||
import Prologue
|
||||
import Term
|
||||
import Test.Hspec (Spec, describe, parallel)
|
||||
import Test.Hspec.Expectations.Pretty
|
||||
|
@ -6,9 +6,10 @@ module Test.Hspec.LeanCheck
|
||||
|
||||
import Control.Exception
|
||||
import Data.Bifunctor (first)
|
||||
import Data.String (String)
|
||||
import Data.List (intercalate)
|
||||
import Data.Maybe (listToMaybe)
|
||||
import Data.Typeable
|
||||
import GHC.Show as Show (showsPrec)
|
||||
import Prologue
|
||||
import Test.Hspec
|
||||
import Test.Hspec.Core.Spec as Hspec
|
||||
import qualified Test.HUnit.Lang as HUnit
|
||||
|
Loading…
Reference in New Issue
Block a user