Personally, I think the bash command, history
, is a neat productivity tool. I use it all the time history | grep
and I wanted the same thing in IEx as well.
To search through the history in IEx, you need to first enable it. Then either use up arrow keys or ctrl + r to search. That's cool but still not exactly what I need.
So, the first thing I had to do was to find the place where the history is stored.
# run it in your IEx
:filename.basedir(:user_cache, "erlang-history")
The history files are written by erlang's disk_log
, so there will be weird characters when you open it in your editor. My initial thought was, if the history is written by disk_log:log/2
which uses erlang:term_to_binary/1
, then I can read those history files with erlang:binary_to_term/1
. But it turns out when writing history, disk_log
appends some kind of weird binaries which you can find in disk_log.hrl
eg <<12,33,44,55>>
. So, I tried disk_log:chunk/2
to parse it.
# this will print the content of the history file with header
# in IEx, it will print charlists
# in erl, it will print strings
:disk_log.chunk(:'$#group_history', :start)
It did parse the history but it had weird headers and also didn't give me the whole content of all the history files.
{{:continuation, #PID<0.81.0>, {1, 52393}, []},
[
{:vsn, {0, 1, 0}},
I found a file called group_history.erl
. It has two public api load/0
and 'add/2'. The load/0
api parsed and returned the whole history just as I wished.
:group_history.load()
And then I finally wrote the rest of the code to emulate bash history
command
defmodule History do
def search(term) do
load_history()
|> Stream.filter(&String.match?(&1, ~r/#{term}/))
|> Enum.reverse()
|> Stream.with_index(1)
|> Enum.each(fn {value, index} ->
IO.write("#{index} ")
IO.write(String.replace(value, term, "#{IO.ANSI.red()}#{term}#{IO.ANSI.default_color()}"))
end)
end
def search do
load_history()
|> Enum.reverse()
|> Stream.with_index(1)
|> Enum.each(fn {value, index} ->
IO.write("#{index} #{value}")
end)
end
defp load_history, do: :group_history.load() |> Stream.map(&List.to_string/1)
end
I have put this code in my .iex.exs
file so that I can call it whenever I am in my IEx.
History.search("map")
History.search()
Hi there!!
Hi, I am looking for awesome opportunities to work with Elixir. If you are hiring or need a hand to finish your pet/side project then let's get connected. Here is my email sussyoung9[at]gmail[dot]com
Top comments (0)