--============================================================================= -- a.lua --- alternate plugin for SpaceVim -- Copyright (c) 2016-2023 Wang Shidong & Contributors -- Author: Wang Shidong < wsdjeg@outlook.com > -- URL: https://spacevim.org -- License: GPLv3 --============================================================================= local M = {} local sp = require('spacevim') local cmp = require('spacevim.api').import('vim.compatible') local cmd = require('spacevim').cmd local sp_file = require('spacevim.api').import('file') local sp_opt = require('spacevim.opt') local sp_json = require('spacevim.api').import('data.json') local toml = require('spacevim.api.data.toml') local logger = require('spacevim.logger').derive('a.lua') local fn = vim.fn or require('spacevim').fn local nt = require('spacevim.api').import('notify') local alternate_conf = {} alternate_conf['_'] = '.project_alt.json' local cache_path = sp_file.unify_path(sp_opt.data_dir, ':p') .. 'SpaceVim/a.json' local project_config = {} local function cache() logger.debug('write cache into file:' .. cache_path) local ok, errors = pcall(fn.writefile, { sp_json.json_encode(project_config) }, cache_path) if not ok then logger.debug('cache failed!') else logger.debug('cache succeeded!') end end local function get_type_path(a, f, b) local begin_len = fn.strlen(a[1]) local end_len = fn.strlen(a[2]) local r = fn.substitute(b, '{}', string.sub(f, begin_len + 1, (end_len + 1) * -1), 'g') return r end local function load_cache() logger.info('load project alternate conf cache from:' .. cache_path) local cache_context = fn.join(fn.readfile(cache_path, ''), '') if cache_context ~= '' then project_config = sp_json.json_decode(cache_context) end end function M.set_config_name(path, name) alternate_conf[path] = name end function M.alt(request_parse, ...) local argvs = ... local alt_type = 'alternate' if argvs ~= nil then alt_type = argvs[1] or alt_type end local alt = nil if fn.exists('b:alternate_file_config') ~= 1 then local conf_file_path = M.getConfigPath() if vim.fn.filereadable(conf_file_path) ~= 1 then nt.notify('no alternate config file!', 'WarningMsg') return end local file = sp_file.unify_path(fn.bufname('%'), ':.') alt = M.get_alt(file, conf_file_path, request_parse, alt_type) end if alt ~= nil and alt ~= '' then logger.info(' > found alternate file: ' .. alt) cmd('e ' .. alt) else logger.info(' > failed to find alternate file') nt.notify('failed to find alternate file!', 'WarningMsg') end end local function get_project_config(conf_file) local conf if conf_file:sub(-4) == 'toml' then conf = toml.parse_file(conf_file) else conf = sp_json.json_decode(fn.join(fn.readfile(conf_file), '\n')) end -- logger.debug(vim.inspect(conf)) if type(conf) ~= 'table' then conf = {} end local root = sp_file.unify_path(conf_file, ':p:h') return { ['root'] = root, ['config'] = conf, } end -- we need to sort the keys in config -- local function _keys(val) local new_keys = {} for k, v in pairs(val) do table.insert(new_keys, k) end return new_keys end local function _comp(a, b) if string.match(a, '*') == '*' and string.match(b, '*') == '*' then return #a < #b elseif string.match(a, '*') == '*' then return true elseif string.match(b, '*') == '*' then return false else local _, al = string.gsub(a, '/', '') local _, bl = string.gsub(b, '/', '') return al < bl end end local function parse(alt_config_json) logger.info('parse alternate file for:' .. alt_config_json.root) project_config[alt_config_json.root] = {} local keys = _keys(alt_config_json.config) table.sort(keys, _comp) for _, key in pairs(keys) do local searchpath = key if string.match(searchpath, '*') == '*' then searchpath = string.gsub(searchpath, '*', '**/*') end for _, file in pairs(cmp.globpath('.', searchpath)) do file = sp_file.unify_path(file, ':.') project_config[alt_config_json.root][file] = {} if alt_config_json.config[file] ~= nil then for alt_type, type_v in pairs(alt_config_json.config[file]) do project_config[alt_config_json.root][file][alt_type] = type_v end else for a_type, _ in pairs(alt_config_json.config[key]) do local begin_end = fn.split(key, '*') if #begin_end == 2 then project_config[alt_config_json.root][file][a_type] = get_type_path(begin_end, file, alt_config_json.config[key][a_type]) end end end end end cache() end local function is_config_changed(conf_path) if fn.getftime(conf_path) > fn.getftime(cache_path) then return true else return false end end function M.get_alt(file, conf_path, request_parse, a_type) logger.info('getting alt file for:' .. file) logger.info(' > type: ' .. a_type) logger.info(' > parse: ' .. request_parse) logger.info(' > config: ' .. conf_path) alt_config_json = get_project_config(conf_path) if project_config[alt_config_json.root] == nil and not is_config_changed(conf_path) and request_parse == 0 then load_cache() if project_config[alt_config_json.root] == nil or project_config[alt_config_json.root][file] == nil then parse(alt_config_json) end else parse(alt_config_json) end if project_config[alt_config_json.root] ~= nil and project_config[alt_config_json.root][file] ~= nil and project_config[alt_config_json.root][file][a_type] ~= nil then return project_config[alt_config_json.root][file][a_type] else return '' end end function M.getConfigPath() local pwd = fn.getcwd() local p = alternate_conf['_'] if alternate_conf[pwd] ~= nil then p = alternate_conf[pwd] end return sp_file.unify_path(p, ':p') end function M.complete(arglead, cmdline, cursorpos) local file = sp_file.unify_path(fn.bufname('%'), ':.') local conf_file_path = M.getConfigPath() local alt_config_json = get_project_config(conf_file_path) M.get_alt(file, conf_file_path, 0, '') local a = project_config[alt_config_json.root][file] if a ~= nil then return fn.join(fn.keys(a), '\n') else return '' end end return M