From 10c63bf6333c09607aff8e5decc2f587beac8d33 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Tue, 27 Jan 2026 10:42:30 +0300 Subject: [PATCH] initial syntax highlighting work Signed-off-by: NotAShelf Change-Id: I9469167a04228dc43f4b67d8bddc090a6a6a6964 --- ftdetect/direnv.lua | 9 +++++ ftplugin/direnv.lua | 47 ++++++++++++++++++++++++ syntax/direnv.vim | 89 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+) create mode 100644 ftdetect/direnv.lua create mode 100644 ftplugin/direnv.lua create mode 100644 syntax/direnv.vim 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: