Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33ff8d9726 | ||
|
|
f0b2af9a9f | ||
|
|
fc21a144aa | ||
|
|
973b4c3cf8 | ||
|
|
bd60977438 | ||
|
|
144bbb9daf | ||
|
|
7c16cd966e | ||
|
|
dd19b26fa2 | ||
|
|
dca410dd44 | ||
|
|
ed51b6e6dc | ||
|
|
18b5e258f6 | ||
|
|
e7108f843b | ||
|
|
ff8d3f10fb | ||
|
|
8f603b8e14 |
@@ -87,7 +87,7 @@ this opens the file, displays it, and can be edited and can save! unknown keys
|
|||||||
are displayed as errors in the status bar. babi will scroll if the cursor
|
are displayed as errors in the status bar. babi will scroll if the cursor
|
||||||
goes off screen either from resize events or from movement. babi can edit
|
goes off screen either from resize events or from movement. babi can edit
|
||||||
multiple files. babi has a command mode (so you can quit it like vim
|
multiple files. babi has a command mode (so you can quit it like vim
|
||||||
<kbd>:q</kbd>!). babi also support syntax highlighting
|
<kbd>:q</kbd>!). babi also supports syntax highlighting
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ resources:
|
|||||||
type: github
|
type: github
|
||||||
endpoint: github
|
endpoint: github
|
||||||
name: asottile/azure-pipeline-templates
|
name: asottile/azure-pipeline-templates
|
||||||
ref: refs/tags/v1.0.0
|
ref: refs/tags/v2.0.0
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
- template: job--pre-commit.yml@asottile
|
- template: job--pre-commit.yml@asottile
|
||||||
- template: job--python-tox.yml@asottile
|
- template: job--python-tox.yml@asottile
|
||||||
parameters:
|
parameters:
|
||||||
toxenvs: [py36, py37, py38]
|
toxenvs: [pypy3, py36, py37, py38, py39]
|
||||||
os: linux
|
os: linux
|
||||||
|
|||||||
17
babi/buf.py
17
babi/buf.py
@@ -19,11 +19,11 @@ DelCallback = Callable[['Buf', int, str], None]
|
|||||||
InsCallback = Callable[['Buf', int], None]
|
InsCallback = Callable[['Buf', int], None]
|
||||||
|
|
||||||
|
|
||||||
def _offsets(s: str) -> Tuple[int, ...]:
|
def _offsets(s: str, tab_size: int) -> Tuple[int, ...]:
|
||||||
ret = [0]
|
ret = [0]
|
||||||
for c in s:
|
for c in s:
|
||||||
if c == '\t':
|
if c == '\t':
|
||||||
ret.append(ret[-1] + (4 - ret[-1] % 4))
|
ret.append(ret[-1] + (tab_size - ret[-1] % tab_size))
|
||||||
else:
|
else:
|
||||||
ret.append(ret[-1] + wcwidth(c))
|
ret.append(ret[-1] + wcwidth(c))
|
||||||
return tuple(ret)
|
return tuple(ret)
|
||||||
@@ -57,8 +57,9 @@ class DelModification(NamedTuple):
|
|||||||
|
|
||||||
|
|
||||||
class Buf:
|
class Buf:
|
||||||
def __init__(self, lines: List[str]) -> None:
|
def __init__(self, lines: List[str], tab_size: int = 4) -> None:
|
||||||
self._lines = lines
|
self._lines = lines
|
||||||
|
self.tab_size = tab_size
|
||||||
self.file_y = self.y = self._x = self._x_hint = 0
|
self.file_y = self.y = self._x = self._x_hint = 0
|
||||||
|
|
||||||
self._set_callbacks: List[SetCallback] = [self._set_cb]
|
self._set_callbacks: List[SetCallback] = [self._set_cb]
|
||||||
@@ -136,6 +137,10 @@ class Buf:
|
|||||||
if self[-1] != '':
|
if self[-1] != '':
|
||||||
self.append('')
|
self.append('')
|
||||||
|
|
||||||
|
def set_tab_size(self, tab_size: int) -> None:
|
||||||
|
self.tab_size = tab_size
|
||||||
|
self._positions = [None]
|
||||||
|
|
||||||
# event handling
|
# event handling
|
||||||
|
|
||||||
def add_set_callback(self, cb: SetCallback) -> None:
|
def add_set_callback(self, cb: SetCallback) -> None:
|
||||||
@@ -219,7 +224,8 @@ class Buf:
|
|||||||
self._extend_positions(idx)
|
self._extend_positions(idx)
|
||||||
value = self._positions[idx]
|
value = self._positions[idx]
|
||||||
if value is None:
|
if value is None:
|
||||||
value = self._positions[idx] = _offsets(self._lines[idx])
|
value = _offsets(self._lines[idx], self.tab_size)
|
||||||
|
self._positions[idx] = value
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def line_x(self, margin: Margin) -> int:
|
def line_x(self, margin: Margin) -> int:
|
||||||
@@ -238,7 +244,8 @@ class Buf:
|
|||||||
|
|
||||||
def rendered_line(self, idx: int, margin: Margin) -> str:
|
def rendered_line(self, idx: int, margin: Margin) -> str:
|
||||||
x = self._cursor_x if idx == self.y else 0
|
x = self._cursor_x if idx == self.y else 0
|
||||||
return scrolled_line(self._lines[idx].expandtabs(4), x, margin.cols)
|
expanded = self._lines[idx].expandtabs(self.tab_size)
|
||||||
|
return scrolled_line(expanded, x, margin.cols)
|
||||||
|
|
||||||
# movement
|
# movement
|
||||||
|
|
||||||
|
|||||||
15
babi/file.py
15
babi/file.py
@@ -244,7 +244,7 @@ class File:
|
|||||||
status.update('(new file)')
|
status.update('(new file)')
|
||||||
lines, self.nl, mixed, self.sha256 = get_lines(io.StringIO(''))
|
lines, self.nl, mixed, self.sha256 = get_lines(io.StringIO(''))
|
||||||
|
|
||||||
self.buf = Buf(lines)
|
self.buf = Buf(lines, self.buf.tab_size)
|
||||||
|
|
||||||
if mixed:
|
if mixed:
|
||||||
status.update(f'mixed newlines will be converted to {self.nl!r}')
|
status.update(f'mixed newlines will be converted to {self.nl!r}')
|
||||||
@@ -523,16 +523,16 @@ class File:
|
|||||||
(s_y, _), (e_y, _) = self.selection.get()
|
(s_y, _), (e_y, _) = self.selection.get()
|
||||||
for l_y in range(s_y, e_y + 1):
|
for l_y in range(s_y, e_y + 1):
|
||||||
if self.buf[l_y]:
|
if self.buf[l_y]:
|
||||||
self.buf[l_y] = ' ' * 4 + self.buf[l_y]
|
self.buf[l_y] = ' ' * self.buf.tab_size + self.buf[l_y]
|
||||||
if l_y == self.buf.y:
|
if l_y == self.buf.y:
|
||||||
self.buf.x += 4
|
self.buf.x += self.buf.tab_size
|
||||||
if l_y == sel_y and sel_x != 0:
|
if l_y == sel_y and sel_x != 0:
|
||||||
sel_x += 4
|
sel_x += self.buf.tab_size
|
||||||
self.selection.set(sel_y, sel_x, self.buf.y, self.buf.x)
|
self.selection.set(sel_y, sel_x, self.buf.y, self.buf.x)
|
||||||
|
|
||||||
@edit_action('insert tab', final=False)
|
@edit_action('insert tab', final=False)
|
||||||
def _tab(self, margin: Margin) -> None:
|
def _tab(self, margin: Margin) -> None:
|
||||||
n = 4 - self.buf.x % 4
|
n = self.buf.tab_size - self.buf.x % self.buf.tab_size
|
||||||
line = self.buf[self.buf.y]
|
line = self.buf[self.buf.y]
|
||||||
self.buf[self.buf.y] = line[:self.buf.x] + n * ' ' + line[self.buf.x:]
|
self.buf[self.buf.y] = line[:self.buf.x] + n * ' ' + line[self.buf.x:]
|
||||||
self.buf.x += n
|
self.buf.x += n
|
||||||
@@ -544,9 +544,8 @@ class File:
|
|||||||
else:
|
else:
|
||||||
self._tab(margin)
|
self._tab(margin)
|
||||||
|
|
||||||
@staticmethod
|
def _dedent_line(self, s: str) -> int:
|
||||||
def _dedent_line(s: str) -> int:
|
bound = min(len(s), self.buf.tab_size)
|
||||||
bound = min(len(s), 4)
|
|
||||||
i = 0
|
i = 0
|
||||||
while i < bound and s[i] == ' ':
|
while i < bound and s[i] == ' ':
|
||||||
i += 1
|
i += 1
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import argparse
|
|||||||
import curses
|
import curses
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import signal
|
||||||
import sys
|
import sys
|
||||||
from typing import List
|
from typing import List
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
@@ -138,6 +139,11 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|||||||
else:
|
else:
|
||||||
stdin = ''
|
stdin = ''
|
||||||
|
|
||||||
|
# ignore backgrounding signals, we'll handle those in curses
|
||||||
|
# fixes a problem with ^Z on termination which would break the terminal
|
||||||
|
if sys.platform != 'win32': # pragma: win32 no cover # pragma: no branch
|
||||||
|
signal.signal(signal.SIGTSTP, signal.SIG_IGN)
|
||||||
|
|
||||||
with perf_log(args.perf_log) as perf, make_stdscr() as stdscr:
|
with perf_log(args.perf_log) as perf, make_stdscr() as stdscr:
|
||||||
if args.key_debug:
|
if args.key_debug:
|
||||||
return _key_debug(stdscr, perf)
|
return _key_debug(stdscr, perf)
|
||||||
|
|||||||
117
babi/reg.py
117
babi/reg.py
@@ -6,80 +6,36 @@ from typing import Tuple
|
|||||||
|
|
||||||
import onigurumacffi
|
import onigurumacffi
|
||||||
|
|
||||||
from babi.cached_property import cached_property
|
|
||||||
|
|
||||||
_BACKREF_RE = re.compile(r'((?<!\\)(?:\\\\)*)\\([0-9]+)')
|
_BACKREF_RE = re.compile(r'((?<!\\)(?:\\\\)*)\\([0-9]+)')
|
||||||
|
|
||||||
|
|
||||||
def _replace_esc(s: str, chars: str) -> str:
|
_FLAGS = {
|
||||||
"""replace the given escape sequences of `chars` with \\uffff"""
|
# (first_line, boundary)
|
||||||
for c in chars:
|
(False, False): (
|
||||||
if f'\\{c}' in s:
|
onigurumacffi.OnigSearchOption.NOT_END_STRING |
|
||||||
break
|
onigurumacffi.OnigSearchOption.NOT_BEGIN_STRING |
|
||||||
else:
|
onigurumacffi.OnigSearchOption.NOT_BEGIN_POSITION
|
||||||
return s
|
),
|
||||||
|
(False, True): (
|
||||||
b = []
|
onigurumacffi.OnigSearchOption.NOT_END_STRING |
|
||||||
i = 0
|
onigurumacffi.OnigSearchOption.NOT_BEGIN_STRING
|
||||||
length = len(s)
|
),
|
||||||
while i < length:
|
(True, False): (
|
||||||
try:
|
onigurumacffi.OnigSearchOption.NOT_END_STRING |
|
||||||
sbi = s.index('\\', i)
|
onigurumacffi.OnigSearchOption.NOT_BEGIN_POSITION
|
||||||
except ValueError:
|
),
|
||||||
b.append(s[i:])
|
(True, True): onigurumacffi.OnigSearchOption.NOT_END_STRING,
|
||||||
break
|
}
|
||||||
if sbi > i:
|
|
||||||
b.append(s[i:sbi])
|
|
||||||
b.append('\\')
|
|
||||||
i = sbi + 1
|
|
||||||
if i < length:
|
|
||||||
if s[i] in chars:
|
|
||||||
b.append('\uffff')
|
|
||||||
else:
|
|
||||||
b.append(s[i])
|
|
||||||
i += 1
|
|
||||||
return ''.join(b)
|
|
||||||
|
|
||||||
|
|
||||||
class _Reg:
|
class _Reg:
|
||||||
def __init__(self, s: str) -> None:
|
def __init__(self, s: str) -> None:
|
||||||
self._pattern = s
|
self._pattern = s
|
||||||
|
self._reg = onigurumacffi.compile(self._pattern)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'{type(self).__name__}({self._pattern!r})'
|
return f'{type(self).__name__}({self._pattern!r})'
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def _reg(self) -> onigurumacffi._Pattern:
|
|
||||||
return onigurumacffi.compile(_replace_esc(self._pattern, 'z'))
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def _reg_no_A(self) -> onigurumacffi._Pattern:
|
|
||||||
return onigurumacffi.compile(_replace_esc(self._pattern, 'Az'))
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def _reg_no_G(self) -> onigurumacffi._Pattern:
|
|
||||||
return onigurumacffi.compile(_replace_esc(self._pattern, 'Gz'))
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def _reg_no_A_no_G(self) -> onigurumacffi._Pattern:
|
|
||||||
return onigurumacffi.compile(_replace_esc(self._pattern, 'AGz'))
|
|
||||||
|
|
||||||
def _get_reg(
|
|
||||||
self,
|
|
||||||
first_line: bool,
|
|
||||||
boundary: bool,
|
|
||||||
) -> onigurumacffi._Pattern:
|
|
||||||
if boundary:
|
|
||||||
if first_line:
|
|
||||||
return self._reg
|
|
||||||
else:
|
|
||||||
return self._reg_no_A
|
|
||||||
else:
|
|
||||||
if first_line:
|
|
||||||
return self._reg_no_G
|
|
||||||
else:
|
|
||||||
return self._reg_no_A_no_G
|
|
||||||
|
|
||||||
def search(
|
def search(
|
||||||
self,
|
self,
|
||||||
line: str,
|
line: str,
|
||||||
@@ -87,7 +43,7 @@ class _Reg:
|
|||||||
first_line: bool,
|
first_line: bool,
|
||||||
boundary: bool,
|
boundary: bool,
|
||||||
) -> Optional[Match[str]]:
|
) -> Optional[Match[str]]:
|
||||||
return self._get_reg(first_line, boundary).search(line, pos)
|
return self._reg.search(line, pos, flags=_FLAGS[first_line, boundary])
|
||||||
|
|
||||||
def match(
|
def match(
|
||||||
self,
|
self,
|
||||||
@@ -96,36 +52,18 @@ class _Reg:
|
|||||||
first_line: bool,
|
first_line: bool,
|
||||||
boundary: bool,
|
boundary: bool,
|
||||||
) -> Optional[Match[str]]:
|
) -> Optional[Match[str]]:
|
||||||
return self._get_reg(first_line, boundary).match(line, pos)
|
return self._reg.match(line, pos, flags=_FLAGS[first_line, boundary])
|
||||||
|
|
||||||
|
|
||||||
class _RegSet:
|
class _RegSet:
|
||||||
def __init__(self, *s: str) -> None:
|
def __init__(self, *s: str) -> None:
|
||||||
self._patterns = s
|
self._patterns = s
|
||||||
|
self._set = onigurumacffi.compile_regset(*self._patterns)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
args = ', '.join(repr(s) for s in self._patterns)
|
args = ', '.join(repr(s) for s in self._patterns)
|
||||||
return f'{type(self).__name__}({args})'
|
return f'{type(self).__name__}({args})'
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def _set(self) -> onigurumacffi._RegSet:
|
|
||||||
return onigurumacffi.compile_regset(*self._patterns)
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def _set_no_A(self) -> onigurumacffi._RegSet:
|
|
||||||
patterns = (_replace_esc(p, 'A') for p in self._patterns)
|
|
||||||
return onigurumacffi.compile_regset(*patterns)
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def _set_no_G(self) -> onigurumacffi._RegSet:
|
|
||||||
patterns = (_replace_esc(p, 'G') for p in self._patterns)
|
|
||||||
return onigurumacffi.compile_regset(*patterns)
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def _set_no_A_no_G(self) -> onigurumacffi._RegSet:
|
|
||||||
patterns = (_replace_esc(p, 'AG') for p in self._patterns)
|
|
||||||
return onigurumacffi.compile_regset(*patterns)
|
|
||||||
|
|
||||||
def search(
|
def search(
|
||||||
self,
|
self,
|
||||||
line: str,
|
line: str,
|
||||||
@@ -133,16 +71,7 @@ class _RegSet:
|
|||||||
first_line: bool,
|
first_line: bool,
|
||||||
boundary: bool,
|
boundary: bool,
|
||||||
) -> Tuple[int, Optional[Match[str]]]:
|
) -> Tuple[int, Optional[Match[str]]]:
|
||||||
if boundary:
|
return self._set.search(line, pos, flags=_FLAGS[first_line, boundary])
|
||||||
if first_line:
|
|
||||||
return self._set.search(line, pos)
|
|
||||||
else:
|
|
||||||
return self._set_no_A.search(line, pos)
|
|
||||||
else:
|
|
||||||
if first_line:
|
|
||||||
return self._set_no_G.search(line, pos)
|
|
||||||
else:
|
|
||||||
return self._set_no_A_no_G.search(line, pos)
|
|
||||||
|
|
||||||
|
|
||||||
def expand_escaped(match: Match[str], s: str) -> str:
|
def expand_escaped(match: Match[str], s: str) -> str:
|
||||||
@@ -151,4 +80,4 @@ def expand_escaped(match: Match[str], s: str) -> str:
|
|||||||
|
|
||||||
make_reg = functools.lru_cache(maxsize=None)(_Reg)
|
make_reg = functools.lru_cache(maxsize=None)(_Reg)
|
||||||
make_regset = functools.lru_cache(maxsize=None)(_RegSet)
|
make_regset = functools.lru_cache(maxsize=None)(_RegSet)
|
||||||
ERR_REG = make_reg(')this pattern always triggers an error when used(')
|
ERR_REG = make_reg('$ ^')
|
||||||
|
|||||||
@@ -421,7 +421,9 @@ class Screen:
|
|||||||
|
|
||||||
def command(self) -> Optional[EditResult]:
|
def command(self) -> Optional[EditResult]:
|
||||||
response = self.prompt('', history='command')
|
response = self.prompt('', history='command')
|
||||||
if response == ':q':
|
if response is PromptResult.CANCELLED:
|
||||||
|
pass
|
||||||
|
elif response == ':q':
|
||||||
return self.quit_save_modified()
|
return self.quit_save_modified()
|
||||||
elif response == ':q!':
|
elif response == ':q!':
|
||||||
return EditResult.EXIT
|
return EditResult.EXIT
|
||||||
@@ -442,7 +444,20 @@ class Screen:
|
|||||||
else:
|
else:
|
||||||
self.file.sort(self.margin, reverse=True)
|
self.file.sort(self.margin, reverse=True)
|
||||||
self.status.update('sorted!')
|
self.status.update('sorted!')
|
||||||
elif response is not PromptResult.CANCELLED:
|
elif response.startswith((':tabstop ', ':tabsize ')):
|
||||||
|
_, _, tab_size = response.partition(' ')
|
||||||
|
try:
|
||||||
|
parsed_tab_size = int(tab_size)
|
||||||
|
except ValueError:
|
||||||
|
self.status.update(f'invalid size: {tab_size}')
|
||||||
|
else:
|
||||||
|
if parsed_tab_size <= 0:
|
||||||
|
self.status.update(f'invalid size: {parsed_tab_size}')
|
||||||
|
else:
|
||||||
|
for file in self.files:
|
||||||
|
file.buf.set_tab_size(parsed_tab_size)
|
||||||
|
self.status.update('updated!')
|
||||||
|
else:
|
||||||
self.status.update(f'invalid command: {response}')
|
self.status.update(f'invalid command: {response}')
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = babi
|
name = babi
|
||||||
version = 0.0.10
|
version = 0.0.12
|
||||||
description = a text editor
|
description = a text editor
|
||||||
long_description = file: README.md
|
long_description = file: README.md
|
||||||
long_description_content_type = text/markdown
|
long_description_content_type = text/markdown
|
||||||
@@ -24,7 +24,7 @@ packages = find:
|
|||||||
install_requires =
|
install_requires =
|
||||||
babi-grammars
|
babi-grammars
|
||||||
identify
|
identify
|
||||||
onigurumacffi>=0.0.10
|
onigurumacffi>=0.0.18
|
||||||
importlib_metadata>=1;python_version<"3.8"
|
importlib_metadata>=1;python_version<"3.8"
|
||||||
windows-curses;sys_platform=="win32"
|
windows-curses;sys_platform=="win32"
|
||||||
python_requires = >=3.6.1
|
python_requires = >=3.6.1
|
||||||
|
|||||||
@@ -391,6 +391,7 @@ class DeferredRunner:
|
|||||||
|
|
||||||
_curses_cbreak = _curses_endwin = _curses_noecho = _curses__noop
|
_curses_cbreak = _curses_endwin = _curses_noecho = _curses__noop
|
||||||
_curses_nonl = _curses_raw = _curses_use_default_colors = _curses__noop
|
_curses_nonl = _curses_raw = _curses_use_default_colors = _curses__noop
|
||||||
|
_curses_set_escdelay = _curses__noop
|
||||||
|
|
||||||
_curses_error = curses.error # so we don't mock the exception
|
_curses_error = curses.error # so we don't mock the exception
|
||||||
|
|
||||||
|
|||||||
30
tests/features/tabsize_test.py
Normal file
30
tests/features/tabsize_test.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from testing.runner import and_exit
|
||||||
|
from testing.runner import trigger_command_mode
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('setting', ('tabsize', 'tabstop'))
|
||||||
|
def test_set_tabstop(run, setting):
|
||||||
|
with run() as h, and_exit(h):
|
||||||
|
h.press('a')
|
||||||
|
h.press('Left')
|
||||||
|
trigger_command_mode(h)
|
||||||
|
h.press_and_enter(f':{setting} 2')
|
||||||
|
h.await_text('updated!')
|
||||||
|
h.press('Tab')
|
||||||
|
h.await_text('\n a')
|
||||||
|
h.await_cursor_position(x=2, y=1)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('tabsize', ('-1', '0', 'wat'))
|
||||||
|
def test_set_invalid_tabstop(run, tabsize):
|
||||||
|
with run() as h, and_exit(h):
|
||||||
|
h.press('a')
|
||||||
|
h.press('Left')
|
||||||
|
trigger_command_mode(h)
|
||||||
|
h.press_and_enter(f':tabstop {tabsize}')
|
||||||
|
h.await_text(f'invalid size: {tabsize}')
|
||||||
|
h.press('Tab')
|
||||||
|
h.await_text(' a')
|
||||||
|
h.await_cursor_position(x=4, y=1)
|
||||||
@@ -35,9 +35,8 @@ def test_reg_other_escapes_left_untouched():
|
|||||||
def test_reg_not_out_of_bounds_at_end():
|
def test_reg_not_out_of_bounds_at_end():
|
||||||
# the only way this is triggerable is with an illegal regex, we'd rather
|
# the only way this is triggerable is with an illegal regex, we'd rather
|
||||||
# produce an error about the regex being wrong than an IndexError
|
# produce an error about the regex being wrong than an IndexError
|
||||||
reg = _Reg('\\A\\')
|
|
||||||
with pytest.raises(onigurumacffi.OnigError) as excinfo:
|
with pytest.raises(onigurumacffi.OnigError) as excinfo:
|
||||||
reg.search('\\', 0, first_line=False, boundary=False)
|
_Reg('\\A\\')
|
||||||
msg, = excinfo.value.args
|
msg, = excinfo.value.args
|
||||||
assert msg == 'end pattern at escape'
|
assert msg == 'end pattern at escape'
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user