You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

135 lines
4.0 KiB

  1. """
  2. Main cheat.sh wrapper.
  3. Parse the query, get answers from getters (using get_answer),
  4. visualize it using frontends and return the result.
  5. Exports:
  6. cheat_wrapper()
  7. """
  8. import re
  9. import json
  10. from routing import get_answers, get_topics_list
  11. from search import find_answers_by_keyword
  12. from languages_data import LANGUAGE_ALIAS, rewrite_editor_section_name
  13. import postprocessing
  14. import frontend.html
  15. import frontend.ansi
  16. def cheat_wrapper(query, request_options=None, output_format='ansi'):
  17. """
  18. Function that delivers cheat sheet for `query`.
  19. If `html` is True, the answer is formatted as HTML.
  20. Additional request options specified in `request_options`.
  21. """
  22. def _add_section_name(query):
  23. # temporary solution before we don't find a fixed one
  24. if ' ' not in query and '+' not in query:
  25. return query
  26. if '/' in query:
  27. return query
  28. if ' ' in query:
  29. # for standalone queries only that may contain ' '
  30. return "%s/%s" % tuple(query.split(' ', 1))
  31. return "%s/%s" % tuple(query.split('+', 1))
  32. def _rewrite_aliases(word):
  33. if word == ':bash.completion':
  34. return ':bash_completion'
  35. return word
  36. def _rewrite_section_name(query):
  37. """
  38. Rewriting special section names:
  39. * EDITOR:NAME => emacs:go-mode
  40. """
  41. if '/' not in query:
  42. return query
  43. section_name, rest = query.split('/', 1)
  44. if ':' in section_name:
  45. section_name = rewrite_editor_section_name(section_name)
  46. section_name = LANGUAGE_ALIAS.get(section_name, section_name)
  47. return "%s/%s" % (section_name, rest)
  48. def _sanitize_query(query):
  49. return re.sub('[<>"]', '', query)
  50. def _strip_hyperlink(query):
  51. return re.sub('(,[0-9]+)+$', '', query)
  52. def _parse_query(query):
  53. topic = query
  54. keyword = None
  55. search_options = ""
  56. keyword = None
  57. if '~' in query:
  58. topic = query
  59. pos = topic.index('~')
  60. keyword = topic[pos+1:]
  61. topic = topic[:pos]
  62. if '/' in keyword:
  63. search_options = keyword[::-1]
  64. search_options = search_options[:search_options.index('/')]
  65. keyword = keyword[:-len(search_options)-1]
  66. return topic, keyword, search_options
  67. query = _sanitize_query(query)
  68. query = _add_section_name(query)
  69. query = _rewrite_aliases(query)
  70. query = _rewrite_section_name(query)
  71. # at the moment, we just remove trailing slashes
  72. # so queries python/ and python are equal
  73. # query = _strip_hyperlink(query.rstrip('/'))
  74. topic, keyword, search_options = _parse_query(query)
  75. # Process special case when 'action' is specified.
  76. # In this case the response must be returned as is,
  77. # without any changes and postprocessing
  78. #
  79. # 'action' is specified for suggestion queries, link queries etc.
  80. if request_options.get("action"):
  81. answers = get_answers(topic, request_options=request_options)
  82. if answers:
  83. answer = answers[0]
  84. if isinstance(answer, dict):
  85. answer = answer.get("answer", "")
  86. return answer, True
  87. else:
  88. return "", False
  89. if keyword:
  90. answers = find_answers_by_keyword(
  91. topic, keyword, options=search_options, request_options=request_options)
  92. else:
  93. answers = get_answers(topic, request_options=request_options)
  94. answers = [
  95. postprocessing.postprocess(
  96. answer, keyword, search_options, request_options=request_options)
  97. for answer in answers
  98. ]
  99. answer_data = {
  100. 'query': query,
  101. 'keyword': keyword,
  102. 'answers': answers,
  103. }
  104. if output_format == 'html':
  105. answer_data['topics_list'] = get_topics_list()
  106. return frontend.html.visualize(answer_data, request_options)
  107. elif output_format == 'json':
  108. return json.dumps(answer_data, indent=4), True
  109. return frontend.ansi.visualize(answer_data, request_options)