{"id":19982030,"url":"https://github.com/nhpip/iex_history2","last_synced_at":"2025-04-13T16:32:37.875Z","repository":{"id":37960064,"uuid":"395425731","full_name":"nhpip/iex_history2","owner":"nhpip","description":"An improved history for the Elixir IEx shell","archived":false,"fork":false,"pushed_at":"2024-08-04T19:18:54.000Z","size":246,"stargazers_count":59,"open_issues_count":2,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-07T17:42:36.638Z","etag":null,"topics":["elixir","elixir-lang","elixir-library","elixir-programming-language","erlang","history","iex","shell"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nhpip.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2021-08-12T19:37:07.000Z","updated_at":"2024-11-17T19:33:31.000Z","dependencies_parsed_at":"2024-02-26T18:49:12.200Z","dependency_job_id":"957542d0-49b0-438b-8904-e2614b414b09","html_url":"https://github.com/nhpip/iex_history2","commit_stats":null,"previous_names":["nhpip/iex_history2","nhpip/history"],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nhpip%2Fiex_history2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nhpip%2Fiex_history2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nhpip%2Fiex_history2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nhpip%2Fiex_history2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nhpip","download_url":"https://codeload.github.com/nhpip/iex_history2/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248743816,"owners_count":21154746,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["elixir","elixir-lang","elixir-library","elixir-programming-language","erlang","history","iex","shell"],"created_at":"2024-11-13T04:08:39.755Z","updated_at":"2025-04-13T16:32:37.844Z","avatar_url":"https://github.com/nhpip.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# iex_history2 \nImproved shell history with variable binding persistance.\n\n* Saves shell history between VM/shell.\n* Saves the shell variable bindings between VM/shell restarts.\n* Ability to paste (most) terms into the shell.\n* Navigation keys allow history traversal where multi-line pastes require a single key up/down.\n* Shortcut functions permit search, pasting, re-evaluation and editing of items in history.\n* Editing can be done in-situ or in a text editor.\n* Shell variable bindings can be set/get outside of scope of the shell to assist in application debugging.\n* Can be enabled and state shared globally, or on individual shell sessions.\n\nSee section on `Configuration` and `Initialization` to get started.\n\n## Navigation Keys\nThe default navigation keys are defined below. They can however be configured to alternative values if so desired.\n```\n    ctrl^u    - Move up through history.\n\n    ctrl^k    - Move down through history.\n\n    ctrl^y    - Allows the currently displayed item to be modified.\n\n    ctrl^l    - Opens the currently displayed item in an editor.\n\n    ctrl^[    - Reset navigation, returns to the prompt.\n```\n### Text Editor\nTo use `ctrl^l` the environment variable EDITOR must be set to point to your editor:\n```\n    export EDITOR=\"vim\"\n```\n\n### Standard Arrow Keys\n  \nIf you want to use the regular up / down arrow (and backspace) keys:\n  \n1. Create the following file in your `HOME` directory:\n```\n    ~/.erlang_keymap.config\n```\n```\n    [{stdlib,\n      [{shell_keymap,\n        \\#{ normal =\u003e \\#{ \"\\\\e\\[A\" =\u003e none, \"\\\\e\\[B\" =\u003e none } }\n      }]\n    }].\n```\n  \n2. Set the following environment variable:\n```\n   ERL_FLAGS='-config $HOME/.erlang_keymap.config'\n``` \n     \n3. Add the following to `IExHistory2` configuration:\n```\n    standard_arrow_keys: true\n```  \nor   \n```  \n    IExHistory2.initialize(standard_arrow_keys: true, ....)\n```        \n  \n4. Restart your VM\n         \n## Shortcut Search and Edit Functions\nKey history navigation functions are automatically imported into the shell.\n```\n    iex\u003e hl()             - Will list the entire history.\n\n    iex\u003e hl(val)          - Will list `val` entries from the start if val is positive, or from the end if negative.\n\n    iex\u003e hl(start, stop)  - Will list entries between `start` and `stop`.\n\n    iex\u003e hs(string)       - Will list entries that match all or part the query string.\n\n    iex\u003e hsi(string)      - Case insensitive list entries that match all or part of the query string.\n\n    iex\u003e hsa(string, dist \\\\ 80)  - Closest match list of entries, e.g \"acr.to_str\" == \"Macro.to_string\"\n\n    iex\u003e hx(pos)          - Will execute the expression at position `pos`.\n\n    iex\u003e hc(pos)          - Will copy the expression at position pos to the shell.\n\n    iex\u003e he(pos)          - Edit the expression in a text editor.\n\n    iex\u003e hb()             - Displays the current bindings.\n\n    iex\u003e hi()             - Summary\n\n```\nNOTE: To use `he/1` the environment variable EDITOR must be set to point to the editor:\n```\n    export EDITOR=\"vim\"\n```\n\n### iex\u003e hl()\nDisplays the entire history.\n```\n    iex\u003e hl()\n    1: 2023-09-01 17:29:27: time = Time.utc_now().second\n    2: 2023-09-01 17:29:31: time = Time.utc_now().second\n    3: 2023-09-01 17:29:36: time\n    4: 2023-09-01 17:29:41: new_time\n    5: 2023-09-01 17:50:10: Process.info self\n    6: 2023-09-01 17:50:33: r = o\n    7: 2023-09-01 17:52:36: Process.get(:iex_history)\n```\n\n### iex\u003e hl(val)\nIf the argument is a positive integer it displays the command at that index.\nIf the argument is a negative number it displays the history that many items from the end.\n```\n    iex\u003e hl(2)\n    2: 2023-09-01 17:29:31: time = Time.utc_now().second\n       \n    iex\u003e IExHistory2.h(-3)\n    5: 2023-09-01 17:50:10: Process.info self\n    6: 2023-09-01 17:50:33: r = o\n    7: 2023-09-01 17:52:36: Process.get(:iex_history)\n```\n\n### iex\u003e hl(start, stop)\nSpecify a range, the atoms :start and :stop can also be used.\n\n\n### iex\u003e hs(match)\nWill search history for anything that matches the passed string.\n```\n    iex\u003e hs(\"Applic\")\n    34: 2023-09-01 18:10:39: Application.put_env(:kernel, :shell_history, :disabled)\n    41: 2023-09-01 18:11:30: Application.get_env(:kernel, :shell_history)\n    48: 2023-09-01 18:14:02: Application.put_env(:kernel, :shell_history, 0)\n    101: 2023-09-01 19:01:15: :rpc.call(:erlang.node(Process.group_leader()), Application, :put_env, [:kernel, :shell_history, :disabled])\n    103: 2023-09-01 19:01:30: :rpc.call(:erlang.node(Process.group_leader()), Application, :put_env, [:kernel, :shell_history, :enabled])\n```\n\n### iex\u003e hsi(match)\nCase insensitive version of `hs/1`.\n\n\n### iex\u003e hsa(match, closeness \\\\ 80)\nLike `hsa/1` a case insensitive search, but also adds a closeness element to the search.\n\nIt uses a combination of Myers Difference and Jaro Distance to get close to a match. The estimated \ncloseness is indicated in the result with a default range of \u003e 80%. This can be set by the user.\n```\n    iex\u003e hsa(\"map_rdce\")\n    786: 83% 2024-01-27 15:01:05: h(Enum.map_reduce)\n    806: 83% 2024-01-27 15:21:49: h(Enum.map_reduce)\n    826: 83% 2024-01-27 16:19:24: h(Enum.map_reduce)\n```\n  \n### iex\u003e hx(idx)\nInvokes the command at index 'i'.\n```\n    iex\u003e hl(114)\n    114: 2023-09-01 19:30:14: Enum.count([1, 2, 3])\n    \n    iex\u003e hx(114)\n    iex\u003e Enum.count([1, 2, 3])\n    3\n```\n\n### iex\u003e hc(idx)\nCopies the command at index 'i' and pastes it to the shell.\n```\n    iex\u003e hl(114)\n    114: 2023-09-01 19:30:14: Enum.count([1, 2, 3])\n    \n    iex\u003e hc(114)\n    :ok\n    iex\u003e Enum.count([1, 2, 3])\n``` \n\n### iex\u003e he(idx)\nUsefull for large terms or pasted modules. Will open the historical item in a text editor, ensuring\nthe result is re-evaluated and returned to the shell.\n```\n    iex\u003e he(114)\n    .....\n    .....\n    {:ok, :changes_made}    \n```\nNOTE: To use `he/1` the environment variable EDITOR must be set to point to the editor:\n```\n    export EDITOR=\"vim\"\n```\n\n### iex\u003e hb()\nShows the variable bindings.\n\n### iex\u003e hi()\nStatus summary.\n\n\n## Binding Functions\nThe functions IExHistory2.add_binding/2 and IExHistory2.get_binding/1 allows variables to be\nset in a module that is invoked in the shell to be accessible in the shell.\n\n### IExHistory2.add_binding/2\n\nThis helper function can be used when testing code (for example a module pasted\ninto the shell). It allows a variable to be set that will become available in\nthe shell. For example:\n```\n    defmodule VarTest do\n      def set_me(var) do\n        var = var * 2\n        IExHistory2.add_binding(:test_var, var)\n        var + 100\n      end\n    end\n\n    iex\u003e VarTest.set_me(7)\n\n    iex\u003e test_var\n    14\n```\nThe variable can be represented as an atom or string.\n\n### IExHistory2.get_binding/1\nThe inverse of `add_binding/2`\nIt allows a variable that is set in the shell to be available in a module under test. For example:\n```\n    defmodule VarTest do\n      def get_me(val) do\n        if IExHistory2.get_binding(:path_to_use) == :path1 do\n          val + 100\n        else\n          val + 200\n        end\n      end\n    end\n\n    iex\u003e path_to_use = :path1\n    :path1\n    iex\u003e VarTest.get_me(50)\n    150\n    iex\u003e path_to_use = :path2\n    :path2\n    iex\u003e VarTest.get_me(50)\n```\nThe complimentary functions `add_binding/3` and `get_binding/2` that take a shell pid or registered name allowing\nthe user to debug applications.\n```\n  defmodule VarTest do\n    def get_me(val) do\n      if IExHistory2.get_binding(:path_to_use, :myshell) == :path1 do\n        result = val + 100\n        IExHistory2.add_binding(:result_var, %{path: :path1, result: result}, :myshell)\n        result\n      else\n        result = val + 200\n        IExHistory2.add_binding(:result_var, %{path: :path2, result: result}, :myshell)\n        result\n      end\n    end\n  end\n\n  iex\u003e spawn(fn -\u003e VarTest.get_me(100) end)\n  #PID\u003c0.1557.0\u003e\n  %{path: :path2, result: 300}\n  iex\u003e result_var\n  %{path: :path2, result: 300}\n```            \nSee also `IExHistory2.register/1`.\n\n## Misc Functions\n\n### IExHistory2.initialize(opts)\nInitializes the IExHistory2 app. See the `Configuration` section for options.\n\n### IExHistory2.clear()\nClears the history and bindings. If scope is  :global the IEx session needs restarting for the changes to take effect.\n\n### IExHistory2.clear_history(range)\nClears the history only, if no argument all history is cleared, else history from 1 to value is cleared\n\n### IExHistory2.clear_bindings()\nClears bindings only\n\n### IExHistory2.unbind(vars)\nUnbinds a variable or list of variables, varibales should be expressed as atoms\n\n### IExHistory2.stop_clear()\nClears the history and bindings then stops the service. If scope is :global the IEx session needs restarting for the changes to take effect.\n\n### IExHistory2.configuration()\nDisplays the current conifuration\n\n### IExHistory2.configure/2\nAllows the following options to be changed:\n```\n    :show_date\n    :history_limit\n    :hide_history_commands,\n    :prepend_identifiers,\n    :command_display_width,\n    :save_invalid_results,\n    :key_buffer_history,\n    :colors\n ```   \nExamples:\n```\n    IExHistory2.configure(:colors, [index: :blue])\n    IExHistory2.configure(:prepend_identifiers, true)\n```\n\n### IExHistory2.is_enabled?()\nReturns true or false is IExHistory2 is enabled\n\n\n## Configuration\nThe following options can be set:\n\n### In `~/.iex.exs`:\n```\n    [\n      colors: [\n        index: :red,\n        date: :green,\n        command: :yellow,\n        label: :red,\n        variable: :green,\n        binding: :cyan\n      ],\n      command_display_width: 150,\n      hide_history_commands: true,\n      history_limit: :infinity,\n      import: true,\n      key_buffer_history: true,\n      navigation_keys: [\n        up: 21,\n        down: 11,\n        editor: 5,\n        modify: 8,\n        abandon: 27,\n        enter: 13\n      ],\n      paste_eval_regex: [\"#Reference\", \"#PID\", \"#Function\", \"#Ecto.Schema.Metadata\", \"#Port\"],\n      prepend_identifiers: true,\n      save_bindings: true,\n      save_invalid_results: false,\n      scope: :local,\n      show_date: true\n    ]\n```\n### Same options in `config/runtime.exs`:\n``` \n  config :iex_history2,\n    scope: :local,\n    history_limit: :infinity,\n    paste_eval_regex: [],\n    import: true,\n    ...\n```\nTo import short-cut functions set `import:` to true.\n  \n      import: true\n      \n  One issue with the current shell is the inconsistent ability to paste large terms into\n  the shell. Types such as process ids and references (`#PID\u003c0.1234.0\u003e`) cause the evaluator to fail. \n  `IExHistory2` will attempt to recognize and parse such terms during evaluation. \n  \n  Currently process ids, references, anonymous functions, ports and `#Ecto.Schema.Metadata` are \n  supported by default. Additional terms can be added:\n  \n      paste_eval_regex: [\"#SpecialItem1\", \"#NewObject\"]\n          \n  This toggle true/false for calls to `IExHistory2.*` (and imports) from been saved.\n        \n      hide_history_commands: true \n      \n  If set to false, the default, commands that were evaluated incorrectly will not be saved.\n  \n      save_invalid_results: false \n\n  If set to true will allow the user to scroll up (ctrl+u) or down (ctrl+k) through history.\n      \n      key_buffer_history: true\n      \n  Unlike the standard up/down arrow history where the up-arrow key has to be pressed multiple times to \n  traverse a large term, `IExHistory2` only requires a single up/down key, and the entire term can then\n  be edited.\n  \n  The default navigation keys are defined above, but can be changed to any reasonable value. Please be aware\n  that certain key are reserved by the runtime and can not be used. The values should be set to decimal, the \n  example below sets opening the editor from `ctrl^l` to `ctrl^e`\n  \n      navigation_keys: [editor: 5]\n    \n  If this is enabled it will prepend identifiers when a call to `x = hx(val)` is issued.\n\n      prepend_identifiers: true\n\nFor example:\n```\n    enabled:\n        iex\u003e time = Time.utc_now().second\n        14\n        iex\u003e new_time = IExHistory2.x(1)\n        22\n\n        iex\u003e new_time\n        22                  # New time is assigned to variable time\n        iex\u003e time\n        13                  # However, the original date variable is unchanged\n\n        iex\u003e IExHistory2.h()\n        1: 2023-09-01 17:13:13: time = Time.utc_now().second\n        2: 2023-09-01 17:13:22: new_time =  time = Time.utc_now().second    # We see the binding to new_time\n\n      disabled:\n        iex\u003e time = Time.utc_now().second\n        43\n        iex\u003e new_time = IExHistory2.x(1)\n        50\n\n        iex\u003e new_time       # New time is assigned to variable time\n        50\n        iex\u003e time\n        50                  # However, this time the original time variable has also changed\n\n        iex\u003e IExHistory2.h\n        1: 2023-09-01 17:17:43: time = Time.utc_now().second\n        2: 2023-09-01 17:17:50: time = Time.utc_now().second      # We do not see the binding to new_time\n```\n\n`:scope` can be one of `:local, :global` or a `node()` name\n\nIf `scope` is `:local` (the default) history will be active on all shells, even those that are remotely connected, but the history for each shell will be unique\n\nIf `scope` is `node()` (e.g. `:mgr@localhost`) history will only be active on that shell\n\nIf `scope` is `:global` history will be shared between all shells. However the saving of variable bindings will be disabled along with the date/time in history\n\nFurthermore, if a `scope` of `:global` is selected following kernel option must be set, either directly as VM options or via an environment variable:\n\n## Initialization\n  \n### Using `.iex.exs`\n  \nIt is recommended to configure and start using `.iex.exs`, for  example:\n```\n      IExHistory2.initialize(history_limit: :infinity,\n                             scope: :local, \n                             paste_eval_regex: [\"#Extra\"], \n                             show_date: true, \n                             colors: [index: :red])\n```  \n### As part of another application\n   \nAdd to `mix.exs` as a dependency: \n```  \n      {:iex_history2, \"~\u003e 5.2\"}\n```  \nOr:\n```  \n      {:iex_history2, github: \"nhpip/iex_history2\", tag: \"5.2.0\"},\n```          \nAdd the configuration to your application `config/runtime.exs`. For example:\n```  \n      config :iex_history2,\n        history_limit: 12345,\n        import: false,\n        scope: :local, \n        paste_eval_regex: [\"#Extra\"], \n        show_date: true, \n        colors: [index: :red])\n```      \nWhen you connect your shell call `IExHistory2.initialize/0` (in `.iex.exs` or as a standalone call):\n```  \n      IExHistory2.initialize()\n```  \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnhpip%2Fiex_history2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnhpip%2Fiex_history2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnhpip%2Fiex_history2/lists"}