Skip to content

Commit 08e6afe

Browse files
author
José Valim
committed
Merge pull request #1096 from alco/iex-break-1089
Implement a break-trigger for incomplete expressions in IEx.
2 parents 8907edc + c4c0798 commit 08e6afe

File tree

2 files changed

+54
-8
lines changed

2 files changed

+54
-8
lines changed

lib/iex/lib/iex/helpers.ex

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ defmodule IEx.Helpers do
66
more joyful to work with.
77
88
This message was triggered by invoking the helper
9-
`h()`, usually referred as `h/0` (since it expects 0
9+
`h()`, usually referred to as `h/0` (since it expects 0
1010
arguments).
1111
1212
There are many other helpers available:
@@ -37,6 +37,36 @@ defmodule IEx.Helpers do
3737
h(Enum)
3838
h(Enum.reverse/1)
3939
40+
41+
## A note about expressions in IEx ##
42+
43+
IEx treats incomplete expressions in a special way, allowing one
44+
to spill an expression over multiple lines. For example,
45+
46+
iex(1)> "ab
47+
...(1)> c"
48+
"ab\nc"
49+
50+
In the example above, the shell will be expecting more input until it finds
51+
the closing quote. Sometimes it is not obvious which character the shell is
52+
expecting, and the user may find themselves trapped in the state of
53+
incomplete expression with no ability to terminate it other than by exiting
54+
the shell.
55+
56+
For such cases, there is a special break-trigger ("#iex:break") that when
57+
encountered on a line by itself will force the shell to break out of any
58+
pending expression and return to its normal state:
59+
60+
iex(1)> ["ab
61+
...(1)> c"
62+
...(1)> "
63+
...(1)> ]
64+
...(1)> #iex:break
65+
** (TokenMissingError) iex:1: incomplete expression
66+
...
67+
68+
iex(1)>
69+
4070
"""
4171

4272
@doc """

lib/iex/lib/iex/server.ex

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ defmodule IEx.Server do
1818

1919
defp do_loop(config) do
2020
counter = config.counter
21-
code = config.cache ++ io_get(config)
21+
code = config.cache
22+
line = io_get(config)
2223

2324
new_config =
2425
try do
25-
eval(code, counter, config)
26+
eval(code, line, counter, config)
2627
rescue
2728
exception ->
2829
print_stacktrace System.stacktrace, fn ->
@@ -49,9 +50,24 @@ defmodule IEx.Server do
4950
# to re-raise it.
5051
#
5152
# Returns updated config.
52-
defp eval(code, line, config) do
53-
file = "iex"
54-
case :elixir_translator.forms(code, line, file, []) do
53+
#
54+
# The first two clauses provide support for the break-trigger allowing to
55+
# break out from a pending incomplete expression. See
56+
# https://github.com/elixir-lang/elixir/issues/1089 for discussion.
57+
#
58+
@break_trigger '#iex:break\n'
59+
defp eval(_, @break_trigger, _, config=IEx.Config[cache: '']) do
60+
# do nothing
61+
config
62+
end
63+
64+
defp eval(_, @break_trigger, line_no, _) do
65+
:elixir_errors.parse_error(line_no, "iex", 'incomplete expression', [])
66+
end
67+
68+
defp eval(code_so_far, latest_input, line_no, config) do
69+
code = code_so_far ++ latest_input
70+
case :elixir_translator.forms(code, line_no, "iex", []) do
5571
{ :ok, forms } ->
5672
{ result, new_binding, scope } =
5773
:elixir.eval_forms(forms, config.binding, config.scope)
@@ -62,14 +78,14 @@ defmodule IEx.Server do
6278
update_history(config.cache(code).scope(nil))
6379
config.update_counter(&1+1).cache('').binding(new_binding).scope(scope)
6480

65-
{ :error, { line, error, token } } ->
81+
{ :error, { line_no, error, token } } ->
6682
if token == [] do
6783
# Update config.cache so that IEx continues to add new input to
6884
# the unfinished expression in `code`
6985
config.cache(code)
7086
else
7187
# Encountered malformed expression
72-
:elixir_errors.parse_error(line, file, error, token)
88+
:elixir_errors.parse_error(line_no, "iex", error, token)
7389
end
7490
end
7591
end

0 commit comments

Comments
 (0)