1
1
mirror of https://github.com/github/semantic.git synced 2024-11-28 01:47:01 +03:00

🔥 Renderer.Split.

This commit is contained in:
Rob Rix 2017-05-04 10:10:45 -04:00
parent d085b11789
commit 9c2b7dfbe0
2 changed files with 0 additions and 240 deletions

View File

@ -59,7 +59,6 @@ library
, Renderer
, Renderer.JSON
, Renderer.Patch
, Renderer.Split
, Renderer.Summary
, Renderer.SExpression
, Renderer.TOC

View File

@ -1,239 +0,0 @@
{-# OPTIONS_GHC -Wno-deprecations #-}
-- Disabling deprecation warnings due to pattern match against RescueModifier.
module Renderer.Split (split) where
import Alignment
import Category as C
import Data.Bifunctor.Join
import Data.Foldable
import Data.Functor.Both
import Data.Record
import qualified Data.Text.Lazy as TL
import Data.These
import Diff
import Info
import Prologue hiding (div, head, fst, snd, link, (<>))
import qualified Prologue
import Source
import SplitDiff
import Syntax
import Term
import Text.Blaze.Html
import Text.Blaze.Html.Renderer.Text
import Text.Blaze.Html5 hiding (map)
import qualified Text.Blaze.Html5.Attributes as A
import qualified Text.Blaze.Internal as Blaze
-- | Add the first category from a Foldable of categories as a class name as a
-- | class name on the markup, prefixed by `category-`.
classifyMarkup :: Category -> Markup -> Markup
classifyMarkup category element = (element !) . A.class_ . textValue $ styleName category
-- | Return the appropriate style name for the given category.
styleName :: Category -> Text
styleName category = "category-" <> case category of
Program -> "program"
C.ParseError -> "error"
BooleanOperator -> "boolean_operator"
MathOperator -> "math_operator"
BitwiseOperator -> "bitwise_operator"
RelationalOperator -> "relational_operator"
Boolean -> "boolean"
DictionaryLiteral -> "dictionary"
C.Pair -> "pair"
StringLiteral -> "string"
SymbolLiteral -> "symbol"
IntegerLiteral -> "integer"
NumberLiteral -> "number"
FloatLiteral -> "float"
C.Comment -> "comment"
C.FunctionCall -> "function_call"
C.Function -> "function"
C.MethodCall -> "method_call"
C.Args -> "arguments"
C.Assignment -> "assignment"
C.MemberAccess -> "member_access"
C.VarDecl -> "var_declaration"
C.VarAssignment -> "var_assignment"
C.Switch -> "switch"
C.Case -> "case"
TemplateString -> "template_string"
Regex -> "regex"
Identifier -> "identifier"
C.Params -> "parameters"
ExpressionStatements -> "expression_statements"
C.MathAssignment -> "math_assignment"
C.SubscriptAccess -> "subscript_access"
C.Ternary -> "ternary"
C.Operator -> "operator"
C.Object -> "object"
C.For -> "for"
C.While -> "while"
C.DoWhile -> "do_while"
C.Return -> "return_statement"
C.Throw -> "throw_statement"
C.Constructor -> "constructor"
C.Try -> "try_statement"
C.Catch -> "catch_statement"
C.Finally -> "finally_statement"
ArrayLiteral -> "array"
C.Class -> "class_statement"
C.Method -> "method"
C.If -> "if_statement"
C.Empty -> "empty_statement"
C.CommaOperator -> "comma_operator"
Other string -> string
C.Module -> "module_statement"
C.Namespace -> "namespace"
C.Interface -> "interface"
C.Import -> "import_statement"
C.Export -> "export_statement"
C.AnonymousFunction -> "anonymous_function"
C.Interpolation -> "interpolation"
C.Subshell -> "subshell"
C.OperatorAssignment -> "operator_assignment"
C.Yield -> "yield_expression"
C.Until -> "until"
C.Unless -> "unless_statement"
C.Begin -> "begin_statement"
C.Else -> "else_block"
C.Elsif -> "elsif_block"
C.Ensure -> "ensure_block"
C.Rescue -> "rescue_block"
C.RescueModifier -> "rescue_modifier"
C.When -> "when_block"
C.RescuedException -> "last_exception"
C.RescueArgs -> "rescue_args"
C.Negate -> "negate"
C.Select -> "select_statement"
C.Go -> "go_statement"
C.Defer -> "defer_statement"
C.Slice -> "slice_expression"
C.TypeAssertion -> "type_assertion"
C.TypeConversion -> "type_conversion"
C.ArgumentPair -> "argument_pair"
C.KeywordParameter -> "keyword_param"
C.OptionalParameter -> "optional_param"
C.SplatParameter -> "splat_param"
C.HashSplatParameter -> "hash_splat_param"
C.BlockParameter -> "block_param"
C.ArrayTy -> "array_type"
C.DictionaryTy -> "dictionary_type"
C.StructTy -> "struct_type"
C.Struct -> "struct"
C.Break -> "break_statement"
C.Continue -> "continue_statement"
C.Binary -> "binary"
C.Unary -> "unary"
C.Constant -> "constant"
C.Superclass -> "superclass"
C.SingletonClass -> "singleton_class"
C.SingletonMethod -> "singleton_method"
C.RangeExpression -> "range"
C.ScopeOperator -> "scope_operator"
C.BeginBlock -> "begin_block"
C.EndBlock -> "end_block"
C.ParameterDecl -> "parameter_declaration"
C.DefaultCase -> "default_statement"
C.TypeDecl -> "type_declaration"
C.PointerTy -> "pointer_type"
C.FieldDecl -> "field_declaration"
C.SliceTy -> "slice_type"
C.Element -> "element"
C.Literal -> "literal"
C.ChannelTy -> "channel_type"
C.FunctionTy -> "function_type"
C.Send -> "send_statement"
C.IncrementStatement -> "increment_statement"
C.DecrementStatement -> "decrement_statement"
C.QualifiedIdentifier -> "qualified_identifier"
C.IndexExpression -> "index_expression"
C.FieldDeclarations -> "field_declarations"
C.RuneLiteral -> "rune_literal"
C.Modifier c -> styleName c <> "_modifier"
C.Ty -> "type"
-- | Pick the class name for a split patch.
splitPatchToClassName :: SplitPatch a -> AttributeValue
splitPatchToClassName patch = stringValue $ "patch " <> case patch of
SplitInsert _ -> "insert"
SplitDelete _ -> "delete"
SplitReplace _ -> "replace"
-- | Render a diff as an HTML split diff.
split :: (HasField fields Category, HasField fields Range) => Both SourceBlob -> Diff (Syntax Text) (Record fields) -> Text
split blobs diff = TL.toStrict . renderHtml
. docTypeHtml
. ((head $ link ! A.rel "stylesheet" ! A.href "style.css") <>)
. body
. (table ! A.class_ (stringValue "diff")) .
((colgroup $ (col ! A.width (stringValue . show $ columnWidth)) <> col <> (col ! A.width (stringValue . show $ columnWidth)) <> col) <>)
. mconcat $ numberedLinesToMarkup <$> numbered
where
sources = Source.source <$> blobs
numbered = numberedRows (alignDiff sources diff)
maxNumber = case numbered of
[] -> 0
(row : _) -> mergeThese max . runJoin $ Prologue.fst <$> row
-- | The number of digits in a number (e.g. 342 has 3 digits).
digits :: Int -> Int
digits n = let base = 10 :: Int in
ceiling (logBase (fromIntegral base) (fromIntegral n) :: Double)
columnWidth = max (20 + digits maxNumber * 8) 40
-- | Render a line with numbers as an HTML row.
numberedLinesToMarkup numberedLines = tr $ runBothWith (<>) (renderLine <$> Join (fromThese Nothing Nothing (runJoin (Just <$> numberedLines))) <*> sources) <> string "\n"
renderLine (Just (number, line)) source = toMarkup $ Cell (hasChanges line) number (Renderable source line)
renderLine _ _
= (td mempty ! A.class_ (stringValue "blob-num blob-num-empty empty-cell"))
<> (td mempty ! A.class_ (stringValue "blob-code blob-code-empty empty-cell"))
<> string "\n"
-- | A cell in a table, characterized by whether it contains changes & its line number.
data Cell a = Cell !Bool !Int !a
-- | Something that can be rendered as markup with reference to some source.
data Renderable a = Renderable !Source !a
contentElements :: (Foldable t, ToMarkup f) => Source -> Range -> t (f, Range) -> [Markup]
contentElements source range children = let (elements, next) = foldr' (markupForContextAndChild source) ([], end range) children in
text (toText (slice (Range (start range) (max next (start range))) source)) : elements
markupForContextAndChild :: ToMarkup f => Source -> (f, Range) -> ([Markup], Int) -> ([Markup], Int)
markupForContextAndChild source (child, range) (rows, next) = (toMarkup child : text (toText (slice (Range (end range) next) source)) : rows, start range)
wrapIn :: (Markup -> Markup) -> Markup -> Markup
wrapIn _ l@Blaze.Leaf{} = l
wrapIn _ l@Blaze.CustomLeaf{} = l
wrapIn _ l@Blaze.Content{} = l
wrapIn _ l@Blaze.Comment{} = l
wrapIn f p = f p
-- Instances
instance (ToMarkup f, HasField fields Category, HasField fields Range) => ToMarkup (Renderable (SyntaxTermF leaf fields (f, Range))) where
toMarkup (Renderable source (info :< syntax)) = classifyMarkup (category info) $ case syntax of
Leaf _ -> span . text . toText $ slice (byteRange info) source
_ -> ul . mconcat $ wrapIn li <$> contentElements source (byteRange info) (toList syntax)
instance (HasField fields Category, HasField fields Range) => ToMarkup (Renderable (SyntaxTerm leaf fields)) where
toMarkup (Renderable source term) = Prologue.fst $ cata (\ t -> (toMarkup $ Renderable source t, byteRange (headF t))) term
instance (HasField fields Category, HasField fields Range) => ToMarkup (Renderable (SplitSyntaxDiff leaf fields)) where
toMarkup (Renderable source diff) = Prologue.fst . iter (\ t -> (toMarkup $ Renderable source t, byteRange (headF t))) $ toMarkupAndRange <$> diff
where toMarkupAndRange patch = let term@(info :< _) = runCofree $ getSplitTerm patch in
((div ! patchAttribute patch) . toMarkup $ Renderable source (cofree term), byteRange info)
patchAttribute patch = A.class_ (splitPatchToClassName patch)
instance ToMarkup a => ToMarkup (Cell a) where
toMarkup (Cell hasChanges num line)
= (td (string (show num)) ! A.class_ (stringValue $ if hasChanges then "blob-num blob-num-replacement" else "blob-num"))
<> (td (toMarkup line) ! A.class_ (stringValue $ if hasChanges then "blob-code blob-code-replacement" else "blob-code"))
<> string "\n"
(<>) :: Monoid m => m -> m -> m
(<>) = mappend