Skip to content

PuffinStudio/wechat-wxml-lsp

Repository files navigation

WeChat WXML Language Server

A high-performance Language Server Protocol (LSP) implementation for WeChat Mini Program WXML files, built with Rust and Tree-sitter.

Features

Code Intelligence

  • Smart Completions: Tag, attribute, and template name suggestions with full support for usingComponents

    • Component tag completions with 70+ built-in WeChat components
    • Attribute completions with type information and documentation
    • Event binding completions (bind:, catch:, mut-bind:, etc.)
    • Data binding completions inside {{}} with TypeScript type inference
    • Template name completions across imported files
    • Custom component completions from usingComponents
  • Hover Information: Rich documentation at your fingertips

    • Component documentation with attributes and events
    • TypeScript type information for data bindings
    • Property definitions for custom components
    • Directive documentation (wx:if, wx:for, etc.)
  • Real-time Diagnostics: Catch errors as you type

    • Undefined variable detection in mustache expressions
    • Unknown component tag detection
    • Missing required attributes
    • Missing wx:key for wx:for loops
    • Invalid directive usage (wx:elif, wx:else validation)
    • Event validation against component definitions
  • Code Navigation: Seamless WXML ↔ TypeScript integration

    • Go to definition from WXML data bindings to TypeScript
    • Go to definition for custom component properties
    • Jump to custom component WXML files
    • Find all references across WXML and TypeScript
    • Support for nested loop variable resolution
  • Safe Refactoring: Rename variables with confidence

    • Rename data bindings across WXML and TypeScript
    • Prepare rename support for accurate range detection
  • Code Formatting: Consistent WXML formatting

    • Automatic indentation and attribute formatting
    • Preserves intentional line breaks
    • Configurable indent size

Supported LSP Features

Feature Status Description
Completion Tags, attributes, template names, custom components, data bindings
Hover Component docs, type info, property definitions, directive docs
Diagnostics Undefined vars, unknown tags, missing attributes, wx:for validation
Go to Definition Data bindings → TS, Component tags → WXML, Attributes → TS properties
References Find variable usages across WXML and TS
Rename Safe variable renaming with prepare support
Document Formatting WXML code formatting
Document Sync Incremental synchronization

Quick Start

Installation

macOS (Apple Silicon)

curl -L https://github.com/puffinstudio/wechat-wxml-lsp/releases/latest/download/wxml-lsp-darwin-arm64 \
  -o ~/.local/bin/wxml-lsp && chmod +x ~/.local/bin/wxml-lsp

macOS (Intel)

curl -L https://github.com/puffinstudio/wechat-wxml-lsp/releases/latest/download/wxml-lsp-darwin-x64 \
  -o ~/.local/bin/wxml-lsp && chmod +x ~/.local/bin/wxml-lsp

Linux (x64)

curl -L https://github.com/puffinstudio/wechat-wxml-lsp/releases/latest/download/wxml-lsp-linux-x64 \
  -o ~/.local/bin/wxml-lsp && chmod +x ~/.local/bin/wxml-lsp

Linux (ARM64)

curl -L https://github.com/puffinstudio/wechat-wxml-lsp/releases/latest/download/wxml-lsp-linux-arm64 \
  -o ~/.local/bin/wxml-lsp && chmod +x ~/.local/bin/wxml-lsp

Windows

Download wxml-lsp-win32-x64.exe from GitHub Releases and add to PATH.

Or use the install script:

bash <(curl -fsSL https://raw.githubusercontent.com/puffinstudio/wechat-wxml-lsp/main/scripts/install.sh)

VSCode

Install from VSCode Marketplace.

The extension automatically bundles the language server binary - no manual installation required.

Neovim

Using lazy.nvim:

local wxml_lsp_cmd = { "wxml-lsp", "--stdio" }

local function add_ensure_installed(opts, lang)
  opts.ensure_installed = opts.ensure_installed or {}
  if type(opts.ensure_installed) == "table" and not vim.tbl_contains(opts.ensure_installed, lang) then
    table.insert(opts.ensure_installed, lang)
  end
end

local function start_wxml_lsp(args)
  local bufname = vim.api.nvim_buf_get_name(args.buf)
  if bufname == "" then
    return
  end

  local root = vim.fs.root(bufname, { "project.config.json", ".git" }) or vim.fs.dirname(bufname)
  if not root then
    return
  end

  vim.lsp.start({
    name = "wxml_lsp",
    cmd = wxml_lsp_cmd,
    root_dir = root,
  })
end

return {
  {
    "nvim-treesitter/nvim-treesitter",
    init = function()
      vim.filetype.add({
        extension = {
          wxml = "wxml",
        },
      })
    end,
    opts = function(_, opts)
      add_ensure_installed(opts, "wxml")
    end,
  },
  {
    "neovim/nvim-lspconfig",
    init = function()
      -- Register for both WXML and TypeScript files
      -- TypeScript registration enables cross-file references (TS -> WXML)
      vim.api.nvim_create_autocmd("FileType", {
        pattern = { "wxml", "typescript" },
        callback = start_wxml_lsp,
      })
    end,
  },
}

This configuration:

  • Registers .wxml files as the wxml filetype
  • Installs the wxml Tree-sitter grammar automatically (via nvim-treesitter)
  • Starts the language server for both wxml and typescript files
  • Enables cross-file references from TypeScript to WXML
  • Auto-detects the project root using project.config.json or .git

Note on Mason

Mason is a Neovim package installer, not the language server itself. This server is not yet in the official Mason registry. The recommended approach is to install the binary manually (Step 1 above) and configure wxml_lsp_cmd to point to it. If wxml-lsp is on your $PATH, the above config works without any path changes.

How It Works

The server analyzes your .ts files alongside .wxml to provide intelligent type information:

// index.ts
Page({
  data: {
    message: 'Hello',
    items: [{ id: 1, name: 'Item' }],
    user: {
      profile: {
        name: 'John'
      }
    }
  }
})

In index.wxml:

  • Type {{mes}} → get completion for message
  • Type {{user.pro}} → get completion for profile
  • Hover over {{message}} → see message: string with markdown-formatted type information
  • Component hover uses markdown tables for clean attribute listings
  • Press gd on {{message}} → jump to TS definition
  • Use wx:for="{{items}}"item and index are automatically typed

Custom components work the same way - define them in usingComponents and get full IDE support including property completions and type checking.

CLI Usage

The language server also provides a CLI linting mode:

# Lint all WXML files in src/
wxml-lsp lint

# Lint with custom pattern
wxml-lsp lint "pages/**/*.wxml"

# Get detailed error output with colors
wxml-lsp lint src/pages/index.wxml

Architecture

This project is organized as a Rust workspace with the following crates:

Core LSP Server

  • server/ - Main LSP server implementation
    • Built on tower-lsp for LSP protocol handling
    • Uses ropey for efficient text manipulation
    • Implements incremental document synchronization
    • Provides both LSP and CLI interfaces

Parser & Analysis

  • crates/wxml-parser/ - Tree-sitter WXML parser

    • Custom Tree-sitter grammar for WXML
    • Generates AST for syntax analysis
    • Supports all WeChat Mini Program syntax including wx: directives
  • crates/ts-analyzer/ - TypeScript type extraction

    • Extracts type information from Page/Component data
    • Resolves imports and exports
    • Supports interface and type alias resolution
    • Handles complex nested object types and arrays

Component System

  • crates/component-registry/ - Built-in WeChat component definitions
    • 70+ official WeChat components with full metadata
    • Attribute schemas with types and descriptions
    • Event definitions for each component
    • Auto-generated from official documentation

Diagnostics & Linting

  • crates/wxml-diagnostics/ - Diagnostic engine

    • Shared diagnostic computation for LSP and CLI
    • Loop context resolution for wx:for variables
    • Variable path type checking
    • Template definition awareness
  • crates/wxml-linter/ - CLI linting tool

    • Git-aware file discovery
    • Batch linting with progress reporting
    • Colored terminal output

Editor Integration

  • editors/vscode/ - VSCode extension

    • Automatic binary management
    • File watching for live updates
    • Integrated output channel
  • Language: Rust (Edition 2021)

  • LSP Framework: tower-lsp

  • Parser: Tree-sitter with custom WXML grammar

  • TypeScript Parser: tree-sitter-typescript

  • Async Runtime: Tokio

  • Concurrent Maps: DashMap

  • Text Rope: ropey

  • Serialization: serde

Development

Prerequisites

  • Rust 1.75+ (stable)
  • Node.js 20+ (for VSCode extension)

Building

# Build all workspace crates
cargo build --workspace --release

# Run tests
cargo test --workspace

# Run specific test
cargo test --package component-registry

VSCode Extension Development

cd editors/vscode
npm install
npm run compile

# Press F5 to launch Extension Development Host

Project Structure

wechat-wxml-lsp/
├── Cargo.toml                    # Workspace root
├── README.md                     # This file
├── scripts/
│   └── install.sh               # One-line installer
├── server/
│   ├── Cargo.toml
│   └── src/
│       ├── main.rs              # CLI entry point
│       ├── lib.rs               # Server library
│       ├── backend.rs           # LSP handlers (2600+ lines)
│       └── document.rs          # Document state management
├── crates/
│   ├── wxml-parser/             # Tree-sitter grammar
│   ├── ts-analyzer/             # TypeScript analysis (1400+ lines)
│   ├── component-registry/      # 70+ component definitions
│   ├── wxml-diagnostics/        # Diagnostic engine
│   └── wxml-linter/             # CLI tool
├── editors/
│   └── vscode/                  # VSCode extension
├── tools/
│   └── fetch-wechat-components/ # Component scraper
└── .github/
    └── workflows/               # CI/CD

CI/CD

  • Continuous Integration: Tests run on every PR
  • Binary Releases: Auto-built for all platforms on version tags (v*)
    • Linux: x64, ARM64
    • macOS: x64, ARM64 (Apple Silicon)
    • Windows: x64
  • VSCode Marketplace: Published automatically via GitHub Actions

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Development Guidelines

  1. Rust Code: Follow standard Rust formatting (cargo fmt)
  2. Tests: Add tests for new functionality
  3. Documentation: Update README.md for user-facing changes
  4. Component Data: Use fetch-wechat-components to update component registry

Reporting Issues

When reporting issues, please include:

  • Language server version (wxml-lsp --version)
  • Editor/IDE and version
  • Steps to reproduce
  • Expected vs actual behavior
  • Sample code (if applicable)

License

MIT

Acknowledgments

  • tower-lsp - LSP framework for Rust
  • tree-sitter - Parser generator
  • WeChat Mini Program Documentation - Component definitions

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors