options.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. #!/usr/bin/env python
  2. # License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
  3. from kitty.fast_data_types import Color, test_cursor_blink_easing_function
  4. from kitty.options.utils import DELETE_ENV_VAR, EasingFunction
  5. from kitty.utils import log_error
  6. from . import BaseTest
  7. class TestConfParsing(BaseTest):
  8. def setUp(self):
  9. super().setUp()
  10. self.error_messages = []
  11. log_error.redirect = self.error_messages.append
  12. def tearDown(self):
  13. del log_error.redirect
  14. super().tearDown()
  15. def test_conf_parsing(self):
  16. from kitty.config import defaults, load_config
  17. from kitty.constants import is_macos
  18. from kitty.fonts import FontModification, ModificationType, ModificationUnit, ModificationValue
  19. from kitty.options.utils import to_modifiers
  20. bad_lines = []
  21. def p(*lines, bad_line_num=0):
  22. del bad_lines[:]
  23. del self.error_messages[:]
  24. ans = load_config(overrides=lines, accumulate_bad_lines=bad_lines)
  25. if bad_line_num:
  26. self.ae(len(bad_lines), bad_line_num)
  27. else:
  28. self.assertFalse(bad_lines)
  29. return ans
  30. def keys_for_func(opts, name):
  31. for key, defns in opts.keyboard_modes[''].keymap.items():
  32. for action in opts.alias_map.resolve_aliases(defns[0].definition):
  33. if action.func == name:
  34. yield key
  35. opts = p('font_size 11.37', 'clear_all_shortcuts y', 'color23 red')
  36. self.ae(opts.font_size, 11.37)
  37. self.ae(opts.mouse_hide_wait, 0 if is_macos else 3)
  38. self.ae(opts.color23, Color(255, 0, 0))
  39. self.assertFalse(opts.keyboard_modes[''].keymap)
  40. opts = p('clear_all_shortcuts y', 'map f1 next_window')
  41. self.ae(len(opts.keyboard_modes[''].keymap), 1)
  42. opts = p('clear_all_mouse_actions y', 'mouse_map left click ungrabbed mouse_click_url_or_select')
  43. self.ae(len(opts.mousemap), 1)
  44. opts = p('strip_trailing_spaces always')
  45. self.ae(opts.strip_trailing_spaces, 'always')
  46. self.assertFalse(bad_lines)
  47. opts = p('pointer_shape_when_grabbed XXX', bad_line_num=1)
  48. self.ae(opts.pointer_shape_when_grabbed, defaults.pointer_shape_when_grabbed)
  49. opts = p('modify_font underline_position -2', 'modify_font underline_thickness 150%', 'modify_font size Test -1px')
  50. self.ae(opts.modify_font, {
  51. 'underline_position': FontModification(ModificationType.underline_position, ModificationValue(-2., ModificationUnit.pt)),
  52. 'underline_thickness': FontModification(ModificationType.underline_thickness, ModificationValue(150, ModificationUnit.percent)),
  53. 'size:Test': FontModification(ModificationType.size, ModificationValue(-1., ModificationUnit.pixel), 'Test'),
  54. })
  55. # test the aliasing options
  56. opts = p('env A=1', 'env B=x$A', 'env C=', 'env D', 'clear_all_shortcuts y', 'kitten_alias a b --moo', 'map f1 kitten a arg')
  57. self.ae(opts.env, {'A': '1', 'B': 'x1', 'C': '', 'D': DELETE_ENV_VAR})
  58. def ac(which=0):
  59. ka = tuple(opts.keyboard_modes[''].keymap.values())[0][0]
  60. acs = opts.alias_map.resolve_aliases(ka.definition)
  61. return acs[which]
  62. ka = ac()
  63. self.ae(ka.func, 'kitten')
  64. self.ae(ka.args, ('b', '--moo', 'arg'))
  65. opts = p('clear_all_shortcuts y', 'kitten_alias hints hints --hi', 'map f1 kitten hints XXX')
  66. ka = ac()
  67. self.ae(ka.func, 'kitten')
  68. self.ae(ka.args, ('hints', '--hi', 'XXX'))
  69. opts = p('clear_all_shortcuts y', 'action_alias la launch --moo', 'map f1 la XXX')
  70. ka = ac()
  71. self.ae(ka.func, 'launch')
  72. self.ae(ka.args, ('--moo', 'XXX'))
  73. opts = p('clear_all_shortcuts y', 'action_alias one launch --moo', 'action_alias two one recursive', 'map f1 two XXX')
  74. ka = ac()
  75. self.ae(ka.func, 'launch')
  76. self.ae(ka.args, ('--moo', 'recursive', 'XXX'))
  77. opts = p('clear_all_shortcuts y', 'action_alias launch two 1', 'action_alias two launch 2', 'map f1 launch 3')
  78. ka = ac()
  79. self.ae(ka.func, 'launch')
  80. self.ae(ka.args, ('2', '1', '3'))
  81. opts = p('clear_all_shortcuts y', 'action_alias launch launch --moo', 'map f1 launch XXX')
  82. ka = ac()
  83. self.ae(ka.func, 'launch')
  84. self.ae(ka.args, ('--moo', 'XXX'))
  85. opts = p('clear_all_shortcuts y', 'action_alias cfs change_font_size current', 'map f1 cfs +2')
  86. ka = ac()
  87. self.ae(ka.func, 'change_font_size')
  88. self.ae(ka.args, (False, '+', 2.0))
  89. opts = p('clear_all_shortcuts y', 'action_alias la launch --moo', 'map f1 combine : new_window : la ')
  90. self.ae((ac().func, ac(1).func), ('new_window', 'launch'))
  91. opts = p('clear_all_shortcuts y', 'action_alias cc combine : new_window : launch --moo', 'map f1 cc XXX')
  92. self.ae((ac().func, ac(1).func), ('new_window', 'launch'))
  93. self.ae(ac(1).args, ('--moo', 'XXX'))
  94. opts = p('kitty_mod alt')
  95. self.ae(opts.kitty_mod, to_modifiers('alt'))
  96. self.ae(next(keys_for_func(opts, 'next_layout')).mods, opts.kitty_mod)
  97. # deprecation handling
  98. opts = p('clear_all_shortcuts y', 'send_text all f1 hello')
  99. self.ae(len(opts.keyboard_modes[''].keymap), 1)
  100. opts = p('macos_hide_titlebar y' if is_macos else 'x11_hide_window_decorations y')
  101. self.assertTrue(opts.hide_window_decorations)
  102. self.ae(len(self.error_messages), 1)
  103. # line breaks
  104. opts = p(" font",
  105. " \t \t \\_size",
  106. " \\ 12",
  107. "\\.35",
  108. "col",
  109. "\\o",
  110. "\t \t\\r",
  111. "\\25",
  112. " \\ blue")
  113. self.ae(opts.font_size, 12.35)
  114. self.ae(opts.color25, Color(0, 0, 255))
  115. # cursor_blink_interval
  116. def cb(src, interval=-1, first=EasingFunction(), second=EasingFunction()):
  117. opts = p('cursor_blink_interval ' + src)
  118. self.ae((float(interval), first, second), (float(opts.cursor_blink_interval[0]), opts.cursor_blink_interval[1], opts.cursor_blink_interval[2]))
  119. cb('3', 3)
  120. cb('-2.3', -2.3)
  121. cb('linear', first=EasingFunction('cubic-bezier', cubic_bezier_points=(0, 0.0, 1.0, 1.0)))
  122. cb('linear 19', 19, EasingFunction('cubic-bezier', cubic_bezier_points=(0, 0.0, 1.0, 1.0)))
  123. cb('ease-in-out cubic-bezier(0.1, 0.2, 0.3, 0.4) 11', 11,
  124. EasingFunction('cubic-bezier', cubic_bezier_points=(0.42, 0, 0.58, 1)),
  125. EasingFunction('cubic-bezier', cubic_bezier_points=(0.1, 0.2, 0.3, 0.4))
  126. )
  127. cb('step-start', first=EasingFunction('steps', num_steps=1, jump_type='start'))
  128. cb('steps(7, jump-none)', first=EasingFunction('steps', num_steps=7, jump_type='none'))
  129. cb('linear(0, 0.25, 1)', first=EasingFunction('linear', linear_x=(0.0, 0.5, 1.0), linear_y=(0, 0.25, 1.0)))
  130. cb('linear(0, 0.25 75%, 1)', first=EasingFunction('linear', linear_x=(0.0, 0.75, 1.0), linear_y=(0, 0.25, 1.0)))
  131. cb('linear(0, 0.25 25% 75%, 1)', first=EasingFunction('linear', linear_x=(0.0, 0.25, 0.75, 1.0), linear_y=(0, 0.25, 0.25, 1.0)))
  132. # test that easing functions give expected values
  133. def ef(spec, tests, only_single=True, duration=0.5, accuracy=0):
  134. cfv = p('cursor_blink_interval ' + spec).cursor_blink_interval
  135. self.set_options({'cursor_blink_interval': cfv})
  136. for t, expected in tests.items():
  137. actual = test_cursor_blink_easing_function(t, only_single, duration)
  138. if abs(actual - expected) > accuracy:
  139. self.ae(expected, actual, f'Failed for {spec=} with {t=}: {expected} != {actual}')
  140. ef('linear', {0:1, 0.25: 0.5, 0.5: 0, 0.75: 0.5, 1: 1}, only_single=False)
  141. ef('linear(0, 0.25 25% 75%, 1)', {0: 0, 0.25: 0.25, 0.3: 0.25, 0.75: 0.25, 1:1})
  142. linear_vals = {0: 0, 1: 1, 0.1234: 0.1234, 0.6453: 0.6453}
  143. for spec in ('linear', 'linear(0, 1)', 'cubic-bezier(0, 0, 1, 1)', 'cubic-bezier(0.2, 0.2, 0.7, 0.7)'):
  144. ef(spec, linear_vals)
  145. # test an almost linear function to test cubic bezier implementation
  146. ef('cubic-bezier(0.2, 0.2, 0.7, 0.71)', linear_vals, accuracy=0.01)
  147. ef('cubic-bezier(0.23, 0.2, 0.7, 0.71)', linear_vals, accuracy=0.01)
  148. ef('steps(5)', {0: 0, 0.1: 0, 0.3: 0.2, 0.9:0.8})
  149. ef('steps(5, start)', {0: 0.2, 0.1: 0.2, 0.3: 0.4, 0.9:1})
  150. ef('steps(4, jump-both)', {0: 0.2, 0.1: 0.2, 0.3: 0.4, 0.9:1})
  151. ef('steps(6, jump-none)', {0: 0, 0.1: 0.0, 0.3: 0.2, 0.9:1})