From 497d9b797cb7032fcb9f2c0fc7294c46c88bac32 Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Sun, 8 Oct 2023 18:49:52 +0200 Subject: [PATCH] Fix regex matching prefix extend character classes to cover unicode letters correctly escape all chars add missing unicode regex modifiers fixes https://github.com/elixir-lsp/elixir_sense/issues/221 --- lib/elixir_sense.ex | 2 +- lib/elixir_sense/core/metadata_builder.ex | 2 +- lib/elixir_sense/core/parser.ex | 2 +- lib/elixir_sense/core/source.ex | 2 +- lib/elixir_sense/plugins/ecto.ex | 2 +- lib/elixir_sense/plugins/ecto/query.ex | 4 ++-- .../providers/suggestion/reducers/callbacks.ex | 2 +- test/elixir_sense/suggestions_test.exs | 16 ++++++++++++++++ 8 files changed, 24 insertions(+), 8 deletions(-) diff --git a/lib/elixir_sense.ex b/lib/elixir_sense.ex index 172d7a2b..178509f5 100644 --- a/lib/elixir_sense.ex +++ b/lib/elixir_sense.ex @@ -535,7 +535,7 @@ defmodule ElixirSense do # Fix incomplete kw key, e.g. cursor after `option1: 1, opt` fix_incomplete_kw_key = fn text_before, text_after -> - if Regex.match?(~r/\,\s*([\p{L}_][\p{L}\p{N}_@]*[?!]?)?$/, text_before) do + if Regex.match?(~r/\,\s*([\p{L}_][\p{L}\p{N}_@]*[?!]?)?$/u, text_before) do text_before <> ": :__fake_value__" <> text_after end end diff --git a/lib/elixir_sense/core/metadata_builder.ex b/lib/elixir_sense/core/metadata_builder.ex index fec68df1..6a00fabf 100644 --- a/lib/elixir_sense/core/metadata_builder.ex +++ b/lib/elixir_sense/core/metadata_builder.ex @@ -778,7 +778,7 @@ defmodule ElixirSense.Core.MetadataBuilder do defp pre({:@, meta_attr, [{name, meta, params}]}, state) when is_atom(name) do name_string = Atom.to_string(name) - if String.match?(name_string, ~r/^[_\p{Ll}\p{Lo}][\p{L}\p{N}_]*[?!]?$/) and + if String.match?(name_string, ~r/^[_\p{Ll}\p{Lo}][\p{L}\p{N}_]*[?!]?$/u) and not String.starts_with?(name_string, "__atom_elixir_marker_") do line = Keyword.fetch!(meta_attr, :line) column = Keyword.fetch!(meta_attr, :column) diff --git a/lib/elixir_sense/core/parser.ex b/lib/elixir_sense/core/parser.ex index 902afe85..46088493 100644 --- a/lib/elixir_sense/core/parser.ex +++ b/lib/elixir_sense/core/parser.ex @@ -246,7 +246,7 @@ defmodule ElixirSense.Core.Parser do |> Enum.join("\n") _ -> - if Regex.match?(~r/^[\p{L}_][\p{L}\p{N}_@]*[?!]?$/, token) do + if Regex.match?(~r/^[\p{L}_][\p{L}\p{N}_@]*[?!]?$/u, token) do remove_line(source, line) else replace_line_with_marker(source, line) diff --git a/lib/elixir_sense/core/source.ex b/lib/elixir_sense/core/source.ex index 9800f174..ed1e22cb 100644 --- a/lib/elixir_sense/core/source.ex +++ b/lib/elixir_sense/core/source.ex @@ -111,7 +111,7 @@ defmodule ElixirSense.Core.Source do line_str = line |> String.slice(0, col - 1) - case Regex.run(~r/[\w0-9\._!\?\:@&\^~+<>=*\/|\\]+$/, line_str) do + case Regex.run(~r/[\p{L}\p{N}\.\_\!\?\:\@\&\^\~\+\-\<\>\=\*\/\|\\]+$/u, line_str) do nil -> "" [prefix] when is_binary(prefix) -> prefix end diff --git a/lib/elixir_sense/plugins/ecto.ex b/lib/elixir_sense/plugins/ecto.ex index 3a47112f..7ee61313 100644 --- a/lib/elixir_sense/plugins/ecto.ex +++ b/lib/elixir_sense/plugins/ecto.ex @@ -80,7 +80,7 @@ defmodule ElixirSense.Plugins.Ecto do end), assoc_code <- Source.text_after(text_before, line, col), [_, var] <- - Regex.run(~r/^assoc\(\s*([_\p{Ll}\p{Lo}][\p{L}\p{N}_]*[?!]?)\s*,/, assoc_code), + Regex.run(~r/^assoc\(\s*([_\p{Ll}\p{Lo}][\p{L}\p{N}_]*[?!]?)\s*,/u, assoc_code), %{^var => %{type: type}} <- Query.extract_bindings(text_before, from_info, env, meta), true <- function_exported?(type, :__schema__, 1) do {:override, Query.find_assoc_suggestions(type, hint)} diff --git a/lib/elixir_sense/plugins/ecto/query.ex b/lib/elixir_sense/plugins/ecto/query.ex index 6dcdf894..26fee2f0 100644 --- a/lib/elixir_sense/plugins/ecto/query.ex +++ b/lib/elixir_sense/plugins/ecto/query.ex @@ -215,7 +215,7 @@ defmodule ElixirSense.Plugins.Ecto.Query do def extract_bindings(prefix, %{pos: {{line, col}, _}} = func_info, env, buffer_metadata) do func_code = Source.text_after(prefix, line, col) - from_matches = Regex.scan(~r/^.+\(?\s*(#{@binding_r})/, func_code) + from_matches = Regex.scan(~r/^.+\(?\s*(#{@binding_r})/u, func_code) # TODO this code is broken # depends on join positions that we are unable to get from AST @@ -223,7 +223,7 @@ defmodule ElixirSense.Plugins.Ecto.Query do join_matches = for join when join in @joins <- func_info.options_so_far, code = Source.text_after(prefix, line, col), - match <- Regex.scan(~r/^#{join}\:\s*(#{@binding_r})/, code) do + match <- Regex.scan(~r/^#{join}\:\s*(#{@binding_r})/u, code) do match end diff --git a/lib/elixir_sense/providers/suggestion/reducers/callbacks.ex b/lib/elixir_sense/providers/suggestion/reducers/callbacks.ex index 9103ed87..e62d81f8 100644 --- a/lib/elixir_sense/providers/suggestion/reducers/callbacks.ex +++ b/lib/elixir_sense/providers/suggestion/reducers/callbacks.ex @@ -75,7 +75,7 @@ defmodule ElixirSense.Providers.Suggestion.Reducers.Callbacks do list = Enum.sort(list) cond do - Regex.match?(~r/\s(def|defmacro)\s+([_\p{Ll}\p{Lo}][\p{L}\p{N}_]*[?!]?)?$/, text_before) -> + Regex.match?(~r/\s(def|defmacro)\s+([_\p{Ll}\p{Lo}][\p{L}\p{N}_]*[?!]?)?$/u, text_before) -> {:halt, %{acc | result: list}} match?({_f, _a}, scope) -> diff --git a/test/elixir_sense/suggestions_test.exs b/test/elixir_sense/suggestions_test.exs index ca756d83..a33da46a 100644 --- a/test/elixir_sense/suggestions_test.exs +++ b/test/elixir_sense/suggestions_test.exs @@ -213,6 +213,22 @@ defmodule ElixirSense.SuggestionsTest do assert list |> Enum.any?(&(&1.type == :function)) end + test "functions from unicode module" do + buffer = """ + defmodule :你好 do + def 运行 do + IO.puts("你好") + end + end + + :你好. + """ + + list = ElixirSense.suggestions(buffer, 7, 5) + + assert list |> Enum.any?(&(&1.type == :function && &1.name == "运行")) + end + test "with an alias" do buffer = """ defmodule MyModule do