" Zlib License " " Copyright (c) 2026 Nicolás A. Ortega " " This software is provided 'as-is', without any express or implied warranty. In " no event will the authors be held liable for any damages arising from the use " of this software. " " Permission is granted to anyone to use this software for any purpose, including " commercial applications, and to alter it and redistribute it freely, subject " to the following restrictions: " " 1. The origin of this software must not be misrepresented; you must not claim " that you wrote the original software. If you use this software in a product, " an acknowledgment in the product documentation would be appreciated but is " not required. " " 2. Altered source versions must be plainly marked as such, and must not be " misrepresented as being the original software. " " 3. This notice may not be removed or altered from any source distribution. " vim-licenser: A plugin for adding license headers to source code files " Usage: " :Licenser " " Available licenses can be found in the licenses/ directory if exists('g:loaded_licenser') finish endif let g:loaded_licenser = 1 function! s:GetCommentStyle(filetype) abort let ft = a:filetype " Define comment styles for different filetypes " - margin: if prefix and suffix should be used " - hashbang: if the file has a hashbang line, we should skip it when " adding the license header " - prefix: the string to use at the beginning of the comment block " - middle: the string to use at the beginning of each line in the comment " - suffix: the string to use at the end of the comment block let comment_styles = { \ 'bash': {'margin': 0, 'hashbang': 1, 'prefix': '', 'middle': '#', 'suffix': ''}, \ 'c': {'margin': 1, 'hashbang': 0, 'prefix': '/*', 'middle': ' *', 'suffix': ' */'}, \ 'cpp': {'margin': 1, 'hashbang': 0, 'prefix': '/*', 'middle': ' *', 'suffix': ' */'}, \ 'csharp': {'margin': 1, 'hashbang': 0, 'prefix': '/*', 'middle': ' *', 'suffix': ' */'}, \ 'css': {'margin': 1, 'hashbang': 0, 'prefix': '/*', 'middle': ' *', 'suffix': ' */'}, \ 'dart': {'margin': 1, 'hashbang': 0, 'prefix': '/*', 'middle': ' *', 'suffix': ' */'}, \ 'go': {'margin': 1, 'hashbang': 0, 'prefix': '/*', 'middle': ' *', 'suffix': ' */'}, \ 'groovy': {'margin': 1, 'hashbang': 0, 'prefix': '/*', 'middle': ' *', 'suffix': ' */'}, \ 'haskell': {'margin': 0, 'hashbang': 1, 'prefix': '', 'middle': '--', 'suffix': ''}, \ 'html': {'margin': 1, 'hashbang': 0, 'prefix': ''}, \ 'java': {'margin': 1, 'hashbang': 0, 'prefix': '/*', 'middle': ' *', 'suffix': ' */'}, \ 'javascript': {'margin': 1, 'hashbang': 0, 'prefix': '/*', 'middle': ' *', 'suffix': ' */'}, \ 'json': {'margin': 1, 'hashbang': 0, 'prefix': '/*', 'middle': ' *', 'suffix': ' */'}, \ 'kotlin': {'margin': 1, 'hashbang': 0, 'prefix': '/*', 'middle': ' *', 'suffix': ' */'}, \ 'lua': {'margin': 0, 'hashbang': 1, 'prefix': '', 'middle': '--', 'suffix': ''}, \ 'matlab': {'margin': 0, 'hashbang': 0, 'prefix': '', 'middle': '%', 'suffix': ''}, \ 'perl': {'margin': 0, 'hashbang': 1, 'prefix': '', 'middle': '#', 'suffix': ''}, \ 'php': {'margin': 1, 'hashbang': 0, 'prefix': '/*', 'middle': ' *', 'suffix': ' */'}, \ 'python': {'margin': 0, 'hashbang': 1, 'prefix': '', 'middle': '#', 'suffix': ''}, \ 'r': {'margin': 0, 'hashbang': 0, 'prefix': '', 'middle': '#', 'suffix': ''}, \ 'ruby': {'margin': 0, 'hashbang': 1, 'prefix': '', 'middle': '#', 'suffix': ''}, \ 'rust': {'margin': 1, 'hashbang': 0, 'prefix': '/*', 'middle': ' *', 'suffix': ' */'}, \ 'scala': {'margin': 1, 'hashbang': 0, 'prefix': '/*', 'middle': ' *', 'suffix': ' */'}, \ 'shell': {'margin': 0, 'hashbang': 1, 'prefix': '', 'middle': '#', 'suffix': ''}, \ 'sql': {'margin': 1, 'hashbang': 0, 'prefix': '/*', 'middle': ' *', 'suffix': ' */'}, \ 'swift': {'margin': 1, 'hashbang': 0, 'prefix': '/*', 'middle': ' *', 'suffix': ' */'}, \ 'typescript': {'margin': 1, 'hashbang': 0, 'prefix': '/*', 'middle': ' *', 'suffix': ' */'}, \ 'typescriptreact': {'margin': 1, 'hashbang': 0, 'prefix': '/*', 'middle': ' *', 'suffix': ' */'}, \ 'vim': {'margin': 0, 'hashbang': 0, 'prefix': '', 'middle': '"', 'suffix': ''}, \ 'xml': {'margin': 1, 'hashbang': 0, 'prefix': ''}, \ 'yaml': {'margin': 0, 'hashbang': 0, 'prefix': '', 'middle': '#', 'suffix': ''}, \ } if has_key(comment_styles, ft) return comment_styles[ft] else " Error message for unsupported filetypes echohl ErrorMsg echo 'Unsupported filetype: ' . ft echohl None return {'margin': 0, 'prefix': '', 'middle': '', 'suffix': ''} endif endfunction function! s:AddLicenseHeader(license) abort let plugin_dir = fnamemodify(expand(''), ':h:h') let license_file = plugin_dir . '/licenses/' . a:license . '.txt' if !filereadable(license_file) echohl ErrorMsg echo 'License file not found: ' . license_file echohl None return endif let license_lines = readfile(license_file) let author = get(g:, 'licenser_author', 'Author Name') let year = strftime('%Y') let comment_style = s:GetCommentStyle(&filetype) let processed_lines = [] if comment_style.margin call add(processed_lines, comment_style.prefix) endif for line in license_lines let line = substitute(line, 'YEAR', year, 'g') let line = substitute(line, 'AUTHOR', author, 'g') if line ==# '' call add(processed_lines, comment_style.middle) else call add(processed_lines, comment_style.middle . ' ' . line) endif endfor if comment_style.margin call add(processed_lines, comment_style.suffix) endif call append(comment_style.hashbang, processed_lines) endfunction function! s:CompleteFiletype(A, L, P) abort let plugin_dir = fnamemodify(expand(''), ':h:h') let licenses_dir = plugin_dir . '/licenses/' if !isdirectory(licenses_dir) return [] endif let licenses = [] for file in glob(licenses_dir . '*.txt', 0, 1) let name = fnamemodify(file, ':t:r') if name =~ '^' . a:A call add(licenses, name) endif endfor return sort(licenses) endfunction command! -nargs=1 -complete=customlist,s:CompleteFiletype Licenser call s:AddLicenseHeader()