from textual.widgets import Static from textual.containers import Container from rich.syntax import Syntax from pygments.lexers import get_lexer_for_mimetype from pygments.util import ClassNotFound # mime list from gemini://geminispace.info/statistics mimetolexer = { "text/x-diff": "diff", "text/x-csrc": "c", "text/x-python": "python", "text/markdown": "markdown", "text/x-patch": "diff", "text/x-c++src": "cpp", "text/x-pascal": "pascal", "text/x-c++hdr": "cpp", "text/x-go": "go", "text/x-rust": "rust", "text/css": "css", "text/x-php": "php", # Hopefully, future handlers will take care of these mimes first. "application/json": "json", "text/html": "html", "application/xml": "xml", "application/atom+xml": "xml", "text/xml": "xml", "image/svg+xml": "xml", # Less popular mimes will call out to get_lexer_for_mimetype } class HighlightedCode(Static): """Plaintext widget.""" def __init__(self, fp, id, mime): super().__init__(id=id) self.addblock(fp, mime) def addblock(self, fp, mime): code = "" # Read the whole file first, then render it all. # Hopefully not a problem, because source code should have terminal length. for lin in fp: line = lin if type(line) is bytes: line = line.decode("UTF-8") code += line lexer = mimetolexer[mime] if mime in mimetolexer else get_lexer_for_mimetype(mime) self.mount(Static(Syntax(code,lexer=lexer,background_color="default"))) def can_handle_mime(mime): if mime in mimetolexer: return True try: get_lexer_for_mimetype(mime) return True except ClassNotFound: pass return False