Merge pull request #6167 from Textualize/guess-lexer

better lexer guessing
This commit is contained in:
Will McGugan
2025-10-08 21:47:28 +01:00
committed by GitHub
3 changed files with 23 additions and 11 deletions

View File

@@ -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 not lexer:
if lexer is None:
from pygments.lexers import guess_lexer
try:
lexer = guess_lexer(code)
except Exception:
pass
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

View File

@@ -870,12 +870,13 @@ class MarkdownFence(MarkdownBlock):
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):

View File

@@ -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"),
("<xml>", "foo.xml", "xml"),
("{}", "data.json", "json"),
("#! python", "", "python"),
("", "foo.py", "python"),
],
)
def test_guess_language(code: str, path: str, language: str) -> None: