1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Reporting.Render.Code
10 , nextLineStartsWithKeyword
11 , nextLineStartsWithCloseCurly
16import qualified Data.ByteString as B
17import qualified Data.ByteString.UTF8 as UTF8_BS
18import qualified Data.Char as Char
19import qualified Data.IntSet as IntSet
20import qualified Data.List as List
21import qualified Data.Name as Name
22import qualified Data.Set as Set
23import Data.Word (Word16)
25import qualified Reporting.Annotation as A
26import qualified Reporting.Doc as D
27import Reporting.Doc (Doc)
28import Parse.Primitives (Row, Col)
29import Parse.Symbol (binopCharSet)
30import Parse.Variable (reservedWords)
38 Source [(Word16, String)]
41toSource :: B.ByteString -> Source
44 lines (UTF8_BS.toString source) ++ [""]
51toSnippet :: Source -> A.Region -> Maybe A.Region -> (D.Doc, D.Doc) -> D.Doc
52toSnippet source region highlight (preHint, postHint) =
56 , render source region highlight
61toPair :: Source -> A.Region -> A.Region -> (D.Doc, D.Doc) -> (D.Doc, D.Doc, D.Doc) -> D.Doc
62toPair source r1 r2 (oneStart, oneEnd) (twoStart, twoMiddle, twoEnd) =
63 case renderPair source r1 r2 of
72 TwoChunks code1 code2 ->
88(|>) :: a -> (a -> b) -> b
93render :: Source -> A.Region -> Maybe A.Region -> Doc
94render (Source sourceLines) region@(A.Region (A.Position startLine _) (A.Position endLine _)) maybeSubRegion =
98 |> drop (fromIntegral (startLine - 1))
99 |> take (fromIntegral (1 + endLine - startLine))
102 length (show (fst (last relevantLines)))
105 maybe region id maybeSubRegion
107 case makeUnderline width endLine smallerRegion of
109 drawLines True width smallerRegion relevantLines D.empty
112 drawLines False width smallerRegion relevantLines underline
115makeUnderline :: Int -> Word16 -> A.Region -> Maybe Doc
116makeUnderline width realEndLine (A.Region (A.Position start c1) (A.Position end c2)) =
117 if start /= end || end < realEndLine then
122 spaces = replicate (fromIntegral c1 + width + 1) ' '
123 zigzag = replicate (max 1 (fromIntegral (c2 - c1))) '^'
125 Just (D.fromChars spaces <> D.red (D.fromChars zigzag))
128drawLines :: Bool -> Int -> A.Region -> [(Word16, String)] -> Doc -> Doc
129drawLines addZigZag width (A.Region (A.Position startLine _) (A.Position endLine _)) sourceLines finalLine =
131 map (drawLine addZigZag width startLine endLine) sourceLines
135drawLine :: Bool -> Int -> Word16 -> Word16 -> (Word16, String) -> Doc
136drawLine addZigZag width startLine endLine (n, line) =
137 addLineNumber addZigZag width startLine endLine n (D.fromChars line)
140addLineNumber :: Bool -> Int -> Word16 -> Word16 -> Word16 -> Doc -> Doc
141addLineNumber addZigZag width start end n line =
147 replicate (width - length number) ' ' ++ number ++ "|"
150 if addZigZag && start <= n && n <= end then
155 D.fromChars lineNumber <> spacer <> line
167renderPair :: Source -> A.Region -> A.Region -> CodePair
168renderPair source@(Source sourceLines) region1 region2 =
170 (A.Region (A.Position startRow1 startCol1) (A.Position endRow1 endCol1)) = region1
171 (A.Region (A.Position startRow2 startCol2) (A.Position endRow2 endCol2)) = region2
173 if startRow1 == endRow1 && endRow1 == startRow2 && startRow2 == endRow2 then
175 lineNumber = show startRow1
176 spaces1 = replicate (fromIntegral startCol1 + length lineNumber + 1) ' '
177 zigzag1 = replicate (fromIntegral (endCol1 - startCol1)) '^'
178 spaces2 = replicate (fromIntegral (startCol2 - endCol1)) ' '
179 zigzag2 = replicate (fromIntegral (endCol2 - startCol2)) '^'
181 (Just line) = List.lookup startRow1 sourceLines
185 [ D.fromChars lineNumber <> "| " <> D.fromChars line
186 , D.fromChars spaces1 <> D.red (D.fromChars zigzag1) <>
187 D.fromChars spaces2 <> D.red (D.fromChars zigzag2)
192 (render source region1 Nothing)
193 (render source region2 Nothing)
209whatIsNext :: Source -> Row -> Col -> Next
210whatIsNext (Source sourceLines) row col =
211 case List.lookup row sourceLines of
216 case drop (fromIntegral col - 1) line of
221 | Char.isUpper c -> Upper c (takeWhile isInner cs)
222 | Char.isLower c -> detectKeywords c cs
223 | isSymbol c -> Operator (c : takeWhile isSymbol cs)
224 | c == ')' -> Close "parenthesis" ')'
225 | c == ']' -> Close "square bracket" ']'
226 | c == '}' -> Close "curly brace" '}'
227 | otherwise -> Other (Just c)
230detectKeywords :: Char -> [Char] -> Next
231detectKeywords c rest =
233 cs = takeWhile isInner rest
236 if Set.member (Name.fromChars name) reservedWords
241isInner :: Char -> Bool
243 Char.isAlphaNum char || char == '_'
246isSymbol :: Char -> Bool
248 IntSet.member (Char.ord char) binopCharSet
251startsWithKeyword :: [Char] -> [Char] -> Bool
252startsWithKeyword restOfLine keyword =
253 List.isPrefixOf keyword restOfLine
255 case drop (length keyword) restOfLine of
263nextLineStartsWithKeyword :: [Char] -> Source -> Row -> Maybe (Row, Col)
264nextLineStartsWithKeyword keyword (Source sourceLines) row =
265 case List.lookup (row + 1) sourceLines of
270 if startsWithKeyword (dropWhile (==' ') line) keyword then
271 Just (row + 1, 1 + fromIntegral (length (takeWhile (==' ') line)))
276nextLineStartsWithCloseCurly :: Source -> Row -> Maybe (Row, Col)
277nextLineStartsWithCloseCurly (Source sourceLines) row =
278 case List.lookup (row + 1) sourceLines of
283 case dropWhile (==' ') line of
285 Just (row + 1, 1 + fromIntegral (length (takeWhile (==' ') line)))
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