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

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