1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Reporting.Render.Code
14import qualified Data.ByteString as B
15import qualified Data.ByteString.UTF8 as UTF8_BS
16import qualified Data.Char as Char
17import qualified Data.IntSet as IntSet
18import qualified Data.List as List
19import qualified Data.Name as Name
20import qualified Data.Set as Set
21import Data.Word (Word16)
23import qualified Reporting.Annotation as A
24import qualified Reporting.Doc as D
25import Reporting.Doc (Doc)
26import Parse.Primitives (Row, Col)
27import Parse.Symbol (binopCharSet)
28import Parse.Variable (reservedWords)
36 Source [(Word16, String)]
39toSource :: B.ByteString -> Source
42 lines (UTF8_BS.toString source) ++ [""]
49toSnippet :: Source -> A.Region -> Maybe A.Region -> (D.Doc, D.Doc) -> D.Doc
50toSnippet source region highlight (preHint, postHint) =
54 , render source region highlight
59toPair :: Source -> A.Region -> A.Region -> (D.Doc, D.Doc) -> (D.Doc, D.Doc, D.Doc) -> D.Doc
60toPair source r1 r2 (oneStart, oneEnd) (twoStart, twoMiddle, twoEnd) =
61 case renderPair source r1 r2 of
70 TwoChunks code1 code2 ->
86(|>) :: a -> (a -> b) -> b
91render :: Source -> A.Region -> Maybe A.Region -> Doc
92render (Source sourceLines) region@(A.Region (A.Position startLine _) (A.Position endLine _)) maybeSubRegion =
96 |> drop (fromIntegral (startLine - 1))
97 |> take (fromIntegral (1 + endLine - startLine))
100 length (show (fst (last relevantLines)))
103 maybe region id maybeSubRegion
105 case makeUnderline width endLine smallerRegion of
107 drawLines True width smallerRegion relevantLines D.empty
110 drawLines False width smallerRegion relevantLines underline
113makeUnderline :: Int -> Word16 -> A.Region -> Maybe Doc
114makeUnderline width realEndLine (A.Region (A.Position start c1) (A.Position end c2)) =
115 if start /= end || end < realEndLine then
120 spaces = replicate (fromIntegral c1 + width + 1) ' '
121 zigzag = replicate (max 1 (fromIntegral (c2 - c1))) '^'
123 Just (D.fromChars spaces <> D.dullred (D.fromChars zigzag))
126drawLines :: Bool -> Int -> A.Region -> [(Word16, String)] -> Doc -> Doc
127drawLines addZigZag width (A.Region (A.Position startLine _) (A.Position endLine _)) sourceLines finalLine =
129 map (drawLine addZigZag width startLine endLine) sourceLines
133drawLine :: Bool -> Int -> Word16 -> Word16 -> (Word16, String) -> Doc
134drawLine addZigZag width startLine endLine (n, line) =
135 addLineNumber addZigZag width startLine endLine n (D.fromChars line)
138addLineNumber :: Bool -> Int -> Word16 -> Word16 -> Word16 -> Doc -> Doc
139addLineNumber addZigZag width start end n line =
145 replicate (width - length number) ' ' ++ number ++ "|"
148 if addZigZag && start <= n && n <= end then
153 D.fromChars lineNumber <> spacer <> line
165renderPair :: Source -> A.Region -> A.Region -> CodePair
166renderPair source@(Source sourceLines) region1 region2 =
168 (A.Region (A.Position startRow1 startCol1) (A.Position endRow1 endCol1)) = region1
169 (A.Region (A.Position startRow2 startCol2) (A.Position endRow2 endCol2)) = region2
171 if startRow1 == endRow1 && endRow1 == startRow2 && startRow2 == endRow2 then
173 lineNumber = show startRow1
174 spaces1 = replicate (fromIntegral startCol1 + length lineNumber + 1) ' '
175 zigzag1 = replicate (fromIntegral (endCol1 - startCol1)) '^'
176 spaces2 = replicate (fromIntegral (startCol2 - endCol1)) ' '
177 zigzag2 = replicate (fromIntegral (endCol2 - startCol2)) '^'
179 (Just line) = List.lookup startRow1 sourceLines
183 [ D.fromChars lineNumber <> "| " <> D.fromChars line
184 , D.fromChars spaces1 <> D.dullred (D.fromChars zigzag1) <>
185 D.fromChars spaces2 <> D.dullred (D.fromChars zigzag2)
190 (render source region1 Nothing)
191 (render source region2 Nothing)
208whatIsNext :: Source -> Row -> Col -> Next
209whatIsNext (Source sourceLines) row col =
210 case List.lookup row sourceLines of
216 chars = drop (fromIntegral col - 1) line
217 keywords = map Name.toChars (Set.toList reservedWords)
219 case List.find (startsWithKeyword chars) keywords of
224 case takeWhile isSymbol chars of
229 | null chars -> Spaces
230 | isNext ' ' chars -> Spaces
231 | isNext ')' chars -> CloseParen
232 | isNext ']' chars -> CloseSquare
233 | isNext '}' chars -> CloseCurly
237isSymbol :: Char -> Bool
239 IntSet.member (Char.ord char) binopCharSet
242isNext :: Char -> [Char] -> Bool
243isNext expected chars =
252startsWithKeyword :: [Char] -> [Char] -> Bool
253startsWithKeyword restOfLine keyword =
254 List.isPrefixOf keyword restOfLine
256 case drop (length keyword) restOfLine of
261 not (Char.isAlphaNum c || c == '_')
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list