@@ -2,21 +2,26 @@
2module Transform.Canonicalize.Variable where
2module Transform.Canonicalize.Variable where
4import Control.Monad.Error
4import Control.Monad.Error
5+import qualified Data.Either as Either
6+import Data.Function (on)
5import qualified Data.List as List
7import qualified Data.List as List
6import qualified Data.Map as Map
8import qualified Data.Map as Map
7−import Data.Maybe (fromMaybe)
9+import Data.Maybe (fromMaybe, mapMaybe)
10+import qualified Data.Set as Set
11+import qualified Text.EditDistance as Dist
9−import AST.Helpers as Help
13+import qualified AST.Helpers as Help
10import qualified AST.Module as Module
14import qualified AST.Module as Module
11import qualified AST.Type as Type
15import qualified AST.Type as Type
12import qualified AST.Variable as Var
16import qualified AST.Variable as Var
13import Transform.Canonicalize.Environment as Env
17import Transform.Canonicalize.Environment as Env
18+import Elm.Utils ((|>))
16variable :: Environment -> String -> Canonicalizer String Var.Canonical
21variable :: Environment -> String -> Canonicalizer String Var.Canonical
17variable env var =
22variable env var =
18− case modul of
23+ case splitName var of
19− Just name
24+ Right (name, varName)
20 | Module.nameIsNative name ->
25 | Module.nameIsNative name ->
21 Env.using (Var.Canonical (Var.Module name) varName)
26 Env.using (Var.Canonical (Var.Module name) varName)
@@ -25,11 +30,6 @@ variable env var =
25 Just [v] -> Env.using v
30 Just [v] -> Env.using v
26 Just vs -> preferLocals env "variable" vs var
31 Just vs -> preferLocals env "variable" vs var
27 Nothing -> notFound "variable" (Map.keys (_values env)) var
32 Nothing -> notFound "variable" (Map.keys (_values env)) var
30− case Help.splitDots var of
32− xs -> (Just (init xs), last xs)
@@ -42,8 +42,11 @@ tvar env var =
42 [v] -> found extract v
42 [v] -> found extract v
43 vs -> preferLocals' env extract "type" vs var
43 vs -> preferLocals' env extract "type" vs var
45− adts = map Left . fromMaybe [] $ Map.lookup var (_adts env)
46+ map Left (fromMaybe [] (Map.lookup var (_adts env)))
46− aliases = map Right . fromMaybe [] $ Map.lookup var (_aliases env)
49+ map Right (fromMaybe [] (Map.lookup var (_aliases env)))
48 extract value =
51 extract value =
49 case value of
52 case value of
@@ -59,25 +62,14 @@ pvar env var =
59 Nothing -> notFound "pattern" (Map.keys (_patterns env)) var
62 Nothing -> notFound "pattern" (Map.keys (_patterns env)) var
62found :: (a -> Var.Canonical) -> a -> Canonicalizer String a
67found :: (a -> Var.Canonical) -> a -> Canonicalizer String a
63found extract v =
68found extract v =
64 do _ <- Env.using (extract v)
69 do _ <- Env.using (extract v)
68−notFound :: String -> [String] -> String -> Canonicalizer String a
69−notFound kind possibilities var =
70− throwError $ "Could not find " ++ kind ++ " '" ++ var ++ "'." ++ msg
73− filter (List.isInfixOf var) possibilities
78− else "\nClose matches include: " ++ List.intercalate ", " matches
81preferLocals
73preferLocals
82 :: Environment
74 :: Environment
@@ -116,3 +108,136 @@ preferLocals' env extract kind possibilities var =
116 msg = "Ambiguous usage of " ++ kind ++ " '" ++ var ++ "'.\n" ++
108 msg = "Ambiguous usage of " ++ kind ++ " '" ++ var ++ "'.\n" ++
117 " Disambiguate between: " ++ List.intercalate ", " vars
109 " Disambiguate between: " ++ List.intercalate ", " vars
114+splitName :: String -> Either String ([String], String)
116+ case Help.splitDots var of
118+ xs -> Right (init xs, last xs)
121+getName :: Either String ([String], String) -> String
128+nameToString :: ([String], String) -> String
129+nameToString (modul, name) =
130+ Module.nameToString (modul ++ [name])
133+isOp :: Either String ([String], String) -> Bool
135+ Help.isOp (getName name)
138+distance :: String -> String -> Int
140+ Dist.restrictedDamerauLevenshteinDistance Dist.defaultEditCosts x y
143+nearbyNames :: (a -> String) -> a -> [a] -> [a]
144+nearbyNames format name names =
146+ if length (format name) < 3 then 1 else 2
149+ |> map (\x -> (distance (format name) (format x), x))
150+ |> List.sortBy (compare `on` fst)
151+ |> filter ( (<= editDistance) . abs . fst )
157+notFound :: String -> [String] -> String -> Canonicalizer String a
158+notFound kind possibilities var =
160+ map splitName possibilities
167+ |> filter (\n -> isOp name == isOp n)
168+ |> nearbyNames getName name
170+ (exposed, qualified) =
171+ Either.partitionEithers closeNames
176+ closeExposedMessage exposed ++ closeQualifiedMessage qualified
179+ qualifiedMessage modul (Either.rights possibleNames) qualified
182+ throwError $ "Could not find " ++ kind ++ " '" ++ var ++ "'." ++ message
186+closeExposedMessage :: [String] -> String
187+closeExposedMessage exposed =
191+ "\n\nClose matches include:" ++ concatMap ("\n " ++) exposed
194+closeQualifiedMessage :: [([String], String)] -> String
195+closeQualifiedMessage qualified =
199+ "\n\nMaybe you forgot to say which module it came from?\n"
200+ ++ "Close qualified names include:"
201+ ++ concatMap (("\n " ++) . nameToString) qualified
202+ ++ usingImportsMessage
205+qualifiedMessage :: [String] -> [([String], String)] -> [([String], String)] -> String
206+qualifiedMessage modul allQualified qualified =
207+ let availableModules =
208+ Set.fromList (map fst allQualified)
210+ case Set.member modul availableModules of
213+ filter ((==) modul . fst) qualified
215+ if null inSameModule
218+ "\n\nClose matches include:"
219+ ++ concatMap (("\n " ++) . nameToString) inSameModule
223+ Set.toList availableModules
224+ |> map Module.nameToString
225+ |> nearbyNames id (Module.nameToString modul)
227+ case closeModules of
229+ "\n\nLooks like the prefix '" ++ Module.nameToString modul
230+ ++ "' is not in scope. Is it spelled correctly?"
231+ ++ "\nIs it imported correctly?"
232+ ++ usingImportsMessage
235+ "\n\nClose matches to '" ++ Module.nameToString modul ++ "' include:"
236+ ++ concatMap ("\n " ++) closeModules
239+usingImportsMessage :: String
240+usingImportsMessage =
241+ "\n\nYou can read about how imports work at the following address:"
242+ ++ "\n<http://elm-lang.org/learn/Syntax.elm#modules>"