Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 37 additions & 24 deletions src/textual/_tree_sitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,51 @@
try:
from tree_sitter import Language

_LANGUAGE_CACHE: dict[str, Language] = {}
except ImportError:
_tree_sitter = False

def get_language(language_name: str) -> Language | None:
return None

else:
_tree_sitter = True
try:
import tree_sitter_language_pack as tslp
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a no abbreviation policy in Textual. I know its verbose, but let's not alias this.


except ImportError:
_LANGUAGE_CACHE: dict[str, Language] = {}

def get_language(language_name: str) -> Language | None:
if language_name in _LANGUAGE_CACHE:
return _LANGUAGE_CACHE[language_name]

def get_language(language_name: str) -> Language | None:
if language_name in _LANGUAGE_CACHE:
return _LANGUAGE_CACHE[language_name]

try:
module = import_module(f"tree_sitter_{language_name}")
except ImportError:
return None
else:
try:
if language_name == "xml":
# xml uses language_xml() instead of language()
# it's the only outlier amongst the languages in the `textual[syntax]` extra
language = Language(module.language_xml())
else:
language = Language(module.language())
except (OSError, AttributeError):
log.warning(f"Could not load language {language_name!r}.")
module = import_module(f"tree_sitter_{language_name}")
except ImportError:
return None
else:
_LANGUAGE_CACHE[language_name] = language
return language
try:
if language_name == "xml":
# xml uses language_xml() instead of language()
# it's the only outlier amongst the languages in the `textual[syntax]` extra
language = Language(module.language_xml())
else:
language = Language(module.language())
except (OSError, AttributeError):
log.warning(f"Could not load language {language_name!r}.")
return None
else:
_LANGUAGE_CACHE[language_name] = language
return language

except ImportError:
_tree_sitter = False
else:

def get_language(language_name: str) -> Language | None:
return None
def get_language(language_name: str) -> Language | None:
try:
return tslp.get_language(language_name)
except LookupError:
log.warning(f"Could not load language {language_name!r}.")
return None
Comment on lines +18 to +54
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new tree-sitter-language-pack integration lacks test coverage. Consider adding tests that verify:

  1. The fallback chain works correctly (tslp -> individual packages -> None)
  2. get_language returns the correct Language object when using tslp
  3. Warning messages are logged appropriately when language loading fails via tslp
  4. The behavior is consistent between tslp and individual package loading

This is important because the new code path has not been validated through automated testing, which could lead to runtime issues if the tslp package behaves differently than expected.

Copilot uses AI. Check for mistakes.
Comment on lines +49 to +54
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The XML language special case handling (using language_xml() instead of language()) is only present in the individual package loading path but not in the tree-sitter-language-pack path. This assumes that tree-sitter-language-pack handles the XML special case internally.

Please verify that tree-sitter-language-pack correctly handles XML language loading, or add explicit handling for XML in the tslp code path if needed. If the special case is indeed handled by tslp, consider adding a comment explaining why the special case is not needed here.

Copilot uses AI. Check for mistakes.


TREE_SITTER = _tree_sitter
Loading