chore: project_cleanup

This commit is contained in:
Raafat Turki 2021-11-26 06:27:56 +03:00
parent 0c0b37d065
commit e6b71d6586
10 changed files with 206 additions and 260 deletions

272
README.md
View File

@ -1,191 +1,136 @@
# WARNING
This is not fully baked, though used by several people. If you experience any
issues, see some improvement you think would be amazing, or just have some
feedback for harpoon (or me), make a ticket!
<div align="center">
# Harpoon
##### Getting you where you want with the fewest keystrokes.
[![Lua](https://img.shields.io/badge/Lua-blue.svg?style=for-the-badge&logo=lua)](http://www.lua.org)
[![Neovim](https://img.shields.io/badge/Neovim%200.5+-green.svg?style=for-the-badge&logo=neovim)](https://neovim.io)
</div>
![Harpoon](harpoon.png)
-- Image provided by Liberty_DevCap
-- image provided by **Liberty_DevCap**
# harpoon
The goal of Harpoon is to get you where you want with the fewest keystrokes.
## The Problem
You work on code. The code base is medium, large, tiny, whatever. You find
yourself frequenting a small set of files (maybe it depends on task) and you
are tired of using a fuzzy finder, :bnext/prev, alternate file doesn't quite
cut it, etc etc.
## ⇁ WIP
This is not fully baked, though used by several people. If you experience any
issues, see some improvement you think would be amazing, or just have some
feedback for harpoon (or me), make an issue!
## The Other Problem
You want to execute some project specific commands or have any number of
## ⇁ The Problems:
1. You're working on a codebase. medium, large, tiny, whatever. You find
yourself frequenting a small set of files and you are tired of using a fuzzy finder,
`:bnext` & `:bprev` are getting too repetitive, alternate file doesn't quite cut it, etc etc.
1. You want to execute some project specific commands or have any number of
persistent terminals that can be easily navigated to.
## The Solution
The ability to specify, or on the fly, mark and create persisting key strokes
## ⇁ The Solutions:
1. The ability to specify, or on the fly, mark and create persisting key strokes
to go to the files you want.
1. Unlimited terminals and navigation.
## The Other Solution
Unlimited terminals and navigation.
## Installation
### Requires Neovim version 0.5.0+
Simply install via your favorite plugin manager.
## ⇁ Installation
* neovim 0.5.0+ required
* install using your favorite plugin manager (`vim-plug` in this example)
```vim
Plug 'nvim-lua/plenary.nvim' " don't forget to add this one if you don't have it yet!
Plug 'ThePrimeagen/harpoon'
```
## Harpooning
There are two modes of harpoon. File navigation and terminal navigation.
Setup of harpoon configuration is at the bottom since its for more advanced use
cases.
## ⇁ Harpooning
here we'll explain how to wield the power of the harpoon:
### File Navigation
#### Mark a file
Marking a file is similar to vim global marks, but differ in a few key ways.
* They auto update their position
* They are unique _per project_.
* They can be hand edited vs replaced (swapping is easier)
To mark a file simply call the following lua function
### Marks
you mark files you want to revisit later on
```lua
:lua require("harpoon.mark").add_file()
```
This will mark the file and add it to the end of the mark list.
#### Navigate to file
To navigate to any of the marked files simply call the navigation function with
which index.
```lua
:lua require("harpoon.ui").nav_file(3) -- This will navigate to file 3
```
#### Manipulating current marks
There is a quick menu that will allow for you to edit your marks. You can hand
edit the name, its position within the list, or remove it from the list. To
bring up the quick list execute the following lua command.
### File Navigation
view all project marks with:
```lua
:lua require("harpoon.ui").toggle_quick_menu()
```
you can go up and down the list, enter, delete or reorder. `q` and `<ESC>` exit and save the menu
You can simply edit this list as if it were a document in vim. `:wq` to save
the new edits or `:q` to ignore the edits. There is to save upon call to
toggle if you prefer that way.
You can also exit the list with `q` or `<ESC>`, which will call `toggle_quick_menu()` again.
### Terminal Navigation
#### Motivation for terminals in neovim
I want to use the terminal since I can gF and <c-w>gF to any errors arising
from execution that are within the terminal that are not appropriate for
something like dispatch. (not just running tests but perhaps a server that runs
for X amount of time before crashing).
I want the terminal to be persistent and I can return to one of many terminals
with some finger wizardry and reparse any of the execution information that was
not necessarily error related.
I would like to have commands that can be tied to terminals and sent them
without much thinking. Some sort of middle ground between vim-test and just
typing them into a terminal (configuring netflix's television project isn't
quite building and there are tons of ways to configure).
#### Navigating to a terminal
To navigate to a terminal simply provide an index and it will go. If there is
no terminal in that index or the terminal has been closed by some means,
harpoon will create a new terminal at that location.
you also can switch to any mark without bringing up the menu, use the below with the desired mark index
```lua
lua require("harpoon.term").gotoTerminal(1)
:lua require("harpoon.ui").nav_file(3) -- navigates to file 3
```
You can provide as high of a number as you would like. There is no terminal
count limitation though I personally find anything beyond two oft confusing.
#### Commands to terminal
Sometimes you wish to send commands to terminals that have been preconfigured
for a project. To make this work properly you must predefine a command or hard
code it as part of the send process.
### Terminal Navigation
this works like file navigation except that if there is no terminal at the specified index
a new terminal is created.
```lua
lua require("harpoon.term").gotoTerminal(1) -- navigates to term 1
```
### Commands to Terminals
commands can be sent to any terminal
```lua
" This will send to terminal 1 either the predefined command 1 in the terminal
" config or "ls -la"
lua require("harpoon.term").sendCommand(1, 1)
lua require("harpoon.term").sendCommand(1, "ls -la")
```
further more commands can be stored for later quick
```lua
lua require('harpoon.cmd-ui').toggle_quick_menu() -- shows the commands menu
lua require("harpoon.term").sendCommand(1, 1) -- sends command 1 to term 1
```
#### Dynamic commands to terminal
This feature adds ability to change commands while working inside a project.
Just call the following function to edit commands inside the list
### Tmux Support
tmux is supported out of the box and can be used as a drop-in replacement to normal terminals
by simply switching `'term' with 'tmux'` like so
```lua
lua require('harpoon.cmd-ui').toggle_quick_menu()
lua require("harpoon.tmux").gotoTerminal(1) -- goes to the first tmux window
lua require("harpoon.tmux").sendCommand(1, "ls -la") -- sends ls -La to tmux window 1
lua require("harpoon.tmux").sendCommand(1, 1) -- sends command 1 to tmux window 1
```
#### Tmux Integration
Harpoon also supports all terminal operations (see above) with tmux terminals.
The configuration for using tmux is exactly the same as the config for using nvim
terminals. To use tmux terminals instead of nvim terminals, simply replace
`harpoon.term` with `harpoon.tmux` in your require statement.
For example:
### Telescope Support
1st register harpoon as a telescope extension
```lua
-- goes to the first tmux window
lua require("harpoon.tmux").gotoTerminal(1)
-- sends a command to the first tmux window
lua require("harpoon.tmux").sendCommand(1, "ls -la")
require("telescope").load_extension('harpoon')
```
currently only marks are supported in telescope
```
:Telescope harpoon marks
```
### Setup
Setup should be called once.
#### TODO: Make this callable more than once and just layer in the commands
Yes... A todo in a readme. Deal with it.
#### The Configuration File
You can configure harpoon via lua in your rc. Here is a simple example that
will add a specific command to a project.
##### Global Settings
## ⇁ Configuration
if configuring harpoon is desired it must be done through harpoons setup function
```lua
Here is the set of global settings and their default values.
require("harpoon").setup({ ... })
```
require("harpoon").setup({
global_settings = {
### Global Settings
here are all the available global settings with their default values
```lua
global_settings = {
-- sets the marks upon calling `toggle` on the ui, instead of require `:w`.
save_on_toggle = false,
-- saves the harpoon file upon every change. disabling is unrecommended.
save_on_change = true,
-- sets harpoon to run the command immediately as it's passed to the terminal when calling `sendCommand`.
enter_on_sendcmd = false,
-- closes any tmux windows harpoon that harpoon creates when you close Neovim.
tmux_autoclose_windows = false,
-- filetypes that you want to prevent from adding to the harpoon list menu.
excluded_filetypes = { "harpoon" }
},
... your other configs ...
})
}
```
* `save_on_toggle` will set the marks upon calling `toggle` on the ui, instead
of require `:w`.
* `save_on_change` will save the harpoon file upon every change. If you don't
enable this option (on by default) harpoon will not save any changes to your
file. It is very unreliable to save your harpoon on exit (at least that is
what I have found).
* `enter_on_sendcmd` will set harpoon to run the command immediately as it's
passed to the terminal when calling `sendCommand`.
* `tmux_autoclose_windows` will close any tmux windows harpoon that harpoon creates
when you close Neovim.
* `excluded_filetypes` filetypes that you want to prevent from adding to the harpoon list menu.
#### Preconfigured Terminal Commands
These are project specific commands that you wish to execute on the regular.
### Preconfigured Terminal Commands
to preconfigure terminal commands for later use
```lua
require("harpoon").setup({
projects = {
-- Yes $HOME works
["$HOME/personal/vim-with-me/server"] = {
@ -194,34 +139,33 @@ require("harpoon").setup({
"./env && npx ts-node src/index.ts"
}
}
},
}
}
```
## Debugging
Harpoon writes logs to a `harpoon.log` file that resides in Neovim's cache
path. (`:echo stdpath("cache")` to find where that is for you.)
## ⇁ Logging
- logs are written to `harpoon.log` within the nvim cache path (`:echo stdpath("cache")`)
- available log levels are `trace`, `debug`, `info`, `warn`, `error`, or `fatal`. `warn` is default
- log level can be set with `vim.g.harpoon_log_level` (must be **before** `setup()`)
- launching nvim with `HARPOON_LOG=debug nvim` takes precedence over `vim.g.harpoon_log_level`.
- invalid values default back to `warn`.
By default, logging is enabled for warnings and above. This can be changed by
setting `vim.g.harpoon_log_level` variable to one of the following log levels:
`trace`, `debug`, `info`, `warn`, `error`, or `fatal`. Note that this would
have to be done **before** harpoon's `setup` call. Alternatively, it can be
more convenient to launch Neovim with an environment variable, e.g. `>
HARPOON_LOG=trace nvim`. In case both, `vim.g` and an environment variable are
used, the log level set by the environment variable overrules. Supplying an
invalid log level defaults back to warnings.
## ⇁ Others
#### How do Harpoon marks differ from vim global marks
they seve a similar purpose however harpoon marks differs in a few key ways:
1. They auto update their position within the file
1. They are saved _per project_.
1. They can be hand edited vs replaced (swapping is easier)
## Telescope
### Setup
Add thet followingt to your config:
```lua
require("telescope").load_extension('harpoon')
```
### Launch
Call the following to launch the telescope window
```lua
:Telescope harpoon marks
```
#### The Motivation behind Harpoon terminals
1. I want to use the terminal since I can gF and <c-w>gF to any errors arising
from execution that are within the terminal that are not appropriate for
something like dispatch. (not just running tests but perhaps a server that runs
for X amount of time before crashing).
1. I want the terminal to be persistent and I can return to one of many terminals
with some finger wizardry and reparse any of the execution information that was
not necessarily error related.
1. I would like to have commands that can be tied to terminals and sent them
without much thinking. Some sort of middle ground between vim-test and just
typing them into a terminal (configuring netflix's television project isn't
quite building and there are tons of ways to configure).

View File

@ -9,4 +9,4 @@
* ackshual tests.
* interactive menu
* cycle
* make setup() callable more than once and just layer in the commands

View File

@ -68,7 +68,7 @@ local function get_menu_items()
return indices
end
M.toggle_quick_menu = function()
function M.toggle_quick_menu()
log.trace("cmd-ui#toggle_quick_menu()")
if
Harpoon_cmd_win_id ~= nil
@ -138,7 +138,7 @@ M.toggle_quick_menu = function()
)
end
M.select_menu_item = function()
function M.select_menu_item()
log.trace("cmd-ui#select_menu_item()")
local cmd = vim.fn.line(".")
close_menu(true)
@ -152,7 +152,7 @@ M.select_menu_item = function()
end
end
M.on_menu_save = function()
function M.on_menu_save()
log.trace("cmd-ui#on_menu_save()")
term.set_cmd_list(get_menu_items())
end

View File

@ -5,7 +5,7 @@
-- :nmap <leader>rr :lua require("harpoon.dev").reload()<CR>
local M = {}
M.reload = function()
function M.reload()
require("plenary.reload").reload_module("harpoon")
end
@ -41,7 +41,7 @@ for _, v in pairs(log_levels) do
override(v)
end
M.get_log_key = function()
function M.get_log_key()
return log_key
end

View File

@ -119,7 +119,7 @@ local function expand_dir(config)
return config
end
M.save = function()
function M.save()
log.trace("save(): Saving cache config to", cache_config)
Path:new(cache_config):write(vim.fn.json_encode(HarpoonConfig), "w")
end
@ -130,7 +130,7 @@ local function read_config(local_config)
end
-- 1. saved. Where do we save?
M.setup = function(config)
function M.setup(config)
log.trace("setup(): Setting up...")
if not config then
@ -171,28 +171,28 @@ M.setup = function(config)
log.trace("setup(): log_key", Dev.get_log_key())
end
M.get_global_settings = function()
function M.get_global_settings()
log.trace("get_global_settings()")
return HarpoonConfig.global_settings
end
M.get_term_config = function()
function M.get_term_config()
log.trace("get_term_config()")
return ensure_correct_config(HarpoonConfig).projects[vim.loop.cwd()].term
end
M.get_mark_config = function()
function M.get_mark_config()
log.trace("get_mark_config()")
return ensure_correct_config(HarpoonConfig).projects[vim.loop.cwd()].mark
end
M.get_menu_config = function()
function M.get_menu_config()
log.trace("get_menu_config()")
return HarpoonConfig.menu or {}
end
-- should only be called for debug purposes
M.print_config = function()
function M.print_config()
print(vim.inspect(HarpoonConfig))
end

View File

@ -136,7 +136,7 @@ local function filter_filetype()
end
end
M.get_index_of = function(item)
function M.get_index_of(item)
log.trace("get_index_of():", item)
if item == nil then
log.error(
@ -172,7 +172,7 @@ M.get_index_of = function(item)
return nil
end
M.status = function(bufnr)
function M.status(bufnr)
log.trace("status()")
local buf_name
if bufnr then
@ -190,7 +190,7 @@ M.status = function(bufnr)
return ""
end
M.valid_index = function(idx)
function M.valid_index(idx)
log.trace("valid_index():", idx)
if idx == nil then
return false
@ -200,7 +200,7 @@ M.valid_index = function(idx)
return file_name ~= nil and file_name ~= ""
end
M.add_file = function(file_name_or_buf_id)
function M.add_file(file_name_or_buf_id)
filter_filetype()
local buf_name = get_buf_name(file_name_or_buf_id)
log.trace("add_file():", buf_name)
@ -219,7 +219,7 @@ M.add_file = function(file_name_or_buf_id)
end
-- _emit_on_changed == false should only be used internally
M.remove_empty_tail = function(_emit_on_changed)
function M.remove_empty_tail(_emit_on_changed)
log.trace("remove_empty_tail()")
_emit_on_changed = _emit_on_changed == nil or _emit_on_changed
local config = harpoon.get_mark_config()
@ -242,7 +242,7 @@ M.remove_empty_tail = function(_emit_on_changed)
end
end
M.store_offset = function()
function M.store_offset()
log.trace("store_offset()")
local ok, res = pcall(function()
local buf_name = get_buf_name()
@ -270,7 +270,7 @@ M.store_offset = function()
emit_changed()
end
M.rm_file = function(file_name_or_buf_id)
function M.rm_file(file_name_or_buf_id)
local buf_name = get_buf_name(file_name_or_buf_id)
local idx = M.get_index_of(buf_name)
log.trace("rm_file(): Removing mark at id", idx)
@ -285,14 +285,14 @@ M.rm_file = function(file_name_or_buf_id)
emit_changed()
end
M.clear_all = function()
function M.clear_all()
harpoon.get_mark_config().marks = {}
log.trace("clear_all(): Clearing all marks.")
emit_changed()
end
--- ENTERPRISE PROGRAMMING
M.get_marked_file = function(idxOrName)
function M.get_marked_file(idxOrName)
log.trace("get_marked_file():", idxOrName)
if type(idxOrName) == "string" then
idxOrName = M.get_index_of(idxOrName)
@ -300,18 +300,18 @@ M.get_marked_file = function(idxOrName)
return harpoon.get_mark_config().marks[idxOrName]
end
M.get_marked_file_name = function(idx)
function M.get_marked_file_name(idx)
local mark = harpoon.get_mark_config().marks[idx]
log.trace("get_marked_file_name():", mark and mark.filename)
return mark and mark.filename
end
M.get_length = function()
function M.get_length()
log.trace("get_length()")
return table.maxn(harpoon.get_mark_config().marks)
end
M.set_current_at = function(idx)
function M.set_current_at(idx)
filter_filetype()
local buf_name = get_buf_name()
log.trace("set_current_at(): Setting id", idx, buf_name)
@ -334,7 +334,7 @@ M.set_current_at = function(idx)
emit_changed()
end
M.to_quickfix_list = function()
function M.to_quickfix_list()
log.trace("to_quickfix_list(): Sending marks to quickfix list.")
local config = harpoon.get_mark_config()
local file_list = filter_empty_string(config.marks)
@ -352,7 +352,7 @@ M.to_quickfix_list = function()
vim.fn.setqflist(qf_list)
end
M.set_mark_list = function(new_list)
function M.set_mark_list(new_list)
log.trace("set_mark_list(): New list:", new_list)
local config = harpoon.get_mark_config()
@ -372,7 +372,7 @@ M.set_mark_list = function(new_list)
emit_changed()
end
M.toggle_file = function(file_name_or_buf_id)
function M.toggle_file(file_name_or_buf_id)
local buf_name = get_buf_name(file_name_or_buf_id)
log.trace("toggle_file():", buf_name)
@ -389,12 +389,12 @@ M.toggle_file = function(file_name_or_buf_id)
end
end
M.get_current_index = function()
function M.get_current_index()
log.trace("get_current_index()")
return M.get_index_of(vim.api.nvim_buf_get_name(0))
end
M.on = function(event, cb)
function M.on(event, cb)
log.trace("on():", event)
if not callbacks[event] then
log.debug("on(): no callbacks yet for", event)

View File

@ -63,14 +63,14 @@ local function get_first_empty_slot()
return M.get_length() + 1
end
M.gotoTerminal = function(idx)
function M.gotoTerminal(idx)
log.trace("term: gotoTerminal(): Terminal:", idx)
local term_handle = find_terminal(idx)
vim.api.nvim_set_current_buf(term_handle.buf_id)
end
M.sendCommand = function(idx, cmd, ...)
function M.sendCommand(idx, cmd, ...)
log.trace("term: sendCommand(): Terminal:", idx)
local term_handle = find_terminal(idx)
@ -88,7 +88,7 @@ M.sendCommand = function(idx, cmd, ...)
end
end
M.clear_all = function()
function M.clear_all()
log.trace("term: clear_all(): Clearing all terminals.")
for _, term in ipairs(terminals) do
vim.api.nvim_buf_delete(term.buf_id, { force = true })
@ -96,33 +96,33 @@ M.clear_all = function()
terminals = {}
end
M.get_length = function()
function M.get_length()
log.trace("_get_length()")
return table.maxn(harpoon.get_term_config().cmds)
end
M.valid_index = function(idx)
function M.valid_index(idx)
if idx == nil or idx > M.get_length() or idx <= 0 then
return false
end
return true
end
M.emit_changed = function()
function M.emit_changed()
log.trace("_emit_changed()")
if harpoon.get_global_settings().save_on_change then
harpoon.save()
end
end
M.add_cmd = function(cmd)
function M.add_cmd(cmd)
log.trace("add_cmd()")
local found_idx = get_first_empty_slot()
harpoon.get_term_config().cmds[found_idx] = cmd
M.emit_changed()
end
M.rm_cmd = function(idx)
function M.rm_cmd(idx)
log.trace("rm_cmd()")
if not M.valid_index(idx) then
log.debug("rm_cmd(): no cmd exists for index", idx)
@ -132,7 +132,7 @@ M.rm_cmd = function(idx)
M.emit_changed()
end
M.set_cmd_list = function(new_list)
function M.set_cmd_list(new_list)
log.trace("set_cmd_list(): New list:", new_list)
for k in pairs(harpoon.get_term_config().cmds) do
harpoon.get_term_config().cmds[k] = nil

View File

@ -106,7 +106,7 @@ local function get_first_empty_slot()
return M.get_length() + 1
end
M.gotoTerminal = function(idx)
function M.gotoTerminal(idx)
log.trace("tmux: gotoTerminal(): Window:", idx)
local window_handle = find_terminal(idx)
@ -118,7 +118,7 @@ M.gotoTerminal = function(idx)
}, vim.loop.cwd())
end
M.sendCommand = function(idx, cmd, ...)
function M.sendCommand(idx, cmd, ...)
log.trace("tmux: sendCommand(): Window:", idx)
local window_handle = find_terminal(idx)
@ -144,7 +144,7 @@ M.sendCommand = function(idx, cmd, ...)
end
end
M.clear_all = function()
function M.clear_all()
log.trace("tmux: clear_all(): Clearing all tmux windows.")
for _, window in pairs(tmux_windows) do
@ -160,33 +160,33 @@ M.clear_all = function()
tmux_windows = {}
end
M.get_length = function()
function M.get_length()
log.trace("_get_length()")
return table.maxn(harpoon.get_term_config().cmds)
end
M.valid_index = function(idx)
function M.valid_index(idx)
if idx == nil or idx > M.get_length() or idx <= 0 then
return false
end
return true
end
M.emit_changed = function()
function M.emit_changed()
log.trace("_emit_changed()")
if harpoon.get_global_settings().save_on_change then
harpoon.save()
end
end
M.add_cmd = function(cmd)
function M.add_cmd(cmd)
log.trace("add_cmd()")
local found_idx = get_first_empty_slot()
harpoon.get_term_config().cmds[found_idx] = cmd
M.emit_changed()
end
M.rm_cmd = function(idx)
function M.rm_cmd(idx)
log.trace("rm_cmd()")
if not M.valid_index(idx) then
log.debug("rm_cmd(): no cmd exists for index", idx)
@ -196,7 +196,7 @@ M.rm_cmd = function(idx)
M.emit_changed()
end
M.set_cmd_list = function(new_list)
function M.set_cmd_list(new_list)
log.trace("set_cmd_list(): New list:", new_list)
for k in pairs(harpoon.get_term_config().cmds) do
harpoon.get_term_config().cmds[k] = nil

View File

@ -70,7 +70,7 @@ local function get_menu_items()
return indices
end
M.toggle_quick_menu = function()
function M.toggle_quick_menu()
log.trace("toggle_quick_menu()")
if Harpoon_win_id ~= nil and vim.api.nvim_win_is_valid(Harpoon_win_id) then
close_menu()
@ -144,18 +144,18 @@ M.toggle_quick_menu = function()
)
end
M.select_menu_item = function()
function M.select_menu_item()
local idx = vim.fn.line(".")
close_menu(true)
M.nav_file(idx)
end
M.on_menu_save = function()
function M.on_menu_save()
log.trace("on_menu_save()")
Marked.set_mark_list(get_menu_items())
end
M.nav_file = function(id)
function M.nav_file(id)
log.trace("nav_file(): Navigating to", id)
local idx = Marked.get_index_of(id)
if not Marked.valid_index(idx) then
@ -233,7 +233,7 @@ function M.close_notification(bufnr)
vim.api.nvim_buf_delete(bufnr)
end
M.nav_next = function()
function M.nav_next()
log.trace("nav_next()")
local current_index = Marked.get_current_index()
local number_of_items = Marked.get_length()
@ -250,7 +250,7 @@ M.nav_next = function()
M.nav_file(current_index)
end
M.nav_prev = function()
function M.nav_prev()
log.trace("nav_prev()")
local current_index = Marked.get_current_index()
local number_of_items = Marked.get_length()

View File

@ -2,12 +2,15 @@ local Path = require("plenary.path")
local data_path = vim.fn.stdpath("data")
local Job = require("plenary.job")
local M = {
data_path = data_path,
normalize_path = function(item)
local M = {}
M.data_path = data_path
function M.normalize_path (item)
return Path:new(item):make_relative(vim.loop.cwd())
end,
get_os_command_output = function(cmd, cwd)
end
function M.get_os_command_output (cmd, cwd)
if type(cmd) ~= "table" then
print("Harpoon: [get_os_command_output]: cmd has to be a table")
return {}
@ -25,19 +28,18 @@ local M = {
})
:sync()
return stdout, ret, stderr
end,
split_string = function(str, delimiter)
local result = {}
end
function M.split_string (str, delimiter)
local result = {}
for match in (str .. delimiter):gmatch("(.-)" .. delimiter) do
table.insert(result, match)
end
return result
end,
is_white_space = function(str)
end
function M.is_white_space (str)
return str:gsub("%s", "") == ""
end,
}
end
return M