Full Code of hkupty/iron.nvim for AI

master 88cd340407b9 cached
62 files
86.0 KB
24.7k tokens
1 requests
Download .txt
Repository: hkupty/iron.nvim
Branch: master
Commit: 88cd340407b9
Files: 62
Total size: 86.0 KB

Directory structure:
gitextract_qzvaewdp/

├── .busted
├── .github/
│   └── FUNDING.yml
├── .gitignore
├── .semaphore/
│   └── semaphore.yml
├── LICENSE
├── README.md
├── bin/
│   └── pctl
├── doc/
│   └── iron.txt
├── lua/
│   └── iron/
│       ├── config.lua
│       ├── core.lua
│       ├── dap.lua
│       ├── debug_level.lua
│       ├── fts/
│       │   ├── clojure.lua
│       │   ├── common.lua
│       │   ├── cpp.lua
│       │   ├── csh.lua
│       │   ├── elixir.lua
│       │   ├── elm.lua
│       │   ├── erlang.lua
│       │   ├── fennel.lua
│       │   ├── fish.lua
│       │   ├── forth.lua
│       │   ├── haskell.lua
│       │   ├── hy.lua
│       │   ├── init.lua
│       │   ├── janet.lua
│       │   ├── javascript.lua
│       │   ├── julia.lua
│       │   ├── lisp.lua
│       │   ├── lua.lua
│       │   ├── markdown.lua
│       │   ├── mma.lua
│       │   ├── ocaml.lua
│       │   ├── php.lua
│       │   ├── prolog.lua
│       │   ├── ps1.lua
│       │   ├── pure.lua
│       │   ├── python.lua
│       │   ├── r.lua
│       │   ├── racket.lua
│       │   ├── ruby.lua
│       │   ├── sbt.lua
│       │   ├── scala.lua
│       │   ├── scheme.lua
│       │   ├── sh.lua
│       │   ├── stata.lua
│       │   ├── tcl.lua
│       │   ├── typescript.lua
│       │   └── zsh.lua
│       ├── init.lua
│       ├── log.lua
│       ├── lowlevel.lua
│       ├── marks.lua
│       ├── providers.lua
│       ├── scope.lua
│       ├── util/
│       │   ├── os.lua
│       │   └── tables.lua
│       ├── view.lua
│       └── visibility.lua
└── spec/
    └── iron/
        ├── core_spec.lua
        ├── fts/
        │   └── common_spec.lua
        └── util/
            └── tables_spec.lua

================================================
FILE CONTENTS
================================================

================================================
FILE: .busted
================================================
return {
  default = {
    verbose = true,
    lpath = "./lua/?.lua;./lua/?/init.lua"
  }
}


================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

github: # []
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']


================================================
FILE: .gitignore
================================================
*.py[eco]
__pycache__/
Makefile
doc/tags


================================================
FILE: .semaphore/semaphore.yml
================================================
version: v1.0
name: Initial Pipeline
agent:
  machine:
    type: e1-standard-2
    os_image: ubuntu2004
blocks:
  - name: Set-up
    task:
      jobs:
        - name: Lint
          commands:
            - checkout
            - sudo apt-get update
            - sudo apt-get install -y lua-check
            - luacheck lua/* --formatter JUnit > ./report.xml
      epilogue:
        on_fail:
          commands:
            - '[[ -f report.xml ]] && test-results publish report.xml'


================================================
FILE: LICENSE
================================================
BSD 3-Clause License

Copyright (c) 2018, Henry John Kupty
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: README.md
================================================
# iron.nvim

Interactive Repls Over Neovim.

## What is iron.nvim

[![asciicast](https://asciinema.org/a/495376.svg)](https://asciinema.org/a/495376)
Iron allows you to quickly interact with the repl without having to leave your
work buffer

It both a plugin and a library, allowing for better user experience and
extensibility at the same time.

## How to install

Using [packer.nvim](https://github.com/wbthomason/packer.nvim) 
(or the plugin manager of your choice):

```lua
  use {'Vigemus/iron.nvim'}
```

## How to configure

Below is a very simple configuration for iron:

```lua
local iron = require("iron.core")
local view = require("iron.view")
local common = require("iron.fts.common")

iron.setup {
  config = {
    -- Whether a repl should be discarded or not
    scratch_repl = true,
    -- Your repl definitions come here
    repl_definition = {
      sh = {
        -- Can be a table or a function that
        -- returns a table (see below)
        command = {"zsh"}
      },
      python = {
        command = { "python3" },  -- or { "ipython", "--no-autoindent" }
        format = common.bracketed_paste_python,
        block_dividers = { "# %%", "#%%" },
        env = {PYTHON_BASIC_REPL = "1"} --this is needed for python3.13 and up.
      }
    },
    -- set the file type of the newly created repl to ft
    -- bufnr is the buffer id of the REPL and ft is the filetype of the 
    -- language being used for the REPL. 
    repl_filetype = function(bufnr, ft)
      return ft
      -- or return a string name such as the following
      -- return "iron"
    end,
    -- Send selections to the DAP repl if an nvim-dap session is running.
    dap_integration = true,
    -- How the repl window will be displayed
    -- See below for more information
    repl_open_cmd = view.bottom(40),

    -- repl_open_cmd can also be an array-style table so that multiple 
    -- repl_open_commands can be given.
    -- When repl_open_cmd is given as a table, the first command given will
    -- be the command that `IronRepl` initially toggles.
    -- Moreover, when repl_open_cmd is a table, each key will automatically
    -- be available as a keymap (see `keymaps` below) with the names 
    -- toggle_repl_with_cmd_1, ..., toggle_repl_with_cmd_k
    -- For example,
    -- 
    -- repl_open_cmd = {
    --   view.split.vertical.rightbelow("%40"), -- cmd_1: open a repl to the right
    --   view.split.rightbelow("%25")  -- cmd_2: open a repl below
    -- }

  },
  -- Iron doesn't set keymaps by default anymore.
  -- You can set them here or manually add keymaps to the functions in iron.core
  keymaps = {
    toggle_repl = "<space>rr", -- toggles the repl open and closed.
    -- If repl_open_command is a table as above, then the following keymaps are
    -- available
    -- toggle_repl_with_cmd_1 = "<space>rv",
    -- toggle_repl_with_cmd_2 = "<space>rh",
    restart_repl = "<space>rR", -- calls `IronRestart` to restart the repl
    send_motion = "<space>sc",
    visual_send = "<space>sc",
    send_file = "<space>sf",
    send_line = "<space>sl",
    send_paragraph = "<space>sp",
    send_until_cursor = "<space>su",
    send_mark = "<space>sm",
    send_code_block = "<space>sb",
    send_code_block_and_move = "<space>sn",
    mark_motion = "<space>mc",
    mark_visual = "<space>mc",
    remove_mark = "<space>md",
    cr = "<space>s<cr>",
    interrupt = "<space>s<space>",
    exit = "<space>sq",
    clear = "<space>cl",
  },
  -- If the highlight is on, you can change how it looks
  -- For the available options, check nvim_set_hl
  highlight = {
    italic = true
  },
  ignore_blank_lines = true, -- ignore blank lines when sending visual select lines
}

-- iron also has a list of commands, see :h iron-commands for all available commands
vim.keymap.set('n', '<space>rf', '<cmd>IronFocus<cr>')
vim.keymap.set('n', '<space>rh', '<cmd>IronHide<cr>')
```

The repl `command` can also be a function:

```lua
iron.setup{
  config = {
    repl_definition = {
      -- custom repl that loads the current file
      haskell = {
        command = function(meta)
          local filename = vim.api.nvim_buf_get_name(meta.current_bufnr)
          return { 'cabal', 'v2-repl', filename}
        end
      }
    },
  },
}
```

### REPL windows

iron.nvim supports both splits and floating windows and has helper functions
for opening new repls in either of them:

#### For splits

If you prefer using splits to your repls, iron provides a few utility functions
to make it simpler:

```lua
local view = require("iron.view")

-- iron.setup {...

-- One can always use the default commands from vim directly
repl_open_cmd = "vertical botright 80 split"

-- But iron provides some utility functions to allow you to declare that dynamically,
-- based on editor size or custom logic, for example.

-- Vertical 50 columns split
-- Split has a metatable that allows you to set up the arguments in a "fluent" API
-- you can write as you would write a vim command.
-- It accepts:
--   - vertical
--   - leftabove/aboveleft
--   - rightbelow/belowright
--   - topleft
--   - botright
-- They'll return a metatable that allows you to set up the next argument
-- or call it with a size parameter
repl_open_cmd = view.split.vertical.botright(50)

-- If the supplied number is a fraction between 1 and 0,
-- it will be used as a proportion
repl_open_cmd = view.split.vertical.botright(0.61903398875)

-- The size parameter can be a number, a string or a function.
-- When it's a *number*, it will be the size in rows/columns
-- If it's a *string*, it requires a "%" sign at the end and is calculated
-- as a percentage of the editor size
-- If it's a *function*, it should return a number for the size of rows/columns

repl_open_cmd = view.split("40%")

-- You can supply custom logic
-- to determine the size of your
-- repl's window
repl_open_cmd = view.split.topleft(function()
  if some_check then
    return vim.o.lines * 0.4
  end
  return 20
end)

-- An optional set of options can be given to the split function if one
-- wants to configure the window behavior.
-- Note that, by default `winfixwidth` and `winfixheight` are set
-- to `true`. If you want to overwrite those values,
-- you need to specify the keys in the option map as the example below

repl_open_cmd = view.split("40%", {
  winfixwidth = false,
  winfixheight = false,
  -- any window-local configuration can be used here
  number = true
})
```

#### For floats

If you prefer floats, the API is the following:

```lua
local view = require("iron.view")

-- iron.setup {...

-- The same size arguments are valid for float functions
repl_open_cmd = view.top("10%")

-- `view.center` takes either one or two arguments
repl_open_cmd = view.center("30%", 20)

-- If you supply only one, it will be used for both dimensions
-- The function takes an argument to whether the orientation is vertical(true) or
-- horizontal (false)
repl_open_cmd = view.center(function(vertical)
-- Useless function, but it will be called twice,
-- once for each dimension (width, height)
  if vertical then
    return 50
  end
  return 20
end)

-- `view.offset` allows you to control both the size of each dimension and
-- the distance of them from the top-left corner of the screen
repl_open_cmd = view.offset{
  width = 60,
  height = vim.o.height * 0.75
  w_offset = 0,
  h_offset = "5%"
}

-- Some helper functions allow you to calculate the offset
-- in relation to the size of the window.
-- While all other sizing functions take only the orientation boolean (vertical or not),
-- for offsets, the functions will also take the repl size in that dimension
-- as argument. The helper functions then return a function that takes two arguments
-- to calculate the offset
repl_open_cmd = view.offset{
  width = 60,
  height = vim.o.height * 0.75
  -- `view.helpers.flip` will subtract the size of the REPL
  -- window from the total dimension, then apply an offset.
  -- Effectively, it flips the top/left to bottom/right orientation
  w_offset = view.helpers.flip(2),
  -- `view.helpers.proportion` allows you to apply a relative
  -- offset considering the REPL window size.
  -- for example, 0.5 will centralize the REPL in that dimension,
  -- 0 will pin it to the top/left and 1 will pin it to the bottom/right.
  h_offset = view.helpers.proportion(0.5)
}

-- Differently from `view.center`, all arguments are required
-- and no defaults will be applied if something is missing.
repl_open_cmd = view.offset{
  width = 60,
  height = vim.o.height * 0.75
  -- Since we know we're using this function in the width offset
  -- calculation, we can ignore the argument
  w_offset = function(_, repl_size)
    -- Ideally this function calculates a value based on something..
    return 42
  end,
  h_offset = view.helpers.flip(2)
}
```


================================================
FILE: bin/pctl
================================================
#!/bin/bash
set -eou pipefail

BIN_DIR="$(dirname "$(realpath "$0")")"
ROOT_DIR="$(dirname "$BIN_DIR")"

LUAROCKS="luarocks-5.1"

exists() {
  command -v "$1" > /dev/null
}

setup(){
  exists luarocks-5.1 || {
    exists luarocks && {
      LUAROCKS="luarocks --lua-version 5.1"
    } || {
      echo 'Please install luarocks'
      exit 1
    }
  }

  $LUAROCKS --local install "$1"
}

tests(){
  exists busted || setup busted
  busted
}

linter(){
  exists luacheck || setup luacheck
  luacheck lua/
}

run() {
  tests
  linter
}


run-dev(){
inotifywait -r -q -m -e close_write --format %e lua spec | while read -r ; do
busted
done
}

cd $ROOT_DIR && "$@"


================================================
FILE: doc/iron.txt
================================================
*iron.nvim* Interactive Repls Over Neovim

===============================================================================
CONTENTS                                                            *iron-help*

  Introduction............................|iron-introduction|
  Awesomeness.............................|iron-awesomeness|
  Languages...............................|iron-languages|
  Commands................................|iron-commands|
  Functions...............................|iron-functions|
  Customizing.............................|iron-customizing|
  Mappings................................|iron-mappings|
  Extending...............................|iron-extending|
  Credits.................................|iron-credits|

===============================================================================
Introduction                                                *iron-introduction*

Iron is a helper plugin that allows you to manage and interact with live
Read-Eval-Print-Loops (REPLs) directly from Neovim, through terminal buffers.
Iron mostly interacts with plugins via stdin/stdout, with few exceptions.

===============================================================================
Awesomeness                                                  *iron-awesomeness*

Iron makes it very easy to focus on the code while interacting with the REPL.
It allows you to, seamlessly, send chunks of code directly into the REPL,
without disrupting your coding workflow.

A lot more can be accomplished through Iron. In order to add new functionality
(such as running tests, evaluating expressions, adding new keybindings, etc),
all it takes is to extend the REPL providers.

===============================================================================
Languages                                                      *iron-languages*

Currently, Iron has support for the following programming languages:

  - clojure
    - boot
    - lein
  - cpp
    - root https://root.cern.ch/
  - csh
    - csh
    - tcsh
  - elixir
  - elm
  - erlang
  - fennel
  - fish
  - forth
  - haskell
    - intero
    - stack ghci
    - cabal repl
    - ghci
  - hylang
  - javascript
  - julia
  - lisp
    - sbcl
    - clisp
  - lua
  - ocaml
    - ocamltop
    - utop
  - php
    - php
    - psysh
  - prolog
    - gprolog
    - swipl
  - ps1 (powershell)
  - pure-lang
  - python
    - python
    - ipython
    - ptpython
    - ptipython
  - r
  - racket
  - ruby
  - scala
    - scala
    - sbt
  - scheme
    - guile
    - csi
  - sh
    - zsh
    - bash
    - sh
  - stata
  - tcl
  - typescript
  - zsh

===============================================================================
Commands                                                        *iron-commands*

Iron provides the following commands for interacting with REPLs:

:IronRepl [ft]
  Open a REPL for current or given file type.

:IronReplHere [ft]
  Open a REPL for current or given file type in the current window.

:IronRestart
  Restart the current REPL.

:IronSend[!] some [text...]
  Sends the supplied chunk of text to the repl for current filetype.
  If used with a `!`, the first argument is the filetype
  This allows for invoking a new repl immediately:
      > :IronSend! python print(20 * 32)

:IronFocus [ft]
  Focuses on the repl for current or given file type.

:IronHide [ft]
  Hide the repl window for current or given file type.

:IronWatch file|mark
  send the file/mark to the repl after writing the buffer

:IronAttach ft
  Attach current buffer regardless of its filetype to a repl

===============================================================================
Functions                                                      *iron-functions*

Iron is a lua plugin so all its functionality is exposed through lua
functions.

The code is mainly divided in two major namespaces:
- `iron.core`, for most user-facing functions and
- `iron.lowlevel` for the functions that interact with the repl directly

The code is well documented, so we'll skip `iron.lowlevel` in this doc.

Below are the core functions:

* core.repl_here(ft)         Creates a repl in the same window

* core.repl_restart()        Restarts the repl in the current window or for
                             the current buffer's filetype

* core.close_repl(ft)        Closes the repl for supplied filetype

* core.repl_for(ft)          Creates a repl for given filetype in a new window

* core.focus_on(ft)          Moves to (or creates) the repl for given filetype

* core.send(ft, data)        Sends data (a table) to the repl for given
                             filetype

* core.send_code_block(move) Sends the lines between two code_dividers as
                             defined in repl_definition or end and start of
                             buffer to the repl. If move is true, the cursor
                             is moved to next code block. If move is false,
                             the cursor position is unchanged.

* core.send_file()           Sends the whole file to the repl

* core.send_line()           Sends line below the cursor to the repl

* core.send_until_cursor()   Sends the buffer from the start until the line
                             where the cursor is (inclusive) to the repl

* core.send_motion(motion)   Applies given motion and sends the result to the
                             repl. See `send_motion` in |iron-mappings|

* core.send_mark()           Sends the marked chunk

* core.mark_visual()         Adds a mark around the visual selection

* core.mark_motion()         Adds a mark around the motion object

* core.watch(handler, bufnr) Watches for saves in the supplied buffer

* core.unwatch(bufnr)        Removes the watch on the supplied buffer

* core.setup(config)         Configures iron. See |iron-customizing|

For more information, check the luadocs in the functions.

===============================================================================
Customizing                                                  *iron-customizing*

Iron is configured through `core.setup`

Below is a sample config:

```
local iron = require("iron.core")

iron.setup{
  config = {
    -- Highlights the last sent block with bold
    highlight_last = "IronLastSent",

    -- Toggling behavior is on by default.
    -- Other options are: `single` and `focus`
    visibility = require("iron.visibility").toggle,

    -- Scope of the repl
    -- By default it is one for the same `pwd`
    -- Other options are `tab_based` and `singleton`
    scope = require("iron.scope").path_based,

    -- Whether the repl buffer is a "throwaway" buffer or not
    scratch_repl = false,

    -- Automatically closes the repl window on process end
    close_window_on_exit = true,
    repl_definition = {
     -- forcing a default
      python = require("iron.fts.python").ipython

     -- new, custom repl
      lua = {
        -- Can be a table or a function that returns a table (see below)
        command = {"my-lua-repl", "-arg"}
      }
     -- setting up code_dividers for core.send_code_block
      python = {
        command = { "python3" }, -- or { "ipython", "--no-autoindent" }
        format = require("iron.fts.common").bracketed_paste_python,
        block_dividers = { "# %%", "#%%" },
      }
    },
    -- Whether iron should map the `<plug>(..)` mappings
    should_map_plug = true,

    -- Repl position. Check `iron.view` for more options,
    -- currently there are four positions: left, right, bottom, top,
    -- the param is the width/height of the float window
    repl_open_cmd = require("iron.view").curry.bottom(40),
    -- Alternatively, pass a function, which is evaluated when a repl is open.
    repl_open_cmd = require('iron.view').curry.right(function()
        return vim.o.columns / 2
    end),
    -- iron.view.curry will open a float window for the REPL.
    -- alternatively, pass a string of vimscript for opening a fixed window:
    repl_open_cmd = 'belowright 15 split',

    -- If the repl buffer is listed
    buflisted = false,
  },

  -- All the keymaps are set individually
  -- Below is a suggested default
  keymaps = {
    send_motion = "<space>sc",
    visual_send = "<space>sc",
    send_file = "<space>sf",
    send_line = "<space>sl",
    send_until_cursor = "<space>su",
    send_mark = "<space>sm",
    send_code_block = "<space>sb",
    send_code_block_and_move = "<space>sn",
    mark_motion = "<space>mc",
    mark_visual = "<space>mc",
    remove_mark = "<space>md",
    cr = "<space>s<cr>",
    interrupt = "<space>s<space>",
    exit = "<space>sq",
    clear = "<space>cl",
  },

  -- If the highlight is on, you can change how it looks
  -- For the available options, check nvim_set_hl
  highlight = {
    italic = true
  }
}
```

The repl command can also be a function:

```
iron.setup{
  config = {
    repl_definition = {
      -- custom repl that loads the current file
      haskell = {
        command = function(meta)
          local filename = vim.api.nvim_buf_get_name(meta.current_bufnr)
          return { 'cabal', 'v2-repl', filename}
        end
      }
    },
  },
}
```

===============================================================================
Mappings                                                        *iron-mappings*

Iron by default doesn't map the keybindings, only those supplied in the
core.setup function.

- send_motion: Sends a motion to the repl
- visual_send: Sends the visual selection to the repl
- send_file: Sends the whole file to the repl
- send_line: Sends the line below the cursor to the repl
- send_until_cursor: Sends the buffer from the start until the line where the
  cursor is (inclusive) to the repl
- send_mark: Sends the text within the mark
- send_code_block: Sends the text between two code dividers
- send_code_block_and_move: Sends the text between two code dividers and move
  to next code block
- mark_motion: Marks the text object
- mark_visual: Marks the visual selection
- remove_mark: Removes the set mark
- cr: Sends a return to the repl
- interrupt: Sends a `<C-c>` signal to the repl
- exit: Exits the repl
- clear: Clears the repl window

===============================================================================
Extending                                                      *iron-extending*

Iron provides some modules that can be used on your configuration to change
the behavior:

* `iron.memory_management`:
  It provides three memory management modes for your repls:
  * `tab_based`:
    It saves all the variables based on your tab, so new tabs create new
    repls.
  * `path_based` (default):
    It saves all the variables according to your base path (pwd),
    so changing the cd/tcd/lcd will create new repls.
  * `singleton`:
    It will never create two repls for the same filetype.

* `iron.visibility`:
  It changes the behavior on how iron deals with windows for the repl.
  * `single`:
    Ensures that a window exists and shows it;
  * `toggle` (default):
    Alternates between opening and closing the window
  * `focus`:
    Moves the focus to the repl window.

* `iron.view`:
  Creates the windows.
  Subject to change for the moment. Please refer to the README while the API
  is still unstable.

===============================================================================
Credits                                                          *iron-credits*

Plugin created by Henry Kupty <hkupty@gmail.com>.
It is free to use and extend. Please consider opening a pull request if
extended.

Source at https://github.com/Vigemus/iron.nvim

 vim:tw=78:ft=help:norl:


================================================
FILE: lua/iron/config.lua
================================================
-- luacheck: globals vim
local view = require("iron.view")

--- Default values
--@module config
local config

--- Default configurations for iron.nvim
-- @table config.values
-- @tfield false|string highlight_last Either false or the name of a highlight group
-- @field scratch_repl When enabled, the repl buffer will be a scratch buffer
-- @field should_map_plug when enabled iron will provide its mappings as `<plug>(..)` as well,
-- for backwards compatibility
-- @field close_window_on_exit closes repl window on process exit
local values = {
  highlight_last = "IronLastSent",
  visibility = require("iron.visibility").toggle,
  scope = require("iron.scope").path_based,
  scratch_repl = false,
  close_window_on_exit = true,
  preferred = setmetatable({}, {
    __newindex = function(tbl, k, v)
      vim.deprecate("config.preferred", "config.repl_definition", "3.1", "iron.nvim")
      rawset(tbl, k, v)
    end
  }),
  repl_definition = setmetatable({}, {
    __index = function(tbl, key)
      local repl_definitions = require("iron.fts")[key]
      local repl_def
      for _, v in pairs(repl_definitions) do
        if vim.fn.executable(v.command[1]) == 1 then
          repl_def = v
          break
        end
      end
      if repl_def == nil then
        error("Failed to locate REPL executable, aborting")
      else
        rawset(tbl, key, repl_def)
        return repl_def
      end
    end
  }),
  repl_filetype = function(bufnr, ft)
    return "iron"
  end,
  should_map_plug = false,
  repl_open_cmd = view.split.botright(40),
  current_view = 1,
  views = {
    view.bottom(40)
  },
  mark = { -- Arbitrary numbers
    save_pos = 20,
    send = 77,
  },
  buflisted = false,
  ignore_blank_lines = true,
}

-- HACK for LDoc to correctly link @see annotations
config = vim.deepcopy(values)
config.values = values

return config


================================================
FILE: lua/iron/core.lua
================================================
-- luacheck: globals vim unpack

local log = require("iron.log")
local ll = require("iron.lowlevel")
local focus = require("iron.visibility").focus
local config = require("iron.config")
local marks = require("iron.marks")
local is_windows = require("iron.util.os").is_windows
local view = require("iron.view")
local dap = require("iron.dap")

local autocmds = {}

--- Core functions of iron
-- @module core
local core = {}

--- Local helpers for creating a new repl.
-- Should be used by core functions, but not
-- exposed to the end-user
-- @local
local new_repl = {}

--- Create a new repl on the current window
-- Simple wrapper around the low level functions
-- Useful to avoid rewriting the get_def + create + save pattern
-- @param ft filetype
-- @param bufnr buffer to be used.
-- @param current_bufnr current buffer.
-- @tparam cleanup function Function to cleanup if call fails
-- @return saved snapshot of repl metadata
new_repl.create = function(ft, bufnr, current_bufnr, cleanup)
  local meta
  local success, repl = pcall(ll.get_repl_def, ft)

  if not success and cleanup ~= nil then
    cleanup()
    error(repl)
  end

  success, meta = pcall(
    ll.create_repl_on_current_window,
    ft, repl, bufnr, current_bufnr
  )
  if success then
    ll.set(ft, meta)

    local filetype = config.repl_filetype(bufnr, ft)
    if filetype ~= nil then
      vim.api.nvim_set_option_value("filetype", filetype, { buf = bufnr })
    end

    return meta
  elseif cleanup ~= nil then
    cleanup()
  end


  error(meta)
end

--- Create a new repl on a new repl window
-- Adds a layer on top of @{new_repl.create},
-- ensuring it is created on a new window
-- @param ft filetype
-- @return saved snapshot of repl metadata
new_repl.create_on_new_window = function(ft)
  local current_bufnr = vim.api.nvim_get_current_buf()
  local bufnr = ll.new_buffer()

  local replwin = ll.new_window(bufnr)
  vim.api.nvim_set_current_win(replwin)
  local meta = new_repl.create(ft, bufnr, current_bufnr, function()
    vim.api.nvim_win_close(replwin, true)
    vim.api.nvim_buf_delete(bufnr, { force = true })
  end)

  return meta
end

--- Creates a repl in the current window
-- @param ft the filetype of the repl to be created
-- @treturn table metadata of the repl
core.repl_here = function(ft)
  local meta = ll.get(ft)
  if ll.repl_exists(meta) then
    vim.api.nvim_set_current_buf(meta.bufnr)
    return meta
  else
    local current_bufnr = vim.api.nvim_get_current_buf()
    local bufnr = ll.new_buffer()
    return new_repl.create(ft, bufnr, current_bufnr, function()
      vim.api.nvim_buf_delete(bufnr, { force = true })
    end)
  end
end

--- Restarts the repl for the current buffer
-- First, check if the cursor is on top or a REPL
-- Then, start a new REPL of the same type and enter it into the window
-- Afterwards, wipe out the old REPL buffer
-- This is done without asking for confirmation, so user beware
-- @todo Split into "restart a repl" and "do X for current buffer's repl"
-- @return saved snapshotof repl metadata
core.repl_restart = function()
  local bufnr_here = vim.fn.bufnr("%")
  local ft = ll.get_repl_ft_for_bufnr(bufnr_here)
  local current_bufnr = vim.api.nvim_get_current_buf()

  if ft ~= nil then
    local bufnr = ll.new_buffer()

    local meta = new_repl.create(
      ft, bufnr, current_bufnr,
      function()
        vim.api.nvim_buf_delete(bufnr, { force = true })
      end
    )

    -- created a new one, now have to kill the old one
    vim.api.nvim_buf_delete(bufnr_here, { force = true })
    return meta
  else
    ft = ll.get_buffer_ft(0)

    local meta = ll.get(ft)
    if ll.repl_exists(meta) then
      local replwin = vim.fn.bufwinid(meta.bufnr)
      local currwin = vim.api.nvim_get_current_win()
      local new_meta

      if replwin == nil or replwin == -1 then
        new_meta = new_repl.create_on_new_window(ft)
      else
        vim.api.nvim_set_current_win(replwin)
        local bufnr = ll.new_buffer()
        new_meta = new_repl.create(
          ft, bufnr, current_bufnr,
          function()
          vim.api.nvim_buf_delete(bufnr, {force = true})
          end
        )
      end

      vim.api.nvim_set_current_win(currwin)
      vim.api.nvim_buf_delete(meta.bufnr, { force = true })

      return new_meta
    else
      error('No repl found in current buffer; cannot restart')
    end
  end
end

--- Sends a close request to the repl
-- if @{config.values.close_window_on_exit} is set to true,
-- all windows associated with that repl will be closed.
-- Otherwise, this will only finish the process.
-- @param ft filetype
core.close_repl = function(ft)
  -- see the similar logic on core.send to see how the REPLs created by
  -- core.attach is handled.
  local meta = vim.b[0].repl

  if not meta or not ll.repl_exists(meta) then
    ft = ft or ll.get_buffer_ft(0)
    meta = ll.get(ft)
  end

  if not ll.repl_exists(meta) then
    return
  end

  ll.send_to_repl(meta, string.char(04))
end

--- Hides the repl windows for a given filetype
-- @param ft filetype
core.hide_repl = function(ft)
  ft = ft or ll.get_buffer_ft(0)
  local meta = ll.get(ft)

  if ll.repl_exists(meta) then
    local window = vim.fn.bufwinid(meta.bufnr)
    if window ~= -1 then
      vim.api.nvim_win_hide(window)
    end
  end
end

--- Creates a repl for a given filetype
-- It should open a new repl on a new window for the filetype
-- supplied as argument.
-- @param ft filetype
core.repl_for = function(ft)
  if dap.is_dap_session_running() then
    -- If there's a dap session running, default to the dap repl. By
    -- intercepting here, we can support dap repls in filetypes that aren't
    -- normally supported (e.g. java).
    -- TODO find and open the dap repl window?
    return
  end
  local meta = ll.get(ft)
  if ll.repl_exists(meta) then
    local currwin = vim.api.nvim_get_current_win()
    config.visibility(meta.bufnr, function()
      local winid = ll.new_window(meta.bufnr)
      vim.api.nvim_win_set_buf(winid, meta.bufnr)
      vim.api.nvim_set_current_win(currwin)
      return winid
    end)
    return meta
  else
    local currwin = vim.api.nvim_get_current_win()
    meta = new_repl.create_on_new_window(ft)
    vim.api.nvim_set_current_win(currwin)
    return meta
  end
end

--- Moves to the repl for given filetype
-- When it doesn't exist, a new repl is created
-- directly moving the focus to it.
-- @param ft filetype
core.focus_on = function(ft)
  local meta = ll.get(ft)
  if ll.repl_exists(meta) then
    focus(meta.bufnr, function()
      local winid = ll.new_window(meta.bufnr)
      vim.api.nvim_win_set_buf(winid, meta.bufnr)
      return winid
    end)
    return meta
  else
    return new_repl.create_on_new_window(ft)
  end
end

--- Sends data to the repl
-- This is a top-level wrapper over the low-level
-- functions. It should send data to the repl, ensuring
-- it exists.
-- @param ft filetype (will be inferred if not supplied)
-- @tparam string|table data data to be sent to the repl.
local send = function(ft, data)
  -- the buffer local variable `repl` is created by core.attach function to
  -- track non-default REPls.
  local meta = vim.b[0].repl

  if dap.is_dap_session_running() then
    dap.send_to_dap(data)
    return
  end

  -- However, this attached meta may associated with a REPL that has been
  -- closed, we need to check for that.
  -- If the attached REPL is not existed or has been closed, we will try to
  -- get the REPL meta based on the ft of current buffer.
  if not meta or not ll.repl_exists(meta) then
    ft = ft or ll.get_buffer_ft(0)
    if data == nil then return end
    meta = ll.get(ft)
  end

  -- If the repl doesn't exist, it will be created
  if not ll.repl_exists(meta) then
    meta = core.repl_for(ft)
  end
  ll.send_to_repl(meta, data)
end


-- TODO fix this hack fix that allows ipython with Windows OS
-- To fix this, there needs to be some sort of check on the progress of the
-- call to vim.fn.chansend(meta.job, dt) inside of ll.send_to_repl. The defer
-- is here to ensure that sending of string.char(13) sends after the commands
-- are finished sending to the ipython REPL
core.send = function(ft, data)
  if not is_windows() then
    send(ft, data)
  else
    send(ft, data)

    -- This is a hack fix that allows windows ipython to run the commands sent
    -- to the repl. However, the same issue will arise as before (the command sent
    -- to the repl but the code not running) if many lines are sent to the ipython
    -- repl (many as in more than 150 milliseconds worth). It appear that windows
    -- and powershell works fine though.
    vim.defer_fn(function()
      send(nil, string.char(13))
      if type(data) ~= "string" then
        send(nil, string.char(13))
      end
    end, 150)
  end
end

core.send_file = function(ft)
  core.send(ft, vim.api.nvim_buf_get_lines(0, 0, -1, false))
end

--- Sends the line under the cursor to the repl
-- Builds upon @{core.send}, extracting
-- the data beforehand.
core.send_line = function()
  local linenr = vim.api.nvim_win_get_cursor(0)[1] - 1
  local cur_line = vim.api.nvim_buf_get_lines(0, linenr, linenr + 1, 0)[1]
  local width = vim.fn.strwidth(cur_line)

  if width == 0 then return end

  marks.set {
    from_line = linenr,
    from_col = 0,
    to_line = linenr,
    to_col = width - 1
  }

  core.send(nil, cur_line)
end

--- Sends the buffer from the beginning until reching the line where the cursor is (inclusive)
-- Builds upon @{core.send}, extracting
-- the data beforehand.
core.send_until_cursor = function()
  local linenr = vim.api.nvim_win_get_cursor(0)[1] - 1
  local text_until_line = vim.api.nvim_buf_get_lines(0, 0, linenr + 1, 0)
  local last_line = vim.api.nvim_buf_get_lines(0, linenr, linenr + 1, 0)[1]
  local last_line_width = vim.fn.strwidth(last_line)

  marks.set {
    from_line = 0,
    from_col = 0,
    to_line = linenr,
    to_col = last_line_width - 1
  }

  core.send(nil, text_until_line)
end

--- Marks visual selection and returns data for usage
-- @treturn table Marked lines
core.mark_visual = function()
  -- HACK Break out of visual mode
  vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('<Esc>', false, true, true), 'nx', false)
  local b_line, b_col
  local e_line, e_col

  local mode = vim.fn.visualmode()

  b_line, b_col = unpack(vim.fn.getpos("'<"), 2, 3)
  e_line, e_col = unpack(vim.fn.getpos("'>"), 2, 3)

  if e_line < b_line or (e_line == b_line and e_col < b_col) then
    e_line, b_line = b_line, e_line
    e_col, b_col = b_col, e_col
  end

  local lines = vim.api.nvim_buf_get_lines(0, b_line - 1, e_line, 0)

  if #lines == 0 then return end

  if mode == "\22" then
    local b_offset = math.max(1, b_col) - 1
    for ix, line in ipairs(lines) do
      -- On a block, remove all presiding chars unless b_col is 0/negative
      lines[ix] = vim.fn.strcharpart(line, b_offset, math.min(e_col, vim.fn.strwidth(line)))
    end
  elseif mode == "v" then
    local last = #lines
    local line_size = vim.fn.strwidth(lines[last])
    local max_width = math.min(e_col, line_size)
    if (max_width < line_size) then
      -- If the selected width is smaller then total line, trim the excess
      lines[last] = vim.fn.strcharpart(lines[last], 0, max_width)
    end

    if b_col > 1 then
      -- on a normal visual selection, if the start column is not 1, trim the beginning part
      lines[1] = vim.fn.strcharpart(lines[1], b_col - 1)
    end
  end

  marks.set {
    from_line = b_line - 1,
    from_col = math.max(b_col - 1, 0),
    to_line = e_line - 1,
    to_col = math.min(e_col, vim.fn.strwidth(lines[#lines])) - 1 -- TODO Check whether this is actually true
  }

  if config.ignore_blank_lines then
    local b_lines = {}
    for _, line in ipairs(lines) do
      if line:gsub("^%s*(.-)%s*$", "%1") ~= '' then
        table.insert(b_lines, line)
      end
    end
    return b_lines
  else
    return lines
  end
end

--- Marks the supplied motion and returns the data for usage
-- @tparam string mtype motion type
-- @treturn table Marked lines
core.mark_motion = function(mtype)
  local b_line, b_col
  local e_line, e_col

  b_line, b_col = unpack(vim.fn.getpos("'["), 2, 3)
  e_line, e_col = unpack(vim.fn.getpos("']"), 2, 3)

  local lines = vim.api.nvim_buf_get_lines(0, b_line - 1, e_line, 0)
  if #lines == 0 then return end

  if mtype == 'line' then
    b_col, e_col = 0, vim.fn.strwidth(lines[#lines])
  end

  if e_col > 1 then
    lines[#lines] = vim.fn.strpart(lines[#lines], 0, e_col)
  end
  if b_col > 1 then
    lines[1] = vim.fn.strpart(lines[1], b_col - 1)
  end

  marks.set {
    from_line = b_line - 1,
    from_col = math.max(b_col - 1, 0),
    to_line = e_line - 1,
    to_col = e_col - 1
  }

  marks.winrestview()
  return lines
end

--- Sends a chunk of text from a motion to the repl
-- It is a simple wrapper over @{core.mark_motion}
-- in which the data is extracted by that function and sent to the repl.
-- @{core.send} will handle the null cases.
-- Additionally, it restores the cursor position as a side-effect.
-- @param mtype motion type
core.send_motion = function(mtype)
  core.send(nil, core.mark_motion(mtype))
end

--- Sends a chunk of text from a visual selection to the repl
-- this is a simple wrapper over @{core.mark_visual} where
-- the data is forwarded to the repl through @{core.send},
-- which will handle the null cases.
core.visual_send = function()
  core.send(nil, core.mark_visual())
end

--- Sends the paragraph to the REPL that the cursor is on
core.send_paragraph = function()
  vim.cmd('normal! vip')
  vim.defer_fn(function()
    core.visual_send()
  end, 100)
end


--- Re-sends latest chunk of text.
-- Sends text contained within a block delimited by
-- the last sent chunk. Uses @{marks.get} to retrieve
-- the boundaries.
core.send_mark = function()
  local pos = marks.get()

  if pos == nil then return end

  local lines = vim.api.nvim_buf_get_lines(0, pos.from_line, pos.to_line + 1, 0)

  if #lines == 1 then
    if pos.from_col >= 1 or pos.to_col < vim.fn.strwidth(lines[1]) - 1 then
      lines[1] = vim.fn.strpart(lines[1], pos.from_col, pos.to_col - pos.from_col + 1)
    end
  else
    if pos.from_col >= 1 then
      lines[1] = vim.fn.strpart(lines[1], pos.from_col)
    end
    if pos.to_col < vim.fn.strwidth(lines[#lines]) - 1 then
      lines[#lines] = vim.fn.strpart(lines[#lines], 0, pos.to_col + 1)
    end
  end

  core.send(nil, lines)
end

--- Checks if line starts with a divider.
-- Helper function for core.send_code_block.
local line_starts_with_block_divider = function(line, block_dividers)
  for _, block_divider in pairs(block_dividers) do
    local length_block_divider = string.len(block_divider)
    if string.sub(line, 1, length_block_divider) == block_divider then return true end
  end
end

--- Sends lines between two block dividers.
-- Block dividers can be defined as table in the
-- repl_definition under key block_dividers.
-- The buffer is scanned from cursor till first line
-- starting with a block_divider or the start of buffer.
-- The buffer is scanned till end of buffer for next line
-- starting with a block divider or end of buffer.
-- If no block_divider is defined an error is returned.
-- If move is true, the cursor is moved to the next block_divider
-- for jumping through the code. If move is false, the cursor is
-- not moved.
core.send_code_block = function(move)
  local block_dividers = config.repl_definition[vim.bo[0].filetype].block_dividers
  if block_dividers == nil then
    error("No block_dividers defined for this repl in repl_definition!")
  end
  local linenr = vim.api.nvim_win_get_cursor(0)[1] - 1
  local buffer_text = vim.api.nvim_buf_get_lines(0, 0, -1, false)
  local mark_start = linenr
  while mark_start ~= 0 do
    local line_text = buffer_text[mark_start + 1]
    if line_starts_with_block_divider(line_text, block_dividers) then break end
    mark_start = mark_start - 1
  end
  local buffer_length = vim.api.nvim_buf_line_count(0)
  local mark_end = linenr + 1
  while mark_end < buffer_length do
    local line_text = buffer_text[mark_end + 1]
    if line_starts_with_block_divider(line_text, block_dividers) then break end
    mark_end = mark_end + 1
  end
  mark_end = mark_end - 1
  local col_end = string.len(buffer_text[mark_end + 1]) - 1
  marks.set {
    from_line = mark_start,
    from_col = 0,
    to_line = mark_end,
    to_col = col_end,
  }
  core.send_mark()
  if move then vim.api.nvim_win_set_cursor(0, { math.min(mark_end + 2, buffer_length), 0 }) end
end

--- Attaches a buffer to a repl regardless of it's filetype
-- If the repl doesn't exist it will be created
core.attach = function(ft, target)
  local meta = ll.get(ft)

  if not ll.repl_exists(meta) then
    meta = core.repl_for(ft)
  end

  vim.b[target].repl = meta
end

--- Provide filtered list of supported fts
-- Auxiliary function to be used by commands to show the user which fts they have
-- available to start repls with
-- @param partial input string
-- @return table with supported filetypes matching input string
local complete_fts = function(partial)
  local starts_with_partial = function(key) return key:sub(1, #partial) == partial end
  local custom_fts = vim.tbl_filter(starts_with_partial, vim.tbl_keys(config.repl_definition))
  vim.list_extend(custom_fts, vim.tbl_filter(
    function(i) return (not vim.tbl_contains(custom_fts, i)) and starts_with_partial(i) end,
    vim.tbl_keys(require("iron.fts")))
  )

  return custom_fts
end

local get_ft = function(arg)
  if arg and arg ~= "" then
    return arg
  end
  return ll.get_buffer_ft(0)
end

--- List of commands created by iron.nvim
-- They'll only be set up after calling the @{core.setup} function
-- which makes it possible to delay initialization and make startup faster.
-- @local
-- @table commands
-- @field IronRepl command for @{core.repl_for}
local commands = {
  { "IronAttach", function(opts)
    core.attach(get_ft(opts.fargs[1]), vim.api.nvim_get_current_buf())
  end, { nargs = "?", complete = complete_fts } },
  { "IronRepl", function(opts)
    core.repl_for(get_ft(opts.fargs[1]))
  end, { nargs = "?", complete = complete_fts } },
  { "IronHide", function(opts)
    core.hide_repl(get_ft(opts.fargs[1]))
  end, { nargs = "?", complete = complete_fts } },
  { "IronSend", function(opts)
    local ft
    if opts.bang then
      ft = opts.fargs[1]
      opts.fargs[1] = ""
    else
      ft = ll.get_buffer_ft(0)
    end
    if ft == nil then return end
    local data = table.concat(opts.fargs, " ")

    core.send(ft, data)
  end, {
    bang = true,
    nargs = "+",
    complete = function(arg_lead, cmd_line)
      local cmd = vim.split(cmd_line, " ")
      if #cmd <= 2 and string.find(cmd[1], "!") then
        return complete_fts(arg_lead)
      end
    end
  } },
  { "IronFocus", function(opts)
    local ft = get_ft(opts.fargs[1])
    if ft == nil then return end

    core.focus_on(ft)
  end, { nargs = "?", complete = complete_fts } },
  { "IronWatch", function(opts)
    local handler

    if opts.fargs[1] == "mark" then
      handler = core.send_mark
    elseif opts.fargs[1] == "file" then
      -- Wrap send_file so we ignore autocmd argument
      handler = function() core.send_file() end
    else
      error("Not a valid handler type")
    end

    core.watch(handler)
  end, {
    nargs = 1,
    complete = function(arg_lead, _)
      local starts_with_partial = function(key) return key:sub(1, #arg_lead) == arg_lead end
      return vim.tbl_filter(starts_with_partial, {
        "mark",
        "file"
      })
    end
  } },
  { "IronReplHere", function(opts)
    local ft = get_ft(opts.fargs[1])
    if ft == nil then return end

    core.repl_here(ft)
  end, { nargs = "?", complete = complete_fts } },
  { "IronRestart", function(_) core.repl_restart() end, { nargs = 0 } }
}

--- Wrapper for calling functions through motion.
-- This should take care of the vim side of calling opfuncs.
-- @param motion_fn_name name of the function in @{core} to be mapped
core.run_motion = function(motion_fn_name)
  marks.winsaveview()
  vim.o.operatorfunc = "v:lua.require'iron.core'." .. motion_fn_name
  vim.api.nvim_feedkeys("g@", "ni", false)
end

core.unwatch = function(bufnr)
  local fname = vim.api.nvim_buf_get_name(bufnr)
  if autocmds[fname] ~= nil then
    vim.api.nvim_del_autocmd(autocmds[fname])
    autocmds[fname] = nil
  end
end

core.watch = function(handler, bufnr)
  bufnr = bufnr or vim.api.nvim_get_current_buf()
  core.unwatch(bufnr)
  local fname = vim.api.nvim_buf_get_name(bufnr)
  autocmds[fname] = vim.api.nvim_create_autocmd("BufWritePost", {
    group = "iron",
    pattern = fname,
    callback = handler,
    desc = "Watch writes to buffer to send data to repl"
  })
end

--- List of keymaps
-- if @{config}.should\_map\_plug is set to true,
-- then they will also be mapped to `<plug>` keymaps.
-- @table named_maps
-- @field send_motion mapping to send a motion/chunk to the repl

-- @field send_mark Sends chunk within marked boundaries
-- @field send_line sends current line to repl
-- @field send_until_cursor sends the buffer from the start until the line where the cursor is (inclusive) to the repl
-- @field visual_send sends visual selection to repl
-- @field clear_hl clears highlighted chunk
-- @field cr sends a <CR> to the repl
-- @field interrupt sends a <C-c> to the repl
-- @field exit closes the repl
-- @field clear clears the text buffer of the repl
local named_maps = {
  -- basic interaction with the repl
  send_motion = {
    { 'n' },
    function() require("iron.core").run_motion("send_motion") end
  },
  send_mark = { { 'n' }, core.send_mark },
  send_line = { { 'n' }, core.send_line },
  send_until_cursor = { { 'n' }, core.send_until_cursor },
  send_file = { { 'n' }, core.send_file },
  visual_send = { { 'v' }, core.visual_send },
  send_paragraph = { { 'n' }, core.send_paragraph },
  send_code_block = { { 'n' }, function() core.send_code_block(false) end },
  send_code_block_and_move = {
    { 'n' }, function() core.send_code_block(true) end
  },

  -- REPL
  restart_repl = { { 'n' }, function() vim.cmd("IronRestart") end },
  toggle_repl = { { 'n' }, function() vim.cmd("IronRepl") end },

  -- Marks
  mark_motion = { { 'n' }, function() require("iron.core").run_motion("mark_motion") end },
  mark_visual = { { 'v' }, core.mark_visual },
  remove_mark = { { 'n' }, marks.drop_last },

  -- Force clear highlight
  clear_hl = { { 'v' }, marks.clear_hl },

  -- Sending special characters to the repl
  cr = { { 'n' }, function() core.send(nil, string.char(13)) end },
  interrupt = { { 'n' }, function() core.send(nil, string.char(03)) end },
  exit = { { 'n' }, core.close_repl },
  clear = { { 'n' }, function() core.send(nil, string.char(12)) end },
}

local tmp_migration = {
  repeat_cmd = "send_mark"
}

local snake_to_kebab = function(name)
  return name:gsub("_", "-")
end

--- Sets up the configuration for iron to run.
-- Also, defines commands and keybindings for iron to run.
-- @param opts table of the configuration to be applied
-- @tparam table opts.config set of config values to override default on @{config}
-- @tparam table opts.keymaps set of keymaps to apply, based on @{named_maps}
core.setup = function(opts)
  config.namespace = vim.api.nvim_create_namespace("iron")
  vim.api.nvim_create_augroup("iron", {})

  if opts.config then
    if type(opts.config.repl_open_cmd) ~= "table" then
      ll.tmp.repl_open_cmd = opts.config.repl_open_cmd

    else
      for idx, cmd in ipairs(opts.config.repl_open_cmd) do
        if idx == 1 then
          ll.tmp.repl_open_cmd = cmd
        end
        named_maps["toggle_repl_with_cmd_" .. idx] = {
          { 'n' },
          function()
            ll.tmp.repl_open_cmd = cmd
            vim.cmd('IronRepl')
          end
        }
      end
    end

    if opts.config.dap_integration then
      dap.enable_integration()
    end

    if ll.tmp.repl_open_cmd == nil then
      local msg = "A default repl_open_cmd was not set. "
      msg = msg .. "Please set a default by adding '_DEFAULT' "
      msg = msg .. "to the end of one of your repl_open_cmd names."
      error(msg)
    end

    for k, v in pairs(opts.config) do
      config[k] = v
    end

  else
    ll.tmp.repl_open_cmd = config.repl_open_cmd
  end


  if config.highlight_last ~= false then
    local hl_cfg = opts.highlight or {
      bold = true
    }


    vim.api.nvim_set_hl(0, config.highlight_last, hl_cfg)
  end

  for _, command in ipairs(commands) do
    vim.api.nvim_create_user_command(unpack(command))
  end

  if config.should_map_plug then
    vim.deprecate("config.should_map_plug", "core.setup{keymaps = {...}}", "3.1", "iron.nvim")
    for key, keymap in pairs(named_maps) do
      local mapping = vim.deepcopy(keymap)
      table.insert(mapping, 2, "<plug>(iron-" .. snake_to_kebab(key) .. ")")
      table.insert(mapping, { silent = true })
      vim.keymap.set(unpack(mapping))
    end
  end

  if opts.keymaps ~= nil then
    for key, lhs in pairs(opts.keymaps) do
      if tmp_migration[key] ~= nil then
        log.deprecate(
          "core.setup{keymaps." .. key .. "}",
          "core.setup{keymaps." .. tmp_migration[key] .. "}",
          "3.1",
          "iron.nvim"
        )
        key = tmp_migration[key]
      end

      if named_maps[key] == nil then
        error("Key `" .. key .. "` doesn't exist, therefore there's nothing to be applied")
      else
        local mapping = vim.deepcopy(named_maps[key])
        table.insert(mapping, 2, lhs)
        table.insert(mapping, { silent = true, desc = 'iron_repl_' .. key })

        vim.keymap.set(unpack(mapping))
      end
    end
  end
end

return core


================================================
FILE: lua/iron/dap.lua
================================================
local M = {}

local is_dap_integration_enabled = false

--- Sets up a hook to keep track of the DAP session state.
function M.enable_integration()
  is_dap_integration_enabled = true
end

--- Returns true if dap_integration is enabled and a dap session is running.
--- This function will always return false if dap_integration is not enabled.
--- @return boolean
function M.is_dap_session_running()
  local has_dap, dap = pcall(require, 'dap')
  return has_dap and is_dap_integration_enabled and dap.session() ~= nil
end

--- Send the lines to the dap-repl
--- @param lines string|string[]
function M.send_to_dap(lines)
  local text
  if type(lines) == 'table' then
    text = table.concat(lines, "\n"):gsub('\r', '')
  else
    text = lines
  end
  require('dap').repl.execute(text)
  require('dap').repl.open()
end

return M


================================================
FILE: lua/iron/debug_level.lua
================================================
--- Sets the level of debug
--@module debug_level
local debug_level = {}

debug_level.fatal = 1
debug_level.err   = 2
debug_level.warn  = 3
debug_level.info  = 4

return debug_level


================================================
FILE: lua/iron/fts/clojure.lua
================================================
local clojure = {}

clojure.boot = {
  command = {"boot", "repl"},
}

clojure.lein = {
  command = {"lein", "repl"},
}

clojure.clj = {
  command = {"clj"},
}

return clojure


================================================
FILE: lua/iron/fts/common.lua
================================================
local is_windows = require("iron.util.os").is_windows
local extend = require("iron.util.tables").extend
local open_code = "\27[200~"
local close_code = "\27[201~"
local cr = "\13"

local common = {}


---@param table table table of strings
---@param substring string
--- Checks in any sting in the table contains the substring
local contains = function(table, substring)
  for _, v in ipairs(table) do
    if string.find(v, substring) then
      return true
    end
  end
  return false
end


---@param lines table
-- Removes empty lines. On unix this includes lines only with whitespaces.
local function remove_empty_lines(lines)
  local newlines = {}

  for _, line in pairs(lines) do
    if string.len(line:gsub("[ \t]", "")) > 0 then
      table.insert(newlines, line)
    end
  end

  return newlines
end


---@param s string
--- A helper function using in bracked_paste_python.
-- Checks in a string starts with any of the exceptions.
local function python_close_indent_exceptions(s)
  local exceptions = { "elif", "else", "except", "finally", "#" }
  for _, exception in ipairs(exceptions) do
    local pattern0 = "^" .. exception .. "[%s:]"
    local pattern1 = "^" .. exception .. "$"
    if string.match(s, pattern0) or string.match(s, pattern1) then
      return true
    end
  end
  return false
end


common.format = function(repldef, lines)
  assert(type(lines) == "table", "Supplied lines is not a table")

  local new

  -- passing the command is for python. this will not affect bracketed_paste.
  if repldef.format then
    return repldef.format(lines, { command = repldef.command })
  elseif #lines == 1 then
    new = lines
  else
    new = extend(repldef.open, lines, repldef.close)
  end

  if #new > 0 then
    if not is_windows() then
      new[#new] = new[#new] .. cr
    end
  end

  return new
end


common.bracketed_paste = function(lines)
  if #lines == 1 then
    return { lines[1] .. cr }
  else
    local new = { open_code .. lines[1] }
    for line = 2, #lines do
      table.insert(new, lines[line])
    end

    table.insert(new, close_code .. cr)

    return new
  end
end


--- @param lines table  "each item of the table is a new line to send to the repl"
--- @return table  "returns the table of lines to be sent the the repl with
-- the return carriage added"
common.bracketed_paste_python = function(lines, extras)
  local result = {}

  local cmd = extras["command"]
  local pseudo_meta = { current_buffer = vim.api.nvim_get_current_buf()}
  if type(cmd) == "function" then
    cmd = cmd(pseudo_meta)
  end

  local windows = is_windows()
  local python = false
  local ipython = false
  local ptpython = false

  if contains(cmd, "ipython") then
    ipython = true
  elseif contains(cmd, "ptpython") then
    ptpython = true
  else
    python = true
  end

  lines = remove_empty_lines(lines)

  local indent_open = false
  for i, line in ipairs(lines) do
    if string.match(line, "^%s") ~= nil then
      indent_open = true
    end

    table.insert(result, line)

    if windows and python or not windows then
      if i < #lines and indent_open and string.match(lines[i + 1], "^%s") == nil then
        if not python_close_indent_exceptions(lines[i + 1]) then
          indent_open = false
          table.insert(result, cr)
        end
      end
    end
  end

  local newline = windows and "\r\n" or cr
  if #result == 0 then  -- handle sending blank lines
    table.insert(result, cr)
  elseif #result > 0 and result[#result]:sub(1, 1) == " " then
    -- Since the last line of code is indented, the Python REPL
    -- requires and extra newline in order to execute the code
    table.insert(result, newline)
  else
    if not is_windows() then
      table.insert(result, "")
    end
  end

  if ptpython then
    table.insert(result, 1, open_code)
    table.insert(result, close_code)
    table.insert(result, "\n")
  end

  return result
end


return common


================================================
FILE: lua/iron/fts/cpp.lua
================================================

local cpp = {}

local function magic(str)
    -- remove every space at beginning of a line
    str = str:gsub('^%s+', '')
    -- remove all comments
    str = str:gsub('//(.*)', '')
    -- remove every space at end of a line
    str = str:gsub('%s+$', '')
    -- remove line break and extra spaces eventually
    str = str:gsub([[\$]], '')
    str = str:gsub('%s+$', '')
    return str
end

local function lastmagic(str)
    -- check what character is the last of the line
    if str:len() == 0 then return false end
    local lastchar = str:sub(-1, -1)
    return lastchar == ';' or lastchar == '{' or lastchar == '}'
end

local function format(lines)
    if #lines == 1 then
        return {magic(lines[1]) .. '\13'}
    else
        local new = {}
        local aus = ''
        for line = 1, #lines do
            -- concatenate lines if they do not end with a lastmagic character.
            local l = magic(lines[line])
            aus = aus .. l
            if lastmagic(l) then
                table.insert(new, aus)
                aus = ''
            end
        end
        return table.insert(new, '')
    end
end

cpp.root = {command = {'root', '-l'}, format = format}

return cpp


================================================
FILE: lua/iron/fts/csh.lua
================================================
local csh = {}

csh.csh = {
  command = {"csh"},
}

csh.tcsh = {
  command = {"tch"},
}

return csh


================================================
FILE: lua/iron/fts/elixir.lua
================================================
local elixir = {}

elixir.iex = {
  command = {"iex"},
}

return elixir


================================================
FILE: lua/iron/fts/elm.lua
================================================
local elm = {}

elm.elm = {
  command = {"elm", "repl"},
}

elm.elm_legacy = {
  command = {"elm-repl"},
}

return elm


================================================
FILE: lua/iron/fts/erlang.lua
================================================
local erlang = {}

erlang.erl = {
  command = {"erl"},
}

return erlang


================================================
FILE: lua/iron/fts/fennel.lua
================================================
local fennel = {}

fennel.fennel = {
  command = {"fennel"},
}

return fennel


================================================
FILE: lua/iron/fts/fish.lua
================================================
local fish = {}

fish.fish = {
  command = {"fish"},
}

return fish


================================================
FILE: lua/iron/fts/forth.lua
================================================
local forth = {}

forth.gforth = {
  command = {"gforth"}
}

return forth


================================================
FILE: lua/iron/fts/haskell.lua
================================================
local haskell = {}

haskell.stack_intero = {
  command = {"stack", "ghci", "--with-ghc", "intero"},
}

haskell.stack = {
  command = {"stack", "ghci"},
}

haskell.cabal = {
  command = {"cabal", "repl"},
}

haskell.ghci = {
  command = {"ghci"},
}

return haskell


================================================
FILE: lua/iron/fts/hy.lua
================================================
local hy = {}

hy.hy = {
  command = {"hy"}
}

return hy


================================================
FILE: lua/iron/fts/init.lua
================================================
-- luacheck: globals unpack

local fts = {
  clojure = require("iron.fts.clojure"),
  cpp = require("iron.fts.cpp"),
  csh = require("iron.fts.csh"),
  elixir = require("iron.fts.elixir"),
  elm = require("iron.fts.elm"),
  erlang = require("iron.fts.erlang"),
  fennel = require("iron.fts.fennel"),
  forth = require("iron.fts.forth"),
  haskell = require("iron.fts.haskell"),
  hy = require("iron.fts.hy"),
  janet = require("iron.fts.janet"),
  javascript = require("iron.fts.javascript"),
  julia = require("iron.fts.julia"),
  lisp = require("iron.fts.lisp"),
  lua = require("iron.fts.lua"),
  mma = require("iron.fts.mma"),
  ocaml = require("iron.fts.ocaml"),
  php = require("iron.fts.php"),
  prolog = require("iron.fts.prolog"),
  ps1 = require("iron.fts.ps1"),
  pure = require("iron.fts.pure"),
  python = require("iron.fts.python"),
  r = require("iron.fts.r"),
  racket = require("iron.fts.racket"),
  ruby = require("iron.fts.ruby"),
  sbt_scala = require("iron.fts.sbt"),
  scala = require("iron.fts.scala"),
  scheme = require("iron.fts.scheme"),
  sh = require("iron.fts.sh"),
  stata = require("iron.fts.stata"),
  tcl = require("iron.fts.tcl"),
  typescript = require("iron.fts.typescript"),
  zsh = require("iron.fts.zsh"),
  fish = require("iron.fts.fish")
}

return fts


================================================
FILE: lua/iron/fts/janet.lua
================================================
local janet = {}

janet.janet = {
  command = {"janet"},
}

return janet


================================================
FILE: lua/iron/fts/javascript.lua
================================================
local javascript = {}

javascript.node = {
  command = {"node"},
  open = ".editor\n",
  close = "\04",
}

return javascript


================================================
FILE: lua/iron/fts/julia.lua
================================================
local julia = {}

julia.julia = {
  command = {"julia"},
}

return julia


================================================
FILE: lua/iron/fts/lisp.lua
================================================
local lisp = {}

lisp.sbcl = {
  command = {"sbcl"},
}

lisp.clisp = {
  command = {"clisp"},
}

return lisp


================================================
FILE: lua/iron/fts/lua.lua
================================================
local lua = {}

lua.lua = {
  command = {"lua"},
}

return lua


================================================
FILE: lua/iron/fts/markdown.lua
================================================
local markdown = {}

markdown.aichat = {
  command = "aichat",
  format = require("iron.fts.common").bracketed_paste,
}

return markdown


================================================
FILE: lua/iron/fts/mma.lua
================================================
local mma = {}

mma.math = {
  command = { "math" },
}

return mma


================================================
FILE: lua/iron/fts/ocaml.lua
================================================
local ocaml = {}

ocaml.utop = {
  command = {"utop"},
}

ocaml.ocamltop = {
  command = {"ocamltop"},
}

return ocaml


================================================
FILE: lua/iron/fts/php.lua
================================================
local php = {}

php.psysh = {
  command = {"psysh"},
}

php.php = {
  command = {"php", "-a"},
}

return php


================================================
FILE: lua/iron/fts/prolog.lua
================================================
local prolog = {}

prolog.gprolog = {
  command = {"gprolog"},
}

prolog.swipl = {
  command = {"swipl"},
}

return prolog


================================================
FILE: lua/iron/fts/ps1.lua
================================================
local ps1 = {}

ps1.ps1 = {
  command = {"powershell", "-noexit", "-executionpolicy", "bypass"},
}

return ps1


================================================
FILE: lua/iron/fts/pure.lua
================================================
local pure = {}

pure.pure = {
  command = {"pure"},
  open = ":paste\n",
  close = "\04",
}

return pure


================================================
FILE: lua/iron/fts/python.lua
================================================
-- luacheck: globals vim
local bracketed_paste_python = require("iron.fts.common").bracketed_paste_python
local python = {}

local executable = function(exe)
  return vim.api.nvim_call_function('executable', {exe}) == 1
end

local pyversion  = executable('python3') and 'python3' or 'python'

local def = function(cmd)
	return {
		command = cmd,
		format = bracketed_paste_python
	}
end

python.ptipython = def({"ptipython"})
python.ipython = def({"ipython", "--no-autoindent"})
python.ptpython = def({"ptpython"})
python.python = def({pyversion})

return python


================================================
FILE: lua/iron/fts/r.lua
================================================
local bracketed_paste = require("iron.fts.common").bracketed_paste
local r = {}

r.R = {
    command = { "R" },
    format = bracketed_paste
}

r.radian = {
    command = { "radian" },
    format = bracketed_paste
}

return r


================================================
FILE: lua/iron/fts/racket.lua
================================================
local racket = {}

racket.racket = {
  command = {"racket"},
}

return racket


================================================
FILE: lua/iron/fts/ruby.lua
================================================
local ruby = {}

ruby.irb = {
  command = {"irb"},
}

return ruby


================================================
FILE: lua/iron/fts/sbt.lua
================================================
local sbt = {}

sbt.sbt = {
  command = {"sbt"},
  open = ":paste\n",
  close = "\04",
}

return sbt


================================================
FILE: lua/iron/fts/scala.lua
================================================
local scala = {}

local def = function(command)
  return {
    command = command,
    open = ":paste",
    close = "\04"
  }
end

scala.sbt = def{"sbt"}
scala.scala = def{"scala"}

return scala


================================================
FILE: lua/iron/fts/scheme.lua
================================================
local scheme = {}

scheme.guile = {
  command = {"guile"},
}

scheme.csi = {
  command = {"csi"},
}

return scheme


================================================
FILE: lua/iron/fts/sh.lua
================================================
local sh = {}

sh.bash = {
  command = {"bash"},
}

sh.sh = {
  command = function(meta)
    local bufnr = meta.current_bufnr
    if vim.b[bufnr].is_posix == 1 then
      return {"sh"}
    elseif vim.b[bufnr].is_bash == 1 then
      return {"bash"}
    elseif vim.b[bufnr].is_kornshell == 1 then
      return {"ksh"}
    else
      return {"sh"}
    end
  end,
}

return sh


================================================
FILE: lua/iron/fts/stata.lua
================================================
local stata = {}

stata.stata = {
	command = { "stata", "-q" },
}

return stata


================================================
FILE: lua/iron/fts/tcl.lua
================================================
local tcl = {}

tcl.tclsh = {
  command = {"tclsh"},
}

return tcl


================================================
FILE: lua/iron/fts/typescript.lua
================================================
local typescript = {}

typescript.ts = {
  command = {"ts-node"},
  open = ".editor\n",
  close = "\04",
}

return typescript


================================================
FILE: lua/iron/fts/zsh.lua
================================================
local zsh = {}

zsh.zsh = {
  command = {"zsh"},
}

return zsh


================================================
FILE: lua/iron/init.lua
================================================
-- luacheck: globals unpack vim

local iron = {
  -- Will eventually be removed
  config = require("iron.config"),
  -- Most likely shouldn't be exposed here
  ll = require("iron.lowlevel"),
  core = require("iron.core"),
  setup = require("iron.core").setup
}

return iron


================================================
FILE: lua/iron/log.lua
================================================
-- luacheck: globals vim
local log = {}

if vim.deprecate then
  log.deprecate = vim.deprecate
else
  log.deprecate = function(old, new, version, app)
    local message = [[%s is deprecated, use %s instead. See :h deprecated
This function will be removed in %s version %s]]

    error(string.format(message, old, new, app, version))
  end
end

return log


================================================
FILE: lua/iron/lowlevel.lua
================================================
-- luacheck: globals vim
-- TODO Remove config from this layer
local config = require("iron.config")
local fts = require("iron.fts")
local providers = require("iron.providers")
local format = require("iron.fts.common").format
local view = require("iron.view")
local is_windows = require("iron.util.os").is_windows

--- Low level functions for iron
-- This is needed to reduce the complexity of the user API functions.
-- There are a few rules to the functions in this document:
--    * They should not interact with each other
--        * An exception for this is @{lowlevel.get_repl_def} during the transition to v3
--    * They should do one small thing only
--    * They should not care about setting/cleaning up state (i.e. moving back to another window)
--    * They must be explicit in their documentation about the state changes they cause.
-- @module lowlevel
-- @alias ll
local ll = {}

ll.store = {}

-- Quick fix for changing repl_open_cmd
ll.tmp = {}

-- TODO This should not be part of lowlevel
ll.get = function(ft)
  if ft == nil or ft == "" then
    error("Empty filetype")
  end
  return config.scope.get(ll.store, ft)
end

-- TODO this should not be part of lowlevel
ll.set = function(ft, fn)
  return config.scope.set(ll.store, ft, fn)
end

ll.get_buffer_ft = function(bufnr)
  local ft = vim.bo[bufnr].filetype
  if ft == nil or ft == "" then
    error("Empty filetype")
  elseif fts[ft] == nil and config.repl_definition[ft] == nil then
    error("There's no REPL definition for current filetype "..ft)
  end
  return ft
end

--- Creates the repl in the current window
-- This function effectively creates the repl without caring
-- about window management. It is expected that the client
-- ensures the right window is created and active before calling this function.
-- If @{\\config.close_window_on_exit} is set to true, it will plug a callback
-- to the repl so the window will automatically close when the process finishes
-- @param ft filetype of the current repl
-- @param repl definition of the repl being created
-- @param repl.command table with the command to be invoked.
-- @param bufnr Buffer to be used
-- @param current_bufnr Current buffer
-- @param opts Options passed through to the terminal
-- @warning changes current window's buffer to bufnr
-- @return unsaved metadata about created repl
ll.create_repl_on_current_window = function(ft, repl, bufnr, current_bufnr, opts)
  vim.api.nvim_win_set_buf(0, bufnr)
  -- TODO Move this out of this function
  -- Checking config should be done on an upper layer.
  -- This layer should be simpler
  opts = opts or {}
  if config.close_window_on_exit then
    opts.on_exit = function()
      local bufwinid = vim.fn.bufwinid(bufnr)
      while bufwinid ~= -1 do
        vim.api.nvim_win_close(bufwinid, true)
        bufwinid = vim.fn.bufwinid(bufnr)
      end
      if vim.api.nvim_buf_is_valid(bufnr) then
        vim.api.nvim_buf_delete(bufnr, { force = true })
      end
    end
  else
    opts.on_exit = function() end
  end

  local cmd = repl.command
  if type(repl.command) == 'function' then
    local meta = {
      current_bufnr = current_bufnr,
    }
    cmd = repl.command(meta)
  end

  if repl.env then
    opts.env = repl.env
  end

  local job_id = vim.fn.termopen(cmd, opts)

  return {
    ft = ft,
    bufnr = bufnr,
    job = job_id,
    repldef = repl
  }
end

--- Wrapper function for getting repl definition from config
-- This allows for an easier transition between old and new methods
-- @tparam string ft filetype of the desired repl
-- @return repl definition
ll.get_repl_def = function(ft)
  -- TODO should not call providers directly, but from config
  return config.repl_definition[ft] or providers.first_matching_binary(ft)
end

--- Creates a new window for placing a repl.
-- Expected to be called before creating the repl.
-- It knows nothing about the repl and only takes in account the
-- configuration.
-- @warning might change the current window
-- @param bufnr buffer to be used
-- @param repl_open_cmd command to be used to open the repl. if nil than will use config.repl_open_cmd
-- @return window id of the newly created window
ll.new_window = function(bufnr, repl_open_cmd)
  if repl_open_cmd == nil then
    repl_open_cmd = ll.tmp.repl_open_cmd
  end

  if type(repl_open_cmd) == "function" then
    local result = repl_open_cmd(bufnr)
    if type(result) == "table" then
      return view.openfloat(result, bufnr)
    else
      return result
    end
  else
    vim.cmd(repl_open_cmd)
    vim.api.nvim_set_current_buf(bufnr)
    return vim.fn.bufwinid(bufnr)
  end
end

--- Creates a new buffer to be used by the repl
-- @return the buffer id
ll.new_buffer = function()
  return vim.api.nvim_create_buf(config.buflisted, config.scratch_repl)
end

--- Wraps the condition checking of whether a repl exists
-- created for convenience
-- @tparam table meta metadata for repl. Can be nil.
-- @treturn boolean whether the repl exists
ll.repl_exists = function(meta)
  return meta ~= nil and vim.api.nvim_buf_is_loaded(meta.bufnr)
end

--- Sends data to an existing repl of given filetype
-- The content supplied is ensured to be a table of lines,
-- being coerced if supplied as a string.
-- As a side-effect of pasting the contents to the repl,
-- it changes the scroll position of that window.
-- Does not affect currently active window and its cursor position.
-- @tparam table meta metadata for repl. Should not be nil
-- @tparam string ft name of the filetype
-- @tparam string|table data A multiline string or a table containing lines to be sent to the repl
-- @warning changes cursor position if window is visible
ll.send_to_repl = function(meta, data)
  local dt = data
  
  if data == string.char(12) then
    vim.fn.chansend(meta.job, {string.char(12)})
    return
  end

  if type(data) == "string" then
    dt = vim.split(data, '\n')
  end

  dt = format(meta.repldef, dt)

  local window = vim.fn.bufwinid(meta.bufnr)
  if window ~= -1 then
    vim.api.nvim_win_set_cursor(window, {vim.api.nvim_buf_line_count(meta.bufnr), 0})
  end

  --TODO check vim.api.nvim_chan_send
  --TODO tool to get the progress of the chan send function
  if is_windows() then
    vim.fn.chansend(meta.job, table.concat(dt, "\r"))
  else
    vim.fn.chansend(meta.job, dt)
  end

  if window ~= -1 then
    vim.api.nvim_win_set_cursor(window, {vim.api.nvim_buf_line_count(meta.bufnr), 0})
  end
end


--- Reshapes the repl window according to a preset config described in views
-- @tparam table meta metadata for the repl
-- @tparam string|number key either name or index in the table for the preset to be active
ll.set_window_shape = function(meta, key)
  local window = vim.fn.bufwinid(meta.bufnr)
  local preset = config.views[key]
  if preset ~= nil then
    if type(preset) == "function" then
      preset = preset(meta.bufnr)
    end
    vim.api.nvim_win_set_config(window, preset)
  end
end

--- Closes the window
-- @tparam table meta metadata for the repl
ll.close_window = function(meta)
  local window = vim.fn.bufwinid(meta.bufnr)
  vim.api.nvim_win_close(window, true)
end

--- Tries to look up the corresponding filetype of a REPL
-- If the corresponding buffer number is a repl,
-- return its filetype otherwise return nil
-- @tparam int bufnr number of the buffer being checked
-- @treturn string filetype of the buffer's repl (or nil if it doesn't have a repl associated)
ll.get_repl_ft_for_bufnr = function(bufnr)
  for _, values  in pairs(ll.store) do
    for _, meta in pairs(values) do
      if meta.bufnr == bufnr then
        return meta.ft
      end
    end
  end
end

return ll


================================================
FILE: lua/iron/marks.lua
================================================
-- luacheck: globals vim
local config = require("iron.config")

--- Marks management for iron
-- This is an intermediary layer between iron and neovim's
-- extmarks. Used primarily to manage the text sent to the repl,
-- but can also be used to set highlight and possibly virtualtext.
local marks = {}

--- Sets a mark for given options
-- The mark is set for a preconfigured position id, which will be used by
-- @{marks.get} for retrieval.
-- @tparam table opts table with the directives
-- @tparam number opts.from_line line where the mark starts
-- @tparam number opts.to_line line where the mark ends. Can be ignored on single line marks
-- @tparam number opts.from_col column where the mark starts
-- @tparam number opts.to_col column where the mark ends
-- @tparam string|false opts.hl Highlight group to be used or false if no highlight should be done.
marks.set = function(opts)
  local extmark_config = {
    id = config.mark.send,
    -- to_line can be ignored if it's single line
    end_line = opts.to_line or opts.from_line,
    end_col = opts.to_col + 1
  }

  if opts.hl ~= nil then

    -- Intentionally check here
    -- so opts.false disables highlight
    if opts.hl ~= false then
      extmark_config.hl_group = opts.hl
    end

  elseif config.highlight_last then
    extmark_config.hl_group = config.highlight_last
  end

  vim.api.nvim_buf_set_extmark(0, config.namespace, opts.from_line, opts.from_col, extmark_config)
end

marks.drop_last = function()
  vim.api.nvim_buf_del_extmark(0, config.namespace, config.mark.send)
end

marks.clear_hl = function()
  local payload = marks.get()
  payload.hl = false
  marks.set(payload)
end

--- Retrieves the mark with a position id.
marks.get = function()
  local mark_pos = vim.api.nvim_buf_get_extmark_by_id(0, config.namespace, config.mark.send, {details = true})

  if #mark_pos == 0 then
    return nil
  end

  return {
    from_line = mark_pos[1],
    from_col = mark_pos[2],
    to_line = mark_pos[3].end_row,
    to_col = mark_pos[3].end_col - 1
  }

end

marks.winrestview = function()
  local mark = vim.api.nvim_buf_get_extmark_by_id(0, config.namespace, config.mark.save_pos, {})

  if #mark ~= 0 then
    -- winrestview is 1-based
    vim.fn.winrestview({lnum = mark[1] + 1, col = mark[2]})
    vim.api.nvim_buf_del_extmark(0, config.namespace, config.mark.save_pos)
  end
end

marks.winsaveview = function()
  local pos = vim.fn.winsaveview()
  vim.api.nvim_buf_set_extmark(0, config.namespace, pos.lnum - 1, pos.col, {id = config.mark.save_pos})
end

return marks


================================================
FILE: lua/iron/providers.lua
================================================
-- luacheck: globals vim
local fts = require("iron.fts")

local providers = {}

--[[ TODO ensure there's a default provider

The user should configure default provider; if none provided, use `first_matching_binary`.

The user should also be allowed to set specific providers for specific languages
--]]

providers.first_matching_binary = function(ft)
  local repl_definitions = fts[ft]
  if not repl_definitions then
    error("No repl definition configured for " .. ft)
  end

  local repl_def

  for _, v in pairs(repl_definitions) do
    if vim.fn.executable(v.command[1]) == 1 then
      repl_def = v
      break
    end
  end

  if not repl_def then
    error("Couldn't find a binary available for " .. ft)
  end
  return repl_def
end

return providers


================================================
FILE: lua/iron/scope.lua
================================================
-- luacheck: globals vim
local scope = {}

local ensure_key = function(map, key)
  map[key] = map[key] or {}
end

local default_map_set = function(map, base, key, value)
  ensure_key(map, base)
  map[base][key] = value
end

scope.tab_based = {
  set = function(memory, ft, repl_data)
    local tab = vim.fn.tabpagenr()
    default_map_set(memory, ft, "tab_" .. tab, repl_data)
    return repl_data
  end,
  get = function(memory, ft)
    ensure_key(memory, ft)
    local tab = vim.fn.tabpagenr()
    return memory[ft]["tab_" .. tab]
  end
}

scope.path_based = {
  set = function(memory, ft, repl_data)
    local pwd = vim.fn.getcwd()
    default_map_set(memory, ft, "pwd_" .. pwd, repl_data)
    return repl_data
  end,
  get = function(memory, ft)
    ensure_key(memory, ft)
    local pwd = vim.fn.getcwd()
    return memory[ft]["pwd_" .. pwd]
  end
}

scope.singleton = {
  set = function(memory, ft, repl_data)
    ensure_key(memory, ft)
    default_map_set(memory, ft, "singleton", repl_data)
    return repl_data
  end,
  get = function(memory, ft)
    ensure_key(memory, ft)
    return memory[ft]["singleton"]
  end
}

return scope


================================================
FILE: lua/iron/util/os.lua
================================================
local checks = {}

checks.is_windows = function()
    return package.config:sub(1,1) == '\\'
end


checks.has = function(feature)
  return vim.api.nvim_call_function('has', {feature}) == 1
end

return checks


================================================
FILE: lua/iron/util/tables.lua
================================================
-- luacheck: globals vim
local fns = {}

fns.extend = function(...)
  local tbl = {}
  local tbls = {n = select("#", ...), ...}
  for ix=1, tbls.n do
    local itm = tbls[ix]
    if itm ~= nil then
      if type(itm) == "table" then
        vim.list_extend(tbl, itm)
      else
        table.insert(tbl, itm)
      end
    end
  end

  return tbl
end


return fns


================================================
FILE: lua/iron/view.lua
================================================
-- luacheck: globals unpack vim
local view = {}

--- Private functions
local with_defaults = function(options)
  return vim.tbl_extend("keep", options or {}, {
    winfixwidth = true,
    winfixheight = true
  })
end

local nested_modifier

local with_nested_metatable = function(tbl)
  return setmetatable(tbl, {
    __index = nested_modifier,
    __call = function(_, arg, options)
      return tbl:mode(arg, options)
    end
  })
end

nested_modifier = function(tbl, key)
  local new = vim.deepcopy(tbl)
  table.insert(new, key)

  return with_nested_metatable(new)
end

local size_extractor = function(size, vertical, ...)
  local new_size
  if type(size) == "string" and string.find(size, "%%") then
    local pct = size:gsub("%%", "")
    pct = tonumber(pct)
    local base
    if vertical then
      base = vim.o.columns
    else
      base = vim.o.lines
    end
    new_size = math.floor((base * pct) / 100.0)
  elseif type(size) == "function" then
    new_size = size(vertical, ...)
  elseif size == nil then
    new_size = 0
  elseif 1 > size and size > 0 then
    local base
    if vertical then
      base = vim.o.columns
    else
      base = vim.o.lines
    end
    new_size = math.floor(base * size)
  else
    new_size = size
  end
  return new_size
end

view.helpers = {}

--- Returns proportional difference between an object and the editor size
-- with the offset adjusting the distance on both sides.
-- Use offset 0.5 for centralization
-- @tparam number offset proportion of distribution (0.5 is centralized)
-- @tparam boolean vertical Whether the oritentation is vertical or not
-- @tparam number sz size of the object
-- @treturn number placement index
-- @treturn function
view.helpers.proportion = function(offset)
  return function(vertical, sz)
    local attr = vertical and "columns" or "lines"
    return math.ceil(vim.o[attr] * offset - sz * offset)
  end
end

--- Flips the orientation from top/left to bottom/right
-- @tparam number offset in columns/lines
-- @tparam boolean vertical Whether the oritentation is vertical or not
-- @tparam number sz size of the object
-- @treturn number placement index
-- @treturn function
view.helpers.flip = function(offset)
  return function(vertical, sz)
    local attr = vertical and "columns" or "lines"
    return math.ceil(vim.o[attr] - sz - offset)
  end
end

--- Open a split window
-- Takes the arguments to the split command as nested values (keys) of this table.
-- @example view.split.vertical.botright(20)
-- @tparam table data itself
-- @tparam number|string|function size Either a number, a size in string or a function that returns the size
-- @tparam number bufnr buffer handle
-- @treturn number window id
-- @treturn function the function that opens the window
view.split = with_nested_metatable{ mode = function(data, size, options)
  return function(bufnr)
    local args = vim.list_slice(data, 1, #data)
    local new_size = size_extractor(size, vim.tbl_contains(data, "vertical") or vim.tbl_contains(data, "vert"))

    if size then
      table.insert(args, tostring(new_size))
    end
    table.insert(args, "split")

    vim.cmd(table.concat(args, " "))
    vim.api.nvim_set_current_buf(bufnr)

    local winid = vim.fn.bufwinid(bufnr)
    for opt, val in pairs(with_defaults(options)) do
      vim.api.nvim_win_set_option(winid, opt, val)
    end
    return winid
  end
end
}

--- Used to open a float window
-- @tparam table config parameters for the float window
-- @tparam number buff buffer handle
-- @treturn number window id
view.openfloat = function(config, buff)
  return vim.api.nvim_open_win(buff, false, config)
end


--- Opens a float at any point in the window
-- @tparam table opts Options for calculating the repl size
-- @tparam number|string|function opts.width width of the window
-- @tparam number|string|function opts.height height of the window
-- @tparam number|string|function opts.w_offset horizontal offset from the bottom-right corner
-- @tparam number|string|function opts.h_offset vertical offset from the bottom-right corner
-- @treturn table configuration for the float window
-- @treturn function
view.offset = function(opts)
  return function()
    local new_w_size = size_extractor(opts.width, true)
    local new_h_size = size_extractor(opts.height, false)
    local new_w_offset = size_extractor(opts.w_offset, true, new_w_size)
    local new_h_offset = size_extractor(opts.h_offset, false, new_h_size)

    return {
      relative = "editor",
      width = new_w_size,
      height = new_h_size,
      row = new_h_offset,
      col = new_w_offset
    }
  end
end

--- Opens a float pinned to the top
-- @tparam number|string|function size height of the window
-- @treturn function
view.top = function(size)
  return view.offset{width = vim.o.columns, height = size}
end

--- Opens a float pinned to the bottom
-- @tparam number|string|function size height of the window
-- @treturn function
view.bottom = function(size)
  return view.offset{width = vim.o.columns, height = size, h_offset = view.helpers.flip(0)}
end

--- Opens a float pinned to the right
-- @tparam number|string|function size width of the window
-- @treturn function
view.right = function(size)
  return view.offset{width = size, height = vim.o.lines, w_offset = view.helpers.flip(0)}
end

--- Opens a float pinned to the left
-- @tparam number|string|function size width of the window
-- @treturn function
view.left = function(size)
  return view.offset{width = size, height = vim.o.lines}
end

--- Opens a repl in the middle of the screen
-- @tparam number|string|function width width of the window
-- @tparam number|string|function height height of the window. If null will use `width` for this size
-- @treturn function
view.center = function(width, height)
  return view.offset{
    width = width,
    height = height or width,
    w_offset = view.helpers.proportion(0.5),
    h_offset = view.helpers.proportion(0.5)
  }
end

view.curry = setmetatable({}, {
  __index = function(_, key)
    if  view[key] == nil then
      error("Function `view." .. key .. "` does not exist.")
    end
    vim.deprecate("view.curry." .. key, "view." .. key, "3.2", "iron.nvim")
    return view[key]
  end
})

return view


================================================
FILE: lua/iron/visibility.lua
================================================
-- luacheck: globals unpack vim
local visibility = {}

--- Show hidden window
-- Creates a window for a buffer if it was previously
-- hidden by nvim_win_hide, otherwise does nothing.
-- @return window handle
-- @treturn boolean wither the window was hidden or not
local show_hidden = function(bufid, showfn)
  local was_hidden = false
  local window = vim.fn.bufwinid(bufid)

  if window == -1 then
    was_hidden = true
    window = showfn()
  end

  return window, was_hidden
end

visibility.single = function(bufid, showfn)
  show_hidden(bufid, showfn)
end

visibility.toggle = function(bufid, showfn)
  local window, was_hidden = show_hidden(bufid, showfn)
  if not was_hidden then
    vim.api.nvim_win_hide(window)
  end
end

visibility.focus = function(bufid, showfn)
  local window = show_hidden(bufid, showfn)
  vim.api.nvim_set_current_win(window)
end

return visibility


================================================
FILE: spec/iron/core_spec.lua
================================================
-- luacheck: globals insulate setup describe it assert mock
-- luacheck: globals before_each after_each

insulate("About #iron functionality", function()
  local tbl = require('iron.util.tables')
  before_each(function()
    _G.vim = mock({
      api = {
        nvim_call_function = function(_, _) return 1 end,
        nvim_command = function(_) return "" end,
        nvim_get_option = function(_) return "" end,
        nvim_get_var = function(_) return "" end,
        nvim_create_namespace = function(_) return 1000 end,
        nvim_set_hl_ns = function(_) return nil end,
        nvim_set_hl = function(_) return nil end,
        nvim_err_writeln = function(_) return nil end,
      },
      fn = {
        executable = function(_) return true end
      }
    })

    _G.os = mock({
      execute = function(_) return 0 end,
    })
  end)

  after_each(function()
     package.loaded['iron'] = nil
     package.loaded['iron.config'] = nil
     package.loaded['iron.core'] = nil
   end)

  describe("dynamic #config", function()
    it("is not called on a stored config", function()
      local iron = require('iron')
      iron.config.stuff = 1
      local _ = iron.config.stuff
      assert.stub(_G.vim.api.nvim_get_var).was_called(0)
    end)
  end)

  describe("#core functions", function()
    it("repl_for", function()
      local iron = require('iron')
      iron.ll.ensure_repl_exists = mock(function() return {bufnr = 1}, true end)
      iron.core.repl_for("python")
      assert.spy(_G.vim.api.nvim_command).was_called(1)
    end)

    it("repl_for if repl exists", function()
      local iron = require('iron')
      iron.ll.ensure_repl_exists = mock(function() return {bufnr = 1}, true end)
      iron.config.visibility = mock(function() end)
      iron.core.repl_for("python")
      assert.spy(_G.vim.api.nvim_command).was_called(1)
      assert.spy(iron.config.visibility).was_called(0)

      iron.ll.ensure_repl_exists = mock(function() return {bufnr = 1}, false end)
      iron.core.repl_for("python")
      assert.spy(iron.config.visibility).was_called(1)

    end)
  end)
end)


================================================
FILE: spec/iron/fts/common_spec.lua
================================================
-- luacheck: globals insulate setup describe it assert mock
-- luacheck: globals before_each after_each


insulate("On fthelper", function()
  local fts = require("iron.fts.common").functions

  describe("the #format function", function()

    it("should fail on string input", function()
        assert.has.errors(function () fts.format({}, "") end)
      end)

    it("should not add a new line on #empty lines input", function()
      assert.are.same(fts.format({}, {}), {})
      end)

    it("should not add a new empty line if the last one is an #empty line", function()
      assert.are.same(fts.format({}, {"asdf", ""}), {"asdf", "\13"})
      end)

    it("should not add a new line if the input contains only one line", function()
      assert.are.same(fts.format({}, {"asdf"}), {"asdf\13"})
      end)


    it("should add a new line if the input contains more than one line", function()
      assert.are.same(fts.format({}, {"asdf", "qwer"}), {"asdf", "qwer\13"})
      end)

    it("should use supplied #format function if one is supplied", function()
      local f = mock(function(lns)
        return lns
      end)

      assert.are.same(fts.format({format = f}, {"1", "2"}), {"1", "2"})

      assert.stub(f).was_called(1)



    end)

    it("should #wrap the lines with whatever supplied enclosing pairs", function()
      assert.are.same(
        fts.format({open = "{", close = "}"}, {"asdf", "qwer"}),
        {"{", "asdf", "qwer", "}\13"}
      )

      assert.are.same(
        fts.format({open = "\x1b[200~", close = "\x1b[201~"}, {"asdf", "qwer"}),
        {"\x1b[200~", "asdf", "qwer", "\x1b[201~\13"}
      )

      assert.are.same(
        fts.format({open = "\27[200~", close = "\27[201~"}, {"asdf", "qwer"}),
        {"\27[200~", "asdf", "qwer", "\27[201~\13"}
      )
      end)
  end)
end)


================================================
FILE: spec/iron/util/tables_spec.lua
================================================
-- luacheck: globals insulate setup describe it assert mock
-- luacheck: globals before_each after_each

insulate("On #tables code", function()
  local fn = require('iron.util.tables')

  describe("#get", function()
    it("should return nil if key doesn't exist", function()
      assert.are_same(nil, fn.get({}, "key"))
      end)

    it("should return nil if map is nil", function()
      assert.are_same(nil, fn.get(nil, "key"))
      end)

    it("should return nil if map is not a table", function()
      assert.are_same(nil, fn.get(0, "key"))
      end)

    it("should return value otherwise", function()
      assert.are_same(0, fn.get({key = 0}, "key"))
      end)
    end)

end)
Download .txt
gitextract_qzvaewdp/

├── .busted
├── .github/
│   └── FUNDING.yml
├── .gitignore
├── .semaphore/
│   └── semaphore.yml
├── LICENSE
├── README.md
├── bin/
│   └── pctl
├── doc/
│   └── iron.txt
├── lua/
│   └── iron/
│       ├── config.lua
│       ├── core.lua
│       ├── dap.lua
│       ├── debug_level.lua
│       ├── fts/
│       │   ├── clojure.lua
│       │   ├── common.lua
│       │   ├── cpp.lua
│       │   ├── csh.lua
│       │   ├── elixir.lua
│       │   ├── elm.lua
│       │   ├── erlang.lua
│       │   ├── fennel.lua
│       │   ├── fish.lua
│       │   ├── forth.lua
│       │   ├── haskell.lua
│       │   ├── hy.lua
│       │   ├── init.lua
│       │   ├── janet.lua
│       │   ├── javascript.lua
│       │   ├── julia.lua
│       │   ├── lisp.lua
│       │   ├── lua.lua
│       │   ├── markdown.lua
│       │   ├── mma.lua
│       │   ├── ocaml.lua
│       │   ├── php.lua
│       │   ├── prolog.lua
│       │   ├── ps1.lua
│       │   ├── pure.lua
│       │   ├── python.lua
│       │   ├── r.lua
│       │   ├── racket.lua
│       │   ├── ruby.lua
│       │   ├── sbt.lua
│       │   ├── scala.lua
│       │   ├── scheme.lua
│       │   ├── sh.lua
│       │   ├── stata.lua
│       │   ├── tcl.lua
│       │   ├── typescript.lua
│       │   └── zsh.lua
│       ├── init.lua
│       ├── log.lua
│       ├── lowlevel.lua
│       ├── marks.lua
│       ├── providers.lua
│       ├── scope.lua
│       ├── util/
│       │   ├── os.lua
│       │   └── tables.lua
│       ├── view.lua
│       └── visibility.lua
└── spec/
    └── iron/
        ├── core_spec.lua
        ├── fts/
        │   └── common_spec.lua
        └── util/
            └── tables_spec.lua
Condensed preview — 62 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (95K chars).
[
  {
    "path": ".busted",
    "chars": 92,
    "preview": "return {\n  default = {\n    verbose = true,\n    lpath = \"./lua/?.lua;./lua/?/init.lua\"\n  }\n}\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 638,
    "preview": "# These are supported funding model platforms\n\ngithub: # []\npatreon: # Replace with a single Patreon username\nopen_colle"
  },
  {
    "path": ".gitignore",
    "chars": 41,
    "preview": "*.py[eco]\n__pycache__/\nMakefile\ndoc/tags\n"
  },
  {
    "path": ".semaphore/semaphore.yml",
    "chars": 483,
    "preview": "version: v1.0\nname: Initial Pipeline\nagent:\n  machine:\n    type: e1-standard-2\n    os_image: ubuntu2004\nblocks:\n  - name"
  },
  {
    "path": "LICENSE",
    "chars": 1516,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Henry John Kupty\nAll rights reserved.\n\nRedistribution and use in source and bi"
  },
  {
    "path": "README.md",
    "chars": 8792,
    "preview": "# iron.nvim\n\nInteractive Repls Over Neovim.\n\n## What is iron.nvim\n\n[![asciicast](https://asciinema.org/a/495376.svg)](ht"
  },
  {
    "path": "bin/pctl",
    "chars": 659,
    "preview": "#!/bin/bash\nset -eou pipefail\n\nBIN_DIR=\"$(dirname \"$(realpath \"$0\")\")\"\nROOT_DIR=\"$(dirname \"$BIN_DIR\")\"\n\nLUAROCKS=\"luaro"
  },
  {
    "path": "doc/iron.txt",
    "chars": 11614,
    "preview": "*iron.nvim* Interactive Repls Over Neovim\n\n============================================================================="
  },
  {
    "path": "lua/iron/config.lua",
    "chars": 1851,
    "preview": "-- luacheck: globals vim\nlocal view = require(\"iron.view\")\n\n--- Default values\n--@module config\nlocal config\n\n--- Defaul"
  },
  {
    "path": "lua/iron/core.lua",
    "chars": 25686,
    "preview": "-- luacheck: globals vim unpack\n\nlocal log = require(\"iron.log\")\nlocal ll = require(\"iron.lowlevel\")\nlocal focus = requi"
  },
  {
    "path": "lua/iron/dap.lua",
    "chars": 827,
    "preview": "local M = {}\n\nlocal is_dap_integration_enabled = false\n\n--- Sets up a hook to keep track of the DAP session state.\nfunct"
  },
  {
    "path": "lua/iron/debug_level.lua",
    "chars": 182,
    "preview": "--- Sets the level of debug\n--@module debug_level\nlocal debug_level = {}\n\ndebug_level.fatal = 1\ndebug_level.err   = 2\nde"
  },
  {
    "path": "lua/iron/fts/clojure.lua",
    "chars": 175,
    "preview": "local clojure = {}\n\nclojure.boot = {\n  command = {\"boot\", \"repl\"},\n}\n\nclojure.lein = {\n  command = {\"lein\", \"repl\"},\n}\n\n"
  },
  {
    "path": "lua/iron/fts/common.lua",
    "chars": 3910,
    "preview": "local is_windows = require(\"iron.util.os\").is_windows\nlocal extend = require(\"iron.util.tables\").extend\nlocal open_code "
  },
  {
    "path": "lua/iron/fts/cpp.lua",
    "chars": 1197,
    "preview": "\nlocal cpp = {}\n\nlocal function magic(str)\n    -- remove every space at beginning of a line\n    str = str:gsub('^%s+', '"
  },
  {
    "path": "lua/iron/fts/csh.lua",
    "chars": 100,
    "preview": "local csh = {}\n\ncsh.csh = {\n  command = {\"csh\"},\n}\n\ncsh.tcsh = {\n  command = {\"tch\"},\n}\n\nreturn csh\n"
  },
  {
    "path": "lua/iron/fts/elixir.lua",
    "chars": 72,
    "preview": "local elixir = {}\n\nelixir.iex = {\n  command = {\"iex\"},\n}\n\nreturn elixir\n"
  },
  {
    "path": "lua/iron/fts/elm.lua",
    "chars": 119,
    "preview": "local elm = {}\n\nelm.elm = {\n  command = {\"elm\", \"repl\"},\n}\n\nelm.elm_legacy = {\n  command = {\"elm-repl\"},\n}\n\nreturn elm\n"
  },
  {
    "path": "lua/iron/fts/erlang.lua",
    "chars": 72,
    "preview": "local erlang = {}\n\nerlang.erl = {\n  command = {\"erl\"},\n}\n\nreturn erlang\n"
  },
  {
    "path": "lua/iron/fts/fennel.lua",
    "chars": 78,
    "preview": "local fennel = {}\n\nfennel.fennel = {\n  command = {\"fennel\"},\n}\n\nreturn fennel\n"
  },
  {
    "path": "lua/iron/fts/fish.lua",
    "chars": 68,
    "preview": "local fish = {}\n\nfish.fish = {\n  command = {\"fish\"},\n}\n\nreturn fish\n"
  },
  {
    "path": "lua/iron/fts/forth.lua",
    "chars": 74,
    "preview": "local forth = {}\n\nforth.gforth = {\n  command = {\"gforth\"}\n}\n\nreturn forth\n"
  },
  {
    "path": "lua/iron/fts/haskell.lua",
    "chars": 264,
    "preview": "local haskell = {}\n\nhaskell.stack_intero = {\n  command = {\"stack\", \"ghci\", \"--with-ghc\", \"intero\"},\n}\n\nhaskell.stack = {"
  },
  {
    "path": "lua/iron/fts/hy.lua",
    "chars": 57,
    "preview": "local hy = {}\n\nhy.hy = {\n  command = {\"hy\"}\n}\n\nreturn hy\n"
  },
  {
    "path": "lua/iron/fts/init.lua",
    "chars": 1294,
    "preview": "-- luacheck: globals unpack\n\nlocal fts = {\n  clojure = require(\"iron.fts.clojure\"),\n  cpp = require(\"iron.fts.cpp\"),\n  c"
  },
  {
    "path": "lua/iron/fts/janet.lua",
    "chars": 73,
    "preview": "local janet = {}\n\njanet.janet = {\n  command = {\"janet\"},\n}\n\nreturn janet\n"
  },
  {
    "path": "lua/iron/fts/javascript.lua",
    "chars": 125,
    "preview": "local javascript = {}\n\njavascript.node = {\n  command = {\"node\"},\n  open = \".editor\\n\",\n  close = \"\\04\",\n}\n\nreturn javasc"
  },
  {
    "path": "lua/iron/fts/julia.lua",
    "chars": 73,
    "preview": "local julia = {}\n\njulia.julia = {\n  command = {\"julia\"},\n}\n\nreturn julia\n"
  },
  {
    "path": "lua/iron/fts/lisp.lua",
    "chars": 109,
    "preview": "local lisp = {}\n\nlisp.sbcl = {\n  command = {\"sbcl\"},\n}\n\nlisp.clisp = {\n  command = {\"clisp\"},\n}\n\nreturn lisp\n"
  },
  {
    "path": "lua/iron/fts/lua.lua",
    "chars": 63,
    "preview": "local lua = {}\n\nlua.lua = {\n  command = {\"lua\"},\n}\n\nreturn lua\n"
  },
  {
    "path": "lua/iron/fts/markdown.lua",
    "chars": 137,
    "preview": "local markdown = {}\n\nmarkdown.aichat = {\n  command = \"aichat\",\n  format = require(\"iron.fts.common\").bracketed_paste,\n}\n"
  },
  {
    "path": "lua/iron/fts/mma.lua",
    "chars": 67,
    "preview": "local mma = {}\n\nmma.math = {\n  command = { \"math\" },\n}\n\nreturn mma\n"
  },
  {
    "path": "lua/iron/fts/ocaml.lua",
    "chars": 119,
    "preview": "local ocaml = {}\n\nocaml.utop = {\n  command = {\"utop\"},\n}\n\nocaml.ocamltop = {\n  command = {\"ocamltop\"},\n}\n\nreturn ocaml\n"
  },
  {
    "path": "lua/iron/fts/php.lua",
    "chars": 109,
    "preview": "local php = {}\n\nphp.psysh = {\n  command = {\"psysh\"},\n}\n\nphp.php = {\n  command = {\"php\", \"-a\"},\n}\n\nreturn php\n"
  },
  {
    "path": "lua/iron/fts/prolog.lua",
    "chars": 123,
    "preview": "local prolog = {}\n\nprolog.gprolog = {\n  command = {\"gprolog\"},\n}\n\nprolog.swipl = {\n  command = {\"swipl\"},\n}\n\nreturn prol"
  },
  {
    "path": "lua/iron/fts/ps1.lua",
    "chars": 111,
    "preview": "local ps1 = {}\n\nps1.ps1 = {\n  command = {\"powershell\", \"-noexit\", \"-executionpolicy\", \"bypass\"},\n}\n\nreturn ps1\n"
  },
  {
    "path": "lua/iron/fts/pure.lua",
    "chars": 106,
    "preview": "local pure = {}\n\npure.pure = {\n  command = {\"pure\"},\n  open = \":paste\\n\",\n  close = \"\\04\",\n}\n\nreturn pure\n"
  },
  {
    "path": "lua/iron/fts/python.lua",
    "chars": 563,
    "preview": "-- luacheck: globals vim\nlocal bracketed_paste_python = require(\"iron.fts.common\").bracketed_paste_python\nlocal python ="
  },
  {
    "path": "lua/iron/fts/r.lua",
    "chars": 226,
    "preview": "local bracketed_paste = require(\"iron.fts.common\").bracketed_paste\nlocal r = {}\n\nr.R = {\n    command = { \"R\" },\n    form"
  },
  {
    "path": "lua/iron/fts/racket.lua",
    "chars": 78,
    "preview": "local racket = {}\n\nracket.racket = {\n  command = {\"racket\"},\n}\n\nreturn racket\n"
  },
  {
    "path": "lua/iron/fts/ruby.lua",
    "chars": 66,
    "preview": "local ruby = {}\n\nruby.irb = {\n  command = {\"irb\"},\n}\n\nreturn ruby\n"
  },
  {
    "path": "lua/iron/fts/sbt.lua",
    "chars": 101,
    "preview": "local sbt = {}\n\nsbt.sbt = {\n  command = {\"sbt\"},\n  open = \":paste\\n\",\n  close = \"\\04\",\n}\n\nreturn sbt\n"
  },
  {
    "path": "lua/iron/fts/scala.lua",
    "chars": 194,
    "preview": "local scala = {}\n\nlocal def = function(command)\n  return {\n    command = command,\n    open = \":paste\",\n    close = \"\\04\""
  },
  {
    "path": "lua/iron/fts/scheme.lua",
    "chars": 115,
    "preview": "local scheme = {}\n\nscheme.guile = {\n  command = {\"guile\"},\n}\n\nscheme.csi = {\n  command = {\"csi\"},\n}\n\nreturn scheme\n"
  },
  {
    "path": "lua/iron/fts/sh.lua",
    "chars": 374,
    "preview": "local sh = {}\n\nsh.bash = {\n  command = {\"bash\"},\n}\n\nsh.sh = {\n  command = function(meta)\n    local bufnr = meta.current_"
  },
  {
    "path": "lua/iron/fts/stata.lua",
    "chars": 80,
    "preview": "local stata = {}\n\nstata.stata = {\n\tcommand = { \"stata\", \"-q\" },\n}\n\nreturn stata\n"
  },
  {
    "path": "lua/iron/fts/tcl.lua",
    "chars": 67,
    "preview": "local tcl = {}\n\ntcl.tclsh = {\n  command = {\"tclsh\"},\n}\n\nreturn tcl\n"
  },
  {
    "path": "lua/iron/fts/typescript.lua",
    "chars": 126,
    "preview": "local typescript = {}\n\ntypescript.ts = {\n  command = {\"ts-node\"},\n  open = \".editor\\n\",\n  close = \"\\04\",\n}\n\nreturn types"
  },
  {
    "path": "lua/iron/fts/zsh.lua",
    "chars": 63,
    "preview": "local zsh = {}\n\nzsh.zsh = {\n  command = {\"zsh\"},\n}\n\nreturn zsh\n"
  },
  {
    "path": "lua/iron/init.lua",
    "chars": 274,
    "preview": "-- luacheck: globals unpack vim\n\nlocal iron = {\n  -- Will eventually be removed\n  config = require(\"iron.config\"),\n  -- "
  },
  {
    "path": "lua/iron/log.lua",
    "chars": 355,
    "preview": "-- luacheck: globals vim\nlocal log = {}\n\nif vim.deprecate then\n  log.deprecate = vim.deprecate\nelse\n  log.deprecate = fu"
  },
  {
    "path": "lua/iron/lowlevel.lua",
    "chars": 7616,
    "preview": "-- luacheck: globals vim\n-- TODO Remove config from this layer\nlocal config = require(\"iron.config\")\nlocal fts = require"
  },
  {
    "path": "lua/iron/marks.lua",
    "chars": 2548,
    "preview": "-- luacheck: globals vim\nlocal config = require(\"iron.config\")\n\n--- Marks management for iron\n-- This is an intermediary"
  },
  {
    "path": "lua/iron/providers.lua",
    "chars": 758,
    "preview": "-- luacheck: globals vim\nlocal fts = require(\"iron.fts\")\n\nlocal providers = {}\n\n--[[ TODO ensure there's a default provi"
  },
  {
    "path": "lua/iron/scope.lua",
    "chars": 1139,
    "preview": "-- luacheck: globals vim\nlocal scope = {}\n\nlocal ensure_key = function(map, key)\n  map[key] = map[key] or {}\nend\n\nlocal "
  },
  {
    "path": "lua/iron/util/os.lua",
    "chars": 208,
    "preview": "local checks = {}\n\nchecks.is_windows = function()\n    return package.config:sub(1,1) == '\\\\'\nend\n\n\nchecks.has = function"
  },
  {
    "path": "lua/iron/util/tables.lua",
    "chars": 364,
    "preview": "-- luacheck: globals vim\nlocal fns = {}\n\nfns.extend = function(...)\n  local tbl = {}\n  local tbls = {n = select(\"#\", ..."
  },
  {
    "path": "lua/iron/view.lua",
    "chars": 6211,
    "preview": "-- luacheck: globals unpack vim\nlocal view = {}\n\n--- Private functions\nlocal with_defaults = function(options)\n  return "
  },
  {
    "path": "lua/iron/visibility.lua",
    "chars": 881,
    "preview": "-- luacheck: globals unpack vim\nlocal visibility = {}\n\n--- Show hidden window\n-- Creates a window for a buffer if it was"
  },
  {
    "path": "spec/iron/core_spec.lua",
    "chars": 2103,
    "preview": "-- luacheck: globals insulate setup describe it assert mock\n-- luacheck: globals before_each after_each\n\ninsulate(\"About"
  },
  {
    "path": "spec/iron/fts/common_spec.lua",
    "chars": 1822,
    "preview": "-- luacheck: globals insulate setup describe it assert mock\n-- luacheck: globals before_each after_each\n\n\ninsulate(\"On f"
  },
  {
    "path": "spec/iron/util/tables_spec.lua",
    "chars": 692,
    "preview": "-- luacheck: globals insulate setup describe it assert mock\n-- luacheck: globals before_each after_each\n\ninsulate(\"On #t"
  }
]

About this extraction

This page contains the full source code of the hkupty/iron.nvim GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 62 files (86.0 KB), approximately 24.7k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!