nix-diff 1.0.7 → 1.0.8
raw patch · 2 files changed
+97/−53 lines, 2 files
Files
- nix-diff.cabal +1/−1
- src/Main.hs +96/−52
nix-diff.cabal view
@@ -1,5 +1,5 @@ name: nix-diff-version: 1.0.7+version: 1.0.8 synopsis: Explain why two Nix derivations differ description: This package provides a @nix-diff@ executable which explains why two Nix derivations (i.e. @*.drv@ files)
src/Main.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE ApplicativeDo #-}+{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}@@ -24,7 +25,7 @@ import qualified Control.Monad.Reader import qualified Control.Monad.State-import qualified Data.Algorithm.Diff+import qualified Data.Algorithm.Diff as Diff import qualified Data.Attoparsec.Text import qualified Data.Map import qualified Data.Set@@ -57,15 +58,37 @@ "never" -> return Never _ -> fail "Invalid color" -data Options = Options { left :: FilePath, right :: FilePath, color :: Color }+parseLineOriented :: Parser Bool+parseLineOriented =+ Options.Applicative.switch+ ( Options.Applicative.long "line-oriented"+ <> Options.Applicative.help "Display textual differences on a per-line basis instead of a per-character basis"+ ) +parseEnvironment :: Parser Bool+parseEnvironment =+ Options.Applicative.switch+ ( Options.Applicative.long "environment"+ <> Options.Applicative.help "Force display of environment differences"+ )++data Options = Options+ { left :: FilePath+ , right :: FilePath+ , color :: Color+ , lineOriented :: Bool+ , environment :: Bool+ }+ parseOptions :: Parser Options parseOptions = do- left <- parseLeft- right <- parseRight- color <- parseColor+ left <- parseLeft+ right <- parseRight+ color <- parseColor+ lineOriented <- parseLineOriented+ environment <- parseEnvironment - return (Options { left, right, color })+ return (Options { left, right, color, lineOriented, environment }) where parseFilePath metavar = do Options.Applicative.strArgument@@ -84,8 +107,10 @@ ) data Context = Context- { tty :: TTY- , indent :: Natural+ { tty :: TTY+ , indent :: Natural+ , lineOriented :: Bool+ , environment :: Bool } newtype Status = Status { visited :: Set Diffed }@@ -180,9 +205,11 @@ red NotTTY text = text -- | Color text background red-redBackground :: TTY -> Text -> Text-redBackground IsTTY text = "\ESC[41m" <> text <> "\ESC[0m"-redBackground NotTTY text = "←" <> text <> "←"+redBackground :: Bool -> TTY -> Text -> Text+redBackground False IsTTY text = "\ESC[41m" <> text <> "\ESC[0m"+redBackground True IsTTY text = "\ESC[41m" <> text <> "\ESC[0m\n"+redBackground False NotTTY text = "←" <> text <> "←"+redBackground True NotTTY text = "- " <> text <> "\n" -- | Color text green green :: TTY -> Text -> Text@@ -190,14 +217,18 @@ green NotTTY text = text -- | Color text background green-greenBackground :: TTY -> Text -> Text-greenBackground IsTTY text = "\ESC[42m" <> text <> "\ESC[0m"-greenBackground NotTTY text = "→" <> text <> "→"+greenBackground :: Bool -> TTY -> Text -> Text+greenBackground False IsTTY text = "\ESC[42m" <> text <> "\ESC[0m"+greenBackground True IsTTY text = "\ESC[42m" <> text <> "\ESC[0m\n"+greenBackground False NotTTY text = "→" <> text <> "→"+greenBackground True NotTTY text = "+ " <> text <> "\n" -- | Color text grey-grey :: TTY -> Text -> Text-grey IsTTY text = "\ESC[1;2m" <> text <> "\ESC[0m"-grey NotTTY text = text+grey :: Bool -> TTY -> Text -> Text+grey False IsTTY text = "\ESC[1;2m" <> text <> "\ESC[0m"+grey True IsTTY text = "\ESC[1;2m" <> text <> "\ESC[0m\n"+grey False NotTTY text = text+grey True NotTTY text = " " <> text <> "\n" -- | Format the left half of a diff minus :: TTY -> Text -> Text@@ -273,6 +304,11 @@ then return () else diffOutput key leftOutput rightOutput +mapDiff :: (a -> b) -> Diff.Diff a -> Diff.Diff b+mapDiff f (Diff.First l) = Diff.First (f l)+mapDiff f (Diff.Second r) = Diff.Second (f r)+mapDiff f (Diff.Both l r) = Diff.Both (f l) (f r)+ -- | Diff two `Text` values diffText :: Text@@ -281,14 +317,22 @@ -- ^ Right value to compare -> Diff Text diffText left right = do- Context { indent, tty } <- ask+ Context { indent, lineOriented, tty } <- ask let n = fromIntegral indent let leftString = Data.Text.unpack left- rightString = Data.Text.unpack right+ let rightString = Data.Text.unpack right - let chunks = Data.Algorithm.Diff.getGroupedDiff leftString rightString+ let leftLines = Data.Text.lines left+ let rightLines = Data.Text.lines right + let chunks =+ if lineOriented+ then+ Diff.getDiff leftLines rightLines+ else+ fmap (mapDiff Data.Text.pack) (Diff.getGroupedDiff leftString rightString)+ let prefix = Data.Text.replicate n " " let format text =@@ -301,12 +345,12 @@ where indentLine line = prefix <> " " <> line - let renderChunk (Data.Algorithm.Diff.First l) =- redBackground tty (Data.Text.pack l)- renderChunk (Data.Algorithm.Diff.Second r) =- greenBackground tty (Data.Text.pack r)- renderChunk (Data.Algorithm.Diff.Both l _) =- grey tty (Data.Text.pack l)+ let renderChunk (Diff.First l) =+ redBackground lineOriented tty l+ renderChunk (Diff.Second r) =+ greenBackground lineOriented tty r+ renderChunk (Diff.Both l _) =+ grey lineOriented tty l return (format (Data.Text.concat (fmap renderChunk chunks))) @@ -352,25 +396,24 @@ text <- indented 4 (diffText leftValue rightValue) echo (" " <> key <> "=" <> text) --- | Diff two environments+-- | Diff input sources diffSrcs :: Set FilePath- -- ^ Left derivation outputs+ -- ^ Left input sources -> Set FilePath- -- ^ Right derivation outputs- -> Diff Bool+ -- ^ Right inputSources+ -> Diff () diffSrcs leftSrcs rightSrcs = do let leftExtraSrcs = Data.Set.difference leftSrcs rightSrcs let rightExtraSrcs = Data.Set.difference rightSrcs leftSrcs if Data.Set.null leftExtraSrcs && Data.Set.null rightExtraSrcs- then return True+ then return () else do echo (explain "The set of input sources do not match:") diffWith leftExtraSrcs rightExtraSrcs $ \(sign, extraSrcs) -> do forM_ extraSrcs $ \extraSrc -> do echo (" " <> sign (pathToText extraSrc))- return False diffPlatform :: Text -> Text -> Diff () diffPlatform leftPlatform rightPlatform = do@@ -399,12 +442,12 @@ echo (explain "The arguments do not match") let leftList = Data.Vector.toList leftArgs let rightList = Data.Vector.toList rightArgs- let diffs = Data.Algorithm.Diff.getDiff leftList rightList- let renderDiff (Data.Algorithm.Diff.First arg) =+ let diffs = Diff.getDiff leftList rightList+ let renderDiff (Diff.First arg) = echo (" " <> minus tty arg)- renderDiff (Data.Algorithm.Diff.Second arg) =+ renderDiff (Diff.Second arg) = echo (" " <> plus tty arg)- renderDiff (Data.Algorithm.Diff.Both arg _) =+ renderDiff (Diff.Both arg _) = echo (" " <> explain arg) mapM_ renderDiff diffs @@ -448,6 +491,10 @@ let rightArgs = Nix.Derivation.args rightDerivation diffArgs leftArgs rightArgs + let leftSrcs = Nix.Derivation.inputSrcs leftDerivation+ let rightSrcs = Nix.Derivation.inputSrcs rightDerivation+ diffSrcs leftSrcs rightSrcs+ let leftInputs = groupByName (Nix.Derivation.inputDrvs leftDerivation) let rightInputs = groupByName (Nix.Derivation.inputDrvs rightDerivation) @@ -482,27 +529,24 @@ forM_ (Data.Map.toList extraPaths) $ \(extraPath, outputs) -> do echo (" " <> sign (pathToText extraPath <> renderOutputs outputs)) return False- if or descended- then return ()- else do- let leftSrcs = Nix.Derivation.inputSrcs leftDerivation- let rightSrcs = Nix.Derivation.inputSrcs rightDerivation- differed <- diffSrcs leftSrcs rightSrcs - if not differed- then return ()- else do- let leftEnv = Nix.Derivation.env leftDerivation- let rightEnv = Nix.Derivation.env rightDerivation- let leftOutNames = Data.Map.keysSet leftOuts- let rightOutNames = Data.Map.keysSet rightOuts- diffEnv leftOutNames rightOutNames leftEnv rightEnv+ Context { environment } <- ask + if or descended && not environment+ then do+ echo (explain "Skipping environment comparison")+ else do+ let leftEnv = Nix.Derivation.env leftDerivation+ let rightEnv = Nix.Derivation.env rightDerivation+ let leftOutNames = Data.Map.keysSet leftOuts+ let rightOutNames = Data.Map.keysSet rightOuts+ diffEnv leftOutNames rightOutNames leftEnv rightEnv+ main :: IO () main = do GHC.IO.Encoding.setLocaleEncoding GHC.IO.Encoding.utf8 - Options { left, right, color } <- Options.Applicative.execParser parserInfo+ Options { left, right, color, lineOriented, environment } <- Options.Applicative.execParser parserInfo tty <- case color of Never -> do@@ -514,7 +558,7 @@ return (if b then IsTTY else NotTTY) let indent = 0- let context = Context { tty, indent }+ let context = Context { tty, indent, lineOriented, environment } let status = Status Data.Set.empty let action = diff True left (Data.Set.singleton "out") right (Data.Set.singleton "out") Control.Monad.State.evalStateT (Control.Monad.Reader.runReaderT (unDiff action) context) status