diff --git a/notebooks/Test.ipynb b/notebooks/Test.ipynb index 49bec2b4..50caafd6 100644 --- a/notebooks/Test.ipynb +++ b/notebooks/Test.ipynb @@ -36,7 +36,43 @@ "cell_type": "code", "collapsed": false, "input": [ - ":doc map" + "getStringTarget :: String -> String\n", + "getStringTarget = go \"\" . reverse\n", + " where\n", + " go acc rest = case rest of\n", + " '\"':'\\\\':rem -> go ('\"':acc) rem\n", + " '\"':rem -> acc\n", + " ' ':'\\\\':rem -> go (' ':acc) rem\n", + " ' ':rem -> acc\n", + " x:rem -> go (x:acc) rem\n", + " [] -> acc\n", + "\n", + "\" ~/archive/\n", + ":load \" ~/archive/" + ], + "language": "python", + "metadata": { + "hidden": false + }, + "outputs": [ + { + "html": [ + "Parse error (line 12, column 13): lexical error in string/character literal at end of input" + ], + "metadata": {}, + "output_type": "display_data", + "text": [ + "Parse error (line 12, column 13): lexical error in string/character literal at end of input" + ] + } + ], + "prompt_number": 1 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "getStringTarget \"absdf\\\" he\\\\\\\"llo\"" ], "language": "python", "metadata": { @@ -45,59 +81,13 @@ "outputs": [ { "metadata": {}, - "output_type": "display_data" - }, - { - "html": [ - "map ∷ (a → b) → [a] → [b](package base, module Prelude)
map f xs is the list obtained by applying f to each element of xs, i.e.,\n", - "
\n", - "
\n", - "
> map f [x1, x2, ..., xn] == [f x1, f x2, ..., f xn]\n", - "> map f [x1, x2, ...] == [f x1, f x2, ...] \n", - "
\n", - "
\n", - "map ∷ (Char → Char) → ByteString → ByteString(package bytestring, module Data.ByteString.Char8)
O(n) map f xs is the ByteString obtained by applying f to each element of xs \n", - "
\n", - "
\n", - "map ∷ (Char → Char) → Text → Text(package text, module Data.Text)
O(n) map f t is the Text obtained by applying f to each element of t. Subject to fusion. Performs replacement on invalid scalar values. \n", - "
\n", - "
\n", - "map ∷ (Key → Key) → IntSet → IntSet(package containers, module Data.IntSet)
O(n*min(n,W)). map f s is the set obtained by applying f to each element of s.\n", - "
\n", - "
\n", - "
It's worth noting that the size of the result may be smaller if, for some (x,y), x /= y && f x == f y \n", - "
\n", - "
\n", - "map ∷ (Word8 → Word8) → ByteString → ByteString(package bytestring, module Data.ByteString.Lazy)
O(n) map f xs is the ByteString obtained by applying f to each element of xs. \n", - "
\n", - "
\n", - "map ∷ (Word8 → Word8) → ByteString → ByteString(package bytestring, module Data.ByteString)
O(n) map f xs is the ByteString obtained by applying f to each element of xs. This function is subject to array fusion. \n", - "
\n", - "
\n", - "map ∷ (a → b) → IntMap a → IntMap b(package containers, module Data.IntMap.Strict)
O(n). Map a function over all values in the map.\n", - "
\n", - "
\n", - "
> map (++ \"x\") (fromList [(5,\"a\"), (3,\"b\")]) == fromList [(3, \"bx\"), (5, \"ax\")] \n", - "
\n", - "
\n", - "map ∷ (a → b) → Map k a → Map k b(package containers, module Data.Map.Lazy)
O(n). Map a function over all values in the map.\n", - "
\n", - "
\n", - "
> map (++ \"x\") (fromList [(5,\"a\"), (3,\"b\")]) == fromList [(3, \"bx\"), (5, \"ax\")] \n", - "
\n", - "
\n", - "map ∷ Ord b ⇒ (a → b) → Set a → Set b(package containers, module Data.Set)
O(n*log n). map f s is the set obtained by applying f to each element of s.\n", - "
\n", - "
\n", - "
It's worth noting that the size of the result may be smaller if, for some (x,y), x /= y && f x == f y \n", - "
\n", - "
\n" - ], - "metadata": {}, - "output_type": "display_data" + "output_type": "display_data", + "text": [ + "\"he\\\"llo\"" + ] } ], - "prompt_number": 4 + "prompt_number": 13 }, { "cell_type": "code", diff --git a/src/IHaskell/Eval/Completion.hs b/src/IHaskell/Eval/Completion.hs index 073a3cfb..49186b4e 100644 --- a/src/IHaskell/Eval/Completion.hs +++ b/src/IHaskell/Eval/Completion.hs @@ -69,14 +69,15 @@ complete line pos = do moduleNames = nub $ concatMap getNames db let target = completionTarget line pos + completion = completionType line pos target - let matchedText = case completionType line pos target of + let matchedText = case completion of HsFilePath _ match -> match FilePath _ match -> match otherwise -> intercalate "." target options <- - case completionType line pos target of + case completion of Empty -> return [] Identifier candidate -> @@ -175,10 +176,18 @@ completionType line loc target -- If it's empty, no completion. | null target = Empty + + -- When in a string, complete filenames. + | cursorInString line loc + = FilePath (getStringTarget lineUpToCursor) (getStringTarget lineUpToCursor) + + -- Complete module names in imports and elsewhere. | startswith "import" stripped && isModName = ModuleName dotted candidate | isModName && (not . null . init) target = Qualified dotted candidate + + -- Default to completing identifiers. | otherwise = Identifier candidate where stripped = strip line @@ -196,6 +205,26 @@ completionType line loc target else [] Left _ -> Empty + cursorInString str loc = nquotes (take loc str) `mod` 2 /= 0 + + nquotes ('\\':'"':xs) = nquotes xs + nquotes ('"':xs) = 1 + nquotes xs + nquotes (_:xs) = nquotes xs + nquotes [] = 0 + + -- Get the bit of a string that might be a filename completion. + -- Logic is a bit convoluted, but basically go backwards from the + -- end, stopping at any quote or space, unless they are escaped. + getStringTarget :: String -> String + getStringTarget = go "" . reverse + where + go acc rest = case rest of + '"':'\\':rem -> go ('"':acc) rem + '"':rem -> acc + ' ':'\\':rem -> go (' ':acc) rem + ' ':rem -> acc + x:rem -> go (x:acc) rem + [] -> acc -- | Get the word under a given cursor location. completionTarget :: String -> Int -> [String]