-
-
Save sushant12/38c8e01ab7a3673ca45abe82d1ffe3c0 to your computer and use it in GitHub Desktop.
# The bash command `history | grep abc` is a neat tool. I abuse it alot. | |
# So, I wrote a module that would list or search through iex history. | |
# https://dev.to/sushant12/list-all-history-in-iex-408b | |
# I prefer to put this module in my .iex.exs file and then import the .iex.exs file into my project. | |
defmodule History do | |
def search do | |
load_history() | |
|> Enum.with_index(1) | |
|> Enum.each(fn {value, index} -> | |
IO.write("#{index} #{value}") | |
end) | |
end | |
def search(term) do | |
load_history() | |
|> Enum.filter(&String.match?(&1, ~r/#{term}/)) | |
|> Enum.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(term, opts) do | |
history = load_history() | |
history_count = Enum.count(history) | |
history | |
|> get_match_indices(term) | |
|> maybe_add_contexts(opts, history_count) | |
|> group_ranges() | |
|> Enum.each(fn range_list -> | |
parse_range(range_list) | |
|> Enum.each(fn match_index -> | |
if match_index < history_count do | |
txt = Enum.at(history, match_index) | |
IO.write("#{match_index} ") | |
IO.write(String.replace(txt, term, "#{IO.ANSI.red()}#{term}#{IO.ANSI.default_color()}")) | |
end | |
end) | |
IO.write("#{IO.ANSI.red()}--- #{IO.ANSI.default_color()} \n") | |
end) | |
end | |
defp group_ranges([head | rest]) do | |
Enum.reduce(rest, {head, [], []}, fn current, {prev, group, grouped} -> | |
if Range.disjoint?(current, prev) do | |
{current, [current], [Enum.reverse(group) | grouped]} | |
else | |
{current, [current | group], grouped} | |
end | |
end) | |
|> elem(2) | |
|> Enum.reverse() | |
end | |
defp get_match_indices(history, term) do | |
history | |
|> Enum.with_index() | |
|> Enum.flat_map(fn {element, index} -> | |
case String.match?(element, ~r/#{term}/) do | |
true -> [index] | |
false -> [] | |
end | |
end) | |
end | |
defp maybe_add_contexts(match_indices, opts, history_count) do | |
context_a = Keyword.get(opts, :A, 0) | |
context_b = Keyword.get(opts, :B, 0) | |
match_indices | |
|> Enum.map(fn index -> | |
upper_bound(index, context_b)..lower_bound(index, context_a, history_count) | |
end) | |
end | |
defp upper_bound(index, 0), do: index | |
defp upper_bound(index, context_b) do | |
potential_index = index - context_b | |
if potential_index < 0 do | |
0 | |
else | |
potential_index | |
end | |
end | |
defp lower_bound(index, 0, _), do: index | |
defp lower_bound(index, context_a, history_count) do | |
potential_index = index + context_a | |
if potential_index > history_count do | |
history_count | |
else | |
potential_index | |
end | |
end | |
defp parse_range(range_list) do | |
Enum.map(range_list, &Enum.to_list/1) | |
|> List.flatten() | |
|> Enum.uniq() | |
end | |
defp load_history do | |
:group_history.load() | |
|> Enum.map(&List.to_string/1) | |
|> Enum.reverse() | |
end | |
end |
Any chance of adding some options such as -A, -B, -C like grep, where you can show lines before, after or surrounding?
I wasnt aware of these options but I am looking into it now. Let me spend more time reading the docs and get back to you after the weekend.
PS: Thanks for your contribution to elixir 😄
You are welcome!
Good, let me know if you come up with something or if you need any help with it. Glad to give it a try or contribute to it.
@eksperimental I have went through the docs for those flags and I found those flags to be very useful.
Do you think this approach is okay?
History.search("Repo", "-A 5")
I will then pattern match on the 2nd param like. "-A " <> num
, "-B" <> num
Let me know what you think of this.
I think an options
argument being it a keyword list is the best aproach.
History.search("Repo", A: 5)
Also if you make an alias named history/2
that resolves to search/2
,
we can add it as a helper in an .iexs files.
import History
iex> history "Enum.reduce", C: 5
acknowledged. I will implement this and I will ping back when I am done with one of the flag. 👨💻
finished implementing for -A flag. I will add the new changes here after I am done with some code changes. ✌️
My apologies for updating you about the progress after months. Life just happened so I couldn't focus.
I have finally finished adding contexts to History.search/2
and please note that I didn't add the -C
flag because I got too damn lazy 😅
You can now use it like this
History.search("Repo.get!", A: 2, B: 2)
Awesome.
I will use it and get back to you.
BTW, have you consider the possibility of having this as a repository? I will be easier to contribute to, and maybe release it as an Elixir package.
have you consider the possibility of having this as a repository?
Yes, I did think about it but I could not convince myself regarding its usefulness as a package.
I love this function @sushant12. I have been using it for a while.
Any chance of adding some options such as -A, -B, -C like grep, where you can show lines before, after or surrounding?