completion.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. #!/usr/bin/env python
  2. # License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>
  3. import json
  4. import os
  5. import shlex
  6. import subprocess
  7. import tempfile
  8. from kitty.constants import kitten_exe as kitten
  9. from . import BaseTest
  10. class TestCompletion(BaseTest):
  11. def test_completion(self):
  12. with tempfile.TemporaryDirectory() as tdir:
  13. completion(self, tdir)
  14. def get_all_words(result):
  15. all_words = set()
  16. for group in result.get('groups', ()):
  17. for m in group['matches']:
  18. all_words.add(m['word'])
  19. return all_words
  20. def has_words(*words):
  21. def t(self, result):
  22. q = set(words)
  23. missing = q - get_all_words(result)
  24. self.assertFalse(missing, f'Words missing. Command line: {self.current_cmd!r}')
  25. return t
  26. def does_not_have_words(*words):
  27. def t(self, result):
  28. q = set(words)
  29. all_words = get_all_words(result)
  30. self.assertFalse(q & all_words, f'Words unexpectedly present. Command line: {self.current_cmd!r}')
  31. return t
  32. def all_words(*words):
  33. def t(self, result):
  34. expected = set(words)
  35. actual = get_all_words(result)
  36. self.assertEqual(expected, actual, f'Command line: {self.current_cmd!r}')
  37. return t
  38. def is_delegate(num_to_remove: int = 0, command: str = ''):
  39. q = {}
  40. if num_to_remove:
  41. q['num_to_remove'] = num_to_remove
  42. if command:
  43. q['command'] = command
  44. def t(self, result):
  45. d = result['delegate']
  46. self.assertEqual(d, q, f'Command line: {self.current_cmd!r}')
  47. return t
  48. def completion(self: TestCompletion, tdir: str):
  49. all_cmds = []
  50. all_argv = []
  51. all_tests = []
  52. def add(cmdline: str, *tests):
  53. all_cmds.append(cmdline)
  54. new_word = cmdline.endswith(' ')
  55. if new_word:
  56. cmdline = cmdline[:-1]
  57. all_argv.append(shlex.split(cmdline))
  58. if new_word:
  59. all_argv[-1].append('')
  60. all_tests.append(tests)
  61. def run_tool():
  62. env = os.environ.copy()
  63. env['PATH'] = os.path.join(tdir, 'bin')
  64. env['HOME'] = os.path.join(tdir, 'sub')
  65. env['KITTY_CONFIG_DIRECTORY'] = os.path.join(tdir, 'sub')
  66. cp = subprocess.run(
  67. [kitten(), '__complete__', 'json'],
  68. check=True, stdout=subprocess.PIPE, cwd=tdir, input=json.dumps(all_argv).encode(), env=env
  69. )
  70. self.assertEqual(cp.returncode, 0, f'kitten __complete__ failed with exit code: {cp.returncode}')
  71. return json.loads(cp.stdout)
  72. add('kitty ', has_words('@', '+', '+open'))
  73. add('kitty @ l', has_words('ls', 'last-used-layout', 'launch'))
  74. def make_file(path, mode=None):
  75. with open(os.path.join(tdir, path), mode='x') as f:
  76. if mode is not None:
  77. os.chmod(f.fileno(), mode)
  78. os.mkdir(os.path.join(tdir, 'bin'))
  79. os.mkdir(os.path.join(tdir, 'sub'))
  80. make_file('bin/exe1', 0o700)
  81. make_file('bin/exe-not1')
  82. make_file('exe2', 0o700)
  83. make_file('exe-not2.jpeg')
  84. make_file('sub/exe3', 0o700)
  85. make_file('sub/exe-not3.png')
  86. add('kitty x', all_words())
  87. add('kitty e', all_words('exe1', 'exe2'))
  88. add('kitty ./', all_words('./bin/', './sub/', './exe2'))
  89. add('kitty ./e', all_words('./exe2'))
  90. add('kitty ./s', all_words('./sub/'))
  91. add('kitty ~', all_words('~/exe3'))
  92. add('kitty ~/', all_words('~/exe3'))
  93. add('kitty ~/e', all_words('~/exe3'))
  94. add('kitty @ goto-layout ', has_words('tall', 'fat'))
  95. add('kitty @ goto-layout spli', all_words('splits'))
  96. add('kitty @ goto-layout f f', all_words())
  97. add('kitty @ set-window-logo ', all_words('exe-not2.jpeg', 'sub/'))
  98. add('kitty @ set-window-logo e', all_words('exe-not2.jpeg'))
  99. add('kitty @ set-window-logo e e', all_words())
  100. add('kitty +ope', has_words('+open'))
  101. add('kitty +open -', has_words('-1', '-T'))
  102. add('kitty -', has_words('-c', '-1', '--'), does_not_have_words('--config', '--single-instance'))
  103. add('kitty -c', all_words('-c'))
  104. add('kitty --', has_words('--config', '--single-instance', '--'))
  105. add('kitty --s', has_words('--session', '--start-as'))
  106. add('kitty --start-as', all_words('--start-as'))
  107. add('kitty --start-as ', all_words('minimized', 'maximized', 'fullscreen', 'normal'))
  108. add('kitty -1 ', does_not_have_words('@ls', '@'))
  109. add('kitty --directory ', all_words('bin/', 'sub/'))
  110. add('kitty -1d ', all_words('exe1'))
  111. add('kitty -1d', all_words('-1d'))
  112. add('kitty -o a', has_words('allow_remote_control='))
  113. add('kitty --listen-on ', all_words('unix:', 'tcp:'))
  114. add('kitty --listen-on unix:b', all_words('unix:bin/'))
  115. add('kitty --directory=', all_words('--directory=bin/', '--directory=sub/'))
  116. add('kitty --start-as=m', all_words('--start-as=minimized', '--start-as=maximized'))
  117. add('kitty @launch --ty', has_words('--type'))
  118. add('kitty @launch --type ', has_words('window', 'background', 'overlay'))
  119. add('kitty @launch --cwd ', has_words('current', 'oldest', 'last_reported'))
  120. add('kitty @launch --logo ', all_words('exe-not3.png'))
  121. add('kitty @launch --logo ~', all_words('~/exe-not3.png'))
  122. add('kitten ', has_words('@'))
  123. add('kitten ', does_not_have_words('__complete__'))
  124. add('kitten @launch --ty', has_words('--type'))
  125. add('kitty + ', has_words('launch', 'kitten'))
  126. add('kitty + kitten ', has_words('icat', 'diff'))
  127. add('kitty +kitten icat ', has_words('sub/', 'exe-not2.jpeg'))
  128. add('kitty + kitten icat --pr', has_words('--print-window-size'))
  129. add('kitty + kitten diff ', has_words('exe-not2.jpeg'))
  130. add('kitty + kitten themes --', has_words('--cache-age'))
  131. add('kitty + kitten themes D', has_words('Default'))
  132. add('kitty + kitten hyperlinked_grep ', is_delegate(3, 'rg'))
  133. add('kitty +kitten hyperlinked_grep ', is_delegate(2, 'rg'))
  134. add('clone-in-kitty --ty', has_words('--type'))
  135. add('kitty bash ', is_delegate(1, 'bash'))
  136. add('kitty -1 bash ', is_delegate(2, 'bash'))
  137. add('kitty -1 bash --n', is_delegate(2, 'bash'))
  138. add('kitty @launch --type tab bash --n', is_delegate(4, 'bash'))
  139. add('kitty +kitten hyperlinked_grep --s', is_delegate(2, 'rg'))
  140. add('kitty @launch e', all_words('exe1', 'exe2'))
  141. for cmd, tests, result in zip(all_cmds, all_tests, run_tool()):
  142. self.current_cmd = cmd
  143. for test in tests:
  144. test(self, result)