man_to_completion.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. #!/usr/bin/env python3
  2. """ Automatically rewrite options in a bash completion file. """
  3. import textwrap
  4. def _wrap(text, width, initial_indent, subsequent_indent, separator):
  5. """ Wrap the given text.
  6. This is a workaround for textwrap.fill(), which appears to
  7. count a tab as a single space.
  8. """
  9. # Sanity check if this function will work.
  10. assert "tttttttt" not in text
  11. assert "~" not in text
  12. # Replace tabs with 8 "t", so that they'll count as 8 chars.
  13. initial_indent_t = initial_indent.replace("\t", "tttttttt")
  14. subsequent_indent_t = subsequent_indent.replace("\t", "tttttttt")
  15. # Replace spaces in indent with ~.
  16. subsequent_indent_tz = subsequent_indent_t.replace(" ", "~")
  17. # Wrap text.
  18. text_wrapped = textwrap.fill(text, width=width,
  19. initial_indent=initial_indent_t,
  20. subsequent_indent=subsequent_indent_tz,
  21. expand_tabs=False, break_long_words=False,
  22. break_on_hyphens=False)
  23. # Apply separator.
  24. text_wrapped_sep = text_wrapped.replace(" ", separator)
  25. # Undo special workarounds.
  26. text_wrapped_sep_tabs = text_wrapped_sep.replace("tttttttt", "\t")
  27. text_wrapped_sep_tabs_spaces = text_wrapped_sep_tabs.replace("~", " ")
  28. return text_wrapped_sep_tabs_spaces
  29. def _bash_opts_multiline(bash_name, sep, options):
  30. """ Format the list of options as a multi-line bash variable. """
  31. # Initial indent text.
  32. init_ind = "\t%s=\"" % (bash_name)
  33. # Calculate how many initial spaces we need on subsequent lines.
  34. init_spaces = len(bash_name) + 8 + 2 - 16 - 1
  35. if sep != " ":
  36. init_spaces += 1
  37. subs_ind = "\t\t%s%s" % (' ' * init_spaces, sep)
  38. # Wrap text.
  39. width = 72
  40. options_string_unwrapped = "%s" % (" ".join(options))
  41. options_string = _wrap(options_string_unwrapped, width,
  42. init_ind, subs_ind, sep)
  43. # Replace end-of-line characters.
  44. lines = options_string.split("\n")
  45. if sep == " ":
  46. options_string = "\n".join(["%s \\" % (line) for line in lines])
  47. # Remove the final 'sep',
  48. options_string = options_string[:-2]
  49. else:
  50. options_string = "\n".join(["%s%s" % (line, sep) for line in lines])
  51. # Remove the final 'sep'.
  52. options_string = options_string[:-1]
  53. # Append the final double-quote.
  54. options_string += "\""
  55. return options_string + "\n"
  56. def bash_completion_update(filename_bash, options):
  57. """ Write the options into the bash completion file. """
  58. # Read file.
  59. with open(filename_bash, encoding="utf-8") as bash:
  60. lines = bash.readlines()
  61. newlines = []
  62. skipping = False
  63. for line in lines:
  64. if skipping:
  65. if line == "\n":
  66. skipping = False
  67. else:
  68. continue
  69. # Handle options which are used in a "case" statement,
  70. # which therefore require a "|" separator.
  71. if "\twfilearg=" in line:
  72. skipping = True
  73. replacement = _bash_opts_multiline("wfilearg", "|",
  74. options["filenameargs"])
  75. newlines.append(replacement)
  76. elif "\twpatharg=" in line:
  77. skipping = True
  78. replacement = _bash_opts_multiline("wpatharg", "|",
  79. options["dirargs"])
  80. newlines.append(replacement)
  81. elif "\twotherarg=" in line:
  82. skipping = True
  83. replacement = _bash_opts_multiline("wotherarg", "|",
  84. options["otherargs"])
  85. newlines.append(replacement)
  86. # Handle options used in "compgen", which therefore
  87. # require a " " separator.
  88. elif "\tlongopts=" in line:
  89. skipping = True
  90. replacement = _bash_opts_multiline("longopts", " ",
  91. options["longargs"])
  92. newlines.append(replacement)
  93. elif "\tshortopts=" in line:
  94. skipping = True
  95. replacement = _bash_opts_multiline("shortopts", " ",
  96. options["shortargs"])
  97. newlines.append(replacement)
  98. # Handle other lines.
  99. else:
  100. newlines.append(line)
  101. # Write file.
  102. with open(filename_bash, "wt", encoding="utf-8") as bash:
  103. newbash = "".join(newlines)
  104. bash.write(newbash)