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.dullred (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.dullred (D.fromChars zigzag1) <>
187 D.fromChars spaces2 <> D.dullred (D.fromChars zigzag2)
192 (render source region1 Nothing)
193 (render source region2 Nothing)
207whatIsNext :: Source -> Row -> Col -> Next
208whatIsNext (Source sourceLines) row col =
209 case List.lookup row sourceLines of
215 chars = drop (fromIntegral col - 1) line
216 keywords = map Name.toChars (Set.toList reservedWords)
218 case List.find (startsWithKeyword chars) keywords of
228 | isSymbol c -> Operator (c : takeWhile isSymbol cs)
229 | c == ')' -> Close "parenthesis" ')'
230 | c == ']' -> Close "square bracket" ']'
231 | c == '}' -> Close "curly brace" '}'
232 | otherwise -> Other (Just c)
235isSymbol :: Char -> Bool
237 IntSet.member (Char.ord char) binopCharSet
240startsWithKeyword :: [Char] -> [Char] -> Bool
241startsWithKeyword restOfLine keyword =
242 List.isPrefixOf keyword restOfLine
244 case drop (length keyword) restOfLine of
249 not (Char.isAlphaNum c || c == '_')
252nextLineStartsWithKeyword :: [Char] -> Source -> Row -> Maybe (Row, Col)
253nextLineStartsWithKeyword keyword (Source sourceLines) row =
254 case List.lookup (row + 1) sourceLines of
259 if startsWithKeyword (dropWhile (==' ') line) keyword then
260 Just (row + 1, 1 + fromIntegral (length (takeWhile (==' ') line)))
265nextLineStartsWithCloseCurly :: Source -> Row -> Maybe (Row, Col)
266nextLineStartsWithCloseCurly (Source sourceLines) row =
267 case List.lookup (row + 1) sourceLines of
272 case dropWhile (==' ') line of
274 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