issue #313 new file with template file

pull/316/head
ray-x 1 year ago
parent 1b6ea0b8ec
commit 55fe49fcaf

@ -660,6 +660,11 @@ Run govulncheck on current project
* Goenum {arguments}
Run goenum on current project
### gonew
* GoNew {filename}
Create new go file. It will use template file. e.g. `GoNew ./pkg/string.go` will create string.go with template file
### Debug Commands
| Command | Description |

@ -358,6 +358,10 @@ COMMANDS *go-nvim-commands*
:Goenum
run goenum on current file
:GoNew {filename}
create a new go file from template e.g. GoNew ./pkg/file.go
==============================================================================
OPTIONS *go-nvim-options*

@ -389,7 +389,11 @@ return {
require('go.gopls').tidy()
end)
create_cmd('GoListImports', function(_)
print(vim.inspect(require('go.gopls').list_imports()))
local lines = require('go.gopls').list_imports().PackageImports or {}
local close_events = { 'CursorMoved', 'CursorMovedI', 'BufHidden', 'InsertCharPre' }
local config = { close_events = close_events, focusable = true, border = 'single', width = 80, zindex = 100, height = #lines }
vim.lsp.util.open_floating_preview(lines, 'go', config)
end)
create_cmd('GoCallstack', function(_)
@ -447,5 +451,8 @@ return {
create_cmd('GoEnum', function(opts)
require('go.enum').run(unpack(opts.fargs))
end, { nargs = '*' })
create_cmd('GoNew', function(opts)
require('go.template.gonew').new(opts.fargs)
end, { nargs = '*' })
end,
}

@ -0,0 +1,64 @@
local tpleval = require('go.template').template_eval
local util = require('go.utils')
local tpl_main = [[
package $(pkg)
import "fmt"
func $(pkg)() {
\tfmt.Println("hello world")
}
]]
local tpl_main_test = [[
package $(pkg)
import (
\t"testing"
)
func Test$(funname)(t *testing.T) {
\tt.Error("not implemented")
}
]]
local current_file = vim.fn.resolve(vim.fn.expand('<sfile>'))
local get_pkg = require('go.package').pkg_from_path
local function go_template_create(args)
local filename = args[1] or 'main.go'
local sep = util.sep()
local package_name = get_pkg()
if vim.fn.empty(package_name) == 1 or vim.fn.empty(package_name[1]) == 1 then
package_name = 'main'
else
if package_name[1]:find('cannot find') then
package_name = 'main'
else
package_name = package_name[1]
local pkgs = vim.split(package_name, sep) -- win?
package_name = pkgs[#pkgs]
end
end
util.log(package_name)
local root_dir = vim.fn.fnamemodify(current_file, ':h:h:h')
local text
if string.find(filename, '_test.go$') then
-- get the function name
local f = vim.split(filename, '_')[1] or 'main'
f = vim.split(f, sep) -- win?
f = f[#f]
f = string.upper(string.sub(f, 1, 1)) .. string.sub(f, 2)
_, text = tpleval(tpl_main_test, { pkg = package_name, funname = f })
else
_, text = tpleval(tpl_main, { pkg = package_name })
end
-- filename = root_dir .. sep .. filename
vim.fn.execute('edit ' .. vim.fn.fnameescape(filename))
text = text:gsub('\\t', '\t')
local lines = vim.split(text, '\n')
vim.api.nvim_buf_set_lines(0, 0, -1, true, lines)
end
return { new = go_template_create }

@ -0,0 +1,218 @@
--https://github.com/mfrigerio17/lua-template-engine/
local function errHandler(e)
-- Try to get the number of the line of the template that caused the error,
-- parsing the text of the stacktrace. Note that the string here in the
-- matching pattern should correspond to whatever is generated in the
-- template_eval function, further down
local stacktrace = debug.traceback()
local linen = tonumber(stacktrace:match('.-"local text={}..."]:(%d+).*'))
return {
error = e,
lineNum = linen,
}
end
--- Evaluate a chunk of code in a constrained environment.
-- @param unsafe_code code string
-- @param optional environment table.
-- @return true or false depending on success
-- @return function or error message
local function eval_sandbox(unsafe_code, env)
local env = env or {}
local unsafe_fun, msg = load(unsafe_code, nil, 't', env)
if unsafe_fun == nil then
return false, { loadError = true, msg = msg }
end
return xpcall(unsafe_fun, errHandler)
end
local function lines(s)
if s:sub(-1) ~= '\n' then
s = s .. '\n'
end
return s:gmatch('(.-)\n')
end
--- Copy every string in the second argument into the first, prepending indentation.
-- The first argument must be a table. The second argument is either a table
-- itself (having strings as elements) or a function returning a factory of
-- a suitable iterator; for example, a function returning 'ipairs(t)', where 't'
-- is a table of strings, is a valid argument.
local insertLines = function(text, lines, totIndent)
local factory = lines
if type(lines) == 'table' then
factory = function()
return ipairs(lines)
end
end
for i, line in factory() do
local lineadd = ''
if line ~= '' then
lineadd = totIndent .. line
end
table.insert(text, lineadd)
end
end
--- Decorates an existing string iteration, adding an optional prefix and suffix.
-- The first argument must be a function returning an existing iterator
-- generator, such as a 'ipairs'.
-- The second and last argument are strings, both optional.
--
-- Sample usage:
-- local t = {"a","b","c","d"}
-- for i,v in ipairs(t) do
-- print(i,v)
-- end
--
-- for i,v in lineDecorator( function() return ipairs(t) end, "--- ", " ###") do
-- print(i,v)
-- end
local lineDecorator = function(generator, prefix, suffix)
local opts = opts or {}
local prefix = prefix or ''
local suffix = suffix or ''
local iter, inv, ctrl = generator()
return function()
local i, line = iter(inv, ctrl)
ctrl = i
local retline = ''
if line ~= nil then
if line ~= '' then
retline = prefix .. line .. suffix
end
end
return i, retline -- nil or ""
end
end
--- Evaluate the given text-template into a string.
-- Regular text in the template is copied verbatim, while expressions in the
-- form $(<var>) are replaced with the textual representation of <var>, which
-- must be defined in the given environment.
-- Finally, lines starting with @ are interpreted entirely as Lua code.
--
-- @param template the text-template, as a string
-- @param env the environment for the evaluation of the expressions in the
-- templates (if not given, 'table', 'pairs', 'ipairs' are added
-- automatically to this enviroment)
-- @param opts non-mandatory options
-- - indent: number of blanks to be prepended before every output line;
-- this applies to the whole template, relative indentation between
-- different lines is preserved
-- - xtendStyle: if true, variables are matched with this pattern "«<var>»"
-- @return The text of the evaluated template; if the option 'returnTable' is
-- set to true, though, the table with the sequence of lines of text is
-- returned instead
local function template_eval(template, env, opts)
local opts = opts or {}
local indent = string.format('%s', string.rep(' ', (opts.indent or 0)))
-- Define the matching patter for the variables, depending on options.
-- The matching pattern reads in general as: <text><var><string position>
local varMatch = {
pattern = '(.-)$(%b())()',
extract = function(expr)
return expr:sub(2, -2)
end,
}
if opts.xtendStyle then
varMatch.pattern = '(.-)«(.-)»()'
varMatch.extract = function(expr)
return expr
end
end
-- Generate a line of code for each line in the input template.
-- The lines of code are also strings; we add them in the 'chunk' table.
-- Every line is either the insertion in a table of a string, or a 1-to-1 copy
-- of the code inserted in the template via the '@' character.
local chunk = { 'local text={}' }
local lineOfCode = nil
for line in lines(template) do
-- Look for a '@' ignoring blanks (%s) at the beginning of the line
-- If it's there, copy the string following the '@'
local s, e = line:find('^%s*@')
if s then
lineOfCode = line:sub(e + 1)
else
-- Look for the specials '${..}', which must be alone in the line
local tableIndent, tableVarName = line:match('^([%s]*)${(.-)}[%s]*')
if tableVarName ~= nil then
-- Preserve the indentation used for the "${..}" in the original template.
-- "Sum" it to the global indentation passed here as an option.
local totIndent = string.format('%q', indent .. tableIndent)
lineOfCode = '__insertLines(text, ' .. tableVarName .. ', ' .. totIndent .. ')'
else
-- Look for the template variables in the current line.
-- All the matches are stored as strings '"<text>" .. <variable>'
-- Note that <text> can be empty
local subexpr = {}
local lastindex = 1
local c = 1
for text, expr, index in line:gmatch(varMatch.pattern) do
subexpr[c] = string.format('%q .. %s', text, varMatch.extract(expr))
lastindex = index
c = c + 1
end
-- Add the remaining part of the line (no further variable) - or the
-- entire line if no $() was found
subexpr[c] = string.format('%q', line:sub(lastindex))
-- Concatenate the subexpressions into a single one, prepending the
-- indentation if it is not empty
local expression = table.concat(subexpr, ' .. ')
if expression ~= '""' and indent ~= '' then
expression = string.format('%q', indent) .. ' .. ' .. expression
end
lineOfCode = 'table.insert(text, ' .. expression .. ')'
end
end
table.insert(chunk, lineOfCode)
end
local returnTable = opts.returnTable or false
if returnTable then
table.insert(chunk, 'return text')
else
-- The last line of code performs string concatenation, so that the evaluation
-- of the code eventually leads to a string
table.insert(chunk, "return table.concat(text, '\\n')")
end
--print( table.concat(chunk, '\n') )
env.table = (env.table or table)
env.pairs = (env.pairs or pairs)
env.ipairs = (env.ipairs or ipairs)
env.__insertLines = insertLines
local ok, ret = eval_sandbox(table.concat(chunk, '\n'), env)
if not ok then
local errMessage = 'Error in template evaluation' -- default, should be overwritten
if ret.loadError then
errMessage = 'Syntactic error in the loaded code: ' .. ret.msg
else
local linen = ret.lineNum or -1
local line = '??'
if linen ~= -1 then
line = chunk[linen]
end
local err1 = 'Template evaluation failed around this line:\n\t>>> '
.. line
.. ' (line #'
.. linen
.. ')'
local err2 = 'Interpreter error: ' .. (tostring(ret.error) or '')
errMessage = err1 .. '\n' .. err2
end
return false, errMessage
end
return ok, ret
end
return {
template_eval = template_eval,
lineDecorator = lineDecorator,
}

@ -651,7 +651,12 @@ end
-- run in current source code path
function util.exec_in_path(cmd, bufnr, ...)
bufnr = bufnr or vim.api.nvim_get_current_buf()
local path = fn.fnamemodify(fn.bufname(bufnr), ':p:h')
local path
if type(bufnr) == 'string' then
path = bufnr
else
path = fn.fnamemodify(fn.bufname(bufnr), ':p:h')
end
local dir = util.chdir(path)
local result
if type(cmd) == 'function' then

Loading…
Cancel
Save