]> git.madduck.net Git - etc/vim.git/blob - plugin/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 bug where black tries to split string on escaped space (#1799)
[etc/vim.git] / plugin / black.vim
1 " black.vim
2 " Author: Łukasz Langa
3 " Created: Mon Mar 26 23:27:53 2018 -0700
4 " Requires: Vim Ver7.0+
5 " Version:  1.1
6 "
7 " Documentation:
8 "   This plugin formats Python files.
9 "
10 " History:
11 "  1.0:
12 "    - initial version
13 "  1.1:
14 "    - restore cursor/window position after formatting
15
16 if v:version < 700 || !has('python3')
17     func! __BLACK_MISSING()
18         echo "The black.vim plugin requires vim7.0+ with Python 3.6 support."
19     endfunc
20     command! Black :call __BLACK_MISSING()
21     command! BlackUpgrade :call __BLACK_MISSING()
22     command! BlackVersion :call __BLACK_MISSING()
23     finish
24 endif
25
26 if exists("g:load_black")
27    finish
28 endif
29
30 let g:load_black = "py1.0"
31 if !exists("g:black_virtualenv")
32   if has("nvim")
33     let g:black_virtualenv = "~/.local/share/nvim/black"
34   else
35     let g:black_virtualenv = "~/.vim/black"
36   endif
37 endif
38 if !exists("g:black_fast")
39   let g:black_fast = 0
40 endif
41 if !exists("g:black_linelength")
42   let g:black_linelength = 88
43 endif
44 if !exists("g:black_string_normalization")
45   if exists("g:black_skip_string_normalization")
46     let g:black_string_normalization = !g:black_skip_string_normalization
47   else
48     let g:black_string_normalization = 1
49   endif
50 endif
51
52 python3 << EndPython3
53 import collections
54 import os
55 import sys
56 import vim
57 from distutils.util import strtobool
58
59
60 class Flag(collections.namedtuple("FlagBase", "name, cast")):
61   @property
62   def var_name(self):
63     return self.name.replace("-", "_")
64
65   @property
66   def vim_rc_name(self):
67     name = self.var_name
68     if name == "line_length":
69       name = name.replace("_", "")
70     return "g:black_" + name
71
72
73 FLAGS = [
74   Flag(name="line_length", cast=int),
75   Flag(name="fast", cast=strtobool),
76   Flag(name="string_normalization", cast=strtobool),
77 ]
78
79
80 def _get_python_binary(exec_prefix):
81   try:
82     default = vim.eval("g:pymode_python").strip()
83   except vim.error:
84     default = ""
85   if default and os.path.exists(default):
86     return default
87   if sys.platform[:3] == "win":
88     return exec_prefix / 'python.exe'
89   return exec_prefix / 'bin' / 'python3'
90
91 def _get_pip(venv_path):
92   if sys.platform[:3] == "win":
93     return venv_path / 'Scripts' / 'pip.exe'
94   return venv_path / 'bin' / 'pip'
95
96 def _get_virtualenv_site_packages(venv_path, pyver):
97   if sys.platform[:3] == "win":
98     return venv_path / 'Lib' / 'site-packages'
99   return venv_path / 'lib' / f'python{pyver[0]}.{pyver[1]}' / 'site-packages'
100
101 def _initialize_black_env(upgrade=False):
102   pyver = sys.version_info[:2]
103   if pyver < (3, 6):
104     print("Sorry, Black requires Python 3.6+ to run.")
105     return False
106
107   from pathlib import Path
108   import subprocess
109   import venv
110   virtualenv_path = Path(vim.eval("g:black_virtualenv")).expanduser()
111   virtualenv_site_packages = str(_get_virtualenv_site_packages(virtualenv_path, pyver))
112   first_install = False
113   if not virtualenv_path.is_dir():
114     print('Please wait, one time setup for Black.')
115     _executable = sys.executable
116     _base_executable = getattr(sys, "_base_executable", _executable)
117     try:
118       executable = str(_get_python_binary(Path(sys.exec_prefix)))
119       sys.executable = executable
120       sys._base_executable = executable
121       print(f'Creating a virtualenv in {virtualenv_path}...')
122       print('(this path can be customized in .vimrc by setting g:black_virtualenv)')
123       venv.create(virtualenv_path, with_pip=True)
124     except Exception:
125       print('Encountered exception while creating virtualenv (see traceback below).')
126       print(f'Removing {virtualenv_path}...')
127       import shutil
128       shutil.rmtree(virtualenv_path)
129       raise
130     finally:
131       sys.executable = _executable
132       sys._base_executable = _base_executable
133     first_install = True
134   if first_install:
135     print('Installing Black with pip...')
136   if upgrade:
137     print('Upgrading Black with pip...')
138   if first_install or upgrade:
139     subprocess.run([str(_get_pip(virtualenv_path)), 'install', '-U', 'black'], stdout=subprocess.PIPE)
140     print('DONE! You are all set, thanks for waiting ✨ 🍰 ✨')
141   if first_install:
142     print('Pro-tip: to upgrade Black in the future, use the :BlackUpgrade command and restart Vim.\n')
143   if virtualenv_site_packages not in sys.path:
144     sys.path.insert(0, virtualenv_site_packages)
145   return True
146
147 if _initialize_black_env():
148   import black
149   import time
150
151 def Black():
152   start = time.time()
153   configs = get_configs()
154   mode = black.FileMode(
155     line_length=configs["line_length"],
156     string_normalization=configs["string_normalization"],
157     is_pyi=vim.current.buffer.name.endswith('.pyi'),
158   )
159
160   buffer_str = '\n'.join(vim.current.buffer) + '\n'
161   try:
162     new_buffer_str = black.format_file_contents(
163       buffer_str,
164       fast=configs["fast"],
165       mode=mode,
166     )
167   except black.NothingChanged:
168     print(f'Already well formatted, good job. (took {time.time() - start:.4f}s)')
169   except Exception as exc:
170     print(exc)
171   else:
172     current_buffer = vim.current.window.buffer
173     cursors = []
174     for i, tabpage in enumerate(vim.tabpages):
175       if tabpage.valid:
176         for j, window in enumerate(tabpage.windows):
177           if window.valid and window.buffer == current_buffer:
178             cursors.append((i, j, window.cursor))
179     vim.current.buffer[:] = new_buffer_str.split('\n')[:-1]
180     for i, j, cursor in cursors:
181       window = vim.tabpages[i].windows[j]
182       try:
183         window.cursor = cursor
184       except vim.error:
185         window.cursor = (len(window.buffer), 0)
186     print(f'Reformatted in {time.time() - start:.4f}s.')
187
188 def get_configs():
189   path_pyproject_toml = black.find_pyproject_toml(vim.eval("fnamemodify(getcwd(), ':t')"))
190   if path_pyproject_toml:
191     toml_config = black.parse_pyproject_toml(path_pyproject_toml)
192   else:
193     toml_config = {}
194
195   return {
196     flag.var_name: flag.cast(toml_config.get(flag.name, vim.eval(flag.vim_rc_name)))
197     for flag in FLAGS
198   }
199
200
201 def BlackUpgrade():
202   _initialize_black_env(upgrade=True)
203
204 def BlackVersion():
205   print(f'Black, version {black.__version__} on Python {sys.version}.')
206
207 EndPython3
208
209 command! Black :py3 Black()
210 command! BlackUpgrade :py3 BlackUpgrade()
211 command! BlackVersion :py3 BlackVersion()