1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Reporting.Helpers
6 , Doc, (<+>), (<>), cat, dullyellow, fillSep, hang
7 , hsep, indent, parens, sep, text, underline, vcat
11 , hintLink, stack, reflowParagraph
12 , commaSep, capitalize, ordinalize, drawCycle
13 , findPotentialTypos, findTypoPairs, vetTypos
14 , nearbyNames, distance, maybeYouWant
18import Data.Function (on)
19import qualified Data.Char as Char
20import qualified Data.List as List
21import qualified Data.Map as Map
22import Data.Monoid ((<>))
23import qualified Data.Set as Set
24import qualified Data.Text as Text
25import Data.Text (Text)
26import qualified Text.EditDistance as Dist
27import qualified Text.PrettyPrint.ANSI.Leijen as P
28import Text.PrettyPrint.ANSI.Leijen hiding ((<>), text)
30import qualified AST.Helpers as Help
31import qualified Elm.Compiler.Version as Compiler
32import qualified Elm.Package as Pkg
39(|>) :: a -> (a -> b) -> b
53 P.text (Text.unpack msg)
61functionName :: Text -> Text
63 if Help.isOp opName then
72 i2t n <> if n == 1 then " argument" else " arguments"
75hintLink :: Text -> Text
77 "<https://github.com/elm-lang/elm-compiler/blob/"
78 <> Text.pack (Pkg.versionToString Compiler.version)
79 <> "/hints/" <> fileName <> ".md>"
86 error "function `stack` requires a non-empty list of docs"
89 List.foldl' (\a b -> a <> hardline <> hardline <> b) doc docs
92reflowParagraph :: Text -> Doc
93reflowParagraph paragraph =
94 fillSep (map text (Text.words paragraph))
97commaSep :: [Text] -> Text
104 " " <> token1 <> " and " <> token2
107 " " <> Text.intercalate ", " (init tokens) <> ", and " <> last tokens
110capitalize :: Text -> Text
112 case Text.uncons txt of
117 Text.cons (Char.toUpper c) rest
120ordinalize :: Int -> Text
130 | remainder100 `elem` [11..13] = "th"
131 | remainder10 == 1 = "st"
132 | remainder10 == 2 = "nd"
133 | remainder10 == 3 = "rd"
140drawCycle :: [Text] -> Doc
147 text "│ " <> dullyellow (text name)
155 vcat (topLine : List.intersperse midLine (map nameLine names) ++ [ bottomLine ])
162findPotentialTypos :: [Text] -> Text -> [Text]
163findPotentialTypos knownNames badName =
164 filter ((==1) . distance badName) knownNames
167findTypoPairs :: [Text] -> [Text] -> [(Text, Text)]
168findTypoPairs leftOnly rightOnly =
171 map ((,) leftName) (findPotentialTypos rightOnly leftName)
173 concatMap veryNear leftOnly
176vetTypos :: [(Text, Text)] -> Maybe (Set.Set Text, Set.Set Text)
177vetTypos potentialTypos =
179 tallyNames (ln, rn) (lc, rc) =
180 ( Map.insertWith (+) ln 1 lc
181 , Map.insertWith (+) rn 1 rc
184 (leftCounts, rightCounts) =
185 foldr tallyNames (Map.empty, Map.empty) potentialTypos
187 acceptable :: Map.Map Text Int -> Bool
189 not (Map.null counts)
191 Map.foldr (\n unique -> n < 2 && unique) True counts
193 if acceptable leftCounts && acceptable rightCounts then
194 Just (Map.keysSet leftCounts, Map.keysSet rightCounts)
204nearbyNames :: (a -> Text) -> a -> [a] -> [a]
205nearbyNames format name names =
207 if Text.length (format name) < 3 then 1 else 2
210 |> map (\x -> (distance (format name) (format x), x))
211 |> List.sortBy (compare `on` fst)
212 |> filter ( (<= editDistance) . abs . fst )
216distance :: Text -> Text -> Int
218 Dist.restrictedDamerauLevenshteinDistance
219 Dist.defaultEditCosts
224maybeYouWant :: [Text] -> Text
225maybeYouWant suggestions =
231 "Maybe you want one of the following?\n"
233 Text.concat (map ("\n " <>) (take 4 suggestions))
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