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

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