diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 954d5a6a..f4f16217 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,12 +12,13 @@ on: jobs: lint: runs-on: ubuntu-latest + name: Stylua Check steps: - uses: actions/checkout@v3 - name: Stylua - uses: JohnnyMorganz/stylua-action@v1.1.2 + uses: JohnnyMorganz/stylua-action@v3 with: - token: ${{ secrets.GITHUB_TOKEN }} + version: latest args: --check . docs: @@ -36,3 +37,32 @@ jobs: commit_user_name: "github-actions[bot]" commit_user_email: "github-actions[bot]@users.noreply.github.com" commit_author: "github-actions[bot] " + + test: + name: Run Test + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest] + steps: + - uses: actions/checkout@v3 + - uses: rhysd/action-setup-vim@v1 + id: vim + with: + neovim: true + version: nightly + + - name: luajit + uses: leafo/gh-actions-lua@v10 + with: + luaVersion: "luajit-2.1.0-beta3" + + - name: luarocks + uses: leafo/gh-actions-luarocks@v4 + + - name: run test + shell: bash + run: | + luarocks install luacheck + luarocks install vusted + vusted ./test diff --git a/doc/dashboard.txt b/doc/dashboard.txt index 93378c84..e2352345 100644 --- a/doc/dashboard.txt +++ b/doc/dashboard.txt @@ -1,4 +1,4 @@ -*dashboard.txt* For Nvim 0.8.0 Last change: 2023 May 03 +*dashboard.txt* For Nvim 0.8.0 Last change: 2023 June 08 ============================================================================== Table of Contents *dashboard-table-of-contents* @@ -19,7 +19,6 @@ Fancy and Blazing Fast start screen plugin of neovim -------------------------- ============================================================================== 1. Feature *dashboard-feature* - - Low memory usage. dashboard does not store the all user configs in memory like header etc these string will take some memory. now it will be clean after you open a file. you can still use dashboard command to open a new one , then dashboard will read the config from cache. - Blazing fast @@ -27,7 +26,6 @@ Fancy and Blazing Fast start screen plugin of neovim -------------------------- ============================================================================== 2. Install *dashboard-install* - - Lazy.nvim >lua @@ -43,7 +41,6 @@ Fancy and Blazing Fast start screen plugin of neovim -------------------------- } < - - Packer >lua @@ -152,7 +149,6 @@ instead COMMANDS ~ - - `Dashboard` open dashboard - `DbProjectDelete count` delete project in cache works for hyper theme. count is number @@ -248,7 +244,6 @@ Doom ~ CHANGED ~ - - Removed Session as a start screen plugin speed is first.if you want use session you can take a look at glepnir/dbsession.nvim - Removed Ueberzug script, as the Ueberzug author has deleted the repository. @@ -256,7 +251,6 @@ CHANGED ~ TODO ~ - - I will write a plugin to implement some popular terminal evaluators image protocol then I think can make it work with dashboard diff --git a/lua/dashboard/async.lua b/lua/dashboard/async.lua new file mode 100644 index 00000000..031f122c --- /dev/null +++ b/lua/dashboard/async.lua @@ -0,0 +1,104 @@ +local uv = vim.loop + +local function async(fn) + local co = coroutine.create(fn) + return function(...) + local status, result = coroutine.resume(co, ...) + if not status then + error(result) + end + return result + end +end + +local function await(promise) + if type(promise) ~= 'function' then + return promise + end + local co = assert(coroutine.running()) + promise(function(...) + coroutine.resume(co, ...) + end) + return (coroutine.yield()) +end + +local function fs_module(filename) + local fs = {} + local task = {} + + function fs:open(flag) + task[#task + 1] = function(callback) + uv.fs_open(filename, flag, tonumber('644', 8), function(err, fd) + assert(not err) + self.fd = fd + callback(fd) + end) + end + return self + end + + function fs:size() + task[#task + 1] = function(callback) + uv.fs_fstat(self.fd, function(err, stat) + assert(not err) + self.size = stat.size + callback(stat.size) + end) + end + return self + end + + function fs:read() + task[#task + 1] = function(callback) + uv.fs_read(self.fd, self.size, function(err, data) + assert(not err) + assert(uv.fs_close(self.fd)) + callback(data) + end) + end + return self + end + + function fs:write(content) + task[#task + 1] = function() + uv.fs_write(self.fd, content, function(err, bytes) + assert(not err) + if bytes == 0 then + print('note write 0 bytes') + end + assert(uv.fs_close(self.fd)) + end) + end + return self + end + + function fs:run(callback) + async(function() + for i, t in ipairs(task) do + local res = await(t) + if i == #task and callback then + callback(res) + end + end + end)() + end + + return fs +end + +local function async_read(filename, callback) + local fs = fs_module(filename) + fs:open('r'):size():read():run(callback) +end + +local function async_write(filename, content) + local fs = fs_module(filename) + fs:open('w'):size():write(content):run() +end + +return { + async = async, + await = await, + async_read = async_read, + async_write = async_write, +} diff --git a/lua/dashboard/entry.lua b/lua/dashboard/entry.lua new file mode 100644 index 00000000..0b278d26 --- /dev/null +++ b/lua/dashboard/entry.lua @@ -0,0 +1,85 @@ +local api = vim.api +local nvim_buf_set_keymap = api.nvim_buf_set_keymap +local add_highlight = api.nvim_buf_add_highlight +local buf_set_lines, buf_set_extmark = api.nvim_buf_set_lines, api.nvim_buf_set_extmark +local ns = api.nvim_create_namespace('dashboard') +local entry = {} + +local function box() + local t = {} + t.__index = t + + function t:append(lines) + local bufnr = self.bufnr + if type(lines) == 'string' then + lines = { lines } + elseif type(lines) == 'function' then + lines = lines() + end + local count = api.nvim_buf_line_count(bufnr) + buf_set_lines(bufnr, count, -1, false, lines) + + local obj = {} + + function obj:iterate() + local index = 1 + return function() + local line = lines[index] + index = index + 1 + return line + end + end + + function obj:hi(callback) + local iter = self:iterate() + local index = -1 + for data in iter do + index = index + 1 + if callback then + local hi = callback(data, count + index) + if hi then + add_highlight(bufnr, ns, hi, count + index, 0, -1) + end + end + end + + return obj + end + + function obj:tailbtn(callback) + local iter = self:iterate() + local index = -1 + for data in iter do + index = index + 1 + local btn, hi, action = callback(data) + if btn then + buf_set_extmark(bufnr, ns, count + index, 0, { + virt_text = { { btn, hi } }, + }) + + nvim_buf_set_keymap(bufnr, 'n', btn, '', { + callback = function() + if type(action) == 'string' then + vim.cmd(action) + elseif type(action) == 'function' then + action() + end + end, + }) + end + end + + return obj + end + + return setmetatable(obj, t) + end + + return t +end + +function entry:new(wininfo) + return setmetatable(wininfo, box()) +end + +return entry diff --git a/lua/dashboard/events.lua b/lua/dashboard/events.lua deleted file mode 100644 index a823c710..00000000 --- a/lua/dashboard/events.lua +++ /dev/null @@ -1,57 +0,0 @@ -local api, lsp, uv = vim.api, vim.lsp, vim.loop -local au = {} - -function au.register_lsp_root(path) - api.nvim_create_autocmd('VimLeavePre', { - callback = function() - local projects = {} - for _, client in pairs(lsp.get_active_clients() or {}) do - local root_dir = client.config.root_dir - if root_dir and not vim.tbl_contains(projects, root_dir) then - table.insert(projects, root_dir) - end - - for _, folder in pairs(client.workspace_folders or {}) do - if not vim.tbl_contains(projects, folder.name) then - table.insert(projects, folder.name) - end - end - end - - if #projects == 0 then - return - end - - -- callback hell holy shit but simply than write a async await lib - -- also I don't link to add a thirdpart plugin. this is just a small code - uv.fs_open(path, 'rs+', 438, function(err, fd) - assert(not err, err) - uv.fs_fstat(fd, function(err, stat) - assert(not err, err) - uv.fs_read(fd, stat.size, 0, function(err, data) - assert(not err, err) - local before = assert(loadstring(data)) - local plist = before() - if plist and #plist > 10 then - plist = vim.list_slice(plist, 10) - end - plist = vim.tbl_filter(function(k) - return not vim.tbl_contains(projects, k) - end, plist or {}) - plist = vim.list_extend(plist, projects) - local dump = 'return ' .. vim.inspect(plist) - uv.fs_write(fd, dump, 0, function(err, _) - assert(not err, err) - uv.fs_ftruncate(fd, #dump, function(err, _) - assert(not err, err) - uv.fs_close(fd) - end) - end) - end) - end) - end) - end, - }) -end - -return au diff --git a/lua/dashboard/init.lua b/lua/dashboard/init.lua index a0539b88..05d88585 100644 --- a/lua/dashboard/init.lua +++ b/lua/dashboard/init.lua @@ -1,280 +1,140 @@ local api, fn = vim.api, vim.fn -local utils = require('dashboard.utils') -local ctx = {} -local db = {} - -db.__index = db -db.__newindex = function(t, k, v) - rawset(t, k, v) -end - -local function clean_ctx() - for k, _ in pairs(ctx) do - ctx[k] = nil - end -end - -local function cache_dir() - local dir = utils.path_join(vim.fn.stdpath('cache'), 'dashboard') - if fn.isdirectory(dir) == 0 then - fn.mkdir(dir, 'p') - end - return dir -end - -local function cache_path() - local dir = cache_dir() - return utils.path_join(dir, 'cache') -end - -local function conf_cache_path() - return utils.path_join(cache_dir(), 'conf') -end - -local function default_options() - return { - theme = 'hyper', - disable_move = false, - shortcut_type = 'letter', - buffer_name = 'Dashboard', - change_to_vcs_root = false, - config = { - week_header = { - enable = false, - concat = nil, - append = nil, - }, - }, - hide = { - statusline = true, - tabline = true, - }, - preview = { - command = '', - file_path = nil, - file_height = 0, - file_width = 0, - }, - } -end +local nvim_create_autocmd = api.nvim_create_autocmd +local opt_local = vim.opt_local +local util = require('dashboard.util') +local entry = require('dashboard.entry') +local asmod = require('dashboard.async') local function buf_local() - local opts = { - ['bufhidden'] = 'wipe', - ['colorcolumn'] = '', - ['foldcolumn'] = '0', - ['matchpairs'] = '', - ['buflisted'] = false, - ['cursorcolumn'] = false, - ['cursorline'] = false, - ['list'] = false, - ['number'] = false, - ['relativenumber'] = false, - ['spell'] = false, - ['swapfile'] = false, - ['readonly'] = false, - ['filetype'] = 'dashboard', - ['wrap'] = false, - ['signcolumn'] = 'no', - ['winbar'] = '', - } - for opt, val in pairs(opts) do - vim.opt_local[opt] = val - end - if fn.has('nvim-0.9') == 1 then - vim.opt_local.stc = '' - end + opt_local.bufhidden = 'wipe' + opt_local.colorcolumn = '' + opt_local.foldcolumn = '0' + opt_local.matchpairs = '' + opt_local.buflisted = false + opt_local.cursorcolumn = false + opt_local.cursorline = false + opt_local.list = false + opt_local.number = false + opt_local.relativenumber = false + opt_local.spell = false + opt_local.swapfile = false + opt_local.readonly = false + opt_local.filetype = 'dashboard' + opt_local.wrap = false + opt_local.signcolumn = 'no' + opt_local.winbar = '' + opt_local.stc = '' end -function db:new_file() - vim.cmd('enew') - if self.user_laststatus_value then - vim.opt_local.laststatus = self.user_laststatus_value - self.user_laststatus_value = nil - end - - if self.user_tabline_value then - vim.opt_local.showtabline = self.user_showtabline_value - self.user_showtabline_value = nil - end -end +local function hide_options(hide) + local tbl = {} --- cache the user options value restore after leave the dahsboard buffer --- or use DashboardNewFile command -function db:cache_ui_options(opts) - if opts.hide.statusline then - ---@diagnostic disable-next-line: param-type-mismatch - self.user_laststatus_value = vim.opt.laststatus:get() + if hide.statusline then + tbl.laststatus = util.get_global_option_value('laststatus') vim.opt.laststatus = 0 end - if opts.hide.tabline then - ---@diagnostic disable-next-line: param-type-mismatch - self.user_tabline_value = vim.opt.showtabline:get() - vim.opt.showtabline = 0 - end -end -function db:restore_options() - if self.user_cursor_line then - vim.opt.cursorline = self.user_cursor_line - self.user_cursor_line = nil + if hide.tabline then + tbl.showtabline = util.get_global_option_value('showtabline') + vim.opt.showtabline = 0 end - if self.user_laststatus_value then - vim.opt.laststatus = tonumber(self.user_laststatus_value) - self.user_laststatus_value = nil + if hide.cursorline then + tbl.cursorline = util.get_global_option_value('cursorline') end - if self.user_tabline_value then - vim.opt.showtabline = tonumber(self.user_tabline_value) - self.user_tabline_value = nil + return function() + for option, val in pairs(tbl) do + vim.opt[option] = val + end end end -function db:cache_opts() - if not self.opts then +-- create dashboard instance +local function instance(opt) + local mode = api.nvim_get_mode().mode + if mode == 'i' or not vim.bo.modifiable then return end - local uv = vim.loop - local path = conf_cache_path() - if self.opts.config.shortcut then - for _, item in pairs(self.opts.config.shortcut) do - if type(item.action) == 'function' then - ---@diagnostic disable-next-line: param-type-mismatch - local dump = assert(string.dump(item.action)) - item.action = dump - end - end - end - if self.opts.config.project and type(self.opts.config.project.action) == 'function' then - ---@diagnostic disable-next-line: param-type-mismatch - local dump = assert(string.dump(self.opts.config.project.action)) - self.opts.config.project.action = dump - end - - if self.opts.config.center then - for _, item in pairs(self.opts.config.center) do - if type(item.action) == 'function' then - ---@diagnostic disable-next-line: param-type-mismatch - local dump = assert(string.dump(item.action)) - item.action = dump - end - end + if not vim.o.hidden and vim.bo.modified then + --save before open + vim.cmd.write() + return end - if self.opts.config.footer and type(self.opts.config.footer) == 'function' then - ---@diagnostic disable-next-line: param-type-mismatch - local dump = assert(string.dump(self.opts.config.footer)) - self.opts.config.footer = dump + local bufnr + if vim.fn.line2byte('$') ~= -1 then + bufnr = api.nvim_create_buf(false, true) + else + bufnr = api.nvim_get_current_buf() end - local dump = vim.json.encode(self.opts) - uv.fs_open(path, 'w+', tonumber('664', 8), function(err, fd) - assert(not err, err) - ---@diagnostic disable-next-line: redefined-local - uv.fs_write(fd, dump, 0, function(err, _) - assert(not err, err) - uv.fs_close(fd) - end) - end) -end - -function db:get_opts(callback) - utils.async_read( - conf_cache_path(), - vim.schedule_wrap(function(data) - if not data or #data == 0 then - return - end - local obj = vim.json.decode(data) - if obj then - callback(obj) - end - end) - ) -end - -function db:load_theme(opts) - local config = vim.tbl_extend('force', opts.config, { - path = cache_path(), - bufnr = self.bufnr, - winid = self.winid, - confirm_key = opts.confirm_key or nil, - shortcut_type = opts.shortcut_type, - change_to_vcs_root = opts.change_to_vcs_root, - }) + local winid = api.nvim_get_current_win() + api.nvim_win_set_buf(winid, bufnr) - -- api.nvim_buf_set_name(self.bufnr, utils.gen_bufname(opts.buffer_name)) + buf_local() - if #opts.preview.command > 0 then - config = vim.tbl_extend('force', config, opts.preview) + local theme = require('dashboard.theme.' .. opt.theme) + if not theme or not theme.init then + vim.notify( + ('[Dashboard] Load theme error missed theme %s or %s.init function'):format( + opt.theme, + opt.theme + ), + vim.log.levels.ERROR + ) + return end - - require('dashboard.theme.' .. opts.theme)(config) - self:cache_ui_options(opts) + local restore = hide_options(opt.hide) + local e = entry:new({ winid = winid, bufnr = bufnr }) + asmod.async(function() + e = asmod.await(theme.init(e, opt.theme_config)) + end)() api.nvim_create_autocmd('VimResized', { - buffer = self.bufnr, + buffer = bufnr, callback = function() - require('dashboard.theme.' .. opts.theme)(config) - vim.bo[self.bufnr].modifiable = false + require('dashboard.theme.' .. opt.theme).init(entry, opt.theme_config) + vim.bo[bufnr].modifiable = false end, }) - api.nvim_create_autocmd('BufEnter', { - callback = function(opt) - local bufs = api.nvim_list_bufs() - bufs = vim.tbl_filter(function(k) - return vim.bo[k].filetype == 'dashboard' - end, bufs) - if #bufs == 0 then - self:cache_opts() - self:restore_options() - clean_ctx() - pcall(api.nvim_del_autocmd, opt.id) - end + nvim_create_autocmd('BufLeave', { + buffer = bufnr, + callback = function() + restore() end, - desc = '[Dashboard] clean dashboard data reduce memory', }) end --- create dashboard instance -function db:instance() - local mode = api.nvim_get_mode().mode - if mode == 'i' or not vim.bo.modifiable then - return - end - - if not vim.o.hidden and vim.bo.modified then - --save before open - vim.cmd.write() +local function setup(opt) + local default = { + theme = 'doom', + change_to_vcs_root = true, + hide = { + statusline = true, + tabline = true, + cursorline = true, + }, + theme_config = {}, + } + opt = vim.tbl_extend('force', default, opt or {}) + if #opt.theme == 0 then + vim.notify('[Dashboard] Please config a theme ', vim.log.levels.WARN) return end - if vim.fn.line2byte('$') ~= -1 then - self.bufnr = api.nvim_create_buf(false, true) - else - self.bufnr = api.nvim_get_current_buf() - end - - self.winid = api.nvim_get_current_win() - api.nvim_win_set_buf(self.winid, self.bufnr) - - self.user_cursor_line = vim.opt.cursorline:get() - buf_local() - if self.opts then - self:load_theme(self.opts) - else - self:get_opts(function(obj) - self:load_theme(obj) - end) - end -end - -function db.setup(opts) - opts = opts or {} - ctx.opts = vim.tbl_deep_extend('force', default_options(), opts) + nvim_create_autocmd('UIEnter', { + group = vim.api.nvim_create_augroup('Dashboard', { clear = true }), + callback = function() + if fn.argc() == 0 and fn.line2byte('$') == -1 then + instance(opt) + end + end, + }) end -return setmetatable(ctx, db) +return { + setup = setup, +} diff --git a/lua/dashboard/theme/doom.lua b/lua/dashboard/theme/doom.lua deleted file mode 100644 index 6798af06..00000000 --- a/lua/dashboard/theme/doom.lua +++ /dev/null @@ -1,188 +0,0 @@ -local api = vim.api -local utils = require('dashboard.utils') - -local function generate_center(config) - local lines = {} - local center = config.center - or { - { desc = 'Please config your own center section', key = 'p' }, - } - - local counts = {} - for _, item in pairs(center) do - local count = item.keymap and #item.keymap or 0 - local line = (item.icon or '') .. item.desc - - if item.key then - line = line .. (' '):rep(#item.key + 4) - count = count + #item.key + 3 - if type(item.action) == 'string' then - vim.keymap.set('n', item.key, function() - local dump = loadstring(item.action) - if not dump then - vim.cmd(item.action) - else - dump() - end - end, { buffer = config.bufnr, nowait = true, silent = true }) - elseif type(item.action) == 'function' then - vim.keymap.set( - 'n', - item.key, - item.action, - { buffer = config.bufnr, nowait = true, silent = true } - ) - end - end - - if item.keymap then - line = line .. (' '):rep(#item.keymap) - end - - table.insert(lines, line) - table.insert(lines, '') - table.insert(counts, count) - table.insert(counts, 0) - end - - lines = utils.element_align(lines) - lines = utils.center_align(lines) - for i, count in ipairs(counts) do - lines[i] = lines[i]:sub(1, #lines[i] - count) - end - - local first_line = api.nvim_buf_line_count(config.bufnr) - api.nvim_buf_set_lines(config.bufnr, first_line, -1, false, lines) - - if not config.center then - return - end - - local ns = api.nvim_create_namespace('DashboardDoom') - local seed = 0 - local pos_map = {} - for i = 1, #lines do - if lines[i]:find('%w') then - local idx = i == 1 and i or i - seed - seed = seed + 1 - pos_map[i] = idx - local _, scol = lines[i]:find('%s+') - local ecol = scol + (config.center[idx].icon and #config.center[idx].icon or 0) - - if config.center[idx].icon then - api.nvim_buf_add_highlight( - config.bufnr, - 0, - config.center[idx].icon_hl or 'DashboardIcon', - first_line + i - 1, - 0, - ecol - ) - end - - api.nvim_buf_add_highlight( - config.bufnr, - 0, - config.center[idx].desc_hl or 'DashboardDesc', - first_line + i - 1, - ecol, - -1 - ) - - if config.center[idx].key then - local virt_tbl = {} - if config.center[idx].keymap then - table.insert(virt_tbl, { config.center[idx].keymap, 'DashboardShortCut' }) - end - table.insert( - virt_tbl, - { ' [' .. config.center[idx].key .. ']', config.center[idx].key_hl or 'DashboardKey' } - ) - api.nvim_buf_set_extmark(config.bufnr, ns, first_line + i - 1, 0, { - virt_text_pos = 'eol', - virt_text = virt_tbl, - }) - end - end - end - - local line = api.nvim_buf_get_lines(config.bufnr, first_line, first_line + 1, false)[1] - local col = line:find('%w') - col = col and col - 1 or 9999 - api.nvim_win_set_cursor(config.winid, { first_line + 1, col }) - - local bottom = api.nvim_buf_line_count(config.bufnr) - vim.defer_fn(function() - local before = 0 - api.nvim_create_autocmd('CursorMoved', { - buffer = config.bufnr, - callback = function() - local curline = api.nvim_win_get_cursor(0)[1] - if curline < first_line + 1 then - curline = bottom - 1 - elseif curline > bottom - 1 then - curline = first_line + 1 - elseif not api.nvim_get_current_line():find('%w') then - curline = curline + (before > curline and -1 or 1) - end - before = curline - api.nvim_win_set_cursor(config.winid, { curline, col }) - end, - }) - end, 0) - - vim.keymap.set('n', config.confirm_key or '', function() - local curline = api.nvim_win_get_cursor(0)[1] - local index = pos_map[curline - first_line] - if index and config.center[index].action then - if type(config.center[index].action) == 'string' then - local dump = loadstring(config.center[index].action) - if not dump then - vim.cmd(config.center[index].action) - else - dump() - end - elseif type(config.center[index].action) == 'function' then - config.center[index].action() - else - print('Error with action, check your config') - end - end - end, { buffer = config.bufnr, nowait = true, silent = true }) -end - -local function generate_footer(config) - local first_line = api.nvim_buf_line_count(config.bufnr) - local footer = { '', '', 'neovim loaded ' .. utils.get_packages_count() .. ' packages' } - if config.footer then - if type(config.footer) == 'function' then - footer = config.footer() - elseif type(config.footer) == 'string' then - local dump = loadstring(config.footer) - if dump then - footer = dump() - end - elseif type(config.footer) == 'table' then - footer = config.footer - end - end - api.nvim_buf_set_lines(config.bufnr, first_line, -1, false, utils.center_align(footer)) - for i = 1, #footer do - api.nvim_buf_add_highlight(config.bufnr, 0, 'DashboardFooter', first_line + i - 1, 0, -1) - end -end - ----@private -local function theme_instance(config) - require('dashboard.theme.header').generate_header(config) - generate_center(config) - generate_footer(config) - api.nvim_set_option_value('modifiable', false, { buf = config.bufnr }) - api.nvim_set_option_value('modified', false, { buf = config.bufnr }) -end - -return setmetatable({}, { - __call = function(_, t) - return theme_instance(t) - end, -}) diff --git a/lua/dashboard/theme/header.lua b/lua/dashboard/theme/header.lua deleted file mode 100644 index 23328cd4..00000000 --- a/lua/dashboard/theme/header.lua +++ /dev/null @@ -1,134 +0,0 @@ -local api = vim.api -local utils = require('dashboard.utils') - -local function week_ascii_text() - return { - ['Monday'] = { - '', - '███╗ ███╗ ██████╗ ███╗ ██╗██████╗ █████╗ ██╗ ██╗', - '████╗ ████║██╔═══██╗████╗ ██║██╔══██╗██╔══██╗╚██╗ ██╔╝', - '██╔████╔██║██║ ██║██╔██╗ ██║██║ ██║███████║ ╚████╔╝ ', - '██║╚██╔╝██║██║ ██║██║╚██╗██║██║ ██║██╔══██║ ╚██╔╝ ', - '██║ ╚═╝ ██║╚██████╔╝██║ ╚████║██████╔╝██║ ██║ ██║ ', - '╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚═════╝ ╚═╝ ╚═╝ ╚═╝ ', - '', - }, - ['Tuesday'] = { - '', - '████████╗██╗ ██╗███████╗███████╗██████╗ █████╗ ██╗ ██╗', - '╚══██╔══╝██║ ██║██╔════╝██╔════╝██╔══██╗██╔══██╗╚██╗ ██╔╝', - ' ██║ ██║ ██║█████╗ ███████╗██║ ██║███████║ ╚████╔╝ ', - ' ██║ ██║ ██║██╔══╝ ╚════██║██║ ██║██╔══██║ ╚██╔╝ ', - ' ██║ ╚██████╔╝███████╗███████║██████╔╝██║ ██║ ██║ ', - ' ╚═╝ ╚═════╝ ╚══════╝╚══════╝╚═════╝ ╚═╝ ╚═╝ ╚═╝ ', - '', - }, - ['Wednesday'] = { - '', - '██╗ ██╗███████╗██████╗ ███╗ ██╗███████╗███████╗██████╗ █████╗ ██╗ ██╗', - '██║ ██║██╔════╝██╔══██╗████╗ ██║██╔════╝██╔════╝██╔══██╗██╔══██╗╚██╗ ██╔╝', - '██║ █╗ ██║█████╗ ██║ ██║██╔██╗ ██║█████╗ ███████╗██║ ██║███████║ ╚████╔╝ ', - '██║███╗██║██╔══╝ ██║ ██║██║╚██╗██║██╔══╝ ╚════██║██║ ██║██╔══██║ ╚██╔╝ ', - '╚███╔███╔╝███████╗██████╔╝██║ ╚████║███████╗███████║██████╔╝██║ ██║ ██║ ', - '', - }, - ['Thursday'] = { - '', - '████████╗██╗ ██╗██╗ ██╗██████╗ ███████╗██████╗ █████╗ ██╗ ██╗', - '╚══██╔══╝██║ ██║██║ ██║██╔══██╗██╔════╝██╔══██╗██╔══██╗╚██╗ ██╔╝', - ' ██║ ███████║██║ ██║██████╔╝███████╗██║ ██║███████║ ╚████╔╝ ', - ' ██║ ██╔══██║██║ ██║██╔══██╗╚════██║██║ ██║██╔══██║ ╚██╔╝ ', - ' ██║ ██║ ██║╚██████╔╝██║ ██║███████║██████╔╝██║ ██║ ██║ ', - ' ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═════╝ ╚═╝ ╚═╝ ╚═╝ ', - '', - }, - ['Friday'] = { - '', - '███████╗██████╗ ██╗██████╗ █████╗ ██╗ ██╗', - '██╔════╝██╔══██╗██║██╔══██╗██╔══██╗╚██╗ ██╔╝', - '█████╗ ██████╔╝██║██║ ██║███████║ ╚████╔╝ ', - '██╔══╝ ██╔══██╗██║██║ ██║██╔══██║ ╚██╔╝ ', - '██║ ██║ ██║██║██████╔╝██║ ██║ ██║ ', - '╚═╝ ╚═╝ ╚═╝╚═╝╚═════╝ ╚═╝ ╚═╝ ╚═╝ ', - '', - }, - ['Saturday'] = { - '', - '███████╗ █████╗ ████████╗██╗ ██╗██████╗ ██████╗ █████╗ ██╗ ██╗', - '██╔════╝██╔══██╗╚══██╔══╝██║ ██║██╔══██╗██╔══██╗██╔══██╗╚██╗ ██╔╝', - '███████╗███████║ ██║ ██║ ██║██████╔╝██║ ██║███████║ ╚████╔╝ ', - '╚════██║██╔══██║ ██║ ██║ ██║██╔══██╗██║ ██║██╔══██║ ╚██╔╝ ', - '███████║██║ ██║ ██║ ╚██████╔╝██║ ██║██████╔╝██║ ██║ ██║ ', - '╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝ ╚═╝ ', - '', - }, - ['Sunday'] = { - '', - '███████╗██╗ ██╗███╗ ██╗██████╗ █████╗ ██╗ ██╗', - '██╔════╝██║ ██║████╗ ██║██╔══██╗██╔══██╗╚██╗ ██╔╝', - '███████╗██║ ██║██╔██╗ ██║██║ ██║███████║ ╚████╔╝ ', - '╚════██║██║ ██║██║╚██╗██║██║ ██║██╔══██║ ╚██╔╝ ', - '███████║╚██████╔╝██║ ╚████║██████╔╝██║ ██║ ██║ ', - '╚══════╝ ╚═════╝ ╚═╝ ╚═══╝╚═════╝ ╚═╝ ╚═╝ ╚═╝ ', - '', - }, - } -end - -local function default_header() - return { - '', - ' ██████╗ █████╗ ███████╗██╗ ██╗██████╗ ██████╗ █████╗ ██████╗ ██████╗ ', - ' ██╔══██╗██╔══██╗██╔════╝██║ ██║██╔══██╗██╔═══██╗██╔══██╗██╔══██╗██╔══██╗ ', - ' ██║ ██║███████║███████╗███████║██████╔╝██║ ██║███████║██████╔╝██║ ██║ ', - ' ██║ ██║██╔══██║╚════██║██╔══██║██╔══██╗██║ ██║██╔══██║██╔══██╗██║ ██║ ', - ' ██████╔╝██║ ██║███████║██║ ██║██████╔╝╚██████╔╝██║ ██║██║ ██║██████╔╝ ', - ' ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ', - '', - } -end - -local function week_header(concat, append) - local week = week_ascii_text() - local daysoftheweek = - { 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' } - local day = daysoftheweek[os.date('*t').wday] - local tbl = week[day] - table.insert(tbl, os.date('%Y-%m-%d %H:%M:%S ') .. (concat or '')) - if append then - vim.list_extend(tbl, append) - end - table.insert(tbl, '') - return tbl -end - -local function generate_header(config) - if not vim.bo[config.bufnr].modifiable then - vim.bo[config.bufnr].modifiable = true - end - if not config.command then - local header = config.week_header - and config.week_header.enable - and week_header(config.week_header.concat, config.week_header.append) - or (config.header or default_header()) - api.nvim_buf_set_lines(config.bufnr, 0, -1, false, utils.center_align(header)) - - for i, _ in ipairs(header) do - vim.api.nvim_buf_add_highlight(config.bufnr, 0, 'DashboardHeader', i - 1, 0, -1) - end - return - end - - local empty_table = utils.generate_empty_table(config.file_height + 4) - api.nvim_buf_set_lines(config.bufnr, 0, -1, false, utils.center_align(empty_table)) - local preview = require('dashboard.preview') - preview:open_preview({ - width = config.file_width, - height = config.file_height, - cmd = config.command .. ' ' .. config.file_path, - }) -end - -return { - generate_header = generate_header, -} diff --git a/lua/dashboard/theme/hyper.lua b/lua/dashboard/theme/hyper.lua deleted file mode 100644 index 16368a28..00000000 --- a/lua/dashboard/theme/hyper.lua +++ /dev/null @@ -1,443 +0,0 @@ -local api, keymap = vim.api, vim.keymap -local utils = require('dashboard.utils') -local ns = api.nvim_create_namespace('dashboard') - -local function gen_shortcut(config) - local shortcut = config.shortcut - or { - { desc = '[ Github]', group = 'DashboardShortCut' }, - { desc = '[ glepnir]', group = 'DashboardShortCut' }, - { desc = '[ 0.2.3]', group = 'DashboardShortCut' }, - } - - if vim.tbl_isempty(shortcut) then - shortcut = {} - end - - local lines = '' - for _, item in pairs(shortcut) do - local str = item.icon and item.icon .. item.desc or item.desc - if item.key then - str = str .. '[' .. item.key .. ']' - end - lines = lines .. ' ' .. str - end - - local first_line = api.nvim_buf_line_count(config.bufnr) - api.nvim_buf_set_lines(config.bufnr, first_line, -1, false, utils.center_align({ lines })) - - local line = api.nvim_buf_get_lines(config.bufnr, first_line, -1, false)[1] - local start = line:find('[^%s]') - 1 - for _, item in pairs(shortcut) do - local _end = start + (item.icon and #(item.icon .. item.desc) or #item.desc) - if item.key then - _end = _end + api.nvim_strwidth(item.key) + 2 - keymap.set('n', item.key, function() - if type(item.action) == 'string' then - local dump = loadstring(item.action) - if not dump then - vim.cmd(item.action) - else - dump() - end - elseif type(item.action) == 'function' then - item.action() - end - end, { buffer = config.bufnr, nowait = true, silent = true }) - end - - api.nvim_buf_add_highlight( - config.bufnr, - 0, - item.group or 'DashboardShortCut', - first_line, - start, - _end - ) - - if item.icon then - api.nvim_buf_add_highlight( - config.bufnr, - 0, - item.icon_hl or 'DashboardShortCutIcon', - first_line, - start, - start + #item.icon - ) - end - start = _end + 2 - end -end - -local function load_packages(config) - local packages = config.packages or { - enable = true, - } - if not packages.enable then - return - end - - local lines = { - '', - 'neovim loaded ' .. utils.get_packages_count() .. ' packages', - } - - local first_line = api.nvim_buf_line_count(config.bufnr) - api.nvim_buf_set_lines(config.bufnr, first_line, -1, false, utils.center_align(lines)) - - for i, _ in pairs(lines) do - api.nvim_buf_add_highlight(config.bufnr, 0, 'Comment', first_line + i - 1, 0, -1) - end -end - -local function reverse(tbl) - for i = 1, math.floor(#tbl / 2) do - tbl[i], tbl[#tbl - i + 1] = tbl[#tbl - i + 1], tbl[i] - end -end - -local function project_list(config, callback) - config.project = vim.tbl_extend('force', { - limit = 8, - enable = true, - icon = '󰏓 ', - icon_hl = 'DashboardRecentProjectIcon', - action = 'Telescope find_files cwd=', - label = ' Recent Projects:', - }, config.project or {}) - - local function read_project(data) - local res = {} - local dump = assert(loadstring(data)) - local list = dump() - if list then - list = vim.list_slice(list, #list - config.project.limit) - end - for _, dir in ipairs(list or {}) do - dir = dir:gsub(vim.env.HOME, '~') - table.insert(res, (' '):rep(3) .. ' ' .. dir) - end - - if #res == 0 then - table.insert(res, (' '):rep(3) .. ' empty project') - else - reverse(res) - end - - table.insert(res, 1, config.project.icon .. config.project.label) - table.insert(res, 1, '') - table.insert(res, '') - return res - end - - utils.async_read( - config.path, - vim.schedule_wrap(function(data) - local res = {} - if config.project.enable then - res = read_project(data) - end - callback(res) - end) - ) -end - -local function mru_list(config) - config.mru = vim.tbl_extend('force', { - icon = ' ', - limit = 10, - icon_hl = 'DashboardMruIcon', - label = ' Most Recent Files:', - }, config.mru or {}) - - local list = { - config.mru.icon .. config.mru.label, - } - - local groups = {} - local mlist = utils.get_mru_list() - - for _, file in pairs(vim.list_slice(mlist, 1, config.mru.limit)) do - local ft = vim.filetype.match({ filename = file }) - local icon, group = utils.get_icon(ft) - icon = icon or ' ' - if not utils.is_win then - file = file:gsub(vim.env.HOME, '~') - end - file = icon .. ' ' .. file - table.insert(groups, { #icon, group }) - table.insert(list, (' '):rep(3) .. file) - end - - if #list == 1 then - table.insert(list, (' '):rep(3) .. ' empty files') - end - return list, groups -end - -local function letter_hotkey(config) - local list = { 106, 107 } - for _, item in pairs(config.shortcut or {}) do - if item.key then - table.insert(list, item.key:byte()) - end - end - math.randomseed(os.time()) - return function() - while true do - local key = math.random(97, 122) - if not vim.tbl_contains(list, key) then - table.insert(list, key) - return string.char(key) - end - end - end -end - -local function number_hotkey() - local start = 0 - return function() - start = start + 1 - return start - end -end - -local function gen_hotkey(config) - if config.shortcut_type == 'number' then - return number_hotkey() - end - return letter_hotkey(config) -end - -local function map_key(config, key, content) - keymap.set('n', key, function() - local text = content or api.nvim_get_current_line() - local scol = utils.is_win and text:find('%w') or text:find('%p') - text = text:sub(scol) - local path = text:sub(1, text:find('%w(%s+)$')) - path = vim.fs.normalize(path) - if vim.fn.isdirectory(path) == 1 then - vim.cmd('lcd ' .. path) - if type(config.project.action) == 'function' then - config.project.action(path) - elseif type(config.project.action) == 'string' then - local dump = loadstring(config.project.action) - if not dump then - vim.cmd(config.project.action .. path) - else - dump(path) - end - end - else - vim.cmd('edit ' .. path) - local root = utils.get_vcs_root() - if not config.change_to_vcs_root then - return - end - if #root > 0 then - vim.cmd('lcd ' .. vim.fn.fnamemodify(root[#root], ':h')) - else - vim.cmd('lcd ' .. vim.fn.fnamemodify(path, ':h')) - end - end - end, { buffer = config.bufnr, silent = true, nowait = true }) -end - -local function gen_center(plist, config) - local mlist, mgroups = mru_list(config) - local plist_len = #plist - if plist_len == 0 then - plist[#plist + 1] = '' - plist_len = 1 - end - ---@diagnostic disable-next-line: param-type-mismatch - vim.list_extend(plist, mlist) - local max_len = utils.get_max_len(plist) - if max_len <= 40 then - local fill = (' '):rep(math.floor(vim.o.columns / 4)) - for i, v in pairs(plist) do - plist[i] = v .. fill - end - end - - plist = utils.element_align(plist) - plist = utils.center_align(plist) - local first_line = api.nvim_buf_line_count(config.bufnr) - api.nvim_buf_set_lines(config.bufnr, first_line, -1, false, plist) - - local start_col = plist[plist_len + 2]:find('[^%s]') - 1 - local _, scol = plist[2]:find('%S') - - local hotkey = gen_hotkey(config) - - api.nvim_buf_add_highlight(config.bufnr, 0, 'DashboardProjectTitle', first_line + 1, 0, -1) - api.nvim_buf_add_highlight( - config.bufnr, - 0, - 'DashboardProjectTitleIcon', - first_line + 1, - 0, - scol + #config.project.icon - ) - - for i = 3, plist_len do - api.nvim_buf_add_highlight( - config.bufnr, - 0, - 'DashboardProjectIcon', - first_line + i - 1, - 0, - start_col + 3 - ) - api.nvim_buf_add_highlight( - config.bufnr, - 0, - 'DashboardFiles', - first_line + i - 1, - start_col + 3, - -1 - ) - local text = api.nvim_buf_get_lines(config.bufnr, first_line + i - 1, first_line + i, false)[1] - if text and text:find('%w') and not text:find('empty') then - local key = tostring(hotkey()) - api.nvim_buf_set_extmark(config.bufnr, ns, first_line + i - 1, 0, { - virt_text = { { key, 'DashboardShortCut' } }, - virt_text_pos = 'eol', - }) - map_key(config, key, text) - end - end - - -- initialize the cursor pos - api.nvim_win_set_cursor(config.winid, { first_line + 3, start_col + 4 }) - - api.nvim_buf_add_highlight(config.bufnr, 0, 'DashboardMruTitle', first_line + plist_len, 0, -1) - api.nvim_buf_add_highlight( - config.bufnr, - 0, - 'DashboardMruIcon', - first_line + plist_len, - 0, - scol + #config.mru.icon - ) - - for i, data in pairs(mgroups) do - local len, group = unpack(data) - api.nvim_buf_add_highlight( - config.bufnr, - 0, - group, - first_line + i + plist_len, - start_col, - start_col + len - ) - api.nvim_buf_add_highlight( - config.bufnr, - 0, - 'DashboardFiles', - first_line + i + plist_len, - start_col + len, - -1 - ) - - local text = api.nvim_buf_get_lines( - config.bufnr, - first_line + i + plist_len, - first_line + i + plist_len + 1, - false - )[1] - if text and text:find('%w') then - local key = tostring(hotkey()) - api.nvim_buf_set_extmark(config.bufnr, ns, first_line + i + plist_len, 0, { - virt_text = { { key, 'DashboardShortCut' } }, - virt_text_pos = 'eol', - }) - map_key(config, key, text) - end - end -end - -local function gen_footer(config) - local footer = { - '', - ' 🚀 Sharp tools make good work.', - } - - if type(config.footer) == 'string' then - local dump = loadstring(config.footer) - if dump then - footer = dump() - end - elseif type(config.footer) == 'function' then - footer = config.footer() - elseif type(config.footer) == 'table' then - footer = config.footer - end - - local first_line = api.nvim_buf_line_count(config.bufnr) - api.nvim_buf_set_lines(config.bufnr, first_line, -1, false, utils.center_align(footer)) - - ---@diagnostic disable-next-line: param-type-mismatch - for i, _ in pairs(footer) do - api.nvim_buf_add_highlight(config.bufnr, 0, 'DashboardFooter', first_line + i - 1, 0, -1) - end -end - -local function project_delete() - api.nvim_create_user_command('DbProjectDelete', function(args) - local path = utils.path_join(vim.fn.stdpath('cache'), 'dashboard', 'cache') - utils.async_read( - path, - vim.schedule_wrap(function(data) - local dump = assert(loadstring(data)) - local list = dump() - local count = tonumber(args.args) - if vim.tbl_count(list) < count then - return - end - list = vim.list_slice(list, count + 1) - local str = string.dump(assert(loadstring('return ' .. vim.inspect(list)))) - local handle = io.open(path, 'w+') - if not handle then - return - end - handle:write(str) - handle:close() - end) - ) - end, { - nargs = '+', - }) -end - -local function theme_instance(config) - project_list(config, function(plist) - if not api.nvim_buf_is_valid(config.bufnr) then - return - end - if config.disable_move then - utils.disable_move_key(config.bufnr) - end - require('dashboard.theme.header').generate_header(config) - gen_shortcut(config) - load_packages(config) - gen_center(plist, config) - gen_footer(config) - map_key(config, config.confirm_key or '') - require('dashboard.events').register_lsp_root(config.path) - local size = math.floor(vim.o.lines / 2) - - math.ceil(api.nvim_buf_line_count(config.bufnr) / 2) - - 2 - local fill = utils.generate_empty_table(size) - api.nvim_buf_set_lines(config.bufnr, 0, 0, false, fill) - vim.bo[config.bufnr].modifiable = false - vim.bo[config.bufnr].modified = false - project_delete() - end) -end - -return setmetatable({}, { - __call = function(_, t) - theme_instance(t) - end, -}) diff --git a/lua/dashboard/util.lua b/lua/dashboard/util.lua new file mode 100644 index 00000000..6868e3ce --- /dev/null +++ b/lua/dashboard/util.lua @@ -0,0 +1,129 @@ +local uv, api, fn = vim.loop, vim.api, vim.fn +local util = {} + +local function strscreenwidth(str) + return vim.fn.strdisplaywidth(str) +end + +---get os path separator +---@return string +function util.path_sep() + local slash = false + local path_sep = '/' + if fn.exists('+shellslash') == 1 then + ---@diagnostic disable-next-line: param-type-mismatch + slash = vim.opt.shellslash:get() + end + local iswin = uv.os_uname().version:match('Windows') + if iswin and not slash then + path_sep = '\\' + end + return path_sep +end + +---join path +---@return string +function util.path_join(...) + return table.concat({ ... }, util.path_sep()) +end + +---get dashboard cache path +---@return string +function util.cache_path() + local dir = util.path_join(fn.stdpath('cache'), 'dashboard') + if fn.isdirectory(dir) == 0 then + fn.mkdir(dir, 'p') + end + return dir +end + +function util.get_global_option_value(name) + return api.nvim_get_option_value(name, { scope = 'global' }) +end + +local function get_max_len(lines) + local cells = {} + vim.tbl_map(function(item) + cells[#cells + 1] = strscreenwidth(item) + end, lines) + + table.sort(cells) + return cells[#cells] +end + +---tail align lines insert spaces at the every item of lines tail +---@param lines table +---@return table +function util.tail_align(lines) + local max = get_max_len(lines) + local res = {} + + for _, item in ipairs(lines) do + item = item .. (' '):rep(max - strscreenwidth(item)) + res[#res + 1] = item + end + + return res +end + +---head align lines insert spaces at the every item of lines head +---according nvim screen width +---@param lines table +---@return table +function util.center_align(lines) + local screen_center = math.floor(bit.rshift(vim.o.columns, 1)) + local new = {} + for _, line in ipairs(lines) do + local line_center = math.floor(bit.rshift(strscreenwidth(line), 1)) + local size = screen_center - line_center + line = (' '):rep(size) .. line + new[#new + 1] = line + end + + return new +end + +---@param ft string +---@return table|nil +function util.get_devicons_icon(ft) + local ok, devicons = pcall(require, 'nvim-web-devicons') + if not ok then + return nil + end + return devicons.get_icon_by_filetype(ft, { default = true }) +end + +---generate an empty table by given capacity +---@param capacity number +---@return table +function util.generate_empty_table(capacity) + local res = {} + if capacity == 0 then + return res + end + + for _ = 1, capacity do + res[#res + 1] = '' + end + return res +end + +---@param bufnr number +---@return string|nil +function util.get_vcs_root(bufnr) + bufnr = bufnr or 0 + local path = fn.fnamemodify(api.nvim_buf_get_name(bufnr), ':p:h') + local patterns = { '.git', '.hg', '.bzr', '.svn' } + for _, pattern in ipairs(patterns) do + local root = vim.fs.find(pattern, { path = path, upward = true, stop = vim.env.HOME }) + if root then + local res = root[#root] + local sep = util.path_sep() + res = vim.split(res, sep) + res[#res] = nil + return table.concat(res, sep) + end + end +end + +return util diff --git a/lua/dashboard/utils.lua b/lua/dashboard/utils.lua deleted file mode 100644 index ee57642f..00000000 --- a/lua/dashboard/utils.lua +++ /dev/null @@ -1,165 +0,0 @@ -local uv = vim.loop -local utils = {} - -utils.is_win = uv.os_uname().version:match('Windows') - -function utils.path_join(...) - local path_sep = utils.is_win and '\\' or '/' - return table.concat({ ... }, path_sep) -end - -function utils.element_align(tbl) - local lens = {} - vim.tbl_map(function(k) - table.insert(lens, vim.api.nvim_strwidth(k)) - end, tbl) - table.sort(lens) - local max = lens[#lens] - local res = {} - for _, item in pairs(tbl) do - local len = vim.api.nvim_strwidth(item) - local times = math.floor((max - len) / vim.api.nvim_strwidth(' ')) - item = item .. (' '):rep(times) - table.insert(res, item) - end - return res -end - -function utils.get_max_len(contents) - vim.validate({ - contents = { contents, 't' }, - }) - local cells = {} - for _, v in pairs(contents) do - table.insert(cells, vim.api.nvim_strwidth(v)) - end - table.sort(cells) - return cells[#cells] -end - --- draw the graphics into the screen center -function utils.center_align(tbl) - vim.validate({ - tbl = { tbl, 'table' }, - }) - local function fill_sizes(lines) - local fills = {} - for _, line in pairs(lines) do - table.insert(fills, math.floor((vim.o.columns - vim.api.nvim_strwidth(line)) / 2)) - end - return fills - end - - local centered_lines = {} - local fills = fill_sizes(tbl) - - for i = 1, #tbl do - local fill_line = (' '):rep(fills[i]) .. tbl[i] - table.insert(centered_lines, fill_line) - end - - return centered_lines -end - -function utils.get_icon(ft) - local ok, devicons = pcall(require, 'nvim-web-devicons') - if not ok then - vim.notify('[dashboard.nvim] not found nvim-web-devicons') - return nil - end - return devicons.get_icon_by_filetype(ft, { default = true }) -end - -function utils.read_project_cache(path) - local fd = assert(uv.fs_open(path, 'r', tonumber('644', 8))) - local stat = uv.fs_fstat(fd) - local chunk = uv.fs_read(fd, stat.size, 0) - local dump = assert(loadstring(chunk)) - return dump() -end - -function utils.async_read(path, callback) - uv.fs_open(path, 'a+', 438, function(err, fd) - assert(not err, err) - uv.fs_fstat(fd, function(err, stat) - assert(not err, err) - uv.fs_read(fd, stat.size, 0, function(err, data) - assert(not err, err) - uv.fs_close(fd, function(err) - assert(not err, err) - callback(data) - end) - end) - end) - end) -end - -function utils.disable_move_key(bufnr) - local keys = { 'w', 'f', 'b', 'h', 'j', 'k', 'l', '', '', '', '' } - vim.tbl_map(function(k) - vim.keymap.set('n', k, '', { buffer = bufnr }) - end, keys) -end - ---- return the most recently files list -function utils.get_mru_list() - local mru = {} - for _, file in pairs(vim.v.oldfiles or {}) do - if file and vim.fn.filereadable(file) == 1 then - table.insert(mru, file) - end - end - return mru -end - -function utils.get_packages_count() - local count = 0 - ---@diagnostic disable-next-line: undefined-global - if packer_plugins then - ---@diagnostic disable-next-line: undefined-global - count = #vim.tbl_keys(packer_plugins) - end - local status, lazy = pcall(require, 'lazy') - if status then - count = lazy.stats().count - end - return count -end - ---- generate an empty table by length -function utils.generate_empty_table(length) - local empty_tbl = {} - if length == 0 then - return empty_tbl - end - - for _ = 1, length do - table.insert(empty_tbl, '') - end - return empty_tbl -end - -function utils.generate_truncateline(cells) - local char = '┉' - return char:rep(math.floor(cells / vim.api.nvim_strwidth(char))) -end - -function utils.get_vcs_root(buf) - buf = buf or 0 - local path = vim.fn.fnamemodify(vim.api.nvim_buf_get_name(buf), ':p:h') - local patterns = { '.git', '.hg', '.bzr', '.svn' } - for _, pattern in pairs(patterns) do - local root = vim.fs.find(pattern, { path = path, upward = true, stop = vim.env.HOME }) - if root then - return root - end - end -end - -local index = 0 -function utils.gen_bufname(prefix) - index = index + 1 - return prefix .. '-' .. index -end - -return utils diff --git a/min.lua b/min.lua new file mode 100644 index 00000000..64b85b38 --- /dev/null +++ b/min.lua @@ -0,0 +1,16 @@ +vim.opt.rtp:append('~/workspace/dashboard-nvim') +vim.opt.rtp:append('~/workspace/dashboard-doom') +require('dashboard').setup() + +vim.opt.termguicolors = true +vim.api.nvim_set_hl(0, 'DashboardHeader', { + fg = 'yellow', +}) + +vim.api.nvim_set_hl(0, 'DashboardCenter', { + fg = 'green', +}) + +vim.api.nvim_set_hl(0, 'DashboardFooter', { + fg = 'gray', +}) diff --git a/plugin/dashboard.lua b/plugin/dashboard.lua index a2d7d071..6f1420aa 100644 --- a/plugin/dashboard.lua +++ b/plugin/dashboard.lua @@ -1,19 +1,8 @@ --- version 0.2.3 +--version 0.3.0 if vim.g.loaded_dashboard then return end vim.g.loaded_dashboard = 1 -vim.api.nvim_create_autocmd('UIEnter', { - group = vim.api.nvim_create_augroup('Dashboard', { clear = true }), - callback = function() - if vim.fn.argc() == 0 and vim.fn.line2byte('$') == -1 then - require('dashboard'):instance() - end - end, -}) - -vim.api.nvim_create_user_command('Dashboard', function() - require('dashboard'):instance() -end, {}) +vim.api.nvim_create_user_command('Dashboard', function() end, {}) diff --git a/test/async_spec.lua b/test/async_spec.lua new file mode 100644 index 00000000..0c757f9e --- /dev/null +++ b/test/async_spec.lua @@ -0,0 +1,39 @@ +local afs = require('dashboard.asyncfs') +local equal = assert.equal +local path = './test/simple.txt' + +describe('asyncfs moudle', function() + setup(function() + os.execute('touch ./test/simple.txt') + local fd = io.open(path, 'w') + if fd then + fd:write('foo bar') + fd:close() + end + end) + + teardown(function() + vim.fn.delete(path) + end) + + it('can read file async', function() + local data + afs.async_read(path, function(content) + data = vim.split(content, '\n') + end) + vim.wait(1000) + equal('foo bar', data[1]) + end) + + it('can write file async', function() + afs.async_write(path, 'bar bar foo foo') + vim.wait(1000) + local fd = io.open(path, 'r') + if not fd then + print('open file failed in write callback') + return + end + local data = fd:read('*a') + equal('bar bar foo foo', data) + end) +end) diff --git a/test/util_spec.lua b/test/util_spec.lua new file mode 100644 index 00000000..419a82bd --- /dev/null +++ b/test/util_spec.lua @@ -0,0 +1,64 @@ +local util = require('dashboard.util') +local eq = assert.equal +local same = assert.same + +describe('util functions', function() + local bufnr + before_each(function() + bufnr = vim.api.nvim_create_buf(true, false) + vim.api.nvim_win_set_buf(0, bufnr) + end) + + it('util.path_sep get path of system', function() + local iswin = vim.loop.os_uname().version:match('Windows') + local path = util.path_sep() + eq('/', path) + if iswin then + eq('\\', path) + vim.opt.shellslash = true + eq('/', path) + end + end) + + it('util.path_join', function() + local nvim = '/Users/runner/.cache/nvim' + local dir = 'dashboard' + local path = util.path_join(nvim, dir) + eq('/Users/runner/.cache/nvim/dashboard', path) + end) + + it('util.tail_align', function() + local lines = { 'balabala ', 'foo ', 'bar ' } + lines = util.tail_align(lines) + same({ + 'balabala ', + 'foo ', + 'bar ', + }, lines) + end) + + it('util.center_align', function() + local lines = { 'balabala ', 'foo ', 'bar ' } + lines = util.tail_align(lines) + lines = util.center_align(lines) + same({ + ' balabala ', + ' foo ', + ' bar ', + }, lines) + end) + + it('util.generate_empty_table', function() + local tbl = util.generate_empty_table(2) + same({ + '', + '', + }, tbl) + end) + + it('util.get_vcs_root', function() + vim.api.nvim_buf_set_name(bufnr, 'test.lua') + local root = util.get_vcs_root(bufnr) + eq(vim.loop.cwd(), root) + end) +end)