PageRenderTime 50ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/xonsh/dotxonsh/completers/git.py

https://github.com/mitnk/dotfiles
Python | 228 lines | 225 code | 3 blank | 0 comment | 2 complexity | 6bcc8640df215a0573bad38a46888217 MD5 | raw file
  1. import glob
  2. import os.path
  3. import re
  4. import subprocess
  5. from dotxonsh.completers import git_helper
  6. from dotxonsh.completers.tools import get_complete_set
  7. _USE_PRECOMPILED = True
  8. _CACHE_GIT_ = {}
  9. def _get_cmd_output_with_re(cmd, regex):
  10. try:
  11. proc = subprocess.Popen(
  12. cmd,
  13. stdout=subprocess.PIPE,
  14. stderr=subprocess.DEVNULL,
  15. )
  16. output = subprocess.check_output(
  17. ['grep', '-Eo', regex],
  18. stdin=proc.stdout,
  19. stderr=subprocess.DEVNULL,
  20. )
  21. proc.wait()
  22. except subprocess.CalledProcessError:
  23. output = b''
  24. return output.decode()
  25. def _get_git_alias_info():
  26. # NOTE: We do not want to precompile alias
  27. if 'alias_info' in _CACHE_GIT_:
  28. return _CACHE_GIT_['alias_info']
  29. info = {}
  30. output = subprocess.check_output(
  31. ('git', 'config', '--get-regexp', 'alias'),
  32. )
  33. for line in output.decode().split('\n'):
  34. if not line.strip():
  35. continue
  36. l, r = line.split(' ', 1)
  37. info[l.replace('alias.', '')] = r
  38. _CACHE_GIT_['alias_info'] = info
  39. return info
  40. def _get_alias_list():
  41. return list(_get_git_alias_info().keys())
  42. def _get_command_list():
  43. if _USE_PRECOMPILED:
  44. return git_helper.GIT_COMMAND_LIST
  45. if 'command_list' in _CACHE_GIT_:
  46. return _CACHE_GIT_['command_list']
  47. cmd = ['git', 'help', '-a']
  48. output = _get_cmd_output_with_re(cmd, "'[^']+git-core'")
  49. output = output.replace("'", '').strip()
  50. p = os.path.join(output, 'git-*')
  51. cmd_list = glob.glob(p)
  52. cmd_list = [re.sub(r'.*/git-', '', x) for x in cmd_list]
  53. _CACHE_GIT_['command_list'] = cmd_list
  54. return cmd_list
  55. def _get_git_dashes():
  56. if _USE_PRECOMPILED:
  57. return git_helper.GIT_DASHES
  58. if 'git_dashes' in _CACHE_GIT_:
  59. return _CACHE_GIT_['git_dashes']
  60. cmd = ['git', 'help']
  61. output = _get_cmd_output_with_re(cmd, '\[\-\-?[^][<|]+')
  62. output = output.replace(' ', '')
  63. dashes = [re.sub(r'=.*', '=', x[1:]) for x in output.split() if x.strip()]
  64. _CACHE_GIT_['git_dashes'] = dashes
  65. return dashes
  66. def _get_command_dashes():
  67. if _USE_PRECOMPILED:
  68. return git_helper.GIT_COMMAND_DASHES
  69. if 'command_dashes' in _CACHE_GIT_:
  70. return _CACHE_GIT_['command_dashes']
  71. result = {}
  72. for cmd in _get_command_list():
  73. _cmd = ['git', 'help', cmd]
  74. output = _get_cmd_output_with_re(
  75. _cmd, '^ {1,}\-\-?[a-zA-Z-]+[a-zA-Z]*=?')
  76. output = output.replace(' ', '')
  77. tokens = [x for x in output.split('\n') if x.strip()]
  78. result[cmd] = set([x for x in tokens if x.strip('-')])
  79. alias_info = _get_git_alias_info()
  80. for alias in _get_alias_list():
  81. cmd = alias_info[alias]
  82. if ' ' in cmd or cmd[0] == '!':
  83. continue
  84. result[alias] = result.get(cmd, None)
  85. _CACHE_GIT_['command_dashes'] = result
  86. return result
  87. def _get_completes_subcommand(prefix):
  88. result = _get_command_list() + _get_alias_list()
  89. prefix = prefix.strip()
  90. if not prefix:
  91. return get_complete_set(result)
  92. else:
  93. return get_complete_set([x for x in result if x.startswith(prefix)])
  94. def _get_completes_git_dashes(prefix):
  95. prefix = prefix.strip()
  96. git_dashes = _get_git_dashes()
  97. if not prefix:
  98. return get_complete_set(git_dashes)
  99. else:
  100. return get_complete_set(
  101. [x for x in git_dashes if x.startswith(prefix)]
  102. )
  103. def _get_completes_subcommand_dashes(cmd, prefix):
  104. command_dashes = _get_command_dashes()
  105. if cmd not in command_dashes or not command_dashes[cmd]:
  106. return {''}
  107. result = command_dashes[cmd]
  108. if not prefix:
  109. return get_complete_set(result)
  110. else:
  111. return get_complete_set([x for x in result if x.startswith(prefix)])
  112. def _get_remote_list():
  113. cmd = ['git', 'remote']
  114. output = _get_cmd_output_with_re(cmd, '.*')
  115. return [x for x in output.split() if x]
  116. def _get_branches():
  117. cmd = ['git', 'branch', '-a']
  118. output = _get_cmd_output_with_re(cmd, '\/?[a-zA-Z0-9_-]+$')
  119. return [x.strip('/') for x in output.split() if x]
  120. def _get_subcommand_subcommands():
  121. if _USE_PRECOMPILED:
  122. return git_helper.GIT_SUBCOMMAND_SUBCOMMANDS
  123. if 'subcommand_subcommands' in _CACHE_GIT_:
  124. return _CACHE_GIT_['subcommand_subcommands']
  125. info = {}
  126. cmd = ['git', 'remote', '-h']
  127. output = _get_cmd_output_with_re(cmd, 'git remote [a-z-]+')
  128. output = re.sub('git remote ', '', output)
  129. tokens = list(set([x.strip() for x in output.split('\n') if x.strip()]))
  130. info['remote'] = tokens
  131. _CACHE_GIT_['subcommand_subcommands'] = info
  132. return info
  133. def _get_completes_subcommand_subcommand(cmd, prefix):
  134. if cmd in ['pull', 'push', 'fetch', 'getpr']:
  135. result = _get_remote_list()
  136. elif cmd in ['checkout', 'co', 'branch', 'br', 'merge']:
  137. result = _get_branches()
  138. elif cmd not in _get_subcommand_subcommands():
  139. return
  140. else:
  141. result = _get_subcommand_subcommands()[cmd]
  142. if not prefix:
  143. return get_complete_set(result)
  144. else:
  145. return get_complete_set([x for x in result if x.startswith(prefix)])
  146. def _get_completes_branches(remote, prefix):
  147. result = _get_branches()
  148. if not prefix:
  149. return get_complete_set(result)
  150. else:
  151. return get_complete_set([x for x in result if x.startswith(prefix)])
  152. def complete(prefix, line, start, end, ctx):
  153. """Complete git."""
  154. result = re.search(r'^ *git +([a-z]*)$', line)
  155. if result:
  156. prefix = result.group(1)
  157. return _get_completes_subcommand(prefix)
  158. result = re.search(r'^ *git +(--?[a-z]*)$', line)
  159. if result:
  160. prefix = result.group(1)
  161. return _get_completes_git_dashes(prefix)
  162. result = re.search(r'^ *git +([a-z]+) +(--?[^ ]*)$', line)
  163. if result:
  164. cmd = result.group(1)
  165. prefix = result.group(2)
  166. return _get_completes_subcommand_dashes(cmd, prefix)
  167. result = re.search(r'^ *git +([a-z]+)( +\-.+)? +([a-zA-Z0-9_-]*)$', line)
  168. if result:
  169. groups = result.groups()
  170. cmd = groups[0]
  171. if groups[-1].strip().startswith('-'):
  172. prefix = ''
  173. else:
  174. prefix = groups[-1]
  175. return _get_completes_subcommand_subcommand(cmd, prefix)
  176. result = re.search(
  177. r'^ *git +(pull|push|fetch) +(\w+-*\w*) +([a-zA-Z0-9_-]*)$', line)
  178. if result:
  179. remote = result.group(2)
  180. prefix = result.group(3)
  181. return _get_completes_branches(remote, prefix)