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

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