]> 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:

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