diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 4376ce6..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Raphael - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100644 index 1927e5d..0000000 --- a/README.md +++ /dev/null @@ -1,202 +0,0 @@ -## Template.nvim - -Quick insert tempalte - - - -## Install - -```lua --- with lazy.nvim - -{'glepnir/template.nvim', cmd = {'Template','TemProject'}, config = function() - require('template').setup({ - -- config in there - }) -end} - --- lazy load you can use cmd or ft. if you are using cmd to lazyload when you edit the template file --- you may see some diagnostics in template file. use ft to lazy load the diagnostic not display --- when you edit the template file. -``` - -## Options - -```lua -{ - temp_dir -- template directory - author -- your name - email -- email address -} -``` - -## Basic Usage - -### Template Grammar - -- `{{_date_}}` insert current date - -- `{{_cursor_}}` set cursor here - -- `{{_file_name_}}` current file name - -- `{{_author_}}` author info - -- `{{_email_}}` email adrress - -- `{{_variable_}}` variable name - -- `{{_upper_file_}}` all-caps file name - -- `{{_camel_case_file_}}` converts snake_case file name to CamelCase - -- `{{_lua:vim.fn.expand(%:.:r)_}}` set by lua script - -### Define your template - -You need to configure the setting variable `temp_dir`. -An example configuration: `temp_dir = '~/.config/nvim/templates'`. -Create the directory at the location specified then proceed to add -template files. - -As an example create the file `main_owner.go` in the `temp_dir` -directory you set (e.g. ~/.config/nvim/templates/main_owner.go) - -Nested folders are also supported (e.g. ~/.config/nvim/templates/rust/http.rs) - -```go -// Copyright {{_date_}} {{_author_}}. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package {{_file_name_}} - -func main() { - {{_cursor_}} -} - -``` - -You can use lua inside the template with {{_lua:}}. -For example -```markdown ---- -created: {{_lua:os.date("%y/%m/%d %H/%M")_}} ---- -``` -above template generates below lines. -```markdown ---- -created: 2022/12/29 21:52 ---- -``` - -- Work with existing file - -if there has a file `main.go`, and open it input `Template ` . select the template `main_owner` - -It will insert template to this file like - -```go -// Copyright 2022-07-05 21:05:36 glephunter. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func main() { - | -- cursor in there -} - -``` - -- Work with not exist file - -use `Template test.go `, it will create a file named `test.go` in current path and auto open -this file insert template. - -- Work with not exist file and custom variable - -A lua template file named `nvim_temp.lua`, content is - -```lua -local api,fn = vim.api,vim.fn -local {{_variable_}} - -{{_cursor_}} - -return {{_variable_}} - -``` - -Use `Template test.lua ` then it will auto fill template name `nvim_temp` if there -is only one lua template file. If are any `_variable_` items in the template file it will -prompt you for these values. - -```lua -local api,fn = vim.api,vim.fn -local template - -| -- cursor here - -return template - -``` - -- Use tpl file - -Also you can use `*.tpl` as a template this extension can avoid trigger FileType event .like start a -lsp server by Filetype event. - -a rule of `tpl` template is you must set `;; filetype` in first line like a rust template file -`http.tpl` and first line must be `;; rust` - -- Add a new template expression - -```lua -require('template').register('{{_path_}}', function() vim.fn.expand('%') end) -``` - -- Config a fancy keymap - - -```lua -vim.keymap.set('n', 't', function() - vim.fn.feedkeys(':Template ') -end, { remap = true}) -``` - -- Find all templates - -template.nvim can use `telescope`, but you need register template telescope extension to `telescope` - -```lua -require("telescope").load_extension('find_template') -``` - -- Use Telescope to create template or insert template - -```lua --- This command will create a template file then show all templates -Telescope find_template name=templatename - --- When you select a template file it will insert this tempalte into current buffer -Telecope find_template type=insert - --- In both cases you can disable filtering templates by file type by passing `filter_ft=false` -Telecope find_template type=insert filter_ft=false -``` - -Then you can use `Telescope find_template` to check all templates - -## Donate - -[![](https://img.shields.io/badge/PayPal-00457C?style=for-the-badge&logo=paypal&logoColor=white)](https://paypal.me/bobbyhub) - -If you'd like to support my work financially, buy me a drink through [paypal](https://paypal.me/bobbyhub) - -## Licenese MIT diff --git a/lua/telescope/_extensions/find_template.lua b/lua/telescope/_extensions/find_template.lua index 64e673e..827bafa 100644 --- a/lua/telescope/_extensions/find_template.lua +++ b/lua/telescope/_extensions/find_template.lua @@ -30,7 +30,7 @@ local find_template = function(opts) if vim.fn.filereadable(file) == 0 then local ok, fd = pcall(vim.loop.fs_open, file, 'w', 420) if not ok then - vim.notify("Couldn't create file " .. file) + vim.notify("[Templates] Couldn't create file " .. file) return end vim.loop.fs_close(fd) @@ -62,6 +62,10 @@ local find_template = function(opts) actions.select_default:replace(function() actions.close(prompt_bufnr) local selection = action_state.get_selected_entry() + if not selection then + vim.notify("[Templates] No file selected") + return false + end local tmp_name = vim.fn.fnamemodify(selection[1], ':t') tmp_name = vim.split(tmp_name, '%.', { trimempty = true })[1] diff --git a/lua/template/init.lua b/lua/template/init.lua index 0e951c9..e7c442e 100644 --- a/lua/template/init.lua +++ b/lua/template/init.lua @@ -4,290 +4,326 @@ local sep = uv.os_uname().sysname == 'Windows_NT' and '\\' or '/' local cursor_pattern = '{{_cursor_}}' local renderer = { - expressions = {}, - expression_replacer_map = {} + expressions = {}, + expression_replacer_map = {}, } ---@param expr string ---@param replacer function(match: string): string -renderer.register = function (expr, replacer) - if renderer.expression_replacer_map[expr] then - vim.notify('The expression '..expr..' is registered already. Will not add the replacer.', vim.log.levels.ERROR) - return - end - table.insert(renderer.expressions, expr) - renderer.expression_replacer_map[expr] = replacer +renderer.register = function(expr, replacer) + if renderer.expression_replacer_map[expr] then + vim.notify('[Templates] The expression ' .. expr .. ' is registered already. Will not add the replacer.', + vim.log.levels.ERROR) + return + end + table.insert(renderer.expressions, expr) + renderer.expression_replacer_map[expr] = replacer end renderer.register_builtins = function() - renderer.register('{{_date_}}', function(_) return os.date('%Y-%m-%d %H:%M:%S') end) - renderer.register(cursor_pattern, function(_) return '' end) - renderer.register('{{_file_name_}}', function(_) return fn.expand('%:t:r') end) - renderer.register('{{_author_}}', function(_) return temp.author end) - renderer.register('{{_email_}}', function(_) return temp.email end) - renderer.register('{{_variable_}}', function(_) return vim.fn.input('Variable name: ', '') end) - renderer.register('{{_upper_file_}}', function(_) return string.upper(fn.expand('%:t:r')) end) - renderer.register('{{_lua:(.-)_}}', function(matched_expression) - return load('return ' .. matched_expression)() - end) - renderer.register('{{_tomorrow_}}', function() - local t = os.date('*t') - t.day = t.day + 1 - ---@diagnostic disable-next-line: param-type-mismatch - return os.date('%c', os.time(t)) - end) - renderer.register('{{_camel_file_}}', function(_) - local file_name = fn.expand('%:t:r') - local camel_case_file_name = '' - local up_next = true - for i = 1, #file_name do - local char = file_name:sub(i,i) - if char == '_' then - up_next = true - elseif up_next then - camel_case_file_name = camel_case_file_name..string.upper(char) - up_next = false - else - camel_case_file_name = camel_case_file_name..char + renderer.register('{{_date_}}', function(_) + return os.date('%Y-%m-%d %H:%M:%S') + end) + renderer.register(cursor_pattern, function(_) + return '' + end) + renderer.register('{{_file_name_}}', function(_) + return fn.expand('%:t:r') + end) + renderer.register('{{_author_}}', function(_) + return temp.author + end) + renderer.register('{{_email_}}', function(_) + return temp.email + end) + renderer.register('{{_variable_}}', function(_) + return vim.fn.input('Variable name: ', '') + end) + renderer.register('{{_upper_file_}}', function(_) + return string.upper(fn.expand('%:t:r')) + end) + renderer.register('{{_lua:(.-)_}}', function(matched_expression) + return load('return ' .. matched_expression)() + end) + renderer.register('{{_tomorrow_}}', function() + local t = os.date('*t') + t.day = t.day + 1 + ---@diagnostic disable-next-line: param-type-mismatch + return os.date('%c', os.time(t)) + end) + renderer.register('{{_camel_file_}}', function(_) + local file_name = fn.expand('%:t:r') + local camel_case_file_name = '' + local up_next = true + for i = 1, #file_name do + local char = file_name:sub(i, i) + if char == '_' then + up_next = true + elseif up_next then + camel_case_file_name = camel_case_file_name .. string.upper(char) + up_next = false + else + camel_case_file_name = camel_case_file_name .. char + end end - end - return camel_case_file_name - end) + return camel_case_file_name + end) end renderer.render_line = function(line) - local rendered = vim.deepcopy(line) - for _, expr in ipairs(renderer.expressions) do - if line:find(expr) then - while rendered:match(expr) do - local replacement = renderer.expression_replacer_map[expr](rendered:match(expr)) - rendered = rendered:gsub(expr, replacement, 1) - end + local rendered = vim.deepcopy(line) + for _, expr in ipairs(renderer.expressions) do + if line:find(expr) then + while rendered:match(expr) do + local replacement = renderer.expression_replacer_map[expr](rendered:match(expr)) + rendered = rendered:gsub(expr, replacement, 1) + end + end end - end - return rendered + return rendered +end + +function Get_file_extention(url) + return url:match('^.+%.(.+)$') end function temp.get_temp_list() - temp.temp_dir = fs.normalize(temp.temp_dir) - local res = {} + local current_ext = vim.bo.filetype + -- Get template directory + temp.temp_dir = fs.normalize(temp.temp_dir) + local res = {} - local result = vim.fs.find(function(name) - return name:match('.*') - end, { type = 'file', path = temp.temp_dir, limit = math.huge }) + -- Read all files in template directory + local result = vim.fs.find(function(name) + return name:match('.*') + end, { type = 'file', path = temp.temp_dir, limit = math.huge }) - local link = vim.fs.find(function(name) - return name:match('.*') - end, { type = 'link', path = temp.temp_dir, limit = math.huge }) + -- Read all symlinked files in template directory + local link = vim.fs.find(function(name) + return name:match('.*') + end, { type = 'link', path = temp.temp_dir, limit = math.huge }) - result = vim.list_extend(result, link) + result = vim.list_extend(result, link) - for _, name in ipairs(result) do - local ft = vim.filetype.match({ filename = name }) - if ft == 'smarty' then - local first_row = vim.fn.readfile(name, '', 1)[1] - ft = vim.split(first_row, '%s')[2] - end + for _, name in ipairs(result) do + if name == nil then + goto continue + end + local extension = Get_file_extention(name) + local buf_extension = Get_file_extention(vim.api.nvim_buf_get_name(0)) + if extension == 'tpl' then + local first_row = vim.fn.readfile(name, '', 1)[1] + extension = vim.split(first_row, '%s')[2] + end - if ft then - if not res[ft] then - res[ft] = {} - end - res[ft][#res[ft] + 1] = name - else - vim.notify('[Template.nvim] Could not find the filetype of template file ' .. name, vim.log.levels.INFO) + if + vim.filetype.match({ filename = name }) == current_ext + or current_ext == extension + or extension == buf_extension + then + if not res[current_ext] then + res[current_ext] = {} + end + res[current_ext][#res[current_ext] + 1] = name + end + ::continue:: end - end - return res + return res end local function expand_expressions(line) - local cursor + local cursor - if line:find(cursor_pattern) then - cursor = true - end + if line:find(cursor_pattern) then + cursor = true + end - line = renderer.render_line(line) + line = renderer.render_line(line) - return line, cursor + return line, cursor end --@private local function create_and_load(file) - local current_path = fn.getcwd() - file = current_path .. sep .. file - local ok, fd = pcall(uv.fs_open, file, 'w', 420) - if not ok then - vim.notify("Couldn't create file " .. file) - return - end - uv.fs_close(fd) - - vim.cmd(':e ' .. file) + local current_path = fn.getcwd() + file = current_path .. sep .. file + local ok, fd = pcall(uv.fs_open, file, 'w', 420) + if not ok then + vim.notify("[Templates] Couldn't create file " .. file) + return + end + uv.fs_close(fd) + + vim.cmd(':e ' .. file) end -local function parse_args(args) - local data = {} +function temp:parse_args(args) + local data = {} - for _, v in pairs(args) do - if v:find('%.%w+') then - data.file = v + for _, v in pairs(args) do + if type(v) ~= "string" then + goto continue + end + if v:find('%.%w+') then + data.file = v + end + data.tp_name = v + ::continue:: end - data.tp_name = v - end - return data + return data end local function async_read(path, callback) - uv.fs_open(path, 'r', 438, function(err, fd) - assert(not err, err) - ---@diagnostic disable-next-line: redefined-local - uv.fs_fstat(fd, function(err, stat) - assert(not err, err) - ---@diagnostic disable-next-line: redefined-local - uv.fs_read(fd, stat.size, 0, function(err, data) + uv.fs_open(path, 'r', 438, function(err, fd) assert(not err, err) ---@diagnostic disable-next-line: redefined-local - uv.fs_close(fd, function(err) - assert(not err, err) - return callback(data) + uv.fs_fstat(fd, function(err, stat) + assert(not err, err) + ---@diagnostic disable-next-line: redefined-local + uv.fs_read(fd, stat.size, 0, function(err, data) + assert(not err, err) + ---@diagnostic disable-next-line: redefined-local + uv.fs_close(fd, function(err) + assert(not err, err) + return callback(data) + end) + end) end) - end) end) - end) end local function get_tpl(buf, name) - local list = temp.get_temp_list() - if not list[vim.bo[buf].filetype] then - return - end - - for _, v in ipairs(list[vim.bo[buf].filetype]) do - if v:find(name) then - return v + local list = temp.get_temp_list() + if not list[vim.bo[buf].filetype] then + return + end + + for _, v in ipairs(list[vim.bo[buf].filetype]) do + if v:find(name) then + return v + end end - end end function temp:generate_template(args) - local data = parse_args(args) + local data = temp:parse_args(args) - if data.file then - create_and_load(data.file) - end - - local current_buf = api.nvim_get_current_buf() - - local tpl = get_tpl(current_buf, data.tp_name) - if not tpl then - return - end + if data.file then + create_and_load(data.file) + end - local lines = {} + local current_buf = api.nvim_get_current_buf() - async_read( - tpl, - ---@diagnostic disable-next-line: redefined-local - vim.schedule_wrap(function(data) - local cursor_pos = {} - data = data:gsub('\r\n?', '\n') - local tbl = vim.split(data, '\n') + local tpl = get_tpl(current_buf, data.tp_name) + if not tpl then + return + end - local skip_lines = 0 + local lines = {} - for i, v in ipairs(tbl) do - if i == 1 then - local line_data = vim.split(v, '%s') - if #line_data == 2 and ";;" == line_data[1] then - skip_lines = skip_lines + 1 - goto continue - end - end - local line, cursor = expand_expressions(v) - lines[#lines + 1] = line - if cursor then - cursor_pos = { i - skip_lines, 2 } - end - ::continue:: - end - - local cur_line = api.nvim_win_get_cursor(0)[1] - local start = cur_line - if cur_line == 1 and #api.nvim_get_current_line() == 0 then - start = cur_line - 1 - end - api.nvim_buf_set_lines(current_buf, start, cur_line, false, lines) - cursor_pos[1] = start ~= 0 and cur_line + cursor_pos[1] or cursor_pos[1] - - if next(cursor_pos) ~= nil then - api.nvim_win_set_cursor(0, cursor_pos) - vim.cmd('startinsert!') - end - end) - ) + async_read( + tpl, + ---@diagnostic disable-next-line: redefined-local + vim.schedule_wrap(function(data) + local cursor_pos = {} + data = data:gsub('\r\n?', '\n') + local tbl = vim.split(data, '\n') + + local skip_lines = 0 + + for i, v in ipairs(tbl) do + if i == 1 then + local line_data = vim.split(v, '%s') + if #line_data == 2 and ';;' == line_data[1] then + skip_lines = skip_lines + 1 + goto continue + end + end + local line, cursor = expand_expressions(v) + lines[#lines + 1] = line + if cursor then + cursor_pos = { i - skip_lines, 2 } + end + ::continue:: + end + + local cur_line = api.nvim_win_get_cursor(0)[1] + local start = cur_line + if cur_line == 1 and #api.nvim_get_current_line() == 0 then + start = cur_line - 1 + end + api.nvim_buf_set_lines(current_buf, start, cur_line, false, lines) + if cursor_pos[1] ~= nil then + cursor_pos[1] = start ~= 0 and cur_line + cursor_pos[1] or cursor_pos[1] + + if next(cursor_pos) ~= nil then + api.nvim_win_set_cursor(0, cursor_pos) + vim.cmd('startinsert!') + end + end + end) + ) end function temp.in_template(buf) - local list = temp.get_temp_list() - if vim.tbl_isempty(list) or not list[vim.bo[buf].filetype] then - return false - end - local bufname = api.nvim_buf_get_name(buf) + local list = temp.get_temp_list() + if vim.tbl_isempty(list) or not list[vim.bo[buf].filetype] then + return false + end + local bufname = api.nvim_buf_get_name(buf) - if vim.tbl_contains(list[vim.bo[buf].filetype], bufname) then - return true - end + if vim.tbl_contains(list[vim.bo[buf].filetype], bufname) then + return true + end - return false + return false end temp.register = renderer.register function temp.setup(config) - renderer.register_builtins() - vim.validate({ - config = { config, 't' }, - }) - - if not config.temp_dir then - vim.notify('[template.nvim] please config the temp_dir variable') - return - end - - temp.temp_dir = config.temp_dir - - temp.author = config.author and config.author or '' - temp.email = config.email and config.email or '' - - local fts = vim.tbl_keys(temp.get_temp_list()) - - if #fts == 0 then - vim.notify('[template.nvim] does not get the filetype in template dir') - return - end - - api.nvim_create_autocmd({ 'BufEnter', 'BufNewFile' }, { - pattern = temp.temp_dir .. '/*', - group = api.nvim_create_augroup('Template', { clear = false }), - callback = function(opt) - if vim.bo[opt.buf].filetype == 'smarty' then - local fname = api.nvim_buf_get_name(opt.buf) - local row = vim.fn.readfile(fname, '', 1)[1] - local lang = vim.split(row, '%s')[2] - vim.treesitter.start(opt.buf, lang) - api.nvim_buf_add_highlight(opt.buf, 0, 'Comment', 0, 0, -1) + renderer.register_builtins() + vim.validate({ + config = { config, 't' }, + }) + + if not config.temp_dir then + vim.notify('[Templates] please config the temp_dir variable') return - end + end + + temp.temp_dir = config.temp_dir + + temp.author = config.author and config.author or '' + temp.email = config.email and config.email or '' + + local fts = vim.tbl_keys(temp.get_temp_list()) + + if #fts == 0 then + vim.notify('[Templates] does not get the filetype in template dir') + return + end - if temp.in_template(opt.buf) then - vim.diagnostic.disable(opt.buf) - end - end, - }) + api.nvim_create_autocmd({ 'BufEnter', 'BufNewFile' }, { + pattern = temp.temp_dir .. '/*', + group = api.nvim_create_augroup('Template', { clear = false }), + callback = function(opt) + if vim.bo[opt.buf].filetype == 'smarty' then + local fname = api.nvim_buf_get_name(opt.buf) + local row = vim.fn.readfile(fname, '', 1)[1] + local lang = vim.split(row, '%s')[2] + vim.treesitter.start(opt.buf, lang) + api.nvim_buf_add_highlight(opt.buf, 0, 'Comment', 0, 0, -1) + return + end + + if temp.in_template(opt.buf) then + vim.diagnostic.disable(opt.buf) + end + end, + }) end return temp diff --git a/plugin/template.lua b/plugin/template.lua index afdbf0f..f0c36d0 100644 --- a/plugin/template.lua +++ b/plugin/template.lua @@ -1,5 +1,5 @@ if vim.g.load_template then - return + return end vim.g.load_template = true @@ -7,44 +7,63 @@ vim.g.load_template = true local api = vim.api api.nvim_create_user_command('Template', function(args) - require('template'):generate_template(args.fargs) + require('template'):generate_template(args.fargs) end, { - nargs = '+', - complete = function(arg, line) - local temp = require('template') - if not temp.temp_dir then - vim.notify('[template.nvim] please config the temp_dir variable') - return {} - end + nargs = '+', + complete = function(arg, line) + local temp = require('template') + if not temp.temp_dir then + vim.notify('[Templates] please config the temp_dir variable') + return {} + end - local list = temp.get_temp_list() + local list = temp.get_temp_list() - local function match_item(ft) - return vim.tbl_map(function(s) - s = vim.fn.fnamemodify(s, ':t:r') - if arg and string.match(s, '^' .. arg) then - return s + local function match_item(ft) + return vim.tbl_map(function(s) + s = vim.fn.fnamemodify(s, ':t:r') + if arg and string.match(s, '^' .. arg) then + return s + end + return s + end, list[ft]) end - return s - end, list[ft]) - end - local ft = api.nvim_buf_get_option(0, 'filetype') - if list[ft] then - return match_item(ft) - end + local ft = api.nvim_buf_get_option(0, 'filetype') + if list[ft] then + return match_item(ft) + end - local args = vim.split(line, '%s+', { trimempty = true }) - if #args == 1 and not list[ft] then - return - end + local args = vim.split(line, '%s+', { trimempty = true }) + if #args == 1 and not list[ft] then + return + end - if #args >= 2 and args[2]:find('%.%w+$') then - ft = vim.filetype.match({ filename = args[2] }) - end + if #args >= 2 and args[2]:find('%.%w+$') then + ft = vim.filetype.match({ filename = args[2] }) + end + + if ft then + return match_item(ft) + end + end, +}) - if ft then - return match_item(ft) +api.nvim_create_user_command('TemplateCreate', function(args) + local temp = require('template') + local temp_dir = temp.temp_dir + if not temp_dir then + vim.notify('[Templates] please config the temp_dir variable') + return end - end, + local file = temp:parse_args(args).file + if not file then + vim.notify('[Templates] please provide a file') + return + end + local temp_loc = temp_dir .. '/' .. file + vim.notify('[Templates] created new template at ' .. temp_loc) + vim.cmd(':edit ' .. temp_loc) +end, { + nargs = 1, })