Adding filepath completion in strings, closes #122

This commit is contained in:
Andrew Gibiansky 2014-05-18 10:09:36 -07:00
parent 75cff70fe5
commit 943eb9fd27
2 changed files with 73 additions and 54 deletions

View File

@ -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": [
"<span class='err-msg'>Parse error (line 12, column 13): lexical error in string/character literal at end of input</span>"
],
"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": [
"<span class='hoogle-name'><a target='_blank' href='http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#v:map'>map</a> &#x2237; (a &#x2192; b) &#x2192; [a] &#x2192; [b]</span><span class='hoogle-sub'>(<span class='hoogle-head'>package</span> <span class='hoogle-package'>base</span>, <span class='hoogle-head'>module</span> <span class='hoogle-module'>Prelude</span>)</span><div class='hoogle-doc'><div class='hoogle-text'>map f xs is the list obtained by applying f to each element of xs, i.e.,\n",
"</div>\n",
"<div class='hoogle-text'></div>\n",
"<div class='hoogle-code'>> map f [x1, x2, ..., xn] == [f x1, f x2, ..., f xn]\n",
"> map f [x1, x2, ...] == [f x1, f x2, ...] \n",
"</div>\n",
"</div>\n",
"<span class='hoogle-name'><a target='_blank' href='http://hackage.haskell.org/packages/archive/bytestring/latest/doc/html/Data-ByteString-Char8.html#v:map'>map</a> &#x2237; (Char &#x2192; Char) &#x2192; ByteString &#x2192; ByteString</span><span class='hoogle-sub'>(<span class='hoogle-head'>package</span> <span class='hoogle-package'>bytestring</span>, <span class='hoogle-head'>module</span> <span class='hoogle-module'>Data.ByteString.Char8</span>)</span><div class='hoogle-doc'><div class='hoogle-text'>O(n) map f xs is the ByteString obtained by applying f to each element of xs \n",
"</div>\n",
"</div>\n",
"<span class='hoogle-name'><a target='_blank' href='http://hackage.haskell.org/packages/archive/text/latest/doc/html/Data-Text.html#v:map'>map</a> &#x2237; (Char &#x2192; Char) &#x2192; Text &#x2192; Text</span><span class='hoogle-sub'>(<span class='hoogle-head'>package</span> <span class='hoogle-package'>text</span>, <span class='hoogle-head'>module</span> <span class='hoogle-module'>Data.Text</span>)</span><div class='hoogle-doc'><div class='hoogle-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",
"</div>\n",
"</div>\n",
"<span class='hoogle-name'><a target='_blank' href='http://hackage.haskell.org/packages/archive/containers/latest/doc/html/Data-IntSet.html#v:map'>map</a> &#x2237; (Key &#x2192; Key) &#x2192; IntSet &#x2192; IntSet</span><span class='hoogle-sub'>(<span class='hoogle-head'>package</span> <span class='hoogle-package'>containers</span>, <span class='hoogle-head'>module</span> <span class='hoogle-module'>Data.IntSet</span>)</span><div class='hoogle-doc'><div class='hoogle-text'>O(n*min(n,W)). map f s is the set obtained by applying f to each element of s.\n",
"</div>\n",
"<div class='hoogle-text'></div>\n",
"<div class='hoogle-text'>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",
"</div>\n",
"</div>\n",
"<span class='hoogle-name'><a target='_blank' href='http://hackage.haskell.org/packages/archive/bytestring/latest/doc/html/Data-ByteString-Lazy.html#v:map'>map</a> &#x2237; (Word8 &#x2192; Word8) &#x2192; ByteString &#x2192; ByteString</span><span class='hoogle-sub'>(<span class='hoogle-head'>package</span> <span class='hoogle-package'>bytestring</span>, <span class='hoogle-head'>module</span> <span class='hoogle-module'>Data.ByteString.Lazy</span>)</span><div class='hoogle-doc'><div class='hoogle-text'>O(n) map f xs is the ByteString obtained by applying f to each element of xs. \n",
"</div>\n",
"</div>\n",
"<span class='hoogle-name'><a target='_blank' href='http://hackage.haskell.org/packages/archive/bytestring/latest/doc/html/Data-ByteString.html#v:map'>map</a> &#x2237; (Word8 &#x2192; Word8) &#x2192; ByteString &#x2192; ByteString</span><span class='hoogle-sub'>(<span class='hoogle-head'>package</span> <span class='hoogle-package'>bytestring</span>, <span class='hoogle-head'>module</span> <span class='hoogle-module'>Data.ByteString</span>)</span><div class='hoogle-doc'><div class='hoogle-text'>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",
"</div>\n",
"</div>\n",
"<span class='hoogle-name'><a target='_blank' href='http://hackage.haskell.org/packages/archive/containers/latest/doc/html/Data-IntMap-Strict.html#v:map'>map</a> &#x2237; (a &#x2192; b) &#x2192; IntMap a &#x2192; IntMap b</span><span class='hoogle-sub'>(<span class='hoogle-head'>package</span> <span class='hoogle-package'>containers</span>, <span class='hoogle-head'>module</span> <span class='hoogle-module'>Data.IntMap.Strict</span>)</span><div class='hoogle-doc'><div class='hoogle-text'>O(n). Map a function over all values in the map.\n",
"</div>\n",
"<div class='hoogle-text'></div>\n",
"<div class='hoogle-code'>> map (++ \"x\") (fromList [(5,\"a\"), (3,\"b\")]) == fromList [(3, \"bx\"), (5, \"ax\")] \n",
"</div>\n",
"</div>\n",
"<span class='hoogle-name'><a target='_blank' href='http://hackage.haskell.org/packages/archive/containers/latest/doc/html/Data-Map-Lazy.html#v:map'>map</a> &#x2237; (a &#x2192; b) &#x2192; Map k a &#x2192; Map k b</span><span class='hoogle-sub'>(<span class='hoogle-head'>package</span> <span class='hoogle-package'>containers</span>, <span class='hoogle-head'>module</span> <span class='hoogle-module'>Data.Map.Lazy</span>)</span><div class='hoogle-doc'><div class='hoogle-text'>O(n). Map a function over all values in the map.\n",
"</div>\n",
"<div class='hoogle-text'></div>\n",
"<div class='hoogle-code'>> map (++ \"x\") (fromList [(5,\"a\"), (3,\"b\")]) == fromList [(3, \"bx\"), (5, \"ax\")] \n",
"</div>\n",
"</div>\n",
"<span class='hoogle-name'><a target='_blank' href='http://hackage.haskell.org/packages/archive/containers/latest/doc/html/Data-Set.html#v:map'>map</a> &#x2237; Ord b &#x21D2; (a &#x2192; b) &#x2192; Set a &#x2192; Set b</span><span class='hoogle-sub'>(<span class='hoogle-head'>package</span> <span class='hoogle-package'>containers</span>, <span class='hoogle-head'>module</span> <span class='hoogle-module'>Data.Set</span>)</span><div class='hoogle-doc'><div class='hoogle-text'>O(n*log n). map f s is the set obtained by applying f to each element of s.\n",
"</div>\n",
"<div class='hoogle-text'></div>\n",
"<div class='hoogle-text'>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",
"</div>\n",
"</div>\n"
],
"metadata": {},
"output_type": "display_data"
"output_type": "display_data",
"text": [
"\"he\\\"llo\""
]
}
],
"prompt_number": 4
"prompt_number": 13
},
{
"cell_type": "code",

View File

@ -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]