Skip to main content

Intelligent syntax highlighting and validation for Python template strings (PEP 750)

Project description

t-linter ๐Ÿโœจ

Intelligent syntax highlighting, validation, and formatting for Python template strings (PEP 750).

License: MIT PyPI VSCode Marketplace

๐Ÿ“ฃ ๐Ÿ’ผ Maintainer update: Open to opportunities. ๐Ÿ”— koxudaxi.dev

๐Ÿ“– Documentation

๐Ÿ‘‰ t-linter.koxudaxi.dev

  • ๐Ÿ“ฆ Installation - PyPI, VSCode Extension, Build from source
  • ๐Ÿ” Check Command - CLI validation & output formats
  • ๐Ÿงน Format Command - Canonical formatting for supported templates
  • ๐Ÿ–ฅ๏ธ LSP Server - Editor integration (VSCode, Claude Code, Codex, Neovim, etc.)
  • โš™๏ธ Configuration - pyproject.toml & ignore files
  • ๐ŸŒ Supported Languages - HTML, T-HTML, TDOM, SQL, JS, CSS, JSON, YAML, TOML

Overview

t-linter validates and formats embedded languages inside Python template strings (PEP 750). Built with Rust and Tree-sitter for speed, it ships as a single binary that works as both a CLI tool and an LSP server.

  • t-linter check โ€” validate template string syntax
  • t-linter format โ€” canonically reformat supported template literals
  • t-linter lsp โ€” start the Language Server Protocol server for editor integration

Features

  • ๐Ÿ” Linting - Detect syntax errors in embedded HTML, JSON, YAML, TOML, CSS, JavaScript, SQL
  • ๐Ÿงน Formatting - Canonical formatting for HTML, T-HTML, TDOM, JSON, YAML, TOML templates
  • ๐ŸŽจ Syntax Highlighting - Smart highlighting via LSP semantic tokens
  • ๐Ÿ”ง Type-based Detection - Understands Annotated[Template, "html"] and type aliases
  • ๐Ÿงฉ Callee Inference - Detects backend languages from helpers such as tdom.html(...)
  • ๐Ÿš€ Fast - Single Rust binary with Tree-sitter parsers

Supported Languages

Language Annotation Check Format Highlight
HTML "html" โœ… โœ… โœ…
T-HTML "thtml" โœ… โœ… โœ…
TDOM "tdom" โœ… โœ… โœ…
JSON "json" โœ… โœ… โœ…
YAML "yaml", "yml" โœ… โœ… โœ…
TOML "toml" โœ… โœ… โœ…
CSS "css" โœ… โ€” โœ…
JavaScript "javascript", "js" โœ… โ€” โœ…
SQL "sql" โœ… โ€” โœ…

For HTML, T-HTML, TDOM, JSON, YAML, and TOML, t-linter uses dedicated Rust backends (tstring-* crates) for strict validation and canonical formatting. CSS, JavaScript, and SQL use Tree-sitter for syntax validation and highlighting.

Installation

PyPI (Recommended)

pip install t-linter

Or add to your project's dependencies:

# Using uv (recommended)
uv add t-linter

# Or using pip with requirements.txt
echo "t-linter" >> requirements.txt
pip install -r requirements.txt

This provides the t-linter CLI tool and LSP server.

โ†’ View on PyPI

VSCode Extension

If you use VSCode, install the extension for seamless editor integration:

  1. Open VSCode โ†’ Extensions (Ctrl+Shift+X / Cmd+Shift+X)
  2. Search for "t-linter" โ†’ Install "T-Linter" by koxudaxi
  3. On Linux x64, macOS x64/arm64, and Windows x64, the extension bundles the t-linter binary, so no separate PyPI install is required.
  4. On unsupported platforms, or if you want to override the bundled binary, install t-linter via PyPI (see above) and set t-linter.serverPath to the full executable path.
  5. Choose one save-time formatting mode:
    • Ruff coexistence mode keeps Ruff as the Python formatter and runs t-linter through source.fixAll.t-linter:
      {
        "[python]": {
          "editor.defaultFormatter": "charliermarsh.ruff",
          "editor.formatOnSave": true,
          "editor.codeActionsOnSave": {
            "source.fixAll.t-linter": "explicit"
          }
        }
      }
      
    • t-linter formatter mode keeps the existing formatter-only workflow:
      {
        "[python]": {
          "editor.defaultFormatter": "koxudaxi.t-linter",
          "editor.formatOnSave": true
        }
      }
      
    VSCode only allows one default formatter per language, which is why t-linter also exposes a save-time code action lane for template-string formatting.
  6. If semantic highlighting conflicts with another Python extension, set the Python language server to "None" in your workspace settings:
    {
        "python.languageServer": "None"
    }
    
    If you need those features, you can keep the Python language server enabled. t-linter will still provide template-string diagnostics, code actions, and formatting, though semantic highlighting may conflict.

If you use an external t-linter binary and it is not in your PATH, set t-linter.serverPath in VSCode settings to the full path of the executable.

โ†’ Install from VSCode Marketplace

Build from Source

git clone https://github.com/koxudaxi/t-linter
cd t-linter
cargo install --path crates/t-linter

Usage

Check

Validate template strings for syntax errors:

# Check a single file
t-linter check file.py

# Check a directory
t-linter check src/

# Output formats: human (default), json, github
t-linter check file.py --format json
t-linter check file.py --format github  # GitHub Actions annotations

# Exit with error code if issues found (useful for CI)
t-linter check file.py --error-on-issues

Example output:

example.py:4:46: error[embedded-parse-error] Invalid json syntax in template string (language=json)
1 files scanned, 1 templates scanned, 1 diagnostics, 0 failed files

Exit codes:

Code Meaning
0 Run completed successfully
1 Issues were found and --error-on-issues was set
2 Operational failure such as an unreadable file

Format

Rewrite supported template literals (HTML, T-HTML, JSON, YAML, TOML) in place:

# Format a single file
t-linter format file.py

# Format a directory
t-linter format src/

# Check whether formatting would change any files (for CI)
t-linter format --check file.py

# Override the formatter line length
t-linter format --line-length 100 file.py

# Format stdin
cat file.py | t-linter format --stdin-filename file.py -

Templates in unsupported languages (CSS, JavaScript, SQL) are left unchanged.

LSP Server

Start the Language Server Protocol server for editor integration:

t-linter lsp

The LSP server provides:

  • Semantic Tokens โ€” syntax highlighting for embedded languages
  • Diagnostics โ€” real-time validation with 250ms debouncing
  • Document Formatting โ€” full document and range formatting
  • Code Actions โ€” source.fixAll.t-linter for document-level rewrites and refactor.rewrite.t-linter for single-template selection rewrites

Claude Code

Add t-linter as an LSP server in your project's .claude/settings.json:

{
  "lsp": {
    "t-linter": {
      "command": "t-linter",
      "args": ["lsp"],
      "languages": ["python"]
    }
  }
}

Claude Code will then use t-linter's diagnostics when editing Python files containing template strings.

Codex

Add the LSP configuration to your project's codex.json or start t-linter's LSP server as part of your development environment. The t-linter check and t-linter format commands can also be used directly in Codex's sandbox:

t-linter check src/
t-linter format --check src/

Neovim

vim.lsp.start({
  name = "t-linter",
  cmd = { "t-linter", "lsp" },
  filetypes = { "python" },
})

Other Editors

Any editor with LSP support can use t-linter. Configure the LSP client to start t-linter lsp as the server command for Python files.

Configuration

Configuration via pyproject.toml:

[tool.t-linter]
line-length = 80
extend-exclude = ["generated", "vendor"]
ignore-file = ".t-linterignore"
Key Description
line-length Formatter print width (applies to HTML and T-HTML only; JSON, YAML, and TOML use fixed formatting rules)
exclude Override the built-in default excludes
extend-exclude Add more exclude patterns on top of the defaults
ignore-file Path to a gitignore-style ignore file, relative to the project root

By default, t-linter also reads .t-linterignore from the project root if it exists.

Quick Start Example

from typing import Annotated
from string.templatelib import Template

def render_html(template: Annotated[Template, "html"]) -> None:
    pass


def run_sql(template: Annotated[Template, "sql"]) -> None:
    pass


type css = Annotated[Template, "css"]
type yaml_config = Annotated[Template, "yaml"]
type toml_config = Annotated[Template, "toml"]


def load_styles(template: css) -> None:
    pass


def load_yaml(template: yaml_config) -> None:
    pass


def load_toml(template: toml_config) -> None:
    pass


title = "t-linter"
heading = "Template strings with syntax highlighting"
content = "Interpolations stay as normal Python expressions."

render_html(t"""
<!DOCTYPE html>
<html>
    <head>
        <title>{title}</title>
    </head>
    <body>
        <h1 style="color: #007acc">{heading}</h1>
        <p>{content}</p>
    </body>
</html>
""")

start_date = "2026-01-01"

run_sql(t"""
SELECT u.name, u.email, p.title
FROM users u
JOIN posts p ON u.id = p.author_id
WHERE u.created_at > {start_date}
ORDER BY u.name
""")

padding = 24

load_styles(t"""
.container {{
    max-width: 1200px;
    margin: 0 auto;
    padding: {padding}px;
}}
""")

app_name = "demo-app"

load_yaml(t"""
app:
  name: {app_name}
  debug: true
""")

project_name = "demo-project"
version = "0.1.0"

load_toml(t"""
[project]
name = "{project_name}"
version = "{version}"
""")

Use {{ and }} when the embedded language needs literal braces, such as CSS or JSON objects.

For html, <title>{value}</title> is allowed and treated as escaped text. <script>, <style>, and <textarea> still reject interpolations for safety.

Roadmap

Planned Features

  • โœ… Language Server Protocol (LSP) - Fully implemented
  • โœ… Syntax Highlighting - Supports HTML, T-HTML, SQL, JavaScript, CSS, JSON, YAML, TOML
  • โœ… Type Alias Support - Recognizes type html = Annotated[Template, "html"]
  • โœ… Linting (check command) - Validate template strings for syntax errors
  • โœ… Formatting (format command) - Canonical formatting for HTML, T-HTML, JSON, YAML, TOML
  • ๐Ÿšง Statistics (stats command) - Analyze template string usage across codebases
  • ๐Ÿ“‹ Cross-file Type Resolution - Track type aliases across module boundaries
  • ๐Ÿ“‹ Auto-completion - Context-aware completions within template strings

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

t_linter-0.7.0.tar.gz (101.7 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

t_linter-0.7.0-py3-none-win_amd64.whl (3.7 MB view details)

Uploaded Python 3Windows x86-64

t_linter-0.7.0-py3-none-manylinux_2_28_x86_64.whl (4.3 MB view details)

Uploaded Python 3manylinux: glibc 2.28+ x86-64

t_linter-0.7.0-py3-none-manylinux_2_28_aarch64.whl (4.3 MB view details)

Uploaded Python 3manylinux: glibc 2.28+ ARM64

t_linter-0.7.0-py3-none-macosx_11_0_arm64.whl (4.1 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

File details

Details for the file t_linter-0.7.0.tar.gz.

File metadata

  • Download URL: t_linter-0.7.0.tar.gz
  • Upload date:
  • Size: 101.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for t_linter-0.7.0.tar.gz
Algorithm Hash digest
SHA256 188a92295304274a0311957f41a43969aab2d953b8e0066faea858bab5ed3b34
MD5 93e997bf51b45e375f29cafd631fddff
BLAKE2b-256 11209bf544bfe55e209ca153636aeb6c83d73344378319645406cf6d1577f421

See more details on using hashes here.

File details

Details for the file t_linter-0.7.0-py3-none-win_amd64.whl.

File metadata

  • Download URL: t_linter-0.7.0-py3-none-win_amd64.whl
  • Upload date:
  • Size: 3.7 MB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for t_linter-0.7.0-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 c6b9d66aece74a9418f05624d8d1636a6cbd9e2ac9670715a043e63a25fc01c5
MD5 86b88857028599d4496017fcea145b26
BLAKE2b-256 949afd0c5d774b2422f8463ed19b060e50f8129ff3b09c9c132324d5233de8fc

See more details on using hashes here.

File details

Details for the file t_linter-0.7.0-py3-none-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for t_linter-0.7.0-py3-none-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 a228c5e8a52b4dc0c41fb58d3985f59e0076f24f5d2315600b53209f60c71820
MD5 8cc6d96b491cf17ba6d0b7abdda20285
BLAKE2b-256 e11379ad6dc20469a875dc33021cd89bd3c3ab712d791dac24a928a1fea9b092

See more details on using hashes here.

File details

Details for the file t_linter-0.7.0-py3-none-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for t_linter-0.7.0-py3-none-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 33037e614746d82aea3bd08c0a7461d0d106000d9665d1addd09d2269fa23689
MD5 87d8c11cc4362ec2080212ebf77abd8c
BLAKE2b-256 72cf23d47c1614d07a1962a582cdfd2376375defdf071c172b62f17ad22b4359

See more details on using hashes here.

File details

Details for the file t_linter-0.7.0-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for t_linter-0.7.0-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 f448a2a910ef471c3d764bda2654c4a5f3b9fe801769d12b0212d2789f838d01
MD5 1698b896a045af0c1a23b9e2e764618c
BLAKE2b-256 2d39674a68ba4f89601d48983ad20d21b733b648d5b23f53f0f6c50092934790

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page