diff --git a/lib/elixir/lib/code/fragment.ex b/lib/elixir/lib/code/fragment.ex index 806c466544..7e7ab221d4 100644 --- a/lib/elixir/lib/code/fragment.ex +++ b/lib/elixir/lib/code/fragment.ex @@ -619,7 +619,7 @@ defmodule Code.Fragment do {reversed_pre, post} = adjust_position(reversed_pre, post) case take_identifier(post, []) do - {_, [], _} -> + :none -> maybe_operator(reversed_pre, post, line, opts) {:identifier, reversed_post, rest} -> @@ -627,7 +627,7 @@ defmodule Code.Fragment do reversed = reversed_post ++ reversed_pre case codepoint_cursor_context(reversed, opts) do - {{:struct, acc}, offset} -> + {{:struct, acc}, offset} when acc != [] -> build_surround({:struct, acc}, reversed, line, offset) {{:alias, acc}, offset} -> @@ -729,15 +729,31 @@ defmodule Code.Fragment do do: take_identifier(t, [h | acc]) defp take_identifier(rest, acc) do - with {[?. | t], _} <- strip_spaces(rest, 0), + {stripped, _} = strip_spaces(rest, 0) + + with [?. | t] <- stripped, {[h | _], _} when h in ?A..?Z <- strip_spaces(t, 0) do take_alias(rest, acc) else - _ -> {:identifier, acc, rest} + # Consider it an identifier if we are at the end of line + # or if we have spaces not followed by . (call) or / (arity) + _ when acc == [] and (rest == [] or (hd(rest) in @space and hd(stripped) not in ~c"/.")) -> + {:identifier, acc, rest} + + # If we are immediately followed by a container, we are still part of the identifier. + # We don't consider << as it _may_ be an operator. + _ when acc == [] and hd(stripped) in ~c"({[" -> + {:identifier, acc, rest} + + _ when acc == [] -> + :none + + _ -> + {:identifier, acc, rest} end end - defp take_alias([h | t], acc) when h not in @non_identifier, + defp take_alias([h | t], acc) when h in ?A..?Z or h in ?a..?z or h in ?0..9 or h == ?_, do: take_alias(t, [h | acc]) defp take_alias(rest, acc) do diff --git a/lib/elixir/test/elixir/code_fragment_test.exs b/lib/elixir/test/elixir/code_fragment_test.exs index cedcd181a4..3c60adc98a 100644 --- a/lib/elixir/test/elixir/code_fragment_test.exs +++ b/lib/elixir/test/elixir/code_fragment_test.exs @@ -428,11 +428,12 @@ defmodule CodeFragmentTest do end test "column out of range" do - assert CF.surround_context("hello", {1, 20}) == :none + assert CF.surround_context("hello", {1, 20}) == + %{begin: {1, 1}, context: {:local_or_var, ~c"hello"}, end: {1, 6}} end test "local_or_var" do - for i <- 1..8 do + for i <- 1..9 do assert CF.surround_context("hello_wo", {1, i}) == %{ context: {:local_or_var, ~c"hello_wo"}, begin: {1, 1}, @@ -440,9 +441,9 @@ defmodule CodeFragmentTest do } end - assert CF.surround_context("hello_wo", {1, 9}) == :none + assert CF.surround_context("hello_wo ", {1, 10}) == :none - for i <- 2..9 do + for i <- 2..10 do assert CF.surround_context(" hello_wo", {1, i}) == %{ context: {:local_or_var, ~c"hello_wo"}, begin: {1, 2}, @@ -450,9 +451,9 @@ defmodule CodeFragmentTest do } end - assert CF.surround_context(" hello_wo", {1, 10}) == :none + assert CF.surround_context(" hello_wo ", {1, 11}) == :none - for i <- 1..6 do + for i <- 1..7 do assert CF.surround_context("hello!", {1, i}) == %{ context: {:local_or_var, ~c"hello!"}, begin: {1, 1}, @@ -460,9 +461,9 @@ defmodule CodeFragmentTest do } end - assert CF.surround_context("hello!", {1, 7}) == :none + assert CF.surround_context("hello! ", {1, 8}) == :none - for i <- 1..5 do + for i <- 1..6 do assert CF.surround_context("안녕_세상", {1, i}) == %{ context: {:local_or_var, ~c"안녕_세상"}, begin: {1, 1}, @@ -470,7 +471,7 @@ defmodule CodeFragmentTest do } end - assert CF.surround_context("안녕_세상", {1, 6}) == :none + assert CF.surround_context("안녕_세상 ", {1, 6}) == :none # Keywords are not local or var for keyword <- ~w(do end after catch else rescue fn true false nil)c do @@ -484,8 +485,38 @@ defmodule CodeFragmentTest do end end - test "local call" do + test "local + operator" do for i <- 1..8 do + assert CF.surround_context("hello_wo+", {1, i}) == %{ + context: {:local_or_var, ~c"hello_wo"}, + begin: {1, 1}, + end: {1, 9} + } + end + + assert CF.surround_context("hello_wo+", {1, 9}) == %{ + begin: {1, 9}, + context: {:operator, ~c"+"}, + end: {1, 10} + } + + for i <- 1..9 do + assert CF.surround_context("hello_wo +", {1, i}) == %{ + context: {:local_or_var, ~c"hello_wo"}, + begin: {1, 1}, + end: {1, 9} + } + end + + assert CF.surround_context("hello_wo +", {1, 10}) == %{ + begin: {1, 10}, + context: {:operator, ~c"+"}, + end: {1, 11} + } + end + + test "local call" do + for i <- 1..9 do assert CF.surround_context("hello_wo(", {1, i}) == %{ context: {:local_call, ~c"hello_wo"}, begin: {1, 1}, @@ -493,9 +524,9 @@ defmodule CodeFragmentTest do } end - assert CF.surround_context("hello_wo(", {1, 9}) == :none + assert CF.surround_context("hello_wo(", {1, 10}) == :none - for i <- 1..8 do + for i <- 1..9 do assert CF.surround_context("hello_wo (", {1, i}) == %{ context: {:local_call, ~c"hello_wo"}, begin: {1, 1}, @@ -503,9 +534,10 @@ defmodule CodeFragmentTest do } end - assert CF.surround_context("hello_wo (", {1, 9}) == :none + assert CF.surround_context("hello_wo (", {1, 10}) == :none + assert CF.surround_context("hello_wo (", {1, 11}) == :none - for i <- 1..6 do + for i <- 1..7 do assert CF.surround_context("hello!(", {1, i}) == %{ context: {:local_call, ~c"hello!"}, begin: {1, 1}, @@ -513,9 +545,9 @@ defmodule CodeFragmentTest do } end - assert CF.surround_context("hello!(", {1, 7}) == :none + assert CF.surround_context("hello!(", {1, 8}) == :none - for i <- 1..5 do + for i <- 1..6 do assert CF.surround_context("안녕_세상(", {1, i}) == %{ context: {:local_call, ~c"안녕_세상"}, begin: {1, 1}, @@ -523,7 +555,7 @@ defmodule CodeFragmentTest do } end - assert CF.surround_context("안녕_세상(", {1, 6}) == :none + assert CF.surround_context("안녕_세상(", {1, 7}) == :none end test "local arity" do @@ -651,7 +683,7 @@ defmodule CodeFragmentTest do end test "alias" do - for i <- 1..8 do + for i <- 1..9 do assert CF.surround_context("HelloWor", {1, i}) == %{ context: {:alias, ~c"HelloWor"}, begin: {1, 1}, @@ -659,9 +691,9 @@ defmodule CodeFragmentTest do } end - assert CF.surround_context("HelloWor", {1, 9}) == :none + assert CF.surround_context("HelloWor ", {1, 10}) == :none - for i <- 2..9 do + for i <- 2..10 do assert CF.surround_context(" HelloWor", {1, i}) == %{ context: {:alias, ~c"HelloWor"}, begin: {1, 2}, @@ -669,9 +701,9 @@ defmodule CodeFragmentTest do } end - assert CF.surround_context(" HelloWor", {1, 10}) == :none + assert CF.surround_context(" HelloWor ", {1, 11}) == :none - for i <- 1..9 do + for i <- 1..10 do assert CF.surround_context("Hello.Wor", {1, i}) == %{ context: {:alias, ~c"Hello.Wor"}, begin: {1, 1}, @@ -679,9 +711,9 @@ defmodule CodeFragmentTest do } end - assert CF.surround_context("Hello.Wor", {1, 10}) == :none + assert CF.surround_context("Hello.Wor ", {1, 11}) == :none - for i <- 1..11 do + for i <- 1..12 do assert CF.surround_context("Hello . Wor", {1, i}) == %{ context: {:alias, ~c"Hello.Wor"}, begin: {1, 1}, @@ -689,9 +721,9 @@ defmodule CodeFragmentTest do } end - assert CF.surround_context("Hello . Wor", {1, 12}) == :none + assert CF.surround_context("Hello . Wor ", {1, 13}) == :none - for i <- 1..15 do + for i <- 1..16 do assert CF.surround_context("Foo . Bar . Baz", {1, i}) == %{ context: {:alias, ~c"Foo.Bar.Baz"}, begin: {1, 1}, @@ -858,7 +890,7 @@ defmodule CodeFragmentTest do end: {1, 15} } - for i <- 2..9 do + for i <- 2..10 do assert CF.surround_context("%HelloWor", {1, i}) == %{ context: {:struct, ~c"HelloWor"}, begin: {1, 1}, @@ -866,7 +898,7 @@ defmodule CodeFragmentTest do } end - assert CF.surround_context("%HelloWor", {1, 10}) == :none + assert CF.surround_context("%HelloWor ", {1, 11}) == :none # With dot assert CF.surround_context("%Hello.Wor", {1, 1}) == %{ @@ -875,7 +907,7 @@ defmodule CodeFragmentTest do end: {1, 11} } - for i <- 2..10 do + for i <- 2..11 do assert CF.surround_context("%Hello.Wor", {1, i}) == %{ context: {:struct, ~c"Hello.Wor"}, begin: {1, 1}, @@ -883,7 +915,7 @@ defmodule CodeFragmentTest do } end - assert CF.surround_context("%Hello.Wor", {1, 11}) == :none + assert CF.surround_context("%Hello.Wor ", {1, 12}) == :none # With spaces assert CF.surround_context("% Hello . Wor", {1, 1}) == %{ @@ -892,7 +924,7 @@ defmodule CodeFragmentTest do end: {1, 14} } - for i <- 2..13 do + for i <- 2..14 do assert CF.surround_context("% Hello . Wor", {1, i}) == %{ context: {:struct, ~c"Hello.Wor"}, begin: {1, 1}, @@ -900,7 +932,7 @@ defmodule CodeFragmentTest do } end - assert CF.surround_context("% Hello . Wor", {1, 14}) == :none + assert CF.surround_context("% Hello . Wor ", {1, 15}) == :none end test "module attributes" do