├── .gitignore ├── LICENSE ├── README.md ├── doc └── spinetta.txt ├── examples ├── test_interruption.lua └── test_spinners.lua └── lua ├── spinetta.lua └── spinetta ├── logger.lua └── spinners.lua /.gitignore: -------------------------------------------------------------------------------- 1 | tags 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Javier Orfo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nvim-spinetta 2 | *nvim-spinetta is a Neovim library written in Lua for using a spinner during a job process.* 3 | 4 | ## Caveats 5 | - This library has been developed on and for Linux following open source philosophy. 6 | 7 | ## Installation 8 | `Packer` 9 | ```lua 10 | use 'javiorfo/nvim-spinetta' 11 | ``` 12 | `Lazy` 13 | ```lua 14 | { 'javiorfo/nvim-spinetta' } 15 | ``` 16 | 17 | ## Overview 18 | | Feature | nvim-spinetta | NOTE | 19 | | ------- | ------------- | ---- | 20 | | Spinners | :heavy_check_mark: | Includes 6 spinners | 21 | | Set your own spinner | :heavy_check_mark: | | 22 | | Set a job | :heavy_check_mark: | | 23 | | Set a another process not only a job | :heavy_check_mark: | | 24 | | **start** function | :heavy_check_mark: | Several overloads | 25 | | On success option | :heavy_check_mark: | By the user | 26 | | On interruption option | :heavy_check_mark: | By the user or by an internal error | 27 | 28 | ## Usage 29 | - By default the values by parameters are: 30 | ```lua 31 | { 32 | spinner = DEFAULT_SPINNER, -- List of figures to use in the spinner 33 | speed_ms = 200, -- Speed of the spinner in miliseconds 34 | main_msg = "", -- Initial message in spinner 35 | on_success = nil, -- Function to implement when the job is finished 36 | on_interrupted = nil -- Function to implement when the job is interrupted 37 | } 38 | ``` 39 | 40 | - First, create a instance: 41 | ```lua 42 | local spinetta = require'spinetta' 43 | local my_spinner = spinetta:new() 44 | ``` 45 | 46 | - Second, start the spinner and the job: 47 | ```lua 48 | local my_job = "curl https://host/path" -- This is ilustrative, change it by your job to run 49 | my_spinner:start(spinetta.job_to_run(my_job)) 50 | ``` 51 | 52 | #### SPINNERS 53 | - Check the spinners availables in [this file](https://github.com/javiorfo/nvim-spinetta/blob/master/lua/spinetta/spinners.lua) 54 | - You can add your own spinner if you like. Further information in `:help spinetta` 55 | 56 | ## Screenshots 57 | #### Examples of the differents spinners included in this plugin. Run `:luafile %` in [this file](https://github.com/javiorfo/nvim-spinetta/blob/master/examples/test_spinners.lua) 58 | 59 | spinetta 60 | 61 | #### Examples of interruption message included in this plugin. Run `:luafile %` and interrupt the process with `Ctrl-C` in [this file](https://github.com/javiorfo/nvim-spinetta/blob/master/examples/test_interruption.lua) 62 | spinetta 63 | 64 | **NOTE:** The colorscheme **nebula** from [nvim-nyctophilia](https://github.com/javiorfo/nvim-nyctophilia) is used in this image. 65 | 66 | --- 67 | 68 | ### Donate 69 | - **Bitcoin** [(QR)](https://raw.githubusercontent.com/javiorfo/img/master/crypto/bitcoin.png) `1GqdJ63RDPE4eJKujHi166FAyigvHu5R7v` 70 | - [Paypal](https://www.paypal.com/donate/?hosted_button_id=FA7SGLSCT2H8G) 71 | -------------------------------------------------------------------------------- /doc/spinetta.txt: -------------------------------------------------------------------------------- 1 | *spinetta.txt* nvim-spinetta 2 | A Neovim plugin written in Lua for using a spinner during a job process 3 | _ _ _ ~ 4 | ___ _ __ (_)_ __ ___| |_| |_ __ _ ~ 5 | / __| '_ \| | '_ \ / _ \ __| __/ _` |~ 6 | \__ \ |_) | | | | | __/ |_| || (_| |~ 7 | |___/ .__/|_|_| |_|\___|\__|\__\__,_|~ 8 | |_| ~ 9 | 10 | REFERENCE MANUAL 11 | ================================================================================ 12 | CONTENTS *nvim-spinetta* 13 | 14 | 0. Introduction ............. |spinetta-introduction| 15 | 1. Usage .................... |spinetta-usage| 16 | 1.1 Default Implementation . |spinetta-default| 17 | 1.2 Spinner Implementation . |spinetta-spinners| 18 | 1.3 Jobs ................... |spinetta-jobs| 19 | 1.4 Function Parameter ..... |spinetta-functional| 20 | 1.5 Interrupt Job .......... |spinetta-interrupt| 21 | 1.6 Spinners ............... |spinetta-spinners| 22 | 23 | ================================================================================ 24 | INTRODUCTION *spinetta-introduction* 25 | 26 | nvim-spinetta is a Neovim plugin for building a proper spinner during a job 27 | process. This is meant to be used mainly with 'jobstart' function but the plugin 28 | is open to be used with any function you like. 29 | 30 | ================================================================================ 31 | 1. USAGE *spinetta-commands* 32 | 33 | The following are examples of different implementations of nvim-spinetta 34 | 35 | -------------------------------------------------------------------------------- 36 | 1.1 SIMPLE IMPLEMENTATION *spinetta-default* 37 | 38 | By default the values are: > 39 | { 40 | spinner = DEFAULT_SPINNER, -- List of figures to use in the spinner 41 | speed_ms = 200, -- Speed of the spinner in miliseconds 42 | main_msg = "", -- Initial message in spinner 43 | on_success = nil, -- Function to implement when the job is finished 44 | on_interrupted = nil -- Function to implement when the job is interrupted 45 | } 46 | < 47 | 48 | First, create a instance : > 49 | local spinetta = require'spinetta' 50 | local my_spinner = spinetta:new() 51 | < 52 | 53 | Second, start the spinner and the job: > 54 | local my_job = "here some job to execute in the background" 55 | my_spinner:start(spinetta.job_to_run(my_job)) 56 | < 57 | 58 | -------------------------------------------------------------------------------- 59 | 1.2 SPINNER IMPLEMENTATION *spinetta-spinner* 60 | 61 | The available spinners are: > 62 | DEFAULT_SPINNER 63 | ARROW_SPINNER 64 | EQUALS_SPINNER 65 | PING_PONG_SPINNER 66 | POINT_SPINNER 67 | PROGRESS_BAR_SPINNER 68 | < 69 | 70 | First, create a instance : > 71 | local spinetta = require'spinetta' 72 | -- Example with ARROW_SPINNER 73 | local my_spinner = spinetta:new{ 74 | spinner = spinetta.ARROW_SPINNER, 75 | speed_ms = 100, 76 | main_msg = "Loading ", 77 | on_success = function() 78 | print("Done!") 79 | end 80 | } 81 | < 82 | 83 | Second, start the spinner and the job: > 84 | local my_job = "here some job to execute in the background" 85 | my_spinner:start(spinetta.job_to_run(my_job)) 86 | < 87 | 88 | -------------------------------------------------------------------------------- 89 | 1.3 JOBS *spinetta-jobs* 90 | 91 | nvim-spinetta contains a method to create a job: > 92 | require'spinetta'.job_to_run("curl https://host/path") 93 | < 94 | 95 | This is simply a wrapper of: > 96 | vim.fn.jobpid(vim.fn.jobstart("curl https://host/path")) 97 | < 98 | 99 | A second alternative (if you want to pass a job pid by another means) would be: 100 | > 101 | local spinetta = require'spinetta' 102 | local my_spinner = spinetta:new() 103 | 104 | local pid = my_job() -- Here your implementation 105 | my_spinner:start(spinetta.break_when_pid_is_complete(pid)) 106 | < 107 | 108 | -------------------------------------------------------------------------------- 109 | 1.4 FUNCTION PARAMETER *spinetta-functional* 110 | 111 | If you want to stop the spinner with your own cut function, pass it by 112 | parameter: > 113 | local spinetta = require'spinetta' 114 | local my_spinner = spinetta:new() 115 | 116 | my_spinner:start(function() 117 | if some_value then 118 | return true 119 | else 120 | return false 121 | end 122 | end) 123 | < 124 | 125 | NOTE: The function passed by parameter must return a boolean value to break the 126 | spinner process 127 | 128 | -------------------------------------------------------------------------------- 129 | 1.5 INTERRUPT JOB *spinetta-interrupt* 130 | 131 | Considering that the job executed maybe could not end. To stop it simply press 132 | Ctrl-C. The function parameter 'on_interrupted' can be set to show a message 133 | when the job is interrupted by the user: > 134 | require'spinetta':new{ 135 | on_interrupted = function() 136 | vim.cmd("redraw") 137 | print("Job interrupted by the user") 138 | } 139 | < 140 | 141 | -------------------------------------------------------------------------------- 142 | 1.6 SPINNERS *spinetta-spinners* 143 | 144 | The set of available spinners included are: > 145 | DEFAULT_SPINNER 146 | ARROW_SPINNER 147 | EQUALS_SPINNER 148 | PING_PONG_SPINNER 149 | POINT_SPINNER 150 | PROGRESS_BAR_SPINNER 151 | < 152 | 153 | If you want to set your own, simply pass it by parameter: > 154 | local spinetta = require'spinetta' 155 | local my_personal_spinner = { 156 | '.', '..', '...', '....', '.....', '......', '.......', '........' 157 | } 158 | local my_spinner = spinetta:new{ spinner = my_personal_spinner } 159 | 160 | local my_job = "here some job to execute in the background" 161 | my_spinner:start(spinetta.job_to_run(my_job)) 162 | < 163 | 164 | -------------------------------------------------------------------------------- 165 | -------------------------------------------------------------------------------- /examples/test_interruption.lua: -------------------------------------------------------------------------------- 1 | -- NOTE: To interrupt the job being executed, press Ctrl-C 2 | 3 | local spinetta = require'spinetta' 4 | 5 | -- To simulate a process that takes 5 seconds 6 | local my_job = "sleep 5" 7 | 8 | -- Create spinner with values 9 | local spinetta_default = spinetta:new { 10 | main_msg = "Job working ", 11 | on_success = function() 12 | print("Job finished!") 13 | end, 14 | on_interrupted = function() 15 | vim.cmd("redraw") 16 | print("Job interrupted by user") 17 | end 18 | } 19 | 20 | -- Run job and spinner 21 | spinetta_default:start(spinetta.job_to_run(my_job)) 22 | 23 | -------------------------------------------------------------------------------- /examples/test_spinners.lua: -------------------------------------------------------------------------------- 1 | local spinetta = require'spinetta' 2 | 3 | -- To simulate a process that takes 3 seconds 4 | local my_job = "sleep 3" 5 | 6 | -- To test a real case with a curl command 7 | -- local my_job = "curl https://httpbin.org/get" 8 | 9 | -- Default values are { main_msg = "", final_msg = nil, interruptec_msg = nil, spinner = spinetta.DEFAULT_SPINNER, speed_ms = 200} 10 | local spinetta_default = spinetta:new() 11 | spinetta_default:start(spinetta.job_to_run(my_job)) 12 | 13 | 14 | -- Values speed_ms, spinner, main_msg and final_msg set by parameters 15 | local spinetta_arrow = spinetta:new { 16 | speed_ms = 400, 17 | spinner = spinetta.ARROW_SPINNER, 18 | main_msg = "Arrows ", 19 | on_success = function() 20 | print("Arrows finished!") 21 | end 22 | } 23 | spinetta_arrow:start(spinetta.job_to_run(my_job)) 24 | 25 | 26 | --Values speed_ms, spinner and final_msg set by parameters 27 | local spinetta_equals = spinetta:new { 28 | speed_ms = 100, 29 | spinner = spinetta.EQUALS_SPINNER, 30 | final_msg = "Equals finished!" 31 | } 32 | -- This is optional to use spinetta.jab_tu_run(my_job) which has this line implicit 33 | local pid = vim.fn.jobpid(vim.fn.jobstart(my_job)) 34 | spinetta_equals:start(spinetta.break_when_pid_is_complete(pid)) 35 | 36 | 37 | -- Values spinner and speed_ms set by parameters 38 | local spinetta_ping_pong = spinetta:new { 39 | spinner = spinetta.PING_PONG_SPINNER, 40 | speed_ms = 50 41 | } 42 | local pid2 = vim.fn.jobpid(vim.fn.jobstart(my_job)) 43 | -- This is optional to use spinetta.jab_tu_run(pid) or spinetta.break_when_pid_is_complete(pid) which has this line implicit 44 | -- If a function is passed to 'start' it has to be a function that return a boolean value to break the spinner process 45 | -- like this: 46 | spinetta_ping_pong:start(function() 47 | local job_status = string.format("[ -f '/proc/%d/status' ] && echo 1 || echo 0", pid2) 48 | return tonumber(vim.fn.system(job_status)) == 0 49 | end) 50 | 51 | 52 | -- Values spinner, main_msg, final_msg and interrupted_msg are set by parameters 53 | local spinetta_bar = spinetta:new { 54 | spinner = spinetta.PROGRESS_BAR_SPINNER, 55 | on_success = function() 56 | print("Done!") 57 | end, 58 | main_msg = "Loading ", 59 | on_interrupted = function() 60 | vim.cmd("redraw") 61 | print("Interrupted!") 62 | end 63 | } 64 | spinetta_bar:start(spinetta.job_to_run(my_job)) 65 | -------------------------------------------------------------------------------- /lua/spinetta.lua: -------------------------------------------------------------------------------- 1 | local logger = require'spinetta.logger' 2 | local spinners = require'spinetta.spinners' 3 | local Logger = logger:new("Spinetta") 4 | 5 | local M = { 6 | DEFAULT_SPINNER = spinners.DEFAULT_SPINNER, 7 | ARROW_SPINNER = spinners.ARROW_SPINNER, 8 | EQUALS_SPINNER = spinners.EQUALS_SPINNER, 9 | PING_PONG_SPINNER = spinners.PING_PONG_SPINNER, 10 | POINT_SPINNER = spinners.POINT_SPINNER, 11 | PROGRESS_BAR_SPINNER = spinners.PROGRESS_BAR_SPINNER 12 | } 13 | 14 | function M:new(params) 15 | local table = {} 16 | self.__index = self 17 | table = params or {} 18 | setmetatable(table, self) 19 | return table 20 | end 21 | 22 | function M:get_speed_ms() 23 | return self.speed_ms 24 | end 25 | 26 | function M:get_main_msg() 27 | return self.main_msg 28 | end 29 | 30 | function M:get_spinner() 31 | return self.spinner 32 | end 33 | 34 | -- Function to start the spinner 35 | -- @param a function to check the state of a job 36 | -- @return a boolean that represents if the job was interrupted 37 | function M:start(fn_to_stop_spinner) 38 | if not fn_to_stop_spinner then 39 | Logger:error("A function is required as parameter to stop the spinner.") 40 | return 41 | end 42 | 43 | local speed_ms = self.speed_ms or 200 44 | local main_msg = self.main_msg or "" 45 | local spinner = self.spinner or spinners.DEFAULT_SPINNER 46 | local internal_logger = logger:new() 47 | 48 | local index = 1 49 | while true do 50 | local _, error = pcall(function() 51 | internal_logger:info(main_msg .. spinner[index]) 52 | if index < #spinner then 53 | index = index + 1 54 | else 55 | index = 1 56 | end 57 | 58 | vim.cmd(string.format("sleep %dms", speed_ms)) 59 | vim.cmd("redraw") 60 | end) 61 | 62 | if fn_to_stop_spinner() or error then 63 | if error then 64 | if self.on_interrupted then 65 | self.on_interrupted() 66 | end 67 | else 68 | if self.on_success then 69 | self.on_success() 70 | end 71 | end 72 | break 73 | end 74 | end 75 | end 76 | 77 | -- Function to check the state of a job 78 | -- @param a number of a job pid. Ex: 8843 79 | -- @return a boolean value representing the active state of the job 80 | function M.break_when_pid_is_complete(pid) 81 | return function() 82 | local job_status = string.format("[ -f '/proc/%d/status' ] && echo 1 || echo 0", pid) 83 | return tonumber(vim.fn.system(job_status)) == 0 84 | end 85 | end 86 | 87 | -- Function to run a job 88 | -- @param a string of a job. Ex: "curl https://host/get" 89 | -- @return a function 90 | function M.job_to_run(job_string) 91 | local pid = vim.fn.jobpid(vim.fn.jobstart(job_string)) 92 | return M.break_when_pid_is_complete(pid) 93 | end 94 | 95 | return M 96 | 97 | -------------------------------------------------------------------------------- /lua/spinetta/logger.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local function logger(plugin_name, msg) 4 | return function(level) 5 | if plugin_name then 6 | msg = string.format("[%s] => %s", plugin_name, msg) 7 | end 8 | vim.notify(msg, level) 9 | end 10 | end 11 | 12 | function M:new(plugin_name) 13 | local table = {} 14 | self.__index = self 15 | table.plugin_name = plugin_name 16 | setmetatable(table, self) 17 | return table 18 | end 19 | 20 | function M:error(msg) 21 | logger(self.plugin_name, msg)(vim.log.levels.ERROR) 22 | end 23 | 24 | function M:info(msg) 25 | logger(self.plugin_name, msg)(vim.log.levels.INFO) 26 | end 27 | 28 | return M 29 | -------------------------------------------------------------------------------- /lua/spinetta/spinners.lua: -------------------------------------------------------------------------------- 1 | return { 2 | DEFAULT_SPINNER = { 3 | '⠋', 4 | '⠙', 5 | '⠹', 6 | '⠸', 7 | '⠼', 8 | '⠴', 9 | '⠦', 10 | '⠧', 11 | '⠇', 12 | '⠏' 13 | }, 14 | ARROW_SPINNER = { 15 | "▹▹▹▹▹", 16 | "▸▹▹▹▹", 17 | "▹▸▹▹▹", 18 | "▹▹▸▹▹", 19 | "▹▹▹▸▹", 20 | "▹▹▹▹▸" 21 | }, 22 | EQUALS_SPINNER = { 23 | "[ ]", 24 | "[= ]", 25 | "[== ]", 26 | "[=== ]", 27 | "[ ===]", 28 | "[ ==]", 29 | "[ =]", 30 | "[ ]", 31 | "[ =]", 32 | "[ ==]", 33 | "[ ===]", 34 | "[====]", 35 | "[=== ]", 36 | "[== ]", 37 | "[= ]" 38 | }, 39 | PING_PONG_SPINNER = { 40 | "( ● )", 41 | "( ● )", 42 | "( ● )", 43 | "( ● )", 44 | "( ●)", 45 | "( ● )", 46 | "( ● )", 47 | "( ● )", 48 | "( ● )", 49 | "(● )" 50 | }, 51 | POINT_SPINNER = { 52 | "∙∙∙", 53 | "●∙∙", 54 | "∙●∙", 55 | "∙∙●", 56 | "∙∙∙" 57 | }, 58 | PROGRESS_BAR_SPINNER = { 59 | "▰▱▱▱▱▱▱", 60 | "▰▰▱▱▱▱▱", 61 | "▰▰▰▱▱▱▱", 62 | "▰▰▰▰▱▱▱", 63 | "▰▰▰▰▰▱▱", 64 | "▰▰▰▰▰▰▱", 65 | "▰▰▰▰▰▰▰", 66 | "▰▱▱▱▱▱▱" 67 | } 68 | } 69 | --------------------------------------------------------------------------------