Skip to content

Language Service: Implement @herb-tools/language-service package#1446

Merged
marcoroth merged 1 commit intomainfrom
herb-language-service
Mar 21, 2026
Merged

Language Service: Implement @herb-tools/language-service package#1446
marcoroth merged 1 commit intomainfrom
herb-language-service

Conversation

@marcoroth
Copy link
Owner

@marcoroth marcoroth commented Mar 21, 2026

This pull request introduces a new package, @herb-tools/language-service, which provides an HTML+ERB language service with a compatible API to vscode-html-languageservice.

It uses the Herb parser to understand both regular HTML elements and ActionView tag helpers, and presents them through the same LanguageService interface that vscode-html-languageservice consumers expect — making it a drop-in replacement.

What it does

The core idea is that parseHTMLDocument uses the Herb parser instead of a plain HTML scanner. This means a template like <%= tag.div data: { controller: "scroll" } %> is understood as a <div> with a data-controller="scroll" attribute completions, diagnostics, and navigation all work as expected, just like they would for regular HTML.

For completions inside ERB tag helpers, the language service detects the Ruby hash context and adapts accordingly. Inside a data: {} hash it suggests action instead of data-action, and for space-separated attributes like data-controller="scroll search" it provides per-token completions rather than replacing the entire value.

Each node in the parsed document tracks where its attributes actually appear in the source text via attributeSourceRanges. This is important because ActionView tag helpers synthesize attribute names (e.g., controller: in Ruby becomes data-controller in HTML), so the language service needs to know where to point diagnostics and highlights in the original ERB source.

When no Herb instance is provided the service falls back to the upstream vscode-html-languageservice parser, so it works as a regular HTML language service too. All types and functions from vscode-html-languageservice are re-exported so consumers only need a single import.

Stimulus LSP

This package was built with Stimulus LSP as the first consumer in mind.

Stimulus LSP currently uses vscode-html-languageservice directly, which means it has no understanding of ERB. Controller completions, diagnostics, and go-to-definition only work inside regular HTML data-controller attributes.

By switching to @herb-tools/language-service, Stimulus LSP gains full support for ActionView tag helpers like <%= tag.div data: { controller: "scroll", action: "click->scroll#go" } %> with the same completions, diagnostics, and navigation that already work for plain HTML.

Usage

- import { getLanguageService } from 'vscode-html-languageservice'
+ import { Herb } from '@herb-tools/node-wasm'
+ import { getLanguageService } from '@herb-tools/language-service'

+ await Herb.load()

  const service = getLanguageService({
+   herb: Herb,
    customDataProviders: [myDataProvider],
  })

@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 21, 2026

npx https://pkg.pr.new/@herb-tools/formatter@1446
npx https://pkg.pr.new/@herb-tools/language-server@1446
npx https://pkg.pr.new/@herb-tools/linter@1446

commit: 4e587bd

@github-actions
Copy link

github-actions bot commented Mar 21, 2026

🌿 Interactive Playground and Documentation Preview

A preview deployment has been built for this pull request. Try out the changes live in the interactive playground:


🌱 Grown from commit 4e587bd


✅ Preview deployment has been cleaned up.

@marcoroth marcoroth merged commit bd698f4 into main Mar 21, 2026
20 checks passed
@marcoroth marcoroth deleted the herb-language-service branch March 21, 2026 22:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant