-
Notifications
You must be signed in to change notification settings - Fork 844
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(text-area): new languages #4160
Conversation
@@ -1,13 +1,43 @@ | |||
BUILTIN_LANGUAGES = sorted( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've found the TheRenegadeCoder/sample-programs repo useful for checking how these languages render.
App for Testing
"""
TextArea Example
"""
import os
import pathlib
from traceback import format_exc
from typing import Any, ClassVar
import click
from textual import on
from textual.app import ComposeResult, App
from textual.binding import BindingType, Binding
from textual.containers import Horizontal
from textual.widgets import TextArea, DirectoryTree, Header, Footer
KNOWN_EXTENSIONS = {
"bash": [".sh", ".bash"],
"c": [".c"],
"c_sharp": [".cs"],
"cpp": [".cpp", ".hpp", ".h"],
"css": [".css"],
"dockerfile": ["Dockerfile"],
"dot": [".dot"],
"elisp": [".el"],
"elixir": [".ex", ".exs"],
"elm": [".elm"],
"embedded_template": [".et"],
"erlang": [".erl", ".hrl"],
"fortran": [".f", ".f90", ".f95", ".f03", ".f08", ".f18", ".for", ".f77"],
"go": [".go"],
"gomod": ["go.mod", "go.sum"],
"hack": [".hh"],
"haskell": [".hs"],
"html": [".html", ".htm"],
"java": [".java"],
"javascript": [".js", ".jsx"],
"jsdoc": [".jsdoc"],
"json": [".json"],
"kotlin": [".kt"],
"make": ["Makefile", "makefile", "GNUmakefile"],
"markdown": [".md", ".markdown", ".mdown", ".mkdn"],
"objc": [".m", ".mm"],
"ocaml": [".ml", ".mli"],
"php": [".php", ".php5", ".php7"],
"python": [".py"],
"r": [".r"],
"regex": [".regex"],
"ruby": [".rb", ".erb"],
"rust": [".rs"],
"scala": [".scala"],
"sql": [".sql"],
"sqlite": [".sqlite"],
"toml": [".toml"],
"typescript": [".ts", ".tsx"],
"yaml": [".yaml", ".yml"],
}
EXTENSION_TO_LANGUAGE_MAPPING = {
extension: language
for language, extensions in KNOWN_EXTENSIONS.items()
for extension in extensions
}
class TextAreaApp(App):
CSS: ClassVar[
str
] = """
DirectoryTree {
width: auto;
}
"""
BINDINGS: ClassVar[list[BindingType]] = [
Binding("q", "quit", "Quit", show=True, priority=True),
Binding("t", "theme", "Change Theme", show=True, priority=True),
]
def __init__(self, path: os.PathLike, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
self.path = pathlib.Path(path)
self.directory_tree = DirectoryTree(path=self.path)
self.text_area = TextArea(
theme="monokai",
soft_wrap=False,
show_line_numbers=True,
)
def compose(self) -> ComposeResult:
yield Header()
yield Horizontal(self.directory_tree, self.text_area)
yield Footer()
@on(DirectoryTree.FileSelected)
def handle_file_selected(self, event: DirectoryTree.FileSelected) -> None:
self.path = pathlib.Path(event.path)
try:
new_text = event.path.read_text()
extension_mapping = EXTENSION_TO_LANGUAGE_MAPPING.get(
self.path.suffix.lower()
)
filename_mapping = EXTENSION_TO_LANGUAGE_MAPPING.get(self.path.name)
language = extension_mapping or filename_mapping or "regex"
self.text_area.text = new_text
self.text_area.language = language
except: # noqa
self.text_area.language = "regex"
self.text_area.text = f"Error: {format_exc()}"
finally:
self.sub_title = f"[{self.text_area.language}] [{self.text_area.theme}]"
def action_theme(self) -> None:
all_themes = list(self.text_area.available_themes)
current_index = all_themes.index(self.text_area.theme)
next_index = (current_index + 1) % len(all_themes)
self.text_area.theme = all_themes[next_index]
self.sub_title = f"[{self.text_area.language}] [{self.text_area.theme}]"
@click.command()
@click.argument(
"path", type=click.Path(exists=True, dir_okay=True, file_okay=False), default="."
)
def cli(path: os.PathLike) -> None:
"""
TextArea CLI
"""
app = TextAreaApp(path=path)
app.run()
if __name__ == "__main__":
cli()
"toml", | ||
"typescript", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typescript has issues
NameError: Invalid node type satisfies
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I've found that this means the parser and highlights file is out of sync: the highlight query file has been updated to refer to a node type that the parser is unaware of. Usually moving to an older version of the highlights file (the version that "aligns" with the parser) fixes the problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh, you're right. Once I pin on tree-sitter-languages==1.10.2
, which is the version I used to map to the .scm
files, everything works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EDIT: See #4160 (comment)
@@ -1,64 +1,13 @@ | |||
(tag_name) @tag | |||
(erroneous_end_tag_name) @html.end_tag_error | |||
(erroneous_end_tag_name) @tag.error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll have to be mindful about changing existing highlight queries as it'll break features of some themes. For example, @html.end_tag_error
is used in a couple of builtin themes:
textual/src/textual/_text_area_theme.py
Line 217 in 5dfa644
"html.end_tag_error": Style(color="red", underline=True), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that the @html.end_tag_error
is completely arbitrary (a choice by us), so we could call it anything we want. However, if anyone has created their own theme it would no longer map correctly if we change it.
I'd recommend reading the section on Syntax Highlighting in the TextArea docs. The syntax highlighting won't work simply by including the highlights For example if you take a look at the queries in |
😞 dang - I was really hoping the hard part was done on this. I will take a closer look as I get some more time on this. Any assistance from tree-sitter <-> textual SMEs is appreciated. |
I branched off of this and checked things work for a few popular languages in #4350 - when Will merged it I think it automatically closed this one. We're still open to other new languages though, but I think it'll need to be a new PR now! |
This PR adds new builtin languages for the TextArea Widget.
Commits
highlights.scm
files - you may want to disregard these if custom work has been done.Language Sources
The following us a summary for where each
.scm
file originated from. This was done by parsing the latest grantjenks/py-tree-sitter-languages/repos.txt - everything was checked on thev1.10.2
tag.Missing Languages
Languages without explicit license
Languages without
highlights.scm
fileIt's possible these have other
.scm
files. See grantjenks/py-tree-sitter-languages/repos.txt for links