2015-11-18 01:44:16 +03:00
|
|
|
module Main where
|
|
|
|
|
2015-11-27 20:22:05 +03:00
|
|
|
import Categorizable
|
2015-11-18 22:45:23 +03:00
|
|
|
import Diff
|
2015-11-27 20:22:05 +03:00
|
|
|
import Interpreter
|
2015-12-09 17:50:55 +03:00
|
|
|
import qualified Parser as P
|
2015-11-18 22:45:23 +03:00
|
|
|
import Syntax
|
2015-12-03 05:40:34 +03:00
|
|
|
import Range
|
2015-12-01 19:33:16 +03:00
|
|
|
import Split
|
2015-11-27 20:41:38 +03:00
|
|
|
import Term
|
2015-12-09 17:58:15 +03:00
|
|
|
import TreeSitter
|
2015-11-27 20:42:00 +03:00
|
|
|
import Unified
|
2015-11-18 22:45:23 +03:00
|
|
|
import Control.Comonad.Cofree
|
2015-12-14 20:08:33 +03:00
|
|
|
import qualified Data.Char as Char
|
2015-11-27 17:19:09 +03:00
|
|
|
import qualified Data.Map as Map
|
2015-12-07 22:34:58 +03:00
|
|
|
import qualified Data.ByteString.Char8 as B1
|
|
|
|
import qualified Data.ByteString.Lazy as B2
|
2015-12-01 19:33:10 +03:00
|
|
|
import Data.Set hiding (split)
|
2015-12-01 19:15:13 +03:00
|
|
|
import Options.Applicative
|
2015-12-03 23:10:49 +03:00
|
|
|
import System.FilePath
|
2015-11-18 22:45:23 +03:00
|
|
|
|
2015-12-09 18:11:30 +03:00
|
|
|
import Foreign.Ptr
|
|
|
|
|
2015-12-01 19:08:04 +03:00
|
|
|
data Output = Unified | Split
|
|
|
|
|
2015-12-01 19:15:05 +03:00
|
|
|
data Argument = Argument { output :: Output, sourceA :: FilePath, sourceB :: FilePath }
|
2015-12-01 19:08:04 +03:00
|
|
|
|
2015-12-01 19:15:13 +03:00
|
|
|
arguments :: Parser Argument
|
|
|
|
arguments = Argument
|
2015-12-01 19:32:52 +03:00
|
|
|
<$> (flag Split Unified (long "unified" <> help "output a unified diff")
|
|
|
|
<|> flag' Split (long "split" <> help "output a split diff"))
|
2015-12-01 19:15:13 +03:00
|
|
|
<*> argument str (metavar "FILE a")
|
|
|
|
<*> argument str (metavar "FILE b")
|
|
|
|
|
2015-11-18 01:44:16 +03:00
|
|
|
main :: IO ()
|
2015-11-20 19:36:54 +03:00
|
|
|
main = do
|
2015-12-01 19:19:40 +03:00
|
|
|
arguments <- execParser opts
|
2015-12-07 22:23:32 +03:00
|
|
|
let (sourceAPath, sourceBPath) = (sourceA arguments, sourceB arguments)
|
|
|
|
aContents <- readFile sourceAPath
|
|
|
|
bContents <- readFile sourceBPath
|
2015-12-09 18:36:37 +03:00
|
|
|
(aTerm, bTerm) <- let parse = (parserForType . takeExtension) sourceAPath in do
|
|
|
|
aTerm <- parse aContents
|
|
|
|
bTerm <- parse bContents
|
2015-12-14 20:15:28 +03:00
|
|
|
return (replaceLeavesWithWordBranches aContents aTerm, replaceLeavesWithWordBranches bContents bTerm)
|
2015-12-07 22:34:58 +03:00
|
|
|
let diff = interpret comparable aTerm bTerm in
|
|
|
|
case output arguments of
|
|
|
|
Unified -> do
|
|
|
|
output <- unified diff aContents bContents
|
|
|
|
B1.putStr output
|
|
|
|
Split -> do
|
|
|
|
output <- split diff aContents bContents
|
|
|
|
B2.putStr output
|
|
|
|
where
|
2015-12-01 19:19:40 +03:00
|
|
|
opts = info (helper <*> arguments)
|
|
|
|
(fullDesc <> progDesc "Diff some things" <> header "semantic-diff - diff semantically")
|
2015-11-25 00:51:53 +03:00
|
|
|
|
2015-12-09 18:36:37 +03:00
|
|
|
parserForType :: String -> P.Parser
|
|
|
|
parserForType mediaType = maybe P.lineByLineParser parseTreeSitterFile $ case mediaType of
|
2015-12-05 00:25:01 +03:00
|
|
|
".h" -> Just ts_language_c
|
|
|
|
".c" -> Just ts_language_c
|
|
|
|
".js" -> Just ts_language_javascript
|
2015-12-03 23:10:49 +03:00
|
|
|
_ -> Nothing
|
2015-12-14 20:08:33 +03:00
|
|
|
|
2015-12-14 20:15:28 +03:00
|
|
|
replaceLeavesWithWordBranches :: String -> Term String Info -> Term String Info
|
2015-12-14 20:37:07 +03:00
|
|
|
replaceLeavesWithWordBranches source term = replaceIn source 0 term
|
2015-12-14 20:09:21 +03:00
|
|
|
where
|
2015-12-14 20:37:23 +03:00
|
|
|
replaceIn source startIndex (info@(Info range lineRange categories) :< syntax) | range <- offsetRange (negate startIndex) range = info :< case syntax of
|
2015-12-14 20:39:09 +03:00
|
|
|
Leaf _ | ranges <- rangesOfWordsFrom (start range) (substring range source), length ranges > 1 -> Indexed $ makeLeaf source startIndex lineRange categories <$> ranges
|
2015-12-14 20:37:07 +03:00
|
|
|
Indexed i -> Indexed $ replaceIn (substring range source) (start range) <$> i
|
|
|
|
Fixed f -> Fixed $ replaceIn (substring range source) (start range) <$> f
|
|
|
|
Keyed k -> Keyed $ replaceIn (substring range source) (start range) <$> k
|
2015-12-14 20:17:30 +03:00
|
|
|
_ -> syntax
|
2015-12-14 20:39:09 +03:00
|
|
|
makeLeaf source startIndex lineRange categories range = Info range lineRange categories :< Leaf (substring range source)
|
2015-12-14 20:09:21 +03:00
|
|
|
|
2015-12-14 20:08:33 +03:00
|
|
|
rangesOfWordsFrom :: Int -> String -> [Range]
|
|
|
|
rangesOfWordsFrom startIndex string = case break Char.isSpace string of
|
|
|
|
([], []) -> []
|
|
|
|
([], rest) -> rangesOfWordsAfterWhitespace startIndex rest
|
|
|
|
(word, []) -> [ Range startIndex $ length word ]
|
|
|
|
(word, rest) -> (Range startIndex $ length word) : rangesOfWordsAfterWhitespace (startIndex + length word) rest
|
|
|
|
where
|
|
|
|
rangesOfWordsAfterWhitespace startIndex string | (whitespace, rest) <- break (not . Char.isSpace) string = rangesOfWordsFrom (startIndex + length whitespace) rest
|