From 34b24331552a6c53b973bbf3d906d86ecc72001b Mon Sep 17 00:00:00 2001 From: doorgan Date: Tue, 8 Apr 2025 14:46:11 -0300 Subject: [PATCH] [fix] Fix `prefix_stream` crashing on interpolations with newlines Interpolations with newlines contain an `:eol` token in the shape of `{:eol, positions}`, which `get_start_pos` wasn't considering, causing a crash. The solution I went for here is to just consider that token shape when dealing with interpolation tokens. As far as I can see the tokens inside the interpolation aren't being normalized, so I kept that behavior. The test I added uses the `~S` sigil instead of the helper `~q` sigil. This is due to the `~q` sigil escaping the interpolation in a way that didn't reproduce the issue I saw in an Expert release. The code in the new test is the snippet that caused the crashes while editing Sourceror, so to reproduce this locally it's enough to paste that into a project and try to trigger a completion like `Enum|`. --- apps/common/lib/lexical/ast/tokens.ex | 4 +++ apps/common/test/lexical/ast/tokens_test.exs | 35 ++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/apps/common/lib/lexical/ast/tokens.ex b/apps/common/lib/lexical/ast/tokens.ex index f7bf3026..f8c3494c 100644 --- a/apps/common/lib/lexical/ast/tokens.ex +++ b/apps/common/lib/lexical/ast/tokens.ex @@ -163,6 +163,10 @@ defmodule Lexical.Ast.Tokens do Enum.reverse(ranges) end + defp get_start_pos([{:eol, {start_line, start_column, _}} | _]) do + {start_line, start_column} + end + defp get_start_pos([{_, {start_line, start_column, _}, _} | _]) do {start_line, start_column} end diff --git a/apps/common/test/lexical/ast/tokens_test.exs b/apps/common/test/lexical/ast/tokens_test.exs index 837b8fcb..15d1d215 100644 --- a/apps/common/test/lexical/ast/tokens_test.exs +++ b/apps/common/test/lexical/ast/tokens_test.exs @@ -47,5 +47,40 @@ defmodule Lexical.Ast.TokensTest do assert Enum.to_list(tokens) == [] end + + test "works on interpolations with newlines" do + text = ~S[ + ~S""" + "foo«#{ + 2 + }»bar" + """ + | + ] + + {position, document} = pop_cursor(text, as: :document) + + tokens = Tokens.prefix_stream(document, position) + + assert Enum.to_list(tokens) == [ + {:eol, '\n', []}, + {:eol, '\n', []}, + {:eol, '\n', []}, + {:eol, '\n', []}, + { + :interpolated_string, + [ + {:literal, "foo«", {{1, 1}, {1, 5}}}, + {:interpolation, + [{:eol, {3, 18, 1}}, {:int, {4, 13, 2}, '2'}, {:eol, {4, 14, 1}}], + {{3, 18}, {5, 11}}}, + {:literal, "»bar", {{5, 11}, {5, 15}}} + ], + {3, 11} + }, + {:eol, '\n', []}, + {:eol, '\n', []} + ] + end end end