@@ -4,14 +4,14 @@ defmodule IEx.Server do
4
4
@ doc """
5
5
Eval loop for an IEx session. Its responsibilities include:
6
6
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
11
11
12
12
"""
13
13
def start ( config ) do
14
- init_systems ( )
14
+ IEx.History . init
15
15
16
16
{ _ , _ , scope } = :elixir . eval ( 'require IEx.Helpers' , [ ] , 0 , config . scope )
17
17
config = config . scope ( scope )
@@ -26,17 +26,22 @@ defmodule IEx.Server do
26
26
27
27
old_flag = Process . flag ( :trap_exit , true )
28
28
self_pid = self
29
+
30
+ # We have one loop for receiving input and
31
+ # another loop for evaluating contents.
29
32
pid = spawn_link ( fn -> input_loop ( self_pid ) end )
30
33
31
34
try do
32
- do_loop ( config . input_pid ( pid ) )
35
+ eval_loop ( config . input_pid ( pid ) )
33
36
after
34
37
Process . exit ( pid , :normal )
35
38
Process . flag ( :trap_exit , old_flag )
36
39
end
37
40
end
38
41
39
- defp do_loop ( config ) do
42
+ ## Eval loop
43
+
44
+ defp eval_loop ( config ) do
40
45
prefix = config . cache != [ ]
41
46
config . input_pid <- { :do_input , prefix , config . counter }
42
47
wait_input ( config )
@@ -57,7 +62,7 @@ defmodule IEx.Server do
57
62
config . cache ( '' )
58
63
end
59
64
60
- do_loop ( new_config )
65
+ eval_loop ( new_config )
61
66
end
62
67
63
68
{ :EXIT , _pid , :normal } ->
@@ -68,16 +73,6 @@ defmodule IEx.Server do
68
73
end
69
74
end
70
75
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
-
81
76
# Instead of doing just `:elixir.eval`, we first parse the expression to see
82
77
# if it's well formed. If parsing succeeds, we evaluate the AST as usual.
83
78
#
@@ -128,45 +123,69 @@ defmodule IEx.Server do
128
123
end
129
124
end
130
125
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
+
131
148
# Locates and loads an .iex file from one of predefined locations. Returns
132
149
# new config.
133
150
defp load_dot_iex ( config , path // nil ) do
134
151
candidates = if path do
135
152
[ path ]
136
153
else
137
- Enum . map [ ".iex" , "~/.iex" ] , & Path . expand ( & 1 )
154
+ Enum . map [ ".iex" , "~/.iex" ] , & Path . expand / 1
138
155
end
139
156
140
- path = Enum . find candidates , fn path -> File . regular? ( path ) end
157
+ path = Enum . find candidates , & File . regular? / 1
141
158
142
159
if nil? ( path ) do
143
160
config
144
161
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 )
163
163
end
164
164
end
165
165
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
168
185
end
169
186
187
+ ## Input loop
188
+
170
189
defp input_loop ( iex_pid ) do
171
190
receive do
172
191
{ :do_input , prefix , counter } ->
@@ -192,26 +211,12 @@ defmodule IEx.Server do
192
211
end
193
212
end
194
213
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
-
211
214
defp remote_prefix do
212
215
if node == node ( :erlang . group_leader ) , do: "iex" , else: "rem"
213
216
end
214
217
218
+ ## Error handling
219
+
215
220
defp print_error ( :error , exception , stacktrace ) do
216
221
print_stacktrace stacktrace , fn ->
217
222
"** (#{ inspect exception . __record__ ( :name ) } ) #{ exception . message } "
0 commit comments