@@ -46,6 +46,9 @@ defmodule Code.Fragment do
4646 or `{:local_or_var, charlist}` and `charlist` is a static part
4747 Examples are `__MODULE__.Submodule` or `@hello.Submodule`
4848
49+ * `{:block_keyword_or_binary_operator, charlist}` - may be a block keyword (do, end, after,
50+ catch, else, rescue) or a binary operator
51+
4952 * `{:dot, inside_dot, charlist}` - the context is a dot
5053 where `inside_dot` is either a `{:var, charlist}`, `{:alias, charlist}`,
5154 `{:module_attribute, charlist}`, `{:unquoted_atom, charlist}` or a `dot`
@@ -139,6 +142,7 @@ defmodule Code.Fragment do
139142 @ spec cursor_context ( List.Chars . t ( ) , keyword ( ) ) ::
140143 { :alias , charlist }
141144 | { :alias , inside_alias , charlist }
145+ | { :block_keyword_or_binary_operator , charlist }
142146 | { :dot , inside_dot , charlist }
143147 | { :dot_arity , inside_dot , charlist }
144148 | { :dot_call , inside_dot , charlist }
@@ -188,15 +192,15 @@ defmodule Code.Fragment do
188192 cursor_context ( to_charlist ( other ) , opts )
189193 end
190194
191- @ operators ~c" \\ <>+-*/:=|&~^%!"
192- @ starter_punctuation ~c" ,([{;"
193- @ non_starter_punctuation ~c" )]}\" '.$ "
195+ @ operators ~c" \\ <>+-*/:=|&~^%!$ "
196+ @ starting_punctuation ~c" ,([{;"
197+ @ closing_punctuation ~c" )]}\" '"
194198 @ space ~c" \t \s "
195199 @ trailing_identifier ~c" ?!"
196200 @ tilde_op_prefix ~c" <=~"
197201
198202 @ non_identifier @ trailing_identifier ++
199- @ operators ++ @ starter_punctuation ++ @ non_starter_punctuation ++ @ space
203+ @ operators ++ @ starting_punctuation ++ @ closing_punctuation ++ @ space ++ [ ?. ]
200204
201205 @ textual_operators ~w( when not and or in) c
202206 @ keywords ~w( do end after else catch rescue fn true false nil) c
@@ -226,11 +230,11 @@ defmodule Code.Fragment do
226230 # A local arity definition
227231 [ ?/ | rest ] -> arity_to_cursor_context ( strip_spaces ( rest , spaces + 1 ) )
228232 # Starting a new expression
229- [ h | _ ] when h in @ starter_punctuation -> { :expr , 0 }
230- # It is a local or remote call without parens
231- rest when spaces > 0 -> call_to_cursor_context ( { rest , spaces } )
233+ [ h | _ ] when h in @ starting_punctuation -> { :expr , 0 }
234+ # It is keyword, binary operator, a local or remote call without parens
235+ rest when spaces > 0 -> closing_or_call_to_cursor_context ( { rest , spaces } )
232236 # It is an identifier
233- _ -> identifier_to_cursor_context ( reverse , 0 , false )
237+ _ -> identifier_to_cursor_context ( reverse , spaces , false )
234238 end
235239 end
236240
@@ -269,6 +273,14 @@ defmodule Code.Fragment do
269273 end
270274 end
271275
276+ defp closing_or_call_to_cursor_context ( { reverse , spaces } ) do
277+ if closing? ( reverse ) do
278+ { { :block_keyword_or_binary_operator , ~c" " } , 0 }
279+ else
280+ call_to_cursor_context ( { reverse , spaces } )
281+ end
282+ end
283+
272284 defp identifier_to_cursor_context ( [ ?. , ?. , ?: | _ ] , n , _ ) , do: { { :unquoted_atom , ~c" .." } , n + 3 }
273285 defp identifier_to_cursor_context ( [ ?. , ?. , ?. | _ ] , n , _ ) , do: { { :local_or_var , ~c" ..." } , n + 3 }
274286 defp identifier_to_cursor_context ( [ ?. , ?: | _ ] , n , _ ) , do: { { :unquoted_atom , ~c" ." } , n + 2 }
@@ -320,15 +332,42 @@ defmodule Code.Fragment do
320332 { ~c" ." ++ rest , count } when rest == [ ] or hd ( rest ) != ?. ->
321333 dot ( rest , count + 1 , acc )
322334
323- _ ->
324- { { :local_or_var , acc } , count }
335+ { rest , rest_count } ->
336+ response =
337+ if rest_count > count and closing? ( rest ) ,
338+ do: :block_keyword_or_binary_operator ,
339+ else: :local_or_var
340+
341+ { { response , acc } , count }
325342 end
326343
327344 { :capture_arg , acc , count } ->
328345 { { :capture_arg , acc } , count }
329346 end
330347 end
331348
349+ # If it is a closing punctuation
350+ defp closing? ( [ h | _ ] ) when h in @ closing_punctuation , do: true
351+ # Closing bitstring (but deal with operators)
352+ defp closing? ( [ ?> , ?> | rest ] ) , do: rest == [ ] or hd ( rest ) not in [ ?> , ?~ ]
353+ # Keywords
354+ defp closing? ( rest ) do
355+ case split_non_identifier ( rest , [ ] ) do
356+ { ~c" nil" , _ } -> true
357+ { ~c" true" , _ } -> true
358+ { ~c" false" , _ } -> true
359+ { [ digit | _ ] , _ } when digit in ?0 .. ?9 -> true
360+ { [ upper | _ ] , _ } when upper in ?A .. ?Z -> true
361+ { [ _ | _ ] , [ ?: | rest ] } -> rest == [ ] or hd ( rest ) != ?:
362+ { _ , _ } -> false
363+ end
364+ end
365+
366+ defp split_non_identifier ( [ h | t ] , acc ) when h not in @ non_identifier ,
367+ do: split_non_identifier ( t , [ h | acc ] )
368+
369+ defp split_non_identifier ( rest , acc ) , do: { acc , rest }
370+
332371 defp identifier ( [ ?? | rest ] , count ) , do: check_identifier ( rest , count + 1 , [ ?? ] )
333372 defp identifier ( [ ?! | rest ] , count ) , do: check_identifier ( rest , count + 1 , [ ?! ] )
334373 defp identifier ( rest , count ) , do: check_identifier ( rest , count , [ ] )
0 commit comments