diff --git a/README.md b/README.md index aa25271..fb5117c 100644 --- a/README.md +++ b/README.md @@ -181,14 +181,24 @@ vim.api.nvim_create_autocmd("User", { }) ``` -## 🫂 Special Thanks +## 🫂 Attributions / Special Thanks -I extend my thanks to the awesome [Lychee](https://github.com/itslychee), -[mrshmllow](https://github.com/mrshmllow) and -[diniamo](https://github.com/diniamo) for their invaluable assistance in the -creation of this plugin. I would also like to thank -[direnv.vim](https://github.com/direnv/direnv.vim) maintainers for their initial -work. +[direnv.vim]: https://github.com/direnv/direnv.vim + +My first thanks go first to the awesome [direnv.vim] (available under the MIT +license) that this plugin draws a lot of inspiration from. It is a small, +practical solution that has served me well. + +[diniamo]: https://github.com/diniamo +[lychee]: https://github.com/itslychee +[mrshmllow]: https://github.com/mrshmllow + +I would also like to extend my thanks to + +I extend my thanks to the awesome [diniamo], [Lychee], [mrshmllow] for their +invaluable assistance in the creation of this plugin. The earlier prototypes of +this plugin were nowhere near what it is today, and thanks to them many +improvements were made possible. ## 📜 License diff --git a/ftdetect/direnv.lua b/ftdetect/direnv.lua new file mode 100644 index 0000000..2ff3164 --- /dev/null +++ b/ftdetect/direnv.lua @@ -0,0 +1,9 @@ +-- Detect .envrc, .direnvrc, direnvrc files and set filetype to 'direnv' +local group = vim.api.nvim_create_augroup("direnv_ftdetect", { clear = true }) +vim.api.nvim_create_autocmd({ "BufRead", "BufNewFile" }, { + group = group, + pattern = { ".envrc*", ".direnvrc*", "direnvrc*" }, + callback = function() + vim.bo.filetype = "direnv" + end, +}) diff --git a/ftplugin/direnv.lua b/ftplugin/direnv.lua new file mode 100644 index 0000000..016c4ef --- /dev/null +++ b/ftplugin/direnv.lua @@ -0,0 +1,47 @@ +if vim.b.did_ftplugin then + return +end +vim.b.did_ftplugin = true + +-- Set buffer-local commentstring +vim.bo.commentstring = "# %s" + +-- Saving an .envrc (or related) should automatically trigger the plugin's +-- reload/refresh behavior without causing errors when the plugin/command +-- is not installed. +local group = vim.api.nvim_create_augroup("DirenvBuffer", { clear = false }) +vim.api.nvim_create_autocmd("BufWritePost", { + group = group, + buffer = 0, + callback = function() + -- Trigger the Direnv reload command if available + if vim.fn.exists(":Direnv") == 2 then + pcall(vim.cmd, "Direnv reload") + end + end, +}) + +-- Decide whether to use tree-sitter or legacy syntax script. Tree-sitter +-- is more modern and is almost always preferrable to Regex-based highlighting +-- that the legacy method requires, but it's difficult to bundle a Tree-sitter +-- grammar, so we cannot unconditionally load my own Direnv grammar. Though, if +-- it IS available then we should skip legacy syntax highlighting. +local has_ts = false +local ok, parsers = pcall(require, "nvim-treesitter.parsers") +if ok and type(parsers.has_parser) == "function" then + has_ts = parsers.has_parser("direnv") +end + +if has_ts then + -- If a parser exists, prefer tree-sitter highlighting. Nothing to do + -- here because nvim-treesitter will attach based on filetype by itself. + return +end + +-- Set syntax to direnv so Vim will load the bundled syntax file. Only set +-- if no syntax is already active for this buffer, i.e., if tree-sitter is +-- not loaded/available. +if not vim.bo.syntax or vim.bo.syntax == "" then + -- setlocal syntax=direnv should cause Vim to load syntax/direnv.vim from runtime + pcall(vim.cmd, "setlocal syntax=direnv") +end diff --git a/syntax/direnv.vim b/syntax/direnv.vim new file mode 100644 index 0000000..07e27f6 --- /dev/null +++ b/syntax/direnv.vim @@ -0,0 +1,89 @@ +" direnv.vim - support for direnv +" Author: JINNOUCHI Yasushi +" Version: 0.2 + +if exists('b:current_syntax') + finish +endif + +" To use syntax for bash in sh.vim, set the g:is_bash value temporarily. +let s:current = get(g:, 'is_bash', '__NO_VALUE__') +let g:is_bash = 1 +runtime! syntax/sh.vim +if s:current ==# '__NO_VALUE__' + unlet g:is_bash +else + let g:is_bash = s:current +endif +unlet s:current + +let b:current_syntax = 'direnv' + +" Func: with commands {{{ +" `has` func takes one argument that represents a CLI command. +syn keyword direnvCommandFunc has nextgroup=direnvCommand,shSingleQuote,shDoubleQuote skipwhite +hi def link direnvCommandFunc shStatement + +" command name is almost the same as direnvPath, but has a different color. +syn region direnvCommand start=/[^'"[:space:]]/ skip=/\\./ end=/\([][(){}#`'";\\[:space:]]\)\@=\|$/ contained nextgroup=shComment skipwhite +hi def link direnvCommand shCommandSub +" }}} + +" Func: with paths {{{ +" These funcs takes one argument that represents a file/dir path. +syn keyword direnvPathFunc dotenv dotenv_if_exists env_vars_required fetchurl join_args user_rel_path on_git_branch find_up has source_env source_env_if_exists source_up source_up_if_exists source_url PATH_add MANPATH_add load_prefix watch_file watch_dir semver_search strict_env unstrict_env nextgroup=direnvPath,shSingleQuote,shDoubleQuote skipwhite +hi def link direnvPathFunc shStatement + +" path string can end before non-escaped [, ], (, ), {, }, #, `, ', ", ;, \, and spaces. +syn region direnvPath start=/[^'"[:space:]]/ skip=/\\./ end=/\([][(){}#`'";\\[:space:]]\)\@=\|$/ contained nextgroup=shComment skipwhite +hi def link direnvPath Directory + +" `expand_path` takes one or two arguments that represents a dir path. +syn keyword direnvExpandPathFunc expand_path nextgroup=direnvExpandPathRel,shSingleQuote,shDoubleQuote skipwhite +hi def link direnvExpandPathFunc shStatement + +syn region direnvExpandPathRel start=/[^'"[:space:]]/ skip=/\\./ end=/\%(\s\|\_$\)/ contained nextgroup=direnvPath,shSingleQuote,shDoubleQuote skipwhite +hi def link direnvExpandPathRel Directory + +" `path_add` takes two arguments that represents a variable name and a dir +" path. +syn keyword direnvPathAddFunc PATH_add MANPATH_add PATH_rm path_rm path_add nextgroup=direnvVar skipwhite +hi def link direnvPathAddFunc shStatement + +syn match direnvVar /\S\+/ nextgroup=direnvPath,shSingleQuote,shDoubleQuote contained skipwhite +hi def link direnvVar shCommandSub +" }}} + +" Func: use {{{ +syn keyword direnvUseFunc use use_flake use_guix use_julia use_nix use_node use_nodenv use_rbenv use_vim rvm nextgroup=direnvUseCommand skipwhite +hi def link direnvUseFunc shStatement + +" `use rbenv/nix/guix` takes several arguments. +syn match direnvUseCommand /\S\+/ contained +hi def link direnvUseCommand shCommandSub +" }}} + +" Func: layout {{{ +" `layout` takes one argument that represents a language name. +syn keyword direnvLayoutFunc layout layout_anaconda layout_go layout_julia layout_node layout_perl layout_php layout_pipenv layout_pyenv layout_python layout_python2 layout_python3 layout_ruby nextgroup=direnvLayoutLanguage,direnvLayoutLanguagePath skipwhite +hi def link direnvLayoutFunc shStatement + +syn keyword direnvLayoutLanguage go node perl python3 ruby contained +hi def link direnvLayoutLanguage shCommandSub + +" `layout python` takes one more argument that represents a file path. +syn keyword direnvLayoutLanguagePath python nextgroup=direnvPath,shSingleQuote,shDoubleQuote contained skipwhite +hi def link direnvLayoutLanguagePath shCommandSub +" }}} + +" Func: others {{{ +" `direnv_load` takes several arguments. +syn keyword direnvFunc direnv_apply_dump direnv_layout_dir direnv_load direnv_version log_error log_status +hi def link direnvFunc shStatement +" }}} + +syn cluster direnvStatement contains=direnvCommandFunc,direnvPathFunc,direnvExpandPathFunc,direnvPathAddFunc,direnvUseFunc,direnvLayoutFunc,direnvFunc +syn cluster shArithParenList add=@direnvStatement +syn cluster shCommandSubList add=@direnvStatement + +" vim:se fdm=marker: