├── .github
└── FUNDING.yml
├── .gitignore
├── CONTRIBUTING.markdown
├── README.markdown
├── addon-info.json
├── doc
└── dotenv.txt
└── plugin
└── dotenv.vim
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: tpope
2 | custom: ["https://www.paypal.me/vimpope"]
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /doc/tags
2 |
--------------------------------------------------------------------------------
/CONTRIBUTING.markdown:
--------------------------------------------------------------------------------
1 | See the [contribution guidelines for pathogen.vim](https://github.com/tpope/vim-pathogen/blob/master/CONTRIBUTING.markdown).
2 |
--------------------------------------------------------------------------------
/README.markdown:
--------------------------------------------------------------------------------
1 | # dotenv.vim
2 |
3 | This plugin provides basic support for `.env` and `Procfile`.
4 |
5 | ## Interactive Usage
6 |
7 | Use `:Dotenv {file}` or `:Dotenv {dir}` to load a `.env` file and set the
8 | corresponding environment variables in Vim. Use `:verbose Dotenv` to see what
9 | variables are actually being set.
10 |
11 | ## Projections
12 |
13 | With [projectionist.vim][] and [dispatch.vim][] installed, you'll get a
14 | default `:Start` of `foreman start` for projects with a `Procfile`, and a
15 | default `:Dispatch` of `foreman check` for the `Procfile` itself.
16 |
17 | [projectionist.vim]: https://github.com/tpope/vim-projectionist
18 | [dispatch.vim]: https://github.com/tpope/vim-dispatch
19 |
20 | ## Dispatch
21 |
22 | If you call `:Dispatch foreman run whatever` or `:Dispatch dotenv whatever`,
23 | the compiler will be correctly selected for the `whatever` command.
24 |
25 | ## API
26 |
27 | While the above are all marginally helpful, this is the use case that inspired
28 | the plugin. Other plugins can call `DotenvGet('VAR')` to get the value of
29 | `$VAR` globally or from the current buffer's `.env`. Here's a wrapper to
30 | optionally use `DotenvGet()` if it's available.
31 |
32 | function! s:env(var) abort
33 | return exists('*DotenvGet') ? DotenvGet(a:var) : eval('$'.a:var)
34 | endfunction
35 |
36 | let db_url = s:env('DATABASE_URL')
37 |
38 | There's also `DotenvExpand()`, a drop-in replacement for `expand()`.
39 |
40 | function! s:expand(expr) abort
41 | return exists('*DotenvExpand') ? DotenvExpand(a:expr) : expand(a:expr)
42 | endfunction
43 |
44 | ## License
45 |
46 | Copyright © Tim Pope. Distributed under the same terms as Vim itself.
47 | See `:help license`.
48 |
--------------------------------------------------------------------------------
/addon-info.json:
--------------------------------------------------------------------------------
1 | {
2 | "vim_script_nr": 5176,
3 | "name": "dotenv",
4 | "version": "1.0",
5 | "author": "Tim Pope ",
6 | "repository" : {"type": "git", "url": "git://github.com/tpope/vim-dotenv.git"},
7 | "description": "Basic support for .env and Procfile"
8 | }
9 |
--------------------------------------------------------------------------------
/doc/dotenv.txt:
--------------------------------------------------------------------------------
1 | *dotenv.txt* Basic support for .env and Procfile
2 |
3 | Author: Tim Pope
4 | Repo: https://github.com/tpope/vim-dotenv
5 | License: Same terms as Vim itself (see |license|)
6 |
7 | USAGE *dotenv* *:Dotenv*
8 |
9 | :Dotenv {file} Load the dotenv file {file} and assign the given
10 | environment variables globally in the current Vim
11 | session. Use |:verbose| to see what actually gets
12 | assigned.
13 |
14 | :Dotenv {dir} Equivalent to :Dotenv {dir}/.env.
15 |
16 | :Dotenv Show the environment for the current buffer.
17 |
18 | API *dotenv-api*
19 |
20 | See the top of plugin/dotenv.vim.
21 |
22 | vim:tw=78:et:ft=help:norl:
23 |
--------------------------------------------------------------------------------
/plugin/dotenv.vim:
--------------------------------------------------------------------------------
1 | if exists('g:loaded_dotenv')
2 | finish
3 | endif
4 | let g:loaded_dotenv = 1
5 |
6 | " Get the value of a variable from the global Vim environment or current
7 | " buffer's .env.
8 | function! DotenvGet(...) abort
9 | if !exists('b:dotenv')
10 | let b:dotenv = DotenvRead()
11 | endif
12 | let env = b:dotenv
13 | if !a:0
14 | " Use this to get the current .env, as b:dotenv is private.
15 | return env
16 | endif
17 | let key = substitute(a:1, '^\$', '', '')
18 | return exists('$'.key) ? eval('$'.key) : get(env, key, (a:0 > 1 ? a:2 : ''))
19 | endfunction
20 |
21 | " Drop in replacement for expand() that takes the current buffer's .env into
22 | " account.
23 | function! DotenvExpand(str, ...) abort
24 | let str = a:str
25 | let pat = '\$\(\w\+\)'
26 | let end = 0
27 | while 1
28 | let pos = match(str, pat, end)
29 | if pos < 0
30 | break
31 | endif
32 | let var = matchstr(str, pat, end)
33 | let end = pos + len(var)
34 | let val = DotenvGet(var)
35 | let str = strpart(str, 0, pos) . (empty(val) ? var : fnameescape(val)) . strpart(str, end)
36 | endwhile
37 | return call('expand', [str] + a:000)
38 | endfunction
39 |
40 | " Find the nearest .env file.
41 | function! DotenvFile() abort
42 | return findfile('.env', isdirectory(expand('%')) ? expand('%').';' : '.;')
43 | endfunction
44 |
45 | " Read and parse a .env file.
46 | function! DotenvRead(...) abort
47 | let env = {}
48 | for file in a:0 ? a:000 : [DotenvFile()]
49 | if len(file)
50 | call s:read_env(isdirectory(file) ? file.'/.env' : file, env)
51 | endif
52 | endfor
53 | lockvar! env
54 | return env
55 | endfunction
56 |
57 | " Section: Implementation
58 |
59 | function! s:lookup(key, env) abort
60 | if a:key ==# '\n'
61 | return "\n"
62 | elseif a:key =~# '^\\'
63 | return a:key[1:-1]
64 | endif
65 | let var = matchstr(a:key, '^\${\zs.*\ze}$\|^\$\zs\(.*\)$')
66 | if exists('$'.var)
67 | return eval('$'.var)
68 | else
69 | return get(a:env, var, '')
70 | endif
71 | endfunction
72 |
73 | let s:env_cache = {}
74 | let s:interpolation = '\\\=\${.\{-\}}\|\\\=\$\w\+'
75 |
76 | function! s:read_env(file, ...) abort
77 | let file = fnamemodify(a:file, ':p')
78 | let ftime = getftime(file)
79 | if ftime < 0
80 | return {}
81 | endif
82 | let [cachetime, lines] = get(s:env_cache, file, [-2, []])
83 | if ftime != cachetime
84 | let lines = []
85 | for line in readfile(file)
86 | let matches = matchlist(line, '\v\C^%(export\s+)=([[:alnum:]_.]+)%(\s*\=\s*|:\s{-})(''%(\\''|[^''])*''|"%(\\"|[^"])*"|[^#]+)=%( *#.*)?$')
87 | if !empty(matches)
88 | call add(lines, matches[1:2])
89 | endif
90 | endfor
91 | let s:env_cache[file] = [ftime, lines]
92 | endif
93 | let env = a:0 ? a:1 : {}
94 | for [key, value] in lines
95 | if !has_key(env, key)
96 | if value =~# '^\s*".*"\s*$'
97 | let value = substitute(value, '\n', "\n", 'g')
98 | let value = substitute(value, '\\\ze[^$]', '', 'g')
99 | endif
100 | let value = substitute(value, '^\s*\([''"]\)\=\(.\{-\}\)\1\s*$', '\2', '')
101 | let value = substitute(value, s:interpolation, '\=s:lookup(submatch(0), env)', 'g')
102 | let env[key] = value
103 | endif
104 | endfor
105 | return env
106 | endfunction
107 |
108 | function! s:echo_let(var, val) abort
109 | echohl Statement
110 | echon 'let '
111 | echohl PreProc
112 | echon '$'.a:var
113 | echohl Operator
114 | echon ' = '
115 | echohl String
116 | echon string(a:val)
117 | echohl None
118 | endfunction
119 |
120 | function! s:Load(bang, ...) abort
121 | if !a:0
122 | let file = DotenvFile()
123 | if empty(file)
124 | echohl Comment
125 | echo "# No dotenv found"
126 | echohl None
127 | elseif a:bang
128 | return 'edit '.fnameescape(file)
129 | else
130 | echohl Comment
131 | echon (&verbose ? '" ' : '# ').fnamemodify(file, ':~:.')
132 | echohl None
133 | let env = DotenvGet()
134 | for var in sort(keys(env))
135 | echon "\n"
136 | if &verbose
137 | call s:echo_let(var, env[var])
138 | else
139 | echohl PreProc
140 | echon var
141 | echohl Operator
142 | echon '='
143 | echohl String
144 | echon escape(env[var], ' $"''')
145 | echohl None
146 | endif
147 | endfor
148 | endif
149 | return ''
150 | endif
151 | let files = []
152 | for glob in a:000
153 | if glob =~# '[*]'
154 | call extend(files, split(glob(glob), "\n"))
155 | else
156 | call extend(files, split(expand(glob), "\n"))
157 | endif
158 | endfor
159 | if !a:bang
160 | for file in files
161 | if !filereadable(file) && !filereadable(file.'/.env')
162 | return 'echoerr '.string('No .env found at '.file)
163 | endif
164 | endfor
165 | endif
166 | let env = call('DotenvRead', files)
167 | let first = 1
168 | for var in sort(keys(env))
169 | if &verbose
170 | if first
171 | let first = 0
172 | else
173 | echon "\n"
174 | endif
175 | call s:echo_let(var, env[var])
176 | endif
177 | execute 'let $'.var '= env[var]'
178 | endfor
179 | return ''
180 | endfunction
181 |
182 | command! -bar -bang -nargs=* -complete=file Dotenv exe s:Load(0, )
183 |
184 | if !exists('g:dispatch_compilers')
185 | let g:dispatch_compilers = {}
186 | endif
187 | let g:dispatch_compilers['dotenv'] = ''
188 | let g:dispatch_compilers['foreman run'] = ''
189 |
190 | if !exists('g:projectionist_heuristics')
191 | let g:projectionist_heuristics = {}
192 | endif
193 | if !has_key(g:projectionist_heuristics, "Procfile")
194 | let g:projectionist_heuristics["Procfile"] = {
195 | \ "Procfile": {"dispatch": get(g:, "foreman", "foreman") . " check"}}
196 | endif
197 |
198 | augroup dotenvPlugin
199 | autocmd BufNewFile,BufReadPost .env.* setfiletype sh
200 |
201 | autocmd BufNewFile,BufReadPre * unlet! b:dotenv
202 | autocmd FileType netrw unlet! b:dotenv
203 | augroup END
204 |
--------------------------------------------------------------------------------