Skip to content

Commit 748b70d

Browse files
author
José Valim
committed
Slightly tidy up IEx.Server
1 parent 0267f31 commit 748b70d

File tree

3 files changed

+103
-88
lines changed

3 files changed

+103
-88
lines changed

lib/iex/lib/iex.ex

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,19 @@ defmodule IEx do
171171
match?({ :ok, true }, :application.get_env(:iex, :started))
172172
end
173173

174+
@doc """
175+
Returns `string` escaped using the specified color.
176+
ANSI escapes in `string` are not processed in any way.
177+
"""
178+
def color(color_name, string) do
179+
colors = IEx.Options.get(:colors)
180+
enabled = colors[:enabled]
181+
IO.ANSI.escape_fragment("%{#{colors[color_name]}}", enabled)
182+
<> string <> IO.ANSI.escape_fragment("%{reset}", enabled)
183+
end
184+
185+
## Callbacks
186+
174187
# This is a callback invoked by Erlang shell utilities
175188
# when someone press Ctrl+G and adds 's Elixir.IEx'.
176189
@doc false
@@ -187,8 +200,6 @@ defmodule IEx do
187200
_ -> :init.wait_until_started()
188201
end
189202

190-
Process.flag(:trap_exit, true)
191-
192203
start_iex()
193204
callback.()
194205

@@ -199,19 +210,6 @@ defmodule IEx do
199210
end
200211

201212
@doc false
202-
def dont_display_result, do: :"do not show this result in iex"
203-
204-
## Boot Helpers
205-
206-
defp start_iex do
207-
:application.start(:elixir)
208-
:application.start(:iex)
209-
end
210-
211-
@doc """
212-
Returns the default config used to launch IEx. This config is also used by
213-
`IEx.TestFramework`.
214-
"""
215213
def boot_config(opts) do
216214
scope = :elixir.scope_for_eval(
217215
file: "iex",
@@ -225,6 +223,22 @@ defmodule IEx do
225223
]
226224
end
227225

226+
@doc false
227+
def dont_display_result, do: :"do not show this result in iex"
228+
229+
## Helpers
230+
231+
defp start_iex do
232+
:application.start(:elixir)
233+
:application.start(:iex)
234+
235+
# Disable ANSI-escape-sequence-based coloring on Windows
236+
# Can be overriden in .iex
237+
if match?({ :win32, _ }, :os.type()) do
238+
IEx.Options.set :colors, enabled: false
239+
end
240+
end
241+
228242
defp set_expand_fun do
229243
gl = Process.group_leader
230244
glnode = node gl
@@ -249,15 +263,4 @@ defmodule IEx do
249263
defp run_after_spawn do
250264
lc fun inlist Enum.reverse(after_spawn), do: fun.()
251265
end
252-
253-
@doc """
254-
Returns `string` escaped using the specified color. ANSI escapes in `string`
255-
are not processed in any way.
256-
"""
257-
def color(color_name, string) do
258-
colors = IEx.Options.get(:colors)
259-
enabled = colors[:enabled]
260-
IO.ANSI.escape_fragment("%{#{colors[color_name]}}", enabled)
261-
<> string <> IO.ANSI.escape_fragment("%{reset}", enabled)
262-
end
263266
end

lib/iex/lib/iex/history.ex

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
defmodule IEx.History do
22
@moduledoc false
33

4+
@doc """
5+
Initializes IEx process variables. All history
6+
information is kept in the process dictionary.
7+
"""
48
def init do
59
Process.put(:iex_history_start_counter, 1)
610
Process.put(:iex_history_counter, 1)
711
end
812

9-
### append ###
10-
13+
@doc """
14+
Appends one entry to the history with the given counter.
15+
"""
1116
def append(entry, counter) do
1217
Process.put({:iex_history, counter}, entry)
1318
Process.put(:iex_history_counter, counter+1)
@@ -93,8 +98,9 @@ defmodule IEx.History do
9398
:erlang.garbage_collect()
9499
end
95100

96-
### each ###
97-
101+
@doc """
102+
Enumerates each item in the history.
103+
"""
98104
def each(fun) do
99105
each(Process.get(:iex_history_start_counter),
100106
Process.get(:iex_history_counter),
@@ -111,8 +117,9 @@ defmodule IEx.History do
111117
:ok
112118
end
113119

114-
### nth ###
115-
120+
@doc """
121+
Gets the nth item in the history.
122+
"""
116123
def nth(n) do
117124
entry = case n do
118125
n when n >= 0 ->

lib/iex/lib/iex/server.ex

Lines changed: 61 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ defmodule IEx.Server do
44
@doc """
55
Eval loop for an IEx session. Its responsibilities include:
66
7-
* loading of .iex files
8-
* reading input
9-
* trapping exceptions in the code being evaluated
10-
* keeping expression history
7+
* loading of .iex files
8+
* reading input
9+
* trapping exceptions in the code being evaluated
10+
* keeping expression history
1111
1212
"""
1313
def start(config) do
14-
init_systems()
14+
IEx.History.init
1515

1616
{ _, _, scope } = :elixir.eval('require IEx.Helpers', [], 0, config.scope)
1717
config = config.scope(scope)
@@ -26,17 +26,22 @@ defmodule IEx.Server do
2626

2727
old_flag = Process.flag(:trap_exit, true)
2828
self_pid = self
29+
30+
# We have one loop for receiving input and
31+
# another loop for evaluating contents.
2932
pid = spawn_link(fn -> input_loop(self_pid) end)
3033

3134
try do
32-
do_loop(config.input_pid(pid))
35+
eval_loop(config.input_pid(pid))
3336
after
3437
Process.exit(pid, :normal)
3538
Process.flag(:trap_exit, old_flag)
3639
end
3740
end
3841

39-
defp do_loop(config) do
42+
## Eval loop
43+
44+
defp eval_loop(config) do
4045
prefix = config.cache != []
4146
config.input_pid <- { :do_input, prefix, config.counter }
4247
wait_input(config)
@@ -57,7 +62,7 @@ defmodule IEx.Server do
5762
config.cache('')
5863
end
5964

60-
do_loop(new_config)
65+
eval_loop(new_config)
6166
end
6267

6368
{ :EXIT, _pid, :normal } ->
@@ -68,16 +73,6 @@ defmodule IEx.Server do
6873
end
6974
end
7075

71-
defp init_systems() do
72-
IEx.History.init
73-
74-
# Disable ANSI-escape-sequence-based coloring on Windows
75-
# Can be overriden in .iex
76-
if match?({ :win32, _ }, :os.type()) do
77-
IEx.Options.set :colors, enabled: false
78-
end
79-
end
80-
8176
# Instead of doing just `:elixir.eval`, we first parse the expression to see
8277
# if it's well formed. If parsing succeeds, we evaluate the AST as usual.
8378
#
@@ -128,45 +123,69 @@ defmodule IEx.Server do
128123
end
129124
end
130125

126+
defp update_history(config) do
127+
IEx.History.append(config, config.counter)
128+
end
129+
130+
defp io_put(result) do
131+
IO.puts :stdio, IEx.color(:eval_result, inspect(result, inspect_opts))
132+
end
133+
134+
defp io_error(result) do
135+
IO.puts :stdio, IEx.color(:error, result)
136+
end
137+
138+
defp inspect_opts do
139+
opts = IEx.Options.get(:inspect)
140+
case :io.columns(:standard_input) do
141+
{ :ok, width } -> Keyword.put(opts, :width, min(width, 80))
142+
{ :error, _ } -> opts
143+
end
144+
end
145+
146+
## Load dot iex helpers
147+
131148
# Locates and loads an .iex file from one of predefined locations. Returns
132149
# new config.
133150
defp load_dot_iex(config, path // nil) do
134151
candidates = if path do
135152
[path]
136153
else
137-
Enum.map [".iex", "~/.iex"], &Path.expand(&1)
154+
Enum.map [".iex", "~/.iex"], &Path.expand/1
138155
end
139156

140-
path = Enum.find candidates, fn path -> File.regular?(path) end
157+
path = Enum.find candidates, &File.regular?/1
141158

142159
if nil?(path) do
143160
config
144161
else
145-
try do
146-
code = File.read!(path)
147-
scope = :elixir.scope_for_eval(config.scope, file: path)
148-
149-
# Evaluate the contents in the same environment do_loop will run in
150-
{ _result, binding, scope } =
151-
:elixir.eval(String.to_char_list!(code),
152-
config.binding,
153-
0,
154-
scope)
155-
156-
scope = :elixir.scope_for_eval(scope, file: "iex")
157-
config.binding(binding).scope(scope)
158-
catch
159-
kind, error ->
160-
print_error(kind, Exception.normalize(kind, error), System.stacktrace)
161-
System.halt(1)
162-
end
162+
eval_dot_iex(config, path)
163163
end
164164
end
165165

166-
defp update_history(config) do
167-
IEx.History.append(config, config.counter)
166+
defp eval_dot_iex(config, path) do
167+
try do
168+
code = File.read!(path)
169+
scope = :elixir.scope_for_eval(config.scope, file: path)
170+
171+
# Evaluate the contents in the same environment eval_loop will run in
172+
{ _result, binding, scope } =
173+
:elixir.eval(String.to_char_list!(code),
174+
config.binding,
175+
0,
176+
scope)
177+
178+
scope = :elixir.scope_for_eval(scope, file: "iex")
179+
config.binding(binding).scope(scope)
180+
catch
181+
kind, error ->
182+
print_error(kind, Exception.normalize(kind, error), System.stacktrace)
183+
System.halt(1)
184+
end
168185
end
169186

187+
## Input loop
188+
170189
defp input_loop(iex_pid) do
171190
receive do
172191
{ :do_input, prefix, counter } ->
@@ -192,26 +211,12 @@ defmodule IEx.Server do
192211
end
193212
end
194213

195-
defp io_put(result) do
196-
IO.puts :stdio, IEx.color(:eval_result, inspect(result, inspect_opts))
197-
end
198-
199-
defp io_error(result) do
200-
IO.puts :stdio, IEx.color(:error, result)
201-
end
202-
203-
defp inspect_opts do
204-
opts = IEx.Options.get(:inspect)
205-
case :io.columns(:standard_input) do
206-
{ :ok, width } -> Keyword.put(opts, :width, min(width, 80))
207-
{ :error, _ } -> opts
208-
end
209-
end
210-
211214
defp remote_prefix do
212215
if node == node(:erlang.group_leader), do: "iex", else: "rem"
213216
end
214217

218+
## Error handling
219+
215220
defp print_error(:error, exception, stacktrace) do
216221
print_stacktrace stacktrace, fn ->
217222
"** (#{inspect exception.__record__(:name)}) #{exception.message}"

0 commit comments

Comments
 (0)