Skip to content

Instantly share code, notes, and snippets.

@alxndr
Last active August 29, 2015 14:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alxndr/aebfd1b9ed2dfa960a86 to your computer and use it in GitHub Desktop.
Save alxndr/aebfd1b9ed2dfa960a86 to your computer and use it in GitHub Desktop.
concept: working `|>`-prefix in iex
diff --git a/lib/iex/lib/iex/evaluator.ex b/lib/iex/lib/iex/evaluator.ex
index ac31d93..9922aba 100644
--- a/lib/iex/lib/iex/evaluator.ex
+++ b/lib/iex/lib/iex/evaluator.ex
@@ -89,6 +89,17 @@ defmodule IEx.Evaluator do
try do
do_eval(String.to_char_list(code), state, history)
catch
+ kind, error = %{description: "syntax error before: '|>'"} ->
+ if can_repipe?(code, history) do
+ history
+ |> IEx.History.nth(-1)
+ |> elem(2)
+ |> repipe(code)
+ |> eval(state, history)
+ else
+ print_error(kind, error, System.stacktrace)
+ {%{state | cache: ''}, history}
+ end
kind, error ->
print_error(kind, error, System.stacktrace)
{%{state | cache: ''}, history}
@@ -112,6 +123,14 @@ defmodule IEx.Evaluator do
Process.delete(:iex_history)
end
+ defp can_repipe?(code, history) do
+ Regex.match?(~r/^\s*\|>/, code) && IEx.History.State.nth(history, -1)
+ end
+
+ defp repipe(last_result, old_code) do
+ "#{inspect last_result} #{String.strip old_code}"
+ end
+
defp handle_eval({:ok, forms}, code, line, state, history) do
{result, binding, env, scope} =
:elixir.eval_forms(forms, state.binding, state.env, state.scope)
~/workspace/elixir 🌵 feature/iex/pipeline ⚡
$ make compile && ./bin/iex
==> elixir (compile)
==> iex (compile)
Compiled lib/iex/app.ex
Compiled lib/iex/cli.ex
Compiled lib/iex.ex
Compiled lib/iex/config.ex
Compiled lib/iex/autocomplete.ex
Compiled lib/iex/remsh.ex
Compiled lib/iex/history.ex
Compiled lib/iex/helpers.ex
Compiled lib/iex/evaluator.ex
Compiled lib/iex/server.ex
Compiled lib/iex/introspection.ex
Generated iex app
Erlang/OTP 17 [erts-6.4] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
Interactive Elixir (1.1.0-dev) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> "foo"
"foo"
iex(2)> |> String.upcase
"FOO"
iex(3)>
@alxndr
Copy link
Author

alxndr commented May 17, 2015

One of the downsides of formatting multi-line pipelines (|>) with the pipeline at the beginning of each line is that a block of code like that will cause an error when copy-and-pasted into IEx. This is because the newlines separating one value from the following pipeline causes the evaluator to kick in, and then the |> has lost its left-hand piece.

This little hack looks for the particular error that happens when a line starts with |>, and instead sticks the most-recently-returned value in front of it and tries to evaluate that.

I think it'd still need to be more specific about what it catches (i.e. make sure the |> error isn't occurring elsewhere in the line), and how it puts the prior value before the |> (i.e. using inspect/1 to build new_code doesn't seem very bulletproof).

@alxndr
Copy link
Author

alxndr commented May 17, 2015

Also needs to does handle being run when there's no history.

@alxndr
Copy link
Author

alxndr commented May 17, 2015

Being able to repipe non-literal values like a pid seems a little more involved, since eval/3 here just expects a string as the first parameter...

@losvedir
Copy link

Neat idea.

Would it make more sense to take a ruby irb style approach where there's a special character like _ which means "the last result"? E.g., in irb:

irb(main):002:0> "foo"
=> "foo"
irb(main):003:0> _.upcase
=> "FOO"

Here, it would be, hypothetically:

iex(1)> "foo"
"foo"
iex(2)> _ |> String.upcase
"FOO"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment