diff --git a/README.md b/README.md index fd1a94d..50cb5cf 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ for `lualine.nvim` with additional components. - [ex.spellcheck](#exspellcheck) - [ex.cwd](#excwd) - [ex.location](#exlocation) + - [ex.progress](#exprogress) - [ex.relative_filename](#exrelative_filename) - [ex.git.branch](#exgitbranch) - [ex.lsp.single](#exlspsingle) @@ -167,6 +168,42 @@ sections = { } ``` +### ex.progress + +This component shows the progress in the file. It has two pre-build modes: 'percent' and 'bar'. The first +one is similar to the default `progress` component, but in the second 'bar' mode the progress is +shown as a progress bar. + +| mode | example | +|:---:|:---:| +| `'percent'` | ex progress-percent | +| `'bar'` | ex progress-bar | + +```lua +sections = { + lualine_a = { + { + 'ex.progress', + + -- How to show the progress. It may be the one of two string constants: + -- 'percent' or 'bar'. In the 'percent' mode the progress is shown as percent of the file. + -- In the 'bar' mode it's shown as the vertical bar. Also, it can be a table with symbols + -- which will be taken to show according to the progress, or a function, which receive three + -- arguments: the component itself, the cursor line and total lines count in the file. + mode = 'percent', + + -- This string will be shown when the cursor is on the first line of the file. Set `false` + -- to turn this logic off. + top = 'Top', + + -- This string will be shown when the cursor is on the last line of the file. Set `false` + -- to turn this logic off. + bottom = 'Bot' + } + } +} +``` + ### ex.relative_filename This component shows a `filename`: a file name and a path to the current file relative to the diff --git a/lua/lualine/components/ex/progress.lua b/lua/lualine/components/ex/progress.lua new file mode 100644 index 0000000..9e34787 --- /dev/null +++ b/lua/lualine/components/ex/progress.lua @@ -0,0 +1,48 @@ +-- stylua: ignore start +local progress_bar = { '█', '▇', '▆', '▅', '▄', '▃', '▂', '▁', ' ' } +-- stylua: ignore end + +local Progress = require('lualine.ex.component'):extend({ + mode = 'percent', + top = 'Top', + bottom = 'Bot', +}) + +local function mode_percent(self, line, total) + if (line == total) and self.options.bottom then + return self.options.bottom + end + if (line == 1) and self.options.top then + return self.options.top + end + return string.format('%3d%%%%', 99 * line / total + 1) +end + +local function mode_table(t) + return function(self, line, total) + if (line == total) and self.options.bottom then + return self.options.bottom + end + if (line == 1) and self.options.top then + return self.options.top + end + local idx = math.floor(line * (#t - 1) / total) + 1 + return t[idx] + end +end + +function Progress:post_init() + if type(self.options.mode) == 'string' then + self.options.mode = (self.options.mode == 'bar') and mode_table(progress_bar) + or mode_percent + end + if type(self.options.mode) == 'table' then + self.options.mode = mode_table(self.options.mode) + end +end + +function Progress:update_status() + return self.options.mode(self, vim.fn.line('.'), vim.fn.line('$')) +end + +return Progress diff --git a/tests/components/progress_spec.lua b/tests/components/progress_spec.lua new file mode 100644 index 0000000..3c238fe --- /dev/null +++ b/tests/components/progress_spec.lua @@ -0,0 +1,92 @@ +local l = require('tests.ex.lualine') +local t = require('tests.ex.busted') --:ignore_all_tests() + +local eq = assert.are.equal + +local orig = { + line = vim.fn.line, +} +local mock = { + line = {}, +} + +local component_name = 'ex.progress' + +describe(component_name, function() + before_each(function() + vim.fn.line = function(arg) + return mock.line[arg] + end + end) + + after_each(function() + vim.fn.line = orig.line + end) + + describe('in "percent" (default) mode', function() + it('should show "Top" when the cursor is on bottom of the buffer', function() + -- { line, total, expected percent } + local cases = { + { 1, 100, 'Top' }, + { 1, 1000, 'Top' }, + { 2, 1000, ' 1%%' }, + } + for _, case in ipairs(cases) do + mock.line['.'] = case[1] + mock.line['$'] = case[2] + local c = l.render_component(component_name) + eq(case[3], c, vim.inspect(case)) + end + end) + it('should show "Bot" when the cursor is on bottom of the buffer', function() + -- { line, total, expected percent } + local cases = { + { 100, 100, 'Bot' }, + { 1000, 1000, 'Bot' }, + { 999, 1000, ' 99%%' }, + } + for _, case in ipairs(cases) do + mock.line['.'] = case[1] + mock.line['$'] = case[2] + local c = l.render_component(component_name) + eq(case[3], c, vim.inspect(case)) + end + end) + it('should show expected persentage of the buffer', function() + -- { line, total, expected percent } + local cases = { + { 1, 100, 1 }, + { 100, 100, 100 }, + { 17, 111, 16 }, + } + for _, case in ipairs(cases) do + mock.line['.'] = case[1] + mock.line['$'] = case[2] + local c = l.render_component(component_name, { top = false, bottom = false }) + eq(string.format('%3d%%%%', case[3]), c, vim.inspect(case)) + end + end) + end) + + describe('in "bar" mode, or "tabe" mode', function() + it('should show appropriate symbol', function() + -- { line, total, expected percent } + local cases = { + { 1, 100, '█' }, + { 100, 100, ' ' }, + { 99, 100, '▁' }, + { 7, 9, '▂' }, + { 19, 31, '▄' }, + } + for _, case in ipairs(cases) do + mock.line['.'] = case[1] + mock.line['$'] = case[2] + local c = l.render_component( + component_name, + { top = false, bottom = false, mode = 'bar' } + ) + eq(case[3], c, vim.inspect(case)) + end + end) + end) +end)