]> git.madduck.net Git - etc/vim.git/blob - autoload/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:

Correctly handle trailing commas that are inside a line's leading non-nested parens...
[etc/vim.git] / autoload / black.vim
1 python3 << EndPython3
2 import collections
3 import os
4 import sys
5 import vim
6
7 def strtobool(text):
8   if text.lower() in ['y', 'yes', 't', 'true', 'on', '1']:
9     return True
10   if text.lower() in ['n', 'no', 'f', 'false', 'off', '0']:
11     return False
12   raise ValueError(f"{text} is not convertable to boolean")
13
14 class Flag(collections.namedtuple("FlagBase", "name, cast")):
15   @property
16   def var_name(self):
17     return self.name.replace("-", "_")
18
19   @property
20   def vim_rc_name(self):
21     name = self.var_name
22     if name == "line_length":
23       name = name.replace("_", "")
24     return "g:black_" + name
25
26
27 FLAGS = [
28   Flag(name="line_length", cast=int),
29   Flag(name="fast", cast=strtobool),
30   Flag(name="skip_string_normalization", cast=strtobool),
31   Flag(name="quiet", cast=strtobool),
32   Flag(name="skip_magic_trailing_comma", cast=strtobool),
33   Flag(name="preview", cast=strtobool),
34 ]
35
36
37 def _get_python_binary(exec_prefix):
38   try:
39     default = vim.eval("g:pymode_python").strip()
40   except vim.error:
41     default = ""
42   if default and os.path.exists(default):
43     return default
44   if sys.platform[:3] == "win":
45     return exec_prefix / 'python.exe'
46   return exec_prefix / 'bin' / 'python3'
47
48 def _get_pip(venv_path):
49   if sys.platform[:3] == "win":
50     return venv_path / 'Scripts' / 'pip.exe'
51   return venv_path / 'bin' / 'pip'
52
53 def _get_virtualenv_site_packages(venv_path, pyver):
54   if sys.platform[:3] == "win":
55     return venv_path / 'Lib' / 'site-packages'
56   return venv_path / 'lib' / f'python{pyver[0]}.{pyver[1]}' / 'site-packages'
57
58 def _initialize_black_env(upgrade=False):
59   if vim.eval("g:black_use_virtualenv ? 'true' : 'false'") == "false":
60     if upgrade:
61       print("Upgrade disabled due to g:black_use_virtualenv being disabled.")
62       print("Either use your system package manager (or pip) to upgrade black separately,")
63       print("or modify your vimrc to have 'let g:black_use_virtualenv = 1'.")
64       return False
65     else:
66       # Nothing needed to be done.
67       return True
68
69   pyver = sys.version_info[:3]
70   if pyver < (3, 7):
71     print("Sorry, Black requires Python 3.7+ to run.")
72     return False
73
74   from pathlib import Path
75   import subprocess
76   import venv
77   virtualenv_path = Path(vim.eval("g:black_virtualenv")).expanduser()
78   virtualenv_site_packages = str(_get_virtualenv_site_packages(virtualenv_path, pyver))
79   first_install = False
80   if not virtualenv_path.is_dir():
81     print('Please wait, one time setup for Black.')
82     _executable = sys.executable
83     _base_executable = getattr(sys, "_base_executable", _executable)
84     try:
85       executable = str(_get_python_binary(Path(sys.exec_prefix)))
86       sys.executable = executable
87       sys._base_executable = executable
88       print(f'Creating a virtualenv in {virtualenv_path}...')
89       print('(this path can be customized in .vimrc by setting g:black_virtualenv)')
90       venv.create(virtualenv_path, with_pip=True)
91     except Exception:
92       print('Encountered exception while creating virtualenv (see traceback below).')
93       print(f'Removing {virtualenv_path}...')
94       import shutil
95       shutil.rmtree(virtualenv_path)
96       raise
97     finally:
98       sys.executable = _executable
99       sys._base_executable = _base_executable
100     first_install = True
101   if first_install:
102     print('Installing Black with pip...')
103   if upgrade:
104     print('Upgrading Black with pip...')
105   if first_install or upgrade:
106     subprocess.run([str(_get_pip(virtualenv_path)), 'install', '-U', 'black'], stdout=subprocess.PIPE)
107     print('DONE! You are all set, thanks for waiting ✨ 🍰 ✨')
108   if first_install:
109     print('Pro-tip: to upgrade Black in the future, use the :BlackUpgrade command and restart Vim.\n')
110   if virtualenv_site_packages not in sys.path:
111     sys.path.insert(0, virtualenv_site_packages)
112   return True
113
114 if _initialize_black_env():
115   import black
116   import time
117
118 def get_target_version(tv):
119   if isinstance(tv, black.TargetVersion):
120     return tv
121   ret = None
122   try:
123     ret = black.TargetVersion[tv.upper()]
124   except KeyError:
125     print(f"WARNING: Target version {tv!r} not recognized by Black, using default target")
126   return ret
127
128 def Black(**kwargs):
129   """
130   kwargs allows you to override ``target_versions`` argument of
131   ``black.FileMode``.
132
133   ``target_version`` needs to be cleaned because ``black.FileMode``
134   expects the ``target_versions`` argument to be a set of TargetVersion enums.
135
136   Allow kwargs["target_version"] to be a string to allow
137   to type it more quickly.
138
139   Using also target_version instead of target_versions to remain
140   consistent to Black's documentation of the structure of pyproject.toml.
141   """
142   start = time.time()
143   configs = get_configs()
144
145   black_kwargs = {}
146   if "target_version" in kwargs:
147     target_version = kwargs["target_version"]
148
149     if not isinstance(target_version, (list, set)):
150       target_version = [target_version]
151     target_version = set(filter(lambda x: x, map(lambda tv: get_target_version(tv), target_version)))
152     black_kwargs["target_versions"] = target_version
153
154   mode = black.FileMode(
155     line_length=configs["line_length"],
156     string_normalization=not configs["skip_string_normalization"],
157     is_pyi=vim.current.buffer.name.endswith('.pyi'),
158     magic_trailing_comma=not configs["skip_magic_trailing_comma"],
159     preview=configs["preview"],
160     **black_kwargs,
161   )
162   quiet = configs["quiet"]
163
164   buffer_str = '\n'.join(vim.current.buffer) + '\n'
165   try:
166     new_buffer_str = black.format_file_contents(
167       buffer_str,
168       fast=configs["fast"],
169       mode=mode,
170     )
171   except black.NothingChanged:
172     if not quiet:
173       print(f'Black: already well formatted, good job. (took {time.time() - start:.4f}s)')
174   except Exception as exc:
175     print(f'Black: {exc}')
176   else:
177     current_buffer = vim.current.window.buffer
178     cursors = []
179     for i, tabpage in enumerate(vim.tabpages):
180       if tabpage.valid:
181         for j, window in enumerate(tabpage.windows):
182           if window.valid and window.buffer == current_buffer:
183             cursors.append((i, j, window.cursor))
184     vim.current.buffer[:] = new_buffer_str.split('\n')[:-1]
185     for i, j, cursor in cursors:
186       window = vim.tabpages[i].windows[j]
187       try:
188         window.cursor = cursor
189       except vim.error:
190         window.cursor = (len(window.buffer), 0)
191     if not quiet:
192       print(f'Black: reformatted in {time.time() - start:.4f}s.')
193
194 def get_configs():
195   filename = vim.eval("@%")
196   path_pyproject_toml = black.find_pyproject_toml((filename,))
197   if path_pyproject_toml:
198     toml_config = black.parse_pyproject_toml(path_pyproject_toml)
199   else:
200     toml_config = {}
201
202   return {
203     flag.var_name: toml_config.get(flag.name, flag.cast(vim.eval(flag.vim_rc_name)))
204     for flag in FLAGS
205   }
206
207
208 def BlackUpgrade():
209   _initialize_black_env(upgrade=True)
210
211 def BlackVersion():
212   print(f'Black, version {black.__version__} on Python {sys.version}.')
213
214 EndPython3
215
216 function black#Black(...)
217     let kwargs = {}
218     for arg in a:000
219         let arg_list = split(arg, '=')
220         let kwargs[arg_list[0]] = arg_list[1]
221     endfor
222 python3 << EOF
223 import vim
224 kwargs = vim.eval("kwargs")
225 EOF
226   :py3 Black(**kwargs)
227 endfunction
228
229 function black#BlackUpgrade()
230   :py3 BlackUpgrade()
231 endfunction
232
233 function black#BlackVersion()
234   :py3 BlackVersion()
235 endfunction