diff --git a/src/textual/highlight.py b/src/textual/highlight.py index fc0ed33abf..2b899f9237 100644 --- a/src/textual/highlight.py +++ b/src/textual/highlight.py @@ -52,7 +52,7 @@ class HighlightTheme: } -def guess_language(code: str, path: str) -> str: +def guess_language(code: str, path: str | None) -> str: """Guess the language based on the code and path. The result may be used in the [highlight][textual.highlight.highlight] function. @@ -64,19 +64,28 @@ def guess_language(code: str, path: str) -> str: The language, suitable for use with Pygments. """ - if path is not None and os.path.splitext(path)[-1] == ".tcss": + if path and os.path.splitext(path)[-1] == ".tcss": # A special case for TCSS files which aren't known outside of Textual return "scss" lexer: Lexer | None = None lexer_name = "default" if code: - try: - lexer = guess_lexer_for_filename(path, code) - except ClassNotFound: - pass + if path: + try: + lexer = guess_lexer_for_filename(path, code) + except ClassNotFound: + pass + + if lexer is None: + from pygments.lexers import guess_lexer + + try: + lexer = guess_lexer(code) + except Exception: + pass - if not lexer: + if not lexer and path: try: _, ext = os.path.splitext(path) if ext: @@ -113,9 +122,7 @@ def highlight( Returns: A Content instance which may be used in a widget. """ - if language is None: - if path is None: - raise RuntimeError("One of 'language' or 'path' must be supplied.") + if not language: language = guess_language(code, path) assert language is not None diff --git a/src/textual/widgets/_markdown.py b/src/textual/widgets/_markdown.py index 397b265b50..786dd67403 100644 --- a/src/textual/widgets/_markdown.py +++ b/src/textual/widgets/_markdown.py @@ -870,12 +870,13 @@ def __init__(self, markdown: Markdown, token: Token, code: str) -> None: self.lexer = token.info self._highlighted_code = self.highlight(self.code, self.lexer) + @property def allow_horizontal_scroll(self) -> bool: return True @classmethod def highlight(cls, code: str, language: str) -> Content: - return highlight(code, language=language) + return highlight(code, language=language or None) def _copy_context(self, block: MarkdownBlock) -> None: if isinstance(block, MarkdownFence): diff --git a/tests/test_highlight.py b/tests/test_highlight.py index 0178835221..a2a116860a 100644 --- a/tests/test_highlight.py +++ b/tests/test_highlight.py @@ -19,9 +19,13 @@ def test_highlight() -> None: @pytest.mark.parametrize( "code,path,language", [ + ("", "", "default"), + ("# Don't matter", "foo.tcss", "scss"), ("import this", "foo.py", "python"), ("", "foo.xml", "xml"), ("{}", "data.json", "json"), + ("#! python", "", "python"), + ("", "foo.py", "python"), ], ) def test_guess_language(code: str, path: str, language: str) -> None: