]> git.madduck.net Git - etc/vim.git/blob - plugin/black.vim

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Omit commit hash and date stamp from doc version (#1322)
[etc/vim.git] / plugin / black.vim
1 " black.vim
2 " Author: Łukasz Langa
3 " Created: Mon Mar 26 23:27:53 2018 -0700
4 " Requires: Vim Ver7.0+
5 " Version:  1.1
6 "
7 " Documentation:
8 "   This plugin formats Python files.
9 "
10 " History:
11 "  1.0:
12 "    - initial version
13 "  1.1:
14 "    - restore cursor/window position after formatting
15
16 if v:version < 700 || !has('python3')
17     func! __BLACK_MISSING()
18         echo "The black.vim plugin requires vim7.0+ with Python 3.6 support."
19     endfunc
20     command! Black :call __BLACK_MISSING()
21     command! BlackUpgrade :call __BLACK_MISSING()
22     command! BlackVersion :call __BLACK_MISSING()
23     finish
24 endif
25
26 if exists("g:load_black")
27    finish
28 endif
29
30 let g:load_black = "py1.0"
31 if !exists("g:black_virtualenv")
32   if has("nvim")
33     let g:black_virtualenv = "~/.local/share/nvim/black"
34   else
35     let g:black_virtualenv = "~/.vim/black"
36   endif
37 endif
38 if !exists("g:black_fast")
39   let g:black_fast = 0
40 endif
41 if !exists("g:black_linelength")
42   let g:black_linelength = 88
43 endif
44 if !exists("g:black_skip_string_normalization")
45   let g:black_skip_string_normalization = 0
46 endif
47
48 python3 << endpython3
49 import collections
50 import os
51 import sys
52 import vim
53
54
55 class Flag(collections.namedtuple("FlagBase", "name, cast")):
56   @property
57   def var_name(self):
58     return self.name.replace("-", "_")
59
60   @property
61   def vim_rc_name(self):
62     name = self.var_name
63     if name == "line_length":
64       name = name.replace("_", "")
65     if name == "string_normalization":
66       name = "skip_" + name
67     return "g:black_" + name
68
69
70 FLAGS = [
71   Flag(name="line_length", cast=int),
72   Flag(name="fast", cast=bool),
73   Flag(name="string_normalization", cast=bool),
74 ]
75
76
77 def _get_python_binary(exec_prefix):
78   try:
79     default = vim.eval("g:pymode_python").strip()
80   except vim.error:
81     default = ""
82   if default and os.path.exists(default):
83     return default
84   if sys.platform[:3] == "win":
85     return exec_prefix / 'python.exe'
86   return exec_prefix / 'bin' / 'python3'
87
88 def _get_pip(venv_path):
89   if sys.platform[:3] == "win":
90     return venv_path / 'Scripts' / 'pip.exe'
91   return venv_path / 'bin' / 'pip'
92
93 def _get_virtualenv_site_packages(venv_path, pyver):
94   if sys.platform[:3] == "win":
95     return venv_path / 'Lib' / 'site-packages'
96   return venv_path / 'lib' / f'python{pyver[0]}.{pyver[1]}' / 'site-packages'
97
98 def _initialize_black_env(upgrade=False):
99   pyver = sys.version_info[:2]
100   if pyver < (3, 6):
101     print("Sorry, Black requires Python 3.6+ to run.")
102     return False
103
104   from pathlib import Path
105   import subprocess
106   import venv
107   virtualenv_path = Path(vim.eval("g:black_virtualenv")).expanduser()
108   virtualenv_site_packages = str(_get_virtualenv_site_packages(virtualenv_path, pyver))
109   first_install = False
110   if not virtualenv_path.is_dir():
111     print('Please wait, one time setup for Black.')
112     _executable = sys.executable
113     try:
114       sys.executable = str(_get_python_binary(Path(sys.exec_prefix)))
115       print(f'Creating a virtualenv in {virtualenv_path}...')
116       print('(this path can be customized in .vimrc by setting g:black_virtualenv)')
117       venv.create(virtualenv_path, with_pip=True)
118     finally:
119       sys.executable = _executable
120     first_install = True
121   if first_install:
122     print('Installing Black with pip...')
123   if upgrade:
124     print('Upgrading Black with pip...')
125   if first_install or upgrade:
126     subprocess.run([str(_get_pip(virtualenv_path)), 'install', '-U', 'black'], stdout=subprocess.PIPE)
127     print('DONE! You are all set, thanks for waiting ✨ 🍰 ✨')
128   if first_install:
129     print('Pro-tip: to upgrade Black in the future, use the :BlackUpgrade command and restart Vim.\n')
130   if virtualenv_site_packages not in sys.path:
131     sys.path.append(virtualenv_site_packages)
132   return True
133
134 if _initialize_black_env():
135   import black
136   import time
137
138 def Black():
139   start = time.time()
140   configs = get_configs()
141   mode = black.FileMode(
142     line_length=configs["line_length"],
143     string_normalization=configs["string_normalization"],
144     is_pyi=vim.current.buffer.name.endswith('.pyi'),
145   )
146
147   buffer_str = '\n'.join(vim.current.buffer) + '\n'
148   try:
149     new_buffer_str = black.format_file_contents(
150       buffer_str,
151       fast=configs["fast"],
152       mode=mode,
153     )
154   except black.NothingChanged:
155     print(f'Already well formatted, good job. (took {time.time() - start:.4f}s)')
156   except Exception as exc:
157     print(exc)
158   else:
159     current_buffer = vim.current.window.buffer
160     cursors = []
161     for i, tabpage in enumerate(vim.tabpages):
162       if tabpage.valid:
163         for j, window in enumerate(tabpage.windows):
164           if window.valid and window.buffer == current_buffer:
165             cursors.append((i, j, window.cursor))
166     vim.current.buffer[:] = new_buffer_str.split('\n')[:-1]
167     for i, j, cursor in cursors:
168       window = vim.tabpages[i].windows[j]
169       try:
170         window.cursor = cursor
171       except vim.error:
172         window.cursor = (len(window.buffer), 0)
173     print(f'Reformatted in {time.time() - start:.4f}s.')
174
175 def get_configs():
176   path_pyproject_toml = black.find_pyproject_toml(vim.eval("fnamemodify(getcwd(), ':t')"))
177   if path_pyproject_toml:
178     toml_config = black.parse_pyproject_toml(path_pyproject_toml)
179   else:
180     toml_config = {}
181
182   return {
183     flag.var_name: toml_config.get(flag.name, flag.cast(vim.eval(flag.vim_rc_name)))
184     for flag in FLAGS
185   }
186
187
188 def BlackUpgrade():
189   _initialize_black_env(upgrade=True)
190
191 def BlackVersion():
192   print(f'Black, version {black.__version__} on Python {sys.version}.')
193
194 endpython3
195
196 command! Black :py3 Black()
197 command! BlackUpgrade :py3 BlackUpgrade()
198 command! BlackVersion :py3 BlackVersion()