mirror of
https://github.com/chubin/cheat.sh.git
synced 2024-11-23 10:35:29 +03:00
cheat_wrapper.py splitted into fmt/ and frontend/
This commit is contained in:
parent
775cf0005c
commit
c59bdab2f2
@ -247,7 +247,11 @@ def answer(topic=None):
|
||||
return "429 %s\n" % not_allowed, 429
|
||||
|
||||
html_is_needed = is_html_needed(user_agent) and not is_result_a_script(topic)
|
||||
result, found = cheat_wrapper(topic, request_options=options, html=html_is_needed)
|
||||
if html_is_needed:
|
||||
output_format='html'
|
||||
else:
|
||||
output_format='ansi'
|
||||
result, found = cheat_wrapper(topic, request_options=options, output_format=output_format)
|
||||
if 'Please come back in several hours' in result and html_is_needed:
|
||||
return MALFORMED_RESPONSE_HTML_PAGE
|
||||
|
||||
|
@ -1,19 +1,50 @@
|
||||
import abc
|
||||
|
||||
class Adapter(object):
|
||||
|
||||
_adapter_name = None
|
||||
_output_format = 'code'
|
||||
|
||||
def __init__(self):
|
||||
self._list = self._get_list()
|
||||
self._list = {None: self._get_list()}
|
||||
|
||||
@abc.abstractmethod
|
||||
def _get_list(self):
|
||||
def _get_list(self, prefix=None):
|
||||
return []
|
||||
|
||||
def get_list(self):
|
||||
return self._list
|
||||
def get_list(self, prefix=None):
|
||||
"""
|
||||
Return available pages for `prefix`
|
||||
"""
|
||||
|
||||
if prefix in self._list:
|
||||
return self._list[prefix]
|
||||
|
||||
self._list[prefix] = self._get_list(prefix=prefix)
|
||||
return self._list[prefix]
|
||||
|
||||
def is_found(self, topic):
|
||||
return topic in self._list
|
||||
"""
|
||||
check if `topic` is available
|
||||
CAUTION: only root is checked
|
||||
"""
|
||||
return topic in self._list[None]
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_page(self, topic, request_options=None):
|
||||
def _get_page(self, topic, request_options=None):
|
||||
"""
|
||||
Return page for `topic`
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_page_dict(self, topic, request_options=None):
|
||||
"""
|
||||
Return page dict for `topic`
|
||||
"""
|
||||
answer_dict = {
|
||||
'topic': topic,
|
||||
'topic_type': self._adapter_name,
|
||||
'answer': self._get_page(topic, request_options=request_options),
|
||||
'format': self._output_format,
|
||||
}
|
||||
return answer_dict
|
||||
|
@ -5,6 +5,8 @@ import glob
|
||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
from globals import PATH_CHEAT_SHEETS
|
||||
|
||||
from adapter import Adapter
|
||||
|
||||
def _remove_initial_underscore(filename):
|
||||
if filename.startswith('_'):
|
||||
filename = filename[1:]
|
||||
@ -31,41 +33,48 @@ def _get_answers_and_dirs():
|
||||
answers = [os.path.split(topic)[1] for topic in topics if not _isdir(topic)]
|
||||
return answers, answer_dirs
|
||||
|
||||
def _update_cheat_sheets_topics():
|
||||
answers = _get_answer_files_from_folder()
|
||||
cheatsheet_answers, cheatsheet_dirs = _get_answers_and_dirs()
|
||||
return answers+cheatsheet_answers, cheatsheet_dirs
|
||||
class CheatSheets(Adapter):
|
||||
|
||||
def get_list():
|
||||
return _update_cheat_sheets_topics()[0]
|
||||
_adapter_name = "cheat.sheets"
|
||||
_output_format = "code"
|
||||
|
||||
def __init__(self):
|
||||
self._answers = []
|
||||
self._cheatsheet_answers = []
|
||||
self._cheatsheet_dirs = []
|
||||
Adapter.__init__(self)
|
||||
|
||||
def get_dirs_list():
|
||||
return _update_cheat_sheets_topics()[1]
|
||||
def _update_cheat_sheets_topics(self):
|
||||
self._answers = _get_answer_files_from_folder()
|
||||
self._cheatsheet_answers, self._cheatsheet_dirs = _get_answers_and_dirs()
|
||||
return self._answers + self._cheatsheet_answers, self._cheatsheet_dirs
|
||||
|
||||
_CHEAT_SHEETS_LIST = get_list()
|
||||
def is_found(topic):
|
||||
return topic in _CHEAT_SHEETS_LIST
|
||||
def _get_list(self, prefix=None):
|
||||
return self._update_cheat_sheets_topics()[0]
|
||||
|
||||
_CHEAT_SHEETS_DIRS = get_dirs_list()
|
||||
def is_dir_found(topic):
|
||||
return topic in _CHEAT_SHEETS_DIRS
|
||||
|
||||
def get_page(topic, request_options=None):
|
||||
"""
|
||||
Get the cheat sheet topic from the own repository (cheat.sheets).
|
||||
It's possible that topic directory starts with omitted underscore
|
||||
"""
|
||||
filename = PATH_CHEAT_SHEETS + "%s" % topic
|
||||
if not os.path.exists(filename):
|
||||
filename = PATH_CHEAT_SHEETS + "_%s" % topic
|
||||
if os.path.isdir(filename):
|
||||
return ""
|
||||
else:
|
||||
def _get_page(self, topic, request_options=None):
|
||||
"""
|
||||
Get the cheat sheet topic from the own repository (cheat.sheets).
|
||||
It's possible that topic directory starts with omitted underscore
|
||||
"""
|
||||
filename = PATH_CHEAT_SHEETS + "%s" % topic
|
||||
if not os.path.exists(filename):
|
||||
filename = PATH_CHEAT_SHEETS + "_%s" % topic
|
||||
if os.path.isdir(filename):
|
||||
return ""
|
||||
return open(filename, "r").read().decode('utf-8')
|
||||
|
||||
def get_dir(topic, request_options=None):
|
||||
answer = []
|
||||
for f_name in glob.glob(PATH_CHEAT_SHEETS + "%s/*" % topic.rstrip('/')):
|
||||
answer.append(os.path.basename(f_name))
|
||||
topics = sorted(answer)
|
||||
return "\n".join(topics) + "\n"
|
||||
class CheatSheetsDir(CheatSheets):
|
||||
|
||||
_adapter_name = "cheat.sheets dir"
|
||||
_output_format = "text"
|
||||
|
||||
def _get_list(self, prefix=None):
|
||||
return self._update_cheat_sheets_topics()[1]
|
||||
|
||||
def _get_page(self, topic, request_options=None):
|
||||
answer = []
|
||||
for f_name in glob.glob(PATH_CHEAT_SHEETS + "%s/*" % topic.rstrip('/')):
|
||||
answer.append(os.path.basename(f_name))
|
||||
topics = sorted(answer)
|
||||
return "\n".join(topics) + "\n"
|
||||
|
@ -15,11 +15,15 @@ def _get_filenames(path):
|
||||
return [os.path.split(topic)[1] for topic in glob.glob(path)]
|
||||
|
||||
class Tldr(Adapter):
|
||||
def _get_list(self):
|
||||
|
||||
_adapter_name = "tldr"
|
||||
_output_format = "code"
|
||||
|
||||
def _get_list(self, prefix=None):
|
||||
return [filename[:-3]
|
||||
for filename in _get_filenames(PATH_TLDR_PAGES) if filename.endswith('.md')]
|
||||
|
||||
def get_page(self, topic, request_options=None):
|
||||
def _get_page(self, topic, request_options=None):
|
||||
cmd = ["tldr", topic]
|
||||
proc = Popen(cmd, stdout=PIPE, stderr=PIPE)
|
||||
answer = proc.communicate()[0]
|
||||
@ -40,30 +44,42 @@ class Tldr(Adapter):
|
||||
return answer.decode('utf-8')
|
||||
|
||||
class Cheat(Adapter):
|
||||
def _get_list(self):
|
||||
|
||||
_adapter_name = "cheat"
|
||||
_output_format = "code"
|
||||
|
||||
def _get_list(self, prefix=None):
|
||||
return _get_filenames(PATH_CHEAT_PAGES)
|
||||
|
||||
def get_page(self, topic, request_options=None):
|
||||
def _get_page(self, topic, request_options=None):
|
||||
cmd = ["cheat", topic]
|
||||
proc = Popen(cmd, stdout=PIPE, stderr=PIPE)
|
||||
answer = proc.communicate()[0].decode('utf-8')
|
||||
return answer
|
||||
|
||||
class Fosdem(Adapter):
|
||||
def _get_list(self):
|
||||
|
||||
_adapter_name = "fosdem"
|
||||
_output_format = "ansi"
|
||||
|
||||
def _get_list(self, prefix=None):
|
||||
return ['fosdem']
|
||||
|
||||
def get_page(self, topic, request_options=None):
|
||||
def _get_page(self, topic, request_options=None):
|
||||
cmd = ["sudo", "/usr/local/bin/current-fosdem-slide"]
|
||||
proc = Popen(cmd, stdout=PIPE, stderr=PIPE)
|
||||
answer = proc.communicate()[0].decode('utf-8')
|
||||
return answer
|
||||
|
||||
class Translation(Adapter):
|
||||
def _get_list(self):
|
||||
|
||||
_adapter_name = "translation"
|
||||
_output_format = "text"
|
||||
|
||||
def _get_list(self, prefix=None):
|
||||
return []
|
||||
|
||||
def get_page(self, topic, request_options=None):
|
||||
def _get_page(self, topic, request_options=None):
|
||||
from_, topic = topic.split('/', 1)
|
||||
to_ = request_options.get('lang', 'en')
|
||||
if '-' in from_:
|
||||
|
@ -7,7 +7,8 @@ from fuzzywuzzy import process, fuzz
|
||||
|
||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
from globals import MYDIR, COLOR_STYLES
|
||||
from colorize_internal import colorize_internal
|
||||
from adapter import Adapter
|
||||
from fmt.internal import colorize_internal
|
||||
|
||||
_INTERNAL_TOPICS = [
|
||||
":cht.sh",
|
||||
@ -33,9 +34,13 @@ _COLORIZED_INTERNAL_TOPICS = [
|
||||
':intro',
|
||||
]
|
||||
|
||||
class InternalPages(object):
|
||||
class InternalPages(Adapter):
|
||||
|
||||
_adapter_name = 'internal'
|
||||
_output_format = 'ansi'
|
||||
|
||||
def __init__(self, get_topic_type=None, get_topics_list=None):
|
||||
Adapter.__init__(self)
|
||||
self.get_topic_type = get_topic_type
|
||||
self.get_topics_list = get_topics_list
|
||||
|
||||
@ -50,9 +55,8 @@ class InternalPages(object):
|
||||
answer += "%s %s\n" % (key, val)
|
||||
return answer
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_list():
|
||||
def get_list(prefix=None):
|
||||
return _INTERNAL_TOPICS
|
||||
|
||||
def _get_list_answer(self, topic, request_options=None):
|
||||
@ -70,7 +74,7 @@ class InternalPages(object):
|
||||
|
||||
return answer
|
||||
|
||||
def get_page(self, topic, request_options=None):
|
||||
def _get_page(self, topic, request_options=None):
|
||||
if topic.endswith('/:list') or topic.lstrip('/') == ':list':
|
||||
return self._get_list_answer(topic)
|
||||
|
||||
@ -91,15 +95,18 @@ class InternalPages(object):
|
||||
|
||||
class UnknownPages(InternalPages):
|
||||
|
||||
_adapter_name = 'unknown'
|
||||
_output_format = 'text'
|
||||
|
||||
@staticmethod
|
||||
def get_list():
|
||||
def get_list(prefix=None):
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def is_found(topic):
|
||||
return False
|
||||
|
||||
def get_page(self, topic, request_options=None):
|
||||
def _get_page(self, topic, request_options=None):
|
||||
topics_list = self.get_topics_list()
|
||||
if topic.startswith(':'):
|
||||
topics_list = [x for x in topics_list if x.startswith(':')]
|
||||
|
@ -1,16 +1,21 @@
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
|
||||
from globals import PATH_LATENZ
|
||||
from adapter import Adapter
|
||||
|
||||
def get_answer(topic, request_options=None):
|
||||
sys.path.append(PATH_LATENZ)
|
||||
import latencies
|
||||
return latencies.render()
|
||||
class Latenz(Adapter):
|
||||
|
||||
def get_list():
|
||||
return ['latencies']
|
||||
_adapter_name = "late.nz"
|
||||
_output_format = "ansi"
|
||||
|
||||
def is_found(topic):
|
||||
return topic.lower() in ['latencies', 'late.nz', 'latency']
|
||||
def _get_page(self, topic, request_options=None):
|
||||
sys.path.append(PATH_LATENZ)
|
||||
import latencies
|
||||
return latencies.render()
|
||||
|
||||
def _get_list(self, prefix=None):
|
||||
return ['latencies']
|
||||
|
||||
def is_found(self, topic):
|
||||
return topic.lower() in ['latencies', 'late.nz', 'latency']
|
||||
|
@ -7,6 +7,8 @@ import os
|
||||
import re
|
||||
from globals import PATH_LEARNXINY
|
||||
|
||||
from adapter import Adapter
|
||||
|
||||
class LearnXYAdapter(object):
|
||||
|
||||
"""
|
||||
@ -23,7 +25,6 @@ class LearnXYAdapter(object):
|
||||
_block_cut_end = 0
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self._whole_cheatsheet = self._read_cheatsheet()
|
||||
self._blocks = self._extract_blocks()
|
||||
|
||||
@ -111,7 +112,7 @@ class LearnXYAdapter(object):
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_list(self, prefix=False):
|
||||
def get_list(self, prefix=None):
|
||||
"""
|
||||
Get list of topics for `prefix`
|
||||
"""
|
||||
@ -119,7 +120,7 @@ class LearnXYAdapter(object):
|
||||
return ["%s/%s" % (self.prefix, x) for x in self._topics_list]
|
||||
return self._topics_list
|
||||
|
||||
def get_cheat_sheet(self, name, partial=False):
|
||||
def get_page(self, name, partial=False):
|
||||
"""
|
||||
Return specified cheat sheet `name` for the language.
|
||||
If `partial`, cheat sheet name may be shortened
|
||||
@ -142,9 +143,6 @@ class LearnXYAdapter(object):
|
||||
|
||||
for block_name, block_contents in self._blocks:
|
||||
if block_name == name:
|
||||
|
||||
print("\n".join(block_contents))
|
||||
print(name)
|
||||
return "\n".join(block_contents)
|
||||
|
||||
return None
|
||||
@ -792,34 +790,40 @@ class LearnVisualBasicAdapter(LearnXYAdapter):
|
||||
_filename = "visualbasic.html.markdown"
|
||||
_splitted = False
|
||||
|
||||
ADAPTERS = {cls.prefix: cls() for cls in vars()['LearnXYAdapter'].__subclasses__()}
|
||||
_ADAPTERS = {cls.prefix: cls() for cls in vars()['LearnXYAdapter'].__subclasses__()}
|
||||
|
||||
def get_learnxiny(topic, request_options=None):
|
||||
"""
|
||||
Return cheat sheet for `topic`
|
||||
or empty string if nothing found
|
||||
"""
|
||||
lang, topic = topic.split('/', 1)
|
||||
if lang not in ADAPTERS:
|
||||
return ''
|
||||
return ADAPTERS[lang].get_cheat_sheet(topic)
|
||||
class LearnXinY(Adapter):
|
||||
|
||||
def get_learnxiny_list():
|
||||
"""
|
||||
Return list of all learnxiny topics
|
||||
"""
|
||||
answer = []
|
||||
for language_adapter in ADAPTERS.values():
|
||||
answer += language_adapter.get_list(prefix=True)
|
||||
return answer
|
||||
def __init__(self):
|
||||
self.adapters = _ADAPTERS
|
||||
Adapter.__init__(self)
|
||||
|
||||
def is_valid_learnxy(topic):
|
||||
"""
|
||||
Return whether `topic` is a valid learnxiny topic
|
||||
"""
|
||||
def _get_page(self, topic, request_options=None):
|
||||
"""
|
||||
Return cheat sheet for `topic`
|
||||
or empty string if nothing found
|
||||
"""
|
||||
lang, topic = topic.split('/', 1)
|
||||
if lang not in self.adapters:
|
||||
return ''
|
||||
return self.adapters[lang].get_page(topic)
|
||||
|
||||
lang, topic = topic.split('/', 1)
|
||||
if lang not in ADAPTERS:
|
||||
return False
|
||||
def _get_list(self, prefix=None):
|
||||
"""
|
||||
Return list of all learnxiny topics
|
||||
"""
|
||||
answer = []
|
||||
for language_adapter in self.adapters.values():
|
||||
answer += language_adapter.get_list(prefix=True)
|
||||
return answer
|
||||
|
||||
return ADAPTERS[lang].is_valid(topic)
|
||||
def is_found(self, topic):
|
||||
"""
|
||||
Return whether `topic` is a valid learnxiny topic
|
||||
"""
|
||||
|
||||
lang, topic = topic.split('/', 1)
|
||||
if lang not in self.adapters:
|
||||
return False
|
||||
|
||||
return self.adapters[lang].is_valid(topic)
|
||||
|
@ -12,52 +12,65 @@ from polyglot.detect.base import UnknownLanguage
|
||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
from globals import MYDIR
|
||||
|
||||
def get_page(topic, request_options=None):
|
||||
"""
|
||||
Find answer for the `topic` question.
|
||||
"""
|
||||
from adapter import Adapter
|
||||
|
||||
# if there is a language name in the section name,
|
||||
# cut it off (de:python => python)
|
||||
if '/' in topic:
|
||||
section_name, topic = topic.split('/', 1)
|
||||
if ':' in section_name:
|
||||
_, section_name = section_name.split(':', 1)
|
||||
topic = "%s/%s" % (section_name, topic)
|
||||
class Question(Adapter):
|
||||
|
||||
# some clients send queries with - instead of + so we have to rewrite them to
|
||||
topic = re.sub(r"(?<!-)-", ' ', topic)
|
||||
_adapter_name = "question"
|
||||
_output_format = "code"
|
||||
|
||||
topic_words = topic.split()
|
||||
def _get_page(self, topic, request_options=None):
|
||||
"""
|
||||
Find answer for the `topic` question.
|
||||
"""
|
||||
|
||||
topic = " ".join(topic_words)
|
||||
# if there is a language name in the section name,
|
||||
# cut it off (de:python => python)
|
||||
if '/' in topic:
|
||||
section_name, topic = topic.split('/', 1)
|
||||
if ':' in section_name:
|
||||
_, section_name = section_name.split(':', 1)
|
||||
topic = "%s/%s" % (section_name, topic)
|
||||
|
||||
lang = 'en'
|
||||
try:
|
||||
query_text = topic # " ".join(topic)
|
||||
query_text = re.sub('^[^/]*/+', '', query_text.rstrip('/'))
|
||||
query_text = re.sub('/[0-9]+$', '', query_text)
|
||||
query_text = re.sub('/[0-9]+$', '', query_text)
|
||||
detector = Detector(query_text)
|
||||
supposed_lang = detector.languages[0].code
|
||||
if len(topic_words) > 2 or supposed_lang in ['az', 'ru', 'uk', 'de', 'fr', 'es', 'it', 'nl']:
|
||||
lang = supposed_lang
|
||||
if supposed_lang.startswith('zh_') or supposed_lang == 'zh':
|
||||
lang = 'zh'
|
||||
elif supposed_lang.startswith('pt_'):
|
||||
lang = 'pt'
|
||||
if supposed_lang in ['ja', 'ko']:
|
||||
lang = supposed_lang
|
||||
# some clients send queries with - instead of + so we have to rewrite them to
|
||||
topic = re.sub(r"(?<!-)-", ' ', topic)
|
||||
|
||||
except UnknownLanguage:
|
||||
print("Unknown language (%s)" % query_text)
|
||||
topic_words = topic.split()
|
||||
|
||||
if lang != 'en':
|
||||
topic = ['--human-language', lang, topic]
|
||||
else:
|
||||
topic = [topic]
|
||||
topic = " ".join(topic_words)
|
||||
|
||||
cmd = [os.path.join(MYDIR, "bin/get-answer-for-question")] + topic
|
||||
proc = Popen(cmd, stdout=PIPE, stderr=PIPE)
|
||||
answer = proc.communicate()[0].decode('utf-8')
|
||||
return answer
|
||||
lang = 'en'
|
||||
try:
|
||||
query_text = topic # " ".join(topic)
|
||||
query_text = re.sub('^[^/]*/+', '', query_text.rstrip('/'))
|
||||
query_text = re.sub('/[0-9]+$', '', query_text)
|
||||
query_text = re.sub('/[0-9]+$', '', query_text)
|
||||
detector = Detector(query_text)
|
||||
supposed_lang = detector.languages[0].code
|
||||
if len(topic_words) > 2 or supposed_lang in ['az', 'ru', 'uk', 'de', 'fr', 'es', 'it', 'nl']:
|
||||
lang = supposed_lang
|
||||
if supposed_lang.startswith('zh_') or supposed_lang == 'zh':
|
||||
lang = 'zh'
|
||||
elif supposed_lang.startswith('pt_'):
|
||||
lang = 'pt'
|
||||
if supposed_lang in ['ja', 'ko']:
|
||||
lang = supposed_lang
|
||||
|
||||
except UnknownLanguage:
|
||||
print("Unknown language (%s)" % query_text)
|
||||
|
||||
if lang != 'en':
|
||||
topic = ['--human-language', lang, topic]
|
||||
else:
|
||||
topic = [topic]
|
||||
|
||||
cmd = [os.path.join(MYDIR, "bin/get-answer-for-question")] + topic
|
||||
proc = Popen(cmd, stdout=PIPE, stderr=PIPE)
|
||||
answer = proc.communicate()[0].decode('utf-8')
|
||||
return answer
|
||||
|
||||
def get_list(self, prefix=None):
|
||||
return []
|
||||
|
||||
def is_found(self, topic):
|
||||
return True
|
||||
|
@ -24,6 +24,8 @@ class Rosetta(Adapter):
|
||||
"""
|
||||
|
||||
__section_name = 'rosetta'
|
||||
_adapter_name = "rosetta"
|
||||
_output_format = "code"
|
||||
|
||||
@staticmethod
|
||||
def _load_rosetta_code_names():
|
||||
@ -101,7 +103,7 @@ class Rosetta(Adapter):
|
||||
) % number_of_pages
|
||||
return answer
|
||||
|
||||
def get_page(self, topic, request_options=None):
|
||||
def _get_page(self, topic, request_options=None):
|
||||
|
||||
if '/' not in topic:
|
||||
return self._rosetta_get_list(topic)
|
||||
@ -118,11 +120,10 @@ class Rosetta(Adapter):
|
||||
|
||||
return self._get_task(lang, topic)
|
||||
|
||||
def _get_list(self):
|
||||
def _get_list(self, prefix=None):
|
||||
return []
|
||||
|
||||
def get_list(self):
|
||||
# return self._get_list()
|
||||
def get_list(self, prefix=None):
|
||||
answer = [self.__section_name]
|
||||
for i in self._rosetta_code_name:
|
||||
answer.append('%s/%s/' % (i, self.__section_name))
|
||||
|
@ -1,274 +1,30 @@
|
||||
"""
|
||||
Main cheat.sh wrapper.
|
||||
Get answers from getters (in get_answer), adds syntax highlighting
|
||||
or html markup and returns the result.
|
||||
Parse the query, get answers from getters (using get_answer),
|
||||
visualize it using frontends and return the result.
|
||||
|
||||
Exports:
|
||||
|
||||
cheat_wrapper()
|
||||
"""
|
||||
|
||||
from gevent.monkey import patch_all
|
||||
from gevent.subprocess import Popen, PIPE
|
||||
patch_all()
|
||||
|
||||
# pylint: disable=wrong-import-position,wrong-import-order
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import json
|
||||
|
||||
import colored
|
||||
from pygments import highlight as pygments_highlight
|
||||
from pygments.formatters import Terminal256Formatter # pylint: disable=no-name-in-module
|
||||
from get_answer import get_answer, find_answer_by_keyword, get_topics_list
|
||||
import frontend.html
|
||||
import frontend.ansi
|
||||
|
||||
MYDIR = os.path.abspath(os.path.join(__file__, '..', '..'))
|
||||
sys.path.append("%s/lib/" % MYDIR)
|
||||
from globals import error, ANSI2HTML, COLOR_STYLES, GITHUB_REPOSITORY
|
||||
from buttons import TWITTER_BUTTON, GITHUB_BUTTON, GITHUB_BUTTON_FOOTER
|
||||
from languages_data import LEXER, get_lexer_name
|
||||
from get_answer import get_topic_type, get_topics_list, get_answer, find_answer_by_keyword
|
||||
from beautifier import code_blocks
|
||||
# import beautifier
|
||||
# pylint: disable=wrong-import-position,wrong-import-order
|
||||
|
||||
ANSI_ESCAPE = re.compile(r'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]')
|
||||
def remove_ansi(sometext):
|
||||
def cheat_wrapper(query, request_options=None, output_format='ansi'):
|
||||
"""
|
||||
Remove ANSI sequences from `sometext` and convert it into plaintext.
|
||||
"""
|
||||
return ANSI_ESCAPE.sub('', sometext)
|
||||
|
||||
def html_wrapper(data):
|
||||
"""
|
||||
Convert ANSI text `data` to HTML
|
||||
"""
|
||||
proc = Popen(
|
||||
["bash", ANSI2HTML, "--palette=solarized", "--bg=dark"],
|
||||
stdin=PIPE, stdout=PIPE, stderr=PIPE)
|
||||
data = data.encode('utf-8')
|
||||
stdout, stderr = proc.communicate(data)
|
||||
if proc.returncode != 0:
|
||||
error(stdout + stderr)
|
||||
return stdout.decode('utf-8')
|
||||
|
||||
def _colorize_internal(topic, answer, html_needed):
|
||||
|
||||
def _colorize_line(line):
|
||||
if line.startswith('T'):
|
||||
line = colored.fg("grey_62") + line + colored.attr('reset')
|
||||
line = re.sub(r"\{(.*?)\}", colored.fg("orange_3") + r"\1"+colored.fg('grey_35'), line)
|
||||
return line
|
||||
|
||||
line = re.sub(r"\[(F.*?)\]",
|
||||
colored.bg("black") + colored.fg("cyan") + r"[\1]"+colored.attr('reset'),
|
||||
line)
|
||||
line = re.sub(r"\[(g.*?)\]",
|
||||
colored.bg("dark_gray") \
|
||||
+ colored.fg("grey_0") \
|
||||
+ r"[\1]"+colored.attr('reset'),
|
||||
line)
|
||||
line = re.sub(r"\{(.*?)\}",
|
||||
colored.fg("orange_3") + r"\1"+colored.attr('reset'),
|
||||
line)
|
||||
line = re.sub(r"<(.*?)>",
|
||||
colored.fg("cyan") + r"\1"+colored.attr('reset'),
|
||||
line)
|
||||
return line
|
||||
|
||||
if topic in [':list', ':bash_completion']:
|
||||
return answer
|
||||
|
||||
if topic == ':firstpage-v1':
|
||||
lines = answer.splitlines()
|
||||
answer_lines = lines[:9]
|
||||
answer_lines.append(colored.fg('grey_35')+lines[9]+colored.attr('reset'))
|
||||
for line in lines[10:]:
|
||||
answer_lines.append(_colorize_line(line))
|
||||
if html_needed:
|
||||
answer_lines = answer_lines[:-2]
|
||||
answer = "\n".join(answer_lines) + "\n"
|
||||
|
||||
return answer
|
||||
|
||||
def _colorize_ansi_answer(topic, answer, color_style, # pylint: disable=too-many-arguments
|
||||
highlight_all=True, highlight_code=False,
|
||||
unindent_code=False):
|
||||
|
||||
color_style = color_style or "native"
|
||||
lexer_class = LEXER['bash']
|
||||
if '/' in topic:
|
||||
section_name = topic.split('/', 1)[0].lower()
|
||||
section_name = get_lexer_name(section_name)
|
||||
lexer_class = LEXER.get(section_name, lexer_class)
|
||||
if section_name == 'php':
|
||||
answer = "<?\n%s?>\n" % answer
|
||||
|
||||
if highlight_all:
|
||||
highlight = lambda answer: pygments_highlight(
|
||||
answer, lexer_class(), Terminal256Formatter(style=color_style)).strip('\n')+'\n'
|
||||
else:
|
||||
highlight = lambda x: x
|
||||
|
||||
if highlight_code:
|
||||
blocks = code_blocks(answer, wrap_lines=True, unindent_code=(4 if unindent_code else False))
|
||||
highlighted_blocks = []
|
||||
for block in blocks:
|
||||
if block[0] == 1:
|
||||
this_block = highlight(block[1])
|
||||
else:
|
||||
this_block = block[1].strip('\n')+'\n'
|
||||
highlighted_blocks.append(this_block)
|
||||
|
||||
result = "\n".join(highlighted_blocks)
|
||||
else:
|
||||
result = highlight(answer).lstrip('\n')
|
||||
return result
|
||||
|
||||
def _github_button(topic_type):
|
||||
|
||||
full_name = GITHUB_REPOSITORY.get(topic_type, '')
|
||||
if not full_name:
|
||||
return ''
|
||||
|
||||
short_name = full_name.split('/', 1)[1] # pylint: disable=unused-variable
|
||||
|
||||
button = (
|
||||
"<!-- Place this tag where you want the button to render. -->"
|
||||
'<a aria-label="Star %(full_name)s on GitHub"'
|
||||
' data-count-aria-label="# stargazers on GitHub"'
|
||||
' data-count-api="/repos/%(full_name)s#stargazers_count"'
|
||||
' data-count-href="/%(full_name)s/stargazers"'
|
||||
' data-icon="octicon-star"'
|
||||
' href="https://github.com/%(full_name)s"'
|
||||
' class="github-button">%(short_name)s</a>'
|
||||
) % locals()
|
||||
return button
|
||||
|
||||
def _render_html(query, result, editable, repository_button, request_options):
|
||||
|
||||
result = result + "\n$"
|
||||
result = html_wrapper(result)
|
||||
title = "<title>cheat.sh/%s</title>" % query
|
||||
# title += ('\n<link rel="stylesheet" href="/files/awesomplete.css" />script'
|
||||
# ' src="/files/awesomplete.min.js" async></script>')
|
||||
# submit button: thanks to http://stackoverflow.com/questions/477691/
|
||||
submit_button = ('<input type="submit" style="position: absolute;'
|
||||
' left: -9999px; width: 1px; height: 1px;" tabindex="-1" />')
|
||||
topic_list = ('<datalist id="topics">%s</datalist>'
|
||||
% ("\n".join("<option value='%s'></option>" % x for x in get_topics_list())))
|
||||
|
||||
curl_line = "<span class='pre'>$ curl cheat.sh/</span>"
|
||||
if query == ':firstpage':
|
||||
query = ""
|
||||
form_html = ('<form action="/" method="GET"/>'
|
||||
'%s%s'
|
||||
'<input'
|
||||
' type="text" value="%s" name="topic"'
|
||||
' list="topics" autofocus autocomplete="off"/>'
|
||||
'%s'
|
||||
'</form>') \
|
||||
% (submit_button, curl_line, query, topic_list)
|
||||
|
||||
edit_button = ''
|
||||
if editable:
|
||||
# It's possible that topic directory starts with omitted underscore
|
||||
if '/' in query:
|
||||
query = '_' + query
|
||||
edit_page_link = 'https://github.com/chubin/cheat.sheets/edit/master/sheets/' + query
|
||||
edit_button = (
|
||||
'<pre style="position:absolute;padding-left:40em;overflow:visible;height:0;">'
|
||||
'[<a href="%s" style="color:cyan">edit</a>]'
|
||||
'</pre>') % edit_page_link
|
||||
result = re.sub("<pre>", edit_button + form_html + "<pre>", result)
|
||||
result = re.sub("<head>", "<head>" + title, result)
|
||||
if not request_options.get('quiet'):
|
||||
result = result.replace('</body>',
|
||||
TWITTER_BUTTON \
|
||||
+ GITHUB_BUTTON \
|
||||
+ repository_button \
|
||||
+ GITHUB_BUTTON_FOOTER \
|
||||
+ '</body>')
|
||||
return result
|
||||
|
||||
def _visualize(query, keyword, answers, request_options, html=None): # pylint: disable=too-many-locals
|
||||
|
||||
search_mode = bool(keyword)
|
||||
|
||||
highlight = not bool(request_options and request_options.get('no-terminal'))
|
||||
color_style = request_options.get('style', '')
|
||||
if color_style not in COLOR_STYLES:
|
||||
color_style = ''
|
||||
|
||||
found = True # if the page was found in the database
|
||||
editable = False # can generated page be edited on github (only cheat.sheets pages can)
|
||||
result = ""
|
||||
for topic, answer in answers: # pylint: disable=too-many-nested-blocks
|
||||
|
||||
if topic == 'LIMITED':
|
||||
result += colored.bg('dark_goldenrod') \
|
||||
+ colored.fg('yellow_1') \
|
||||
+ ' ' + answer + ' ' \
|
||||
+ colored.attr('reset') + "\n"
|
||||
break
|
||||
|
||||
topic_type = get_topic_type(topic)
|
||||
highlight = (highlight
|
||||
and not topic.endswith('/:list')
|
||||
and topic not in [":list", ":bash_completion"]
|
||||
and topic_type not in ["unknown"]
|
||||
)
|
||||
found = found and not topic_type == 'unknown'
|
||||
editable = editable or topic_type == "cheat.sheets"
|
||||
|
||||
if topic_type == "internal" and highlight:
|
||||
answer = _colorize_internal(topic, answer, html)
|
||||
elif topic_type in ["late.nz", "fosdem"]:
|
||||
pass
|
||||
else:
|
||||
answer = _colorize_ansi_answer(
|
||||
topic, answer, color_style,
|
||||
highlight_all=highlight,
|
||||
highlight_code=(topic_type == 'question'
|
||||
and not request_options.get('add_comments')
|
||||
and not request_options.get('remove_text')),
|
||||
unindent_code=request_options.get('unindent_code')
|
||||
)
|
||||
|
||||
if search_mode:
|
||||
if not highlight:
|
||||
result += "\n[%s]\n" % topic
|
||||
else:
|
||||
result += "\n%s%s %s %s%s\n" % (colored.bg('dark_gray'),
|
||||
colored.attr("res_underlined"),
|
||||
topic,
|
||||
colored.attr("res_underlined"),
|
||||
colored.attr('reset'))
|
||||
result += answer
|
||||
|
||||
result = result.strip('\n') + "\n"
|
||||
|
||||
if search_mode:
|
||||
editable = False
|
||||
repository_button = ''
|
||||
else:
|
||||
repository_button = _github_button(topic_type)
|
||||
|
||||
if html and query:
|
||||
result = _render_html(
|
||||
query, result, editable, repository_button, request_options)
|
||||
|
||||
|
||||
return result, found
|
||||
|
||||
def _sanitize_query(query):
|
||||
return re.sub('[<>"]', '', query)
|
||||
|
||||
def cheat_wrapper(query, request_options=None, html=False):
|
||||
"""
|
||||
Giant megafunction that delivers cheat sheet for `query`.
|
||||
Function that delivers cheat sheet for `query`.
|
||||
If `html` is True, the answer is formatted as HTML.
|
||||
Additional request options specified in `request_options`.
|
||||
|
||||
This function is really really bad, and should be rewritten
|
||||
as soon as possible.
|
||||
"""
|
||||
|
||||
def _sanitize_query(query):
|
||||
return re.sub('[<>"]', '', query)
|
||||
|
||||
def _strip_hyperlink(query):
|
||||
return re.sub('(,[0-9]+)+$', '', query)
|
||||
|
||||
@ -302,6 +58,17 @@ def cheat_wrapper(query, request_options=None, html=False):
|
||||
answers = find_answer_by_keyword(
|
||||
topic, keyword, options=search_options, request_options=request_options)
|
||||
else:
|
||||
answers = [(topic, get_answer(topic, keyword, request_options=request_options))]
|
||||
answers = [get_answer(topic, keyword, request_options=request_options)]
|
||||
|
||||
return _visualize(query, keyword, answers, request_options, html=html)
|
||||
answer_data = {
|
||||
'query': query,
|
||||
'keyword': keyword,
|
||||
'answers': answers,
|
||||
}
|
||||
|
||||
if output_format == 'html':
|
||||
answer_data['topics_list'] = get_topics_list()
|
||||
return frontend.html.visualize(answer_data, request_options)
|
||||
elif output_format == 'json':
|
||||
return json.dumps(answer_data, indent=4)
|
||||
return frontend.ansi.visualize(answer_data, request_options)
|
||||
|
0
lib/fmt/__init__.py
Normal file
0
lib/fmt/__init__.py
Normal file
@ -93,3 +93,38 @@ def colorize_internal(text, palette_number=1):
|
||||
text = re.sub("{.*?}", _colorize_curlies_block, text)
|
||||
text = re.sub("#(.*?)\n", _colorize_headers, text)
|
||||
return text
|
||||
|
||||
def colorize_internal_firstpage_v1(answer):
|
||||
"""
|
||||
Colorize "/:firstpage-v1".
|
||||
Legacy.
|
||||
"""
|
||||
|
||||
def _colorize_line(line):
|
||||
if line.startswith('T'):
|
||||
line = colored.fg("grey_62") + line + colored.attr('reset')
|
||||
line = re.sub(r"\{(.*?)\}", colored.fg("orange_3") + r"\1"+colored.fg('grey_35'), line)
|
||||
return line
|
||||
|
||||
line = re.sub(r"\[(F.*?)\]",
|
||||
colored.bg("black") + colored.fg("cyan") + r"[\1]"+colored.attr('reset'),
|
||||
line)
|
||||
line = re.sub(r"\[(g.*?)\]",
|
||||
colored.bg("dark_gray")+colored.fg("grey_0")+r"[\1]"+colored.attr('reset'),
|
||||
line)
|
||||
line = re.sub(r"\{(.*?)\}",
|
||||
colored.fg("orange_3") + r"\1"+colored.attr('reset'),
|
||||
line)
|
||||
line = re.sub(r"<(.*?)>",
|
||||
colored.fg("cyan") + r"\1"+colored.attr('reset'),
|
||||
line)
|
||||
return line
|
||||
|
||||
lines = answer.splitlines()
|
||||
answer_lines = lines[:9]
|
||||
answer_lines.append(colored.fg('grey_35')+lines[9]+colored.attr('reset'))
|
||||
for line in lines[10:]:
|
||||
answer_lines.append(_colorize_line(line))
|
||||
answer = "\n".join(answer_lines) + "\n"
|
||||
|
||||
return answer
|
89
lib/fmt/markdown.py
Normal file
89
lib/fmt/markdown.py
Normal file
@ -0,0 +1,89 @@
|
||||
"""
|
||||
Markdown support.
|
||||
|
||||
Exports:
|
||||
format_text(text, config=None, highlighter=None):
|
||||
|
||||
Uses external pygments formatters for highlighting (passed as an argument).
|
||||
"""
|
||||
|
||||
import re
|
||||
import ansiwrap
|
||||
import colored
|
||||
|
||||
def format_text(text, config=None, highlighter=None):
|
||||
"""
|
||||
Renders `text` according to markdown rules.
|
||||
Uses `highlighter` for syntax highlighting.
|
||||
Returns a dictionary with "output" and "links".
|
||||
"""
|
||||
return _format_section(text, config=config, highlighter=highlighter)
|
||||
|
||||
def _split_into_paragraphs(text):
|
||||
return re.split('\n\n+', text)
|
||||
|
||||
def _colorize(text):
|
||||
return \
|
||||
re.sub(
|
||||
r"`(.*?)`",
|
||||
colored.bg("dark_gray") \
|
||||
+ colored.fg("white") \
|
||||
+ " " + r"\1" + " " \
|
||||
+ colored.attr('reset'),
|
||||
re.sub(
|
||||
r"\*\*(.*?)\*\*",
|
||||
colored.attr('bold') \
|
||||
+ colored.fg("white") \
|
||||
+ r"\1" \
|
||||
+ colored.attr('reset'),
|
||||
text))
|
||||
|
||||
def _format_section(section_text, config=None, highlighter=None):
|
||||
|
||||
answer = ''
|
||||
|
||||
# cut code blocks
|
||||
block_number = 0
|
||||
while True:
|
||||
section_text, replacements = re.subn(
|
||||
'^```.*?^```',
|
||||
'MULTILINE_BLOCK_%s' % block_number,
|
||||
section_text,
|
||||
1,
|
||||
flags=re.S | re.MULTILINE)
|
||||
block_number += 1
|
||||
if not replacements:
|
||||
break
|
||||
|
||||
# cut links
|
||||
links = []
|
||||
while True:
|
||||
regexp = re.compile(r'\[(.*?)\]\((.*?)\)')
|
||||
match = regexp.search(section_text)
|
||||
if match:
|
||||
links.append(match.group(0))
|
||||
text = match.group(1)
|
||||
# links are not yet supported
|
||||
#
|
||||
text = '\x1B]8;;%s\x1B\\\\%s\x1B]8;;\x1B\\\\' % (match.group(2), match.group(1))
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
section_text, replacements = regexp.subn(
|
||||
text, # 'LINK_%s' % len(links),
|
||||
section_text,
|
||||
1)
|
||||
block_number += 1
|
||||
if not replacements:
|
||||
break
|
||||
|
||||
for paragraph in _split_into_paragraphs(section_text):
|
||||
answer += "\n".join(
|
||||
ansiwrap.fill(_colorize(line)) + "\n"
|
||||
for line in paragraph.splitlines()) + "\n"
|
||||
|
||||
return {
|
||||
'ansi': answer,
|
||||
'links': links
|
||||
}
|
0
lib/frontend/__init__.py
Normal file
0
lib/frontend/__init__.py
Normal file
129
lib/frontend/ansi.py
Normal file
129
lib/frontend/ansi.py
Normal file
@ -0,0 +1,129 @@
|
||||
"""
|
||||
ANSI frontend.
|
||||
|
||||
Exports:
|
||||
visualize(answer_data, request_options)
|
||||
|
||||
Format:
|
||||
answer_data = {
|
||||
'answers': '...',}
|
||||
|
||||
answers = [answer,...]
|
||||
|
||||
answer = {
|
||||
'topic': '...',
|
||||
'topic_type': '...',
|
||||
'answer': '...',
|
||||
'format': 'ansi|code|markdown|text...',
|
||||
}
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
|
||||
import colored
|
||||
from pygments import highlight as pygments_highlight
|
||||
from pygments.formatters import Terminal256Formatter # pylint: disable=no-name-in-module
|
||||
# pylint: disable=wrong-import-position
|
||||
sys.path.append(os.path.abspath(os.path.join(__file__, '..')))
|
||||
from globals import COLOR_STYLES
|
||||
import languages_data
|
||||
import beautifier # pylint: enable=wrong-import-position
|
||||
|
||||
import fmt.internal
|
||||
|
||||
def visualize(answer_data, request_options):
|
||||
"""
|
||||
Renders `answer_data` as ANSI output.
|
||||
"""
|
||||
answers = answer_data['answers']
|
||||
return _visualize(answers, request_options, search_mode=bool(answer_data['keyword']))
|
||||
|
||||
ANSI_ESCAPE = re.compile(r'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]')
|
||||
def remove_ansi(sometext):
|
||||
"""
|
||||
Remove ANSI sequences from `sometext` and convert it into plaintext.
|
||||
"""
|
||||
return ANSI_ESCAPE.sub('', sometext)
|
||||
|
||||
def _limited_answer(answer):
|
||||
return colored.bg('dark_goldenrod') + colored.fg('yellow_1') \
|
||||
+ ' ' + answer + ' ' \
|
||||
+ colored.attr('reset') + "\n"
|
||||
|
||||
def _colorize_ansi_answer(topic, answer, color_style, # pylint: disable=too-many-arguments
|
||||
highlight_all=True, highlight_code=False,
|
||||
unindent_code=False):
|
||||
|
||||
color_style = color_style or "native"
|
||||
lexer_class = languages_data.LEXER['bash']
|
||||
if '/' in topic:
|
||||
section_name = topic.split('/', 1)[0].lower()
|
||||
section_name = languages_data.get_lexer_name(section_name)
|
||||
lexer_class = languages_data.LEXER.get(section_name, lexer_class)
|
||||
if section_name == 'php':
|
||||
answer = "<?\n%s?>\n" % answer
|
||||
|
||||
if highlight_all:
|
||||
highlight = lambda answer: pygments_highlight(
|
||||
answer, lexer_class(), Terminal256Formatter(style=color_style)).strip('\n')+'\n'
|
||||
else:
|
||||
highlight = lambda x: x
|
||||
|
||||
if highlight_code:
|
||||
blocks = beautifier.code_blocks(
|
||||
answer, wrap_lines=True, unindent_code=(4 if unindent_code else False))
|
||||
highlighted_blocks = []
|
||||
for block in blocks:
|
||||
if block[0] == 1:
|
||||
this_block = highlight(block[1])
|
||||
else:
|
||||
this_block = block[1].strip('\n')+'\n'
|
||||
highlighted_blocks.append(this_block)
|
||||
|
||||
result = "\n".join(highlighted_blocks)
|
||||
else:
|
||||
result = highlight(answer).lstrip('\n')
|
||||
return result
|
||||
|
||||
def _visualize(answers, request_options, search_mode=False):
|
||||
|
||||
highlight = not bool(request_options and request_options.get('no-terminal'))
|
||||
color_style = request_options.get('style', '')
|
||||
if color_style not in COLOR_STYLES:
|
||||
color_style = ''
|
||||
|
||||
found = True
|
||||
result = ""
|
||||
for answer_dict in answers:
|
||||
topic = answer_dict['topic']
|
||||
topic_type = answer_dict['topic_type']
|
||||
answer = answer_dict['answer']
|
||||
found = found and not topic_type == 'unknown'
|
||||
|
||||
if answer_dict['format'] in ['ansi', 'text']:
|
||||
result += answer
|
||||
elif topic == ':firstpage-v1':
|
||||
result += fmt.internal.colorize_internal_firstpage_v1(answer)
|
||||
elif topic == 'LIMITED':
|
||||
result += _limited_answer(answer)
|
||||
else:
|
||||
result += _colorize_ansi_answer(
|
||||
topic, answer, color_style,
|
||||
highlight_all=highlight,
|
||||
highlight_code=(topic_type == 'question'
|
||||
and not request_options.get('add_comments')
|
||||
and not request_options.get('remove_text')))
|
||||
|
||||
if search_mode:
|
||||
if not highlight:
|
||||
result += "\n[%s]\n" % topic
|
||||
else:
|
||||
result += "\n%s%s %s %s%s\n" % (
|
||||
colored.bg('dark_gray'), colored.attr("res_underlined"),
|
||||
topic,
|
||||
colored.attr("res_underlined"), colored.attr('reset'))
|
||||
|
||||
result = result.strip('\n') + "\n"
|
||||
return result, found
|
106
lib/frontend/html.py
Normal file
106
lib/frontend/html.py
Normal file
@ -0,0 +1,106 @@
|
||||
from gevent.monkey import patch_all
|
||||
from gevent.subprocess import Popen, PIPE
|
||||
patch_all()
|
||||
|
||||
# pylint: disable=wrong-import-position,wrong-import-order
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
|
||||
MYDIR = os.path.abspath(os.path.join(__file__, '..', '..'))
|
||||
sys.path.append("%s/lib/" % MYDIR)
|
||||
|
||||
from globals import error, ANSI2HTML, GITHUB_REPOSITORY
|
||||
from buttons import TWITTER_BUTTON, GITHUB_BUTTON, GITHUB_BUTTON_FOOTER
|
||||
import frontend.ansi
|
||||
# pylint: disable=wrong-import-position,wrong-import-order
|
||||
|
||||
def visualize(answer_data, request_options):
|
||||
query = answer_data['query']
|
||||
answers = answer_data['answers']
|
||||
topics_list = answer_data['topics_list']
|
||||
editable = (len(answers) == 1 and answers[0]['topic_type'] == 'cheat.sheets')
|
||||
|
||||
repository_button = ''
|
||||
if len(answers) == 1:
|
||||
repository_button = _github_button(answers[0]['topic_type'])
|
||||
|
||||
result = frontend.ansi.visualize(answer_data, request_options)
|
||||
return _render_html(query, result, editable, repository_button, topics_list, request_options)
|
||||
|
||||
def _github_button(topic_type):
|
||||
|
||||
full_name = GITHUB_REPOSITORY.get(topic_type, '')
|
||||
if not full_name:
|
||||
return ''
|
||||
|
||||
short_name = full_name.split('/', 1)[1] # pylint: disable=unused-variable
|
||||
|
||||
button = (
|
||||
"<!-- Place this tag where you want the button to render. -->"
|
||||
'<a aria-label="Star %(full_name)s on GitHub"'
|
||||
' data-count-aria-label="# stargazers on GitHub"'
|
||||
' data-count-api="/repos/%(full_name)s#stargazers_count"'
|
||||
' data-count-href="/%(full_name)s/stargazers"'
|
||||
' data-icon="octicon-star"'
|
||||
' href="https://github.com/%(full_name)s"'
|
||||
' class="github-button">%(short_name)s</a>'
|
||||
) % locals()
|
||||
return button
|
||||
|
||||
def _render_html(query, result, editable, repository_button, topics_list, request_options):
|
||||
|
||||
def _html_wrapper(data):
|
||||
"""
|
||||
Convert ANSI text `data` to HTML
|
||||
"""
|
||||
proc = Popen(
|
||||
["bash", ANSI2HTML, "--palette=solarized", "--bg=dark"],
|
||||
stdin=PIPE, stdout=PIPE, stderr=PIPE)
|
||||
data = data.encode('utf-8')
|
||||
stdout, stderr = proc.communicate(data)
|
||||
if proc.returncode != 0:
|
||||
error(stdout + stderr)
|
||||
return stdout.decode('utf-8')
|
||||
|
||||
|
||||
result = result + "\n$"
|
||||
result = _html_wrapper(result)
|
||||
title = "<title>cheat.sh/%s</title>" % query
|
||||
submit_button = ('<input type="submit" style="position: absolute;'
|
||||
' left: -9999px; width: 1px; height: 1px;" tabindex="-1" />')
|
||||
topic_list = ('<datalist id="topics">%s</datalist>'
|
||||
% ("\n".join("<option value='%s'></option>" % x for x in topics_list)))
|
||||
|
||||
curl_line = "<span class='pre'>$ curl cheat.sh/</span>"
|
||||
if query == ':firstpage':
|
||||
query = ""
|
||||
form_html = ('<form action="/" method="GET"/>'
|
||||
'%s%s'
|
||||
'<input'
|
||||
' type="text" value="%s" name="topic"'
|
||||
' list="topics" autofocus autocomplete="off"/>'
|
||||
'%s'
|
||||
'</form>') \
|
||||
% (submit_button, curl_line, query, topic_list)
|
||||
|
||||
edit_button = ''
|
||||
if editable:
|
||||
# It's possible that topic directory starts with omitted underscore
|
||||
if '/' in query:
|
||||
query = '_' + query
|
||||
edit_page_link = 'https://github.com/chubin/cheat.sheets/edit/master/sheets/' + query
|
||||
edit_button = (
|
||||
'<pre style="position:absolute;padding-left:40em;overflow:visible;height:0;">'
|
||||
'[<a href="%s" style="color:cyan">edit</a>]'
|
||||
'</pre>') % edit_page_link
|
||||
result = re.sub("<pre>", edit_button + form_html + "<pre>", result)
|
||||
result = re.sub("<head>", "<head>" + title, result)
|
||||
if not request_options.get('quiet'):
|
||||
result = result.replace('</body>',
|
||||
TWITTER_BUTTON \
|
||||
+ GITHUB_BUTTON \
|
||||
+ repository_button \
|
||||
+ GITHUB_BUTTON_FOOTER \
|
||||
+ '</body>')
|
||||
return result
|
@ -20,11 +20,11 @@ from languages_data import LANGUAGE_ALIAS, SO_NAME, rewrite_editor_section_name
|
||||
|
||||
import adapter.cheat_sheets
|
||||
import adapter.cmd
|
||||
import adapter.latenz
|
||||
import adapter.question
|
||||
import adapter.internal
|
||||
import adapter.rosetta
|
||||
import adapter.latenz
|
||||
import adapter.learnxiny
|
||||
import adapter.question
|
||||
import adapter.rosetta
|
||||
|
||||
class Router(object):
|
||||
|
||||
@ -61,42 +61,39 @@ class Router(object):
|
||||
"fosdem": adapter.cmd.Fosdem(),
|
||||
"translation": adapter.cmd.Translation(),
|
||||
"rosetta": adapter.rosetta.Rosetta(),
|
||||
"late.nz": adapter.latenz.Latenz(),
|
||||
"question": adapter.question.Question(),
|
||||
"cheat.sheets": adapter.cheat_sheets.CheatSheets(),
|
||||
"cheat.sheets dir": adapter.cheat_sheets.CheatSheetsDir(),
|
||||
"learnxiny": adapter.learnxiny.LearnXinY(),
|
||||
}
|
||||
|
||||
self._topic_list = {
|
||||
"late.nz": adapter.latenz.get_list(),
|
||||
"cheat.sheets": adapter.cheat_sheets.get_list(),
|
||||
"cheat.sheets dir": adapter.cheat_sheets.get_dirs_list(),
|
||||
"learnxiny": adapter.learnxiny.get_learnxiny_list(),
|
||||
key: obj.get_list()
|
||||
for key, obj in self._adapter.items()
|
||||
}
|
||||
for key, obj in self._adapter.items():
|
||||
self._topic_list[key] = obj.get_list()
|
||||
|
||||
self._topic_found = {
|
||||
"late.nz": adapter.latenz.is_found,
|
||||
"cheat.sheets": adapter.cheat_sheets.is_found,
|
||||
"cheat.sheets dir": adapter.cheat_sheets.is_dir_found,
|
||||
"learnxiny": adapter.learnxiny.is_valid_learnxy,
|
||||
key: obj.is_found
|
||||
for key, obj in self._adapter.items()
|
||||
}
|
||||
for key, obj in self._adapter.items():
|
||||
self._topic_found[key] = obj.is_found
|
||||
|
||||
# topic_type, function_getter
|
||||
# should be replaced with a decorator
|
||||
# pylint: disable=bad-whitespace
|
||||
self.topic_getters = (
|
||||
("late.nz", adapter.latenz.get_answer),
|
||||
("cheat.sheets", adapter.cheat_sheets.get_page),
|
||||
("cheat.sheets dir", adapter.cheat_sheets.get_dir),
|
||||
("learnxiny", adapter.learnxiny.get_learnxiny),
|
||||
("question", adapter.question.get_page),
|
||||
("fosdem", self._adapter["fosdem"].get_page),
|
||||
("rosetta", self._adapter["rosetta"].get_page),
|
||||
("tldr", self._adapter["tldr"].get_page),
|
||||
("internal", self._adapter["internal"].get_page),
|
||||
("cheat", self._adapter["cheat"].get_page),
|
||||
("translation", self._adapter["translation"].get_page),
|
||||
("unknown", self._adapter["unknown"].get_page),
|
||||
("cheat.sheets", self._adapter["cheat.sheets"].get_page_dict),
|
||||
("cheat.sheets dir", self._adapter["cheat.sheets dir"].get_page_dict),
|
||||
("learnxiny", self._adapter["learnxiny"].get_page_dict),
|
||||
("question", self._adapter["question"].get_page_dict),
|
||||
("fosdem", self._adapter["fosdem"].get_page_dict),
|
||||
("late.nz", self._adapter["late.nz"].get_page_dict),
|
||||
("rosetta", self._adapter["rosetta"].get_page_dict),
|
||||
("tldr", self._adapter["tldr"].get_page_dict),
|
||||
("internal", self._adapter["internal"].get_page_dict),
|
||||
("cheat", self._adapter["cheat"].get_page_dict),
|
||||
("translation", self._adapter["translation"].get_page_dict),
|
||||
("unknown", self._adapter["unknown"].get_page_dict),
|
||||
)
|
||||
# pylint: enable=bad-whitespace
|
||||
|
||||
@ -120,10 +117,10 @@ class Router(object):
|
||||
answer.update({name:key for name in self._topic_list[key]})
|
||||
answer = sorted(set(answer.keys()))
|
||||
|
||||
# doing it in this strange way to save the order of the topics
|
||||
for topic in adapter.learnxiny.get_learnxiny_list():
|
||||
if topic not in answer:
|
||||
answer.append(topic)
|
||||
# # doing it in this strange way to save the order of the topics
|
||||
# for topic in adapter.learnxiny.get_learnxiny_list():
|
||||
# if topic not in answer:
|
||||
# answer.append(topic)
|
||||
|
||||
self._cached_topics_list = answer
|
||||
return answer
|
||||
@ -159,7 +156,7 @@ class Router(object):
|
||||
|
||||
# topic contains '/'
|
||||
#
|
||||
if adapter.learnxiny.is_valid_learnxy(topic):
|
||||
if self._adapter['learnxiny'].is_found(topic):
|
||||
return 'learnxiny'
|
||||
topic_type = topic.split('/', 1)[0]
|
||||
if topic_type in ['ru', 'fr'] or re.match(r'[a-z][a-z]-[a-z][a-z]$', topic_type):
|
||||
@ -337,10 +334,20 @@ def get_answer(topic, keyword, options="", request_options=None): # pylint: disa
|
||||
if filetype.startswith('q:'):
|
||||
filetype = filetype[2:]
|
||||
|
||||
answer = beautifier.beautify(answer.encode('utf-8'), filetype, request_options)
|
||||
answer['answer'] = beautifier.beautify(answer['answer'].encode('utf-8'), filetype, request_options)
|
||||
|
||||
# if isinstance(answer, str):
|
||||
# answer_dict = {
|
||||
# 'topic': topic,
|
||||
# 'topic_type': topic_type,
|
||||
# 'answer': answer,
|
||||
# 'format': 'code',
|
||||
# }
|
||||
# else:
|
||||
answer_dict = answer
|
||||
|
||||
if not keyword:
|
||||
return answer
|
||||
return answer_dict
|
||||
|
||||
#
|
||||
# shorten the answer, because keyword is specified
|
||||
@ -357,6 +364,7 @@ def get_answer(topic, keyword, options="", request_options=None): # pylint: disa
|
||||
return ""
|
||||
|
||||
answer = _join_paragraphs(paragraphs)
|
||||
|
||||
return answer
|
||||
|
||||
def find_answer_by_keyword(directory, keyword, options="", request_options=None):
|
||||
@ -379,10 +387,13 @@ def find_answer_by_keyword(directory, keyword, options="", request_options=None)
|
||||
|
||||
answer = get_answer(topic, keyword, options=options, request_options=request_options)
|
||||
if answer:
|
||||
answer_paragraphs.append((topic, answer))
|
||||
answer_paragraphs.append(answer)
|
||||
|
||||
if len(answer_paragraphs) > MAX_SEARCH_LEN:
|
||||
answer_paragraphs.append(("LIMITED", "LIMITED TO %s ANSWERS" % MAX_SEARCH_LEN))
|
||||
answer_paragraphs.append({
|
||||
'topic_type': 'LIMITED',
|
||||
'answer': "LIMITED TO %s ANSWERS" % MAX_SEARCH_LEN,
|
||||
})
|
||||
break
|
||||
|
||||
return answer_paragraphs
|
||||
|
Loading…
Reference in New Issue
Block a user