marks.py 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. #!/usr/bin/env python
  2. # License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
  3. import re
  4. from ctypes import POINTER, c_uint, c_void_p, cast
  5. from typing import Callable, Generator, Iterable, Pattern, Sequence, Tuple, Union
  6. from .utils import resolve_custom_file
  7. pointer_to_uint = POINTER(c_uint)
  8. MarkerFunc = Callable[[str, int, int, int], Generator[None, None, None]]
  9. def get_output_variables(left_address: int, right_address: int, color_address: int) -> Tuple[c_uint, c_uint, c_uint]:
  10. return (
  11. cast(c_void_p(left_address), pointer_to_uint).contents,
  12. cast(c_void_p(right_address), pointer_to_uint).contents,
  13. cast(c_void_p(color_address), pointer_to_uint).contents,
  14. )
  15. def marker_from_regex(expression: Union[str, 'Pattern[str]'], color: int, flags: int = re.UNICODE) -> MarkerFunc:
  16. color = max(1, min(color, 3))
  17. if isinstance(expression, str):
  18. pat = re.compile(expression, flags=flags)
  19. else:
  20. pat = expression
  21. def marker(text: str, left_address: int, right_address: int, color_address: int) -> Generator[None, None, None]:
  22. left, right, colorv = get_output_variables(left_address, right_address, color_address)
  23. colorv.value = color
  24. for match in pat.finditer(text):
  25. left.value = match.start()
  26. right.value = match.end() - 1
  27. yield
  28. return marker
  29. def marker_from_multiple_regex(regexes: Iterable[Tuple[int, str]], flags: int = re.UNICODE) -> MarkerFunc:
  30. expr = ''
  31. color_map = {}
  32. for i, (color, spec) in enumerate(regexes):
  33. grp = f'mcg{i}'
  34. expr += f'|(?P<{grp}>{spec})'
  35. color_map[grp] = color
  36. expr = expr[1:]
  37. pat = re.compile(expr, flags=flags)
  38. def marker(text: str, left_address: int, right_address: int, color_address: int) -> Generator[None, None, None]:
  39. left, right, color = get_output_variables(left_address, right_address, color_address)
  40. for match in pat.finditer(text):
  41. left.value = match.start()
  42. right.value = match.end() - 1
  43. grp = match.lastgroup
  44. color.value = color_map[grp] if grp is not None else 0
  45. yield
  46. return marker
  47. def marker_from_text(expression: str, color: int) -> MarkerFunc:
  48. return marker_from_regex(re.escape(expression), color)
  49. def marker_from_function(func: Callable[[str], Iterable[Tuple[int, int, int]]]) -> MarkerFunc:
  50. def marker(text: str, left_address: int, right_address: int, color_address: int) -> Generator[None, None, None]:
  51. left, right, colorv = get_output_variables(left_address, right_address, color_address)
  52. for (ll, r, c) in func(text):
  53. left.value = ll
  54. right.value = r
  55. colorv.value = c
  56. yield
  57. return marker
  58. def marker_from_spec(ftype: str, spec: Union[str, Sequence[Tuple[int, str]]], flags: int) -> MarkerFunc:
  59. if ftype == 'regex':
  60. assert not isinstance(spec, str)
  61. if len(spec) == 1:
  62. return marker_from_regex(spec[0][1], spec[0][0], flags=flags)
  63. return marker_from_multiple_regex(spec, flags=flags)
  64. if ftype == 'function':
  65. import runpy
  66. assert isinstance(spec, str)
  67. path = resolve_custom_file(spec)
  68. return marker_from_function(runpy.run_path(path, run_name='__marker__')["marker"])
  69. raise ValueError(f'Unknown marker type: {ftype}')