From 6bc487313837c3e29f4b9fc8cb253cdfab367932 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 16 Jul 2025 18:31:35 +0100 Subject: [PATCH] updated code browser to use new code highlight --- examples/code_browser.py | 12 ++++------- examples/code_browser.tcss | 2 ++ src/textual/highlight.py | 44 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/examples/code_browser.py b/examples/code_browser.py index d693cd0e2..56382f1a6 100644 --- a/examples/code_browser.py +++ b/examples/code_browser.py @@ -9,12 +9,13 @@ Run with: from __future__ import annotations import sys +from pathlib import Path -from rich.syntax import Syntax from rich.traceback import Traceback from textual.app import App, ComposeResult from textual.containers import Container, VerticalScroll +from textual.highlight import highlight from textual.reactive import reactive, var from textual.widgets import DirectoryTree, Footer, Header, Static @@ -68,13 +69,8 @@ class CodeBrowser(App): code_view.update("") return try: - syntax = Syntax.from_path( - path, - line_numbers=True, - word_wrap=False, - indent_guides=True, - theme="github-dark" if self.current_theme.dark else "github-light", - ) + code = Path(path).read_text(encoding="utf-8") + syntax = highlight(code, path=path) except Exception: code_view.update(Traceback(theme="github-dark", width=None)) self.sub_title = "ERROR" diff --git a/examples/code_browser.tcss b/examples/code_browser.tcss index d3f179330..7d2e5f625 100644 --- a/examples/code_browser.tcss +++ b/examples/code_browser.tcss @@ -26,4 +26,6 @@ CodeBrowser.-show-tree #tree-view { } #code { width: auto; + padding: 0 1; + background: $surface; } diff --git a/src/textual/highlight.py b/src/textual/highlight.py index a64668a88..cea129036 100644 --- a/src/textual/highlight.py +++ b/src/textual/highlight.py @@ -1,6 +1,9 @@ from __future__ import annotations -from pygments.lexers import get_lexer_by_name +import os + +from pygments.lexer import Lexer +from pygments.lexers import get_lexer_by_name, guess_lexer_for_filename from pygments.token import Token from pygments.util import ClassNotFound @@ -16,6 +19,8 @@ class HighlightTheme: Token.Comment: "$text 60%", Token.Error: "$error on $error-muted", Token.Generic.Error: "$error on $error-muted", + Token.Generic.Heading: "$primary underline", + Token.Generic.Subheading: "$primary", Token.Keyword: "$accent", Token.Keyword.Constant: "bold $success 80%", Token.Keyword.Namespace: "$error", @@ -45,8 +50,9 @@ class HighlightTheme: def highlight( code: str, - language: str = "text", *, + language: str | None = None, + path: str | None = None, theme: type[HighlightTheme] = HighlightTheme, tab_size: int = 8, ) -> Content: @@ -61,6 +67,40 @@ def highlight( Returns: A Content instance which may be used in a widget. """ + if language is None and path is None: + raise RuntimeError("One of 'language' or 'path' must be supplied.") + + if language is None and path is not None: + if os.path.splitext(path)[-1] == ".tcss": + language = "scss" + + if language is None and path is not None: + lexer: Lexer | None = None + lexer_name = "default" + if code: + try: + lexer = guess_lexer_for_filename(path, code) + except ClassNotFound: + pass + + if not lexer: + try: + _, ext = os.path.splitext(path) + if ext: + extension = ext.lstrip(".").lower() + lexer = get_lexer_by_name(extension) + except ClassNotFound: + pass + + if lexer: + if lexer.aliases: + lexer_name = lexer.aliases[0] + else: + lexer_name = lexer.name + + language = lexer_name + + assert language is not None code = "\n".join(code.splitlines()) try: lexer = get_lexer_by_name(