2022-09-20 15:56:32 +01:00
"""
Code browser example.
Run with:
python code_browser.py PATH
"""
2024-12-03 10:04:58 +00:00
from __future__ import annotations
2022-09-08 11:00:05 +01:00
import sys
2025-07-16 18:31:35 +01:00
from pathlib import Path
2022-09-08 11:00:05 +01:00
from rich . traceback import Traceback
2022-10-27 14:30:22 +01:00
2022-09-08 11:00:05 +01:00
from textual . app import App , ComposeResult
2023-03-08 18:31:24 +00:00
from textual . containers import Container , VerticalScroll
2025-07-16 18:31:35 +01:00
from textual . highlight import highlight
2024-11-13 17:38:46 +00:00
from textual . reactive import reactive , var
2022-09-08 11:00:05 +01:00
from textual . widgets import DirectoryTree , Footer , Header , Static
class CodeBrowser ( App ) :
2022-09-08 21:11:05 +01:00
""" Textual code browser app. """
2023-08-22 11:48:17 +01:00
CSS_PATH = " code_browser.tcss "
2022-09-08 21:11:05 +01:00
BINDINGS = [
2022-09-09 15:54:16 +01:00
( " f " , " toggle_files " , " Toggle Files " ) ,
2022-09-08 21:11:05 +01:00
( " q " , " quit " , " Quit " ) ,
]
2022-09-08 11:00:05 +01:00
2022-09-09 17:52:08 +01:00
show_tree = var ( True )
2024-11-13 17:38:46 +00:00
path : reactive [ str | None ] = reactive ( None )
2022-09-08 11:00:05 +01:00
def watch_show_tree ( self , show_tree : bool ) - > None :
2022-09-08 21:11:05 +01:00
""" Called when show_tree is modified. """
2022-10-27 14:30:22 +01:00
self . set_class ( show_tree , " -show-tree " )
2022-09-08 11:00:05 +01:00
def compose ( self ) - > ComposeResult :
2022-09-08 21:11:05 +01:00
""" Compose our UI. """
2022-09-08 11:00:05 +01:00
path = " ./ " if len ( sys . argv ) < 2 else sys . argv [ 1 ]
yield Header ( )
2023-02-20 16:16:10 +00:00
with Container ( ) :
yield DirectoryTree ( path , id = " tree-view " )
2023-03-08 18:31:24 +00:00
with VerticalScroll ( id = " code-view " ) :
2023-02-20 16:26:12 +00:00
yield Static ( id = " code " , expand = True )
2022-09-08 11:00:05 +01:00
yield Footer ( )
2023-08-08 11:39:32 +01:00
def on_mount ( self ) - > None :
2022-10-27 14:30:22 +01:00
self . query_one ( DirectoryTree ) . focus ( )
2024-11-13 17:38:46 +00:00
def theme_change ( _signal ) - > None :
""" Force the syntax to use a different theme. """
self . watch_path ( self . path )
self . theme_changed_signal . subscribe ( self , theme_change )
2022-11-19 17:21:23 +00:00
def on_directory_tree_file_selected (
self , event : DirectoryTree . FileSelected
) - > None :
2022-09-08 21:11:05 +01:00
""" Called when the user click a file in the directory tree. """
2022-11-19 17:21:23 +00:00
event . stop ( )
2024-11-13 17:38:46 +00:00
self . path = str ( event . path )
def watch_path ( self , path : str | None ) - > None :
""" Called when path changes. """
2022-09-08 11:00:05 +01:00
code_view = self . query_one ( " #code " , Static )
2024-11-13 17:38:46 +00:00
if path is None :
code_view . update ( " " )
return
2022-09-08 11:00:05 +01:00
try :
2025-07-16 18:31:35 +01:00
code = Path ( path ) . read_text ( encoding = " utf-8 " )
syntax = highlight ( code , path = path )
2022-09-08 11:00:05 +01:00
except Exception :
2022-09-13 10:53:22 +01:00
code_view . update ( Traceback ( theme = " github-dark " , width = None ) )
2022-09-08 11:00:05 +01:00
self . sub_title = " ERROR "
else :
code_view . update ( syntax )
self . query_one ( " #code-view " ) . scroll_home ( animate = False )
2024-11-13 17:38:46 +00:00
self . sub_title = path
2022-09-08 11:00:05 +01:00
2022-09-09 15:54:16 +01:00
def action_toggle_files ( self ) - > None :
2022-09-20 15:42:08 +01:00
""" Called in response to key binding. """
2022-09-08 11:00:05 +01:00
self . show_tree = not self . show_tree
if __name__ == " __main__ " :
2022-09-18 22:02:08 +01:00
CodeBrowser ( ) . run ( )