Skip to content

Allow using tree-sitter-language-pack if installed#6344

Open
dhirschfeld wants to merge 2 commits intoTextualize:mainfrom
dhirschfeld:tslp-support
Open

Allow using tree-sitter-language-pack if installed#6344
dhirschfeld wants to merge 2 commits intoTextualize:mainfrom
dhirschfeld:tslp-support

Conversation

@dhirschfeld
Copy link

Resolves #6324

In [1]: import tree_sitter_language_pack as tslp

In [2]: tslp.get_language('python')
Out[2]: <Language id=140111323851968, version=15, name="python">

In [3]: from textual._tree_sitter import get_language

In [4]: get_language('python')
Out[4]: <Language id=140111323851968, version=15, name="python">

In [5]: get_language??
Signature: get_language(language_name: 'str') -> 'Language | None'
Docstring: <no docstring>
Source:
        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
File:      ~/code/github/textualize/textual/src/textual/_tree_sitter.py
Type:      function

In [6]:

@dhirschfeld
Copy link
Author

bump!

@dhirschfeld
Copy link
Author

🙏

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request adds support for the tree-sitter-language-pack package as an alternative way to load tree-sitter language parsers in Textual. The implementation creates a fallback chain: if tree-sitter-language-pack is installed, it will be used to load languages; otherwise, Textual falls back to loading individual tree-sitter-{language} packages as before.

Changes:

  • Restructured the tree-sitter import logic to support optional tree-sitter-language-pack
  • Maintained backward compatibility with individual tree-sitter language packages
  • Preserved consistent error handling and logging across both code paths

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +18 to +54
try:
import tree_sitter_language_pack as tslp

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
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
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
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.
Copy link
Member

@willmcgugan willmcgugan left a comment

Choose a reason for hiding this comment

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

Just a small request.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants