edits 0.1.0.1 → 0.1.1.0
raw patch · 5 files changed
+103/−61 lines, 5 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Data.Text.Difference: taggedDisplayEditOperation :: EditOperation Char -> Text
- Data.Text.Edits: taggedDisplayEditOperation :: EditOperation Char -> Text
+ Data.Text.Color: Black :: Color
+ Data.Text.Color: Blue :: Color
+ Data.Text.Color: Cyan :: Color
+ Data.Text.Color: Green :: Color
+ Data.Text.Color: Magenta :: Color
+ Data.Text.Color: Red :: Color
+ Data.Text.Color: White :: Color
+ Data.Text.Color: Yellow :: Color
+ Data.Text.Color: colorAs :: Color -> Text -> Text
+ Data.Text.Color: data Color
+ Data.Text.Color: instance GHC.Classes.Eq Data.Text.Color.Color
+ Data.Text.Color: instance GHC.Show.Show Data.Text.Color.Color
+ Data.Text.Difference: coloredDisplayEditOperation :: EditOperation Char -> Text
+ Data.Text.Edits: Black :: Color
+ Data.Text.Edits: Blue :: Color
+ Data.Text.Edits: Cyan :: Color
+ Data.Text.Edits: Green :: Color
+ Data.Text.Edits: Magenta :: Color
+ Data.Text.Edits: Red :: Color
+ Data.Text.Edits: White :: Color
+ Data.Text.Edits: Yellow :: Color
+ Data.Text.Edits: colorAs :: Color -> Text -> Text
+ Data.Text.Edits: coloredDisplayEditOperation :: EditOperation Char -> Text
+ Data.Text.Edits: data Color
+ Data.Text.Edits: levenshteinOperations :: Text -> Text -> [EditOperation Char]
+ Data.Text.Edits: showDistanceColored :: Text -> Text -> Text
+ Data.Text.Edits: showDistanceWith :: SplitSize -> DisplayOptions -> Text -> Text -> Text
- Data.Text.Edits: showDistance :: Text -> Text -> (Text, Text)
+ Data.Text.Edits: showDistance :: Text -> Text -> Text
Files
- edits.cabal +2/−1
- src/Data/Text/Color.hs +23/−0
- src/Data/Text/Difference.hs +12/−11
- src/Data/Text/Edits.hs +56/−42
- test/Test/Data/Text/EditsSpec.hs +10/−7
edits.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: edits-version: 0.1.0.1+version: 0.1.1.0 synopsis: show the differences between 2 pieces of Text using the Levenshtein distance description: This library computes the minimum number of edit operations 'insert', 'delete', 'substitute' which are necessary to transform a piece of text into another. It then displays the parts which are different in brackets. category: Data@@ -20,6 +20,7 @@ library exposed-modules:+ Data.Text.Color Data.Text.Costs Data.Text.Difference Data.Text.EditMatrix
+ src/Data/Text/Color.hs view
@@ -0,0 +1,23 @@+{-|+ This module can color some Text to be displayed in a terminal+-}+module Data.Text.Color where++import Protolude++-- | Available ASCII colors+data Color =+ Black | Red | Green | Yellow | Blue | Magenta | Cyan | White deriving (Eq, Show)++-- | Surround a piece of text with ASCII control characters to color it+colorAs :: Color -> Text -> Text+colorAs c t = "\x1b[" <> code c <> "m" <> t <> "\x1b[0m"+ where+ code Black = "30"+ code Red = "31"+ code Green = "32"+ code Yellow = "33"+ code Blue = "34"+ code Magenta = "35"+ code Cyan = "36"+ code White = "37"
src/Data/Text/Difference.hs view
@@ -4,6 +4,7 @@ module Data.Text.Difference where import Data.Text qualified as T+import Data.Text.Color import Data.Text.EditOperation import Data.Text.Shorten import Data.Text.Token@@ -40,19 +41,19 @@ defaultDisplayOptions :: DisplayOptions defaultDisplayOptions = DisplayOptions bracketsSeparators (ShortenOptions 20 "...") defaultDisplayEditOperations --- | Default display for edit operations+-- | Display an edit operation by prepending a symbol showing which operation is used defaultDisplayEditOperations :: EditOperation Char -> Text-defaultDisplayEditOperations (Insert c) = T.singleton c-defaultDisplayEditOperations (Delete c) = T.singleton c-defaultDisplayEditOperations (Substitute c _) = T.singleton c+defaultDisplayEditOperations (Insert c) = "+" <> T.singleton c+defaultDisplayEditOperations (Delete c) = "-" <> T.singleton c+defaultDisplayEditOperations (Substitute c1 c2) = "~" <> T.singleton c1 <> "/" <> T.singleton c2 defaultDisplayEditOperations (Keep c) = T.singleton c --- | Display an edit operation by prepending a symbol showing which operation is used-taggedDisplayEditOperation :: EditOperation Char -> Text-taggedDisplayEditOperation (Insert c) = "+" <> T.singleton c-taggedDisplayEditOperation (Delete c) = "-" <> T.singleton c-taggedDisplayEditOperation (Substitute c1 c2) = "~" <> T.singleton c1 <> "/" <> T.singleton c2-taggedDisplayEditOperation (Keep c) = T.singleton c+-- | Display an edit operation using ascii colors: green = added, red = removed, blue = substituted+coloredDisplayEditOperation :: EditOperation Char -> Text+coloredDisplayEditOperation (Insert c) = colorAs Green (T.singleton c)+coloredDisplayEditOperation (Delete c) = colorAs Red (T.singleton c)+coloredDisplayEditOperation (Substitute c _) = colorAs Cyan (T.singleton c)+coloredDisplayEditOperation (Keep c) = T.singleton c -- | Show the differences by enclosing them in separators -- Additionally shorten the text outside the separators if it is too long@@ -65,7 +66,7 @@ -- this allows us to open a 'start' delimiter -- then when we go back to keeping the same character, we can close with an 'end' delimiter case operation of- Insert {} -> (True, res <> [Delimiter start | not different])+ Insert {} -> (True, res <> [Delimiter start | not different] <> [Kept $ displayEditOperation operation]) Delete {} -> (True, res <> [Delimiter start | not different] <> [Kept $ displayEditOperation operation]) Substitute {} -> (True, res <> [Delimiter start | not different] <> [Kept $ displayEditOperation operation]) Keep {} -> (False, res <> [Delimiter end | different] <> [Kept $ displayEditOperation operation])
src/Data/Text/Edits.hs view
@@ -1,50 +1,53 @@ {-# LANGUAGE OverloadedLists #-} -{- |- This module provides a 'showDistance' function showing the differences between 2 pieces of text- using the Levenshtein distance. That distance is defined as the minimum number of edits: insertions, deletions, substitutions- to go from one text to another.-- Several options are available to customize this processing:- - split size: texts are broken into new lines first. Then if the texts are too large there are split into smaller pieces- in order to compute their difference. This is done in order to reduce the size of the edit matrix which is used to compute all the edit costs- the default is 200-- - separators: opening and closing pieces of text (brackets by default) used to highlight a difference-- - shorten size: there is the possibly to display mostly the differences with a bit of context around if the input text is too large.- The text gets elided around separators if it gets greater than the shorten size (the default is 20)-- - shorten text: the text to use when eliding characters in the original text (the default is "...")-- - display edit operations: edit operations, insert/delete/substitute/keep can be annotated if necessary--Here are some examples:--@-import Data.Text.Edits---- "between the e and the n the letter i was added"-showDistance "kitten" "kittein" === ("kitte[]n", "kitte[i]n")---- "at the end of the text 3 letters have been deleted"-showDistance "kitten" "kit" === ("kit[ten]", "kit[]")---- "between the t and the n 2 letters have been modified"-showDistance "kitten" "kitsin" === ("kit[te]n", "kit[si]n" )-@---}+-- |+-- This module provides a 'showDistance' function showing the differences between 2 pieces of text+-- using the Levenshtein distance. That distance is defined as the minimum number of edits: insertions, deletions, substitutions+-- to go from one text to another.+--+-- Several options are available to customize this processing:+-- - split size: texts are broken into new lines first. Then if the texts are too large there are split into smaller pieces+-- in order to compute their difference. This is done in order to reduce the size of the edit matrix which is used to compute all the edit costs+-- the default is 200+--+-- - separators: opening and closing pieces of text (brackets by default) used to highlight a difference+--+-- - shorten size: there is the possibly to display mostly the differences with a bit of context around if the input text is too large.+-- The text gets elided around separators if it gets greater than the shorten size (the default is 20)+--+-- - shorten text: the text to use when eliding characters in the original text (the default is "...")+--+-- - display edit operations: edit operations, insert/delete/substitute/keep can be annotated if necessary+--+-- Here are some examples:+--+-- @+-- import Data.Text.Edits+--+-- -- "between the e and the n the letter i was added"+-- showDistance "kitten" "kittein" === "kitte[+i]n"+--+-- -- "at the end of the text 3 letters have been deleted"+-- showDistance "kitten" "kit" === "kit[-t-e-n]"+--+-- -- "between the t and the n 2 letters have been modified"+-- showDistance "kitten" "kitsin" === "kit[~t/s~e/i]"+-- @ module Data.Text.Edits ( SplitSize (..), ShortenOptions (..), Separators (..), DisplayOptions (..), EditOperation (..),+ Color (..),+ colorAs, showDistance,+ showDistanceColored,+ showDistanceWith,+ levenshteinOperations, defaultDisplayOptions, defaultDisplayEditOperations,- taggedDisplayEditOperation,+ coloredDisplayEditOperation, defaultSplitSize, parensSeparators, bracketsSeparators,@@ -53,6 +56,7 @@ where import Data.Text qualified as T+import Data.Text.Color import Data.Text.Costs import Data.Text.Difference import Data.Text.EditMatrix@@ -69,16 +73,26 @@ defaultSplitSize = SplitSize 200 -- | Show the distance between 2 pieces of text-showDistance :: Text -> Text -> (Text, Text)+showDistance :: Text -> Text -> Text showDistance = showDistanceWith defaultSplitSize defaultDisplayOptions +-- | Show the distance between 2 pieces of text with colors instead of symbols+showDistanceColored :: Text -> Text -> Text+showDistanceColored = showDistanceWith defaultSplitSize defaultDisplayOptions {_displayEditOperation = coloredDisplayEditOperation}+ -- | Show the distance between 2 pieces of text and specify splitting / display options-showDistanceWith :: SplitSize -> DisplayOptions -> Text -> Text -> (Text, Text)+showDistanceWith :: SplitSize -> DisplayOptions -> Text -> Text -> Text showDistanceWith splitSize displayOptions ts1 ts2 =- foldSplitTexts splitSize ts1 ts2 ([], []) $ \ts (line1, line2) -> do- let matrix = createEditMatrix textLevenshteinCosts (toS line1) (toS line2)- let operations = toList $ makeEditOperations (V.fromList . toS $ line1) (V.fromList . toS $ line2) matrix- ts <> (displayDiffs displayOptions operations, displayDiffs displayOptions $ inverse <$> operations)+ foldSplitTexts splitSize ts1 ts2 [] $ \ts (line1, line2) -> do+ let operations = levenshteinOperations (toS line1) (toS line2)+ ts <> displayDiffs displayOptions operations++-- | Return the list of operations necessary to go from one piece of text to another+-- using the Levenshtein distance+levenshteinOperations :: Text -> Text -> [EditOperation Char]+levenshteinOperations t1 t2 = do+ let matrix = createEditMatrix textLevenshteinCosts (toS t1) (toS t2)+ toList $ makeEditOperations (V.fromList . toS $ t1) (V.fromList . toS $ t2) matrix -- | Split texts and apply the difference on each part foldSplitTexts :: SplitSize -> Text -> Text -> a -> (a -> (Text, Text) -> a) -> a
test/Test/Data/Text/EditsSpec.hs view
@@ -4,10 +4,13 @@ import Protolude import Test.Tasty.Hedgehogx -test_show_distance = test "show the distance between different strings" $ do- showDistance "kitte" "kittei" === ("kitte[]", "kitte[i]")- showDistance "kitten" "kittein" === ("kitte[]n", "kitte[i]n")- showDistance "kitten" "kit" === ("kit[ten]", "kit[]")- showDistance "kit" "kitten" === ("kit[]", "kit[ten]")- showDistance "kitten" "kitsin" === ("kit[te]n", "kit[si]n")- showDistance "kitte" "kitte" === ("kitte", "kitte")+test_show_distance = test "show the distance between different texts" $ do+ showDistance "kitte" "kittei" === "kitte[+i]"+ showDistance "kitten" "kittein" === "kitte[+i]n"+ showDistance "kitten" "kit" === "kit[-t-e-n]"+ showDistance "kit" "kitten" === "kit[+t+e+n]"+ showDistance "kitten" "kitsin" === "kit[~t/s~e/i]n"+ showDistance "kitte" "kitte" === "kitte"++test_edit_operations = test "list the edit operations as a difference between 2 texts" $ do+ levenshteinOperations "kitte" "kittei" === [Keep 'k', Keep 'i', Keep 't', Keep 't', Keep 'e', Insert 'i']