一键导入
position-encoding
Position encoding, span types, coordinate systems, path normalization tables for Verter's multi-layer architecture (OXC, Rust, LSP, FFI, VS Code)
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Position encoding, span types, coordinate systems, path normalization tables for Verter's multi-layer architecture (OXC, Rust, LSP, FFI, VS Code)
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
Verter codebase architecture: Rust compiler modules, TypeScript packages, plugin system, LSP features, CSS analysis, MCP server, analysis types, key files index
Build dependency chains, rebuild sequences, profiling with MCP, and Analysis MCP server setup for Verter
Rust performance optimization patterns: batch operations, allocation hierarchy, object pooling, CodeTransform API for verter_core
Testing patterns, TDD workflow, TypeScript and Rust test conventions, sourcemap testing, E2E best practices, server cleanup for Verter
| name | position-encoding |
| description | Position encoding, span types, coordinate systems, path normalization tables for Verter's multi-layer architecture (OXC, Rust, LSP, FFI, VS Code) |
verter_span crate)All Rust span types are defined in crates/verter_span/src/lib.rs. Each type enforces a specific coordinate system at compile time:
| Type | Meaning | Serde? | Used Where |
|---|---|---|---|
Span | SFC-absolute byte offsets [start, end) | Yes (spanStart/spanEnd) | Analysis types, diagnostics, CSS analysis, CodeTransform, Raw* template data, CSS variable spans |
RelativeSpan | Byte offsets relative to a base stored elsewhere | No | CSS scanner internals, OXC binding extraction (Binding.span) |
PartialGeneratedSpan | Unresolved position in generated output (TSX) | No | TSGO response parsing before PositionMapper resolution |
GeneratedSpan | Resolved mapping: generated position + SFC origin | No | TSGO diagnostics after resolution, codegen error mapping |
Span (SFC-absolute). RelativeSpan, PartialGeneratedSpan, and GeneratedSpan do not implement Serialize/Deserialize. Attempting to put them in a serializable struct is a compile error. Convert with to_absolute(base) before serialization.Span. Types in analysis snapshots, host results, and diagnostic structs that cross crate boundaries use Span. RelativeSpan is for intra-crate processing only (e.g., CSS scanner working on a style block, OXC binding extraction within an expression).RelativeSpan is 8 bytes, same as Span. The base offset lives in context (field on parent struct, function parameter). The value of RelativeSpan is compile-time type safety, not runtime data.PartialGeneratedSpan → GeneratedSpan via resolution. Use PartialGeneratedSpan for raw TSGO byte offsets. After PositionMapper lookup resolves the SFC origin, use partial.resolve(origin_span) to get a GeneratedSpan. For display (LSP diagnostics), use generated_span.origin.Span::new(start, end) / RelativeSpan::new(start, end) / PartialGeneratedSpan::new(start, end)RelativeSpan::to_absolute(base: u32) -> Span — add base offsetSpan::to_relative(base: u32) -> RelativeSpan — subtract base offsetPartialGeneratedSpan::resolve(origin: Span) -> GeneratedSpan — resolve with SFC originGeneratedSpan::new(generated: Span, origin: Span) — create resolved mapping directlyslice(&self, source: &str) -> &str — on Span, RelativeSpan, PartialGeneratedSpanFrom<oxc_span::Span> for both Span and RelativeSpanFrom conversions between span types — type safety enforced at compile timeAll CSS variable span fields use SFC-absolute Span:
| Type | Field | Meaning |
|---|---|---|
AnalyzedCustomProperty | name_span | Span of --name in declaration |
AnalyzedCustomProperty | value_span | Span of the value text after : |
CssVarReference | span | Span of entire var(...) expression |
CssVarReference | name_span | Span of variable name within var() |
CssVarFallback | span | Span of fallback text within var() |
CssVarManipulation | span | Span of DOM API call expression (e.g., setProperty(...)) |
These spans are computed as content_offset + local_offset during CSS scanning, where content_offset is the SFC-absolute byte offset of the <style> block content start. For script-side CssVarManipulation, spans come from OXC and are adjusted by the script block's SFC offset.
| Layer | Offset Format | Line/Col Base | Description |
|---|---|---|---|
| oxc_span | UTF-8 byte offset, relative to parse start | N/A (byte offsets only) | OXC parser spans are byte offsets from the start of the parsed source text |
verter Span | UTF-8 byte offset, absolute for the document | N/A (byte offsets only) | All stored Rust spans (span fields in analysis types, CodeTransform positions) are byte offsets from the start of the SFC source |
verter RelativeSpan | UTF-8 byte offset, relative to a base | N/A (byte offsets only) | CSS scanner internals (relative to style content start), OXC bindings (relative to expression start) |
| PositionResolver | N/A | 1-based line, 1-based UTF-16 column | cursor/position.rs — returns 1-based. When passing to source maps or LSP, subtract 1 |
| Source maps | N/A | 0-based line, 0-based column | VLQ-encoded. source_map.rs converts from PositionResolver via (line - 1, col - 1) |
| LSP Protocol | Negotiated (UTF-8/UTF-16/UTF-32) | 0-based line, 0-based character | Position { line: 0, character: 0 } = first char of file. LineIndex handles conversion |
| VS Code API | UTF-16 code units | 0-based line, 0-based character | new Position(0, 0) = first char. Matches LSP UTF-16 |
| verter_ffi | UTF-16 code units | N/A (byte offsets only) | NAPI/WASM boundary always communicates in UTF-16 offsets. Reference: crates/verter_ffi/src/convert.rs:byte_offset_to_utf16() |
| verter_lsp | Negotiated encoding (UTF-8, UTF-16, or UTF-32) | 0-based | The LSP negotiates encoding with the client during initialize(). All positions sent to and received from the client use the negotiated encoding |
cursor/position.rs): offset_to_line_and_col() and offset_to_line_col() return (1-based line, 1-based column). Always subtract 1 before passing to source maps or LSP.Position { line: 0, character: 0 } is the first character.new Position(0, 0) is the first character.capabilities.general.positionEncodings from the client during initialize()ServerCapabilities.position_encodingCRITICAL: The negotiated encoding MUST be used everywhere that produces LSP positions (diagnostics, hover ranges, completion positions, etc.). This includes the SyncCoordinator which publishes diagnostics after typing stops — it shares the encoding via Arc<RwLock<PositionEncodingKind>> with the server. The default is UTF-16 (per LSP spec) until initialize() negotiates the actual encoding.
Rust-internal code should prefer UTF-8 byte offsets (Rust's native string encoding). LSP boundary code must convert to the negotiated encoding. JS/VS Code always uses UTF-16 (JS string encoding).
Standard LSP positions (line:character): handled by LineIndex in documents/line_index.rs.
Custom protocol data (analysis spans): converted at the LSP boundary before serialization.
| Boundary | Pattern | Reference Implementation |
|---|---|---|
| Rust internal → NAPI/WASM | byte_offset_to_utf16() | crates/verter_ffi/src/convert.rs:281 |
| Rust internal → LSP client | Negotiated encoding conversion | crates/verter_lsp/src/documents/mod.rs |
| LSP Position → byte offset | LineIndex::position_to_offset() | crates/verter_lsp/src/documents/line_index.rs |
| Byte offset → LSP Position | LineIndex::offset_to_position() | crates/verter_lsp/src/documents/line_index.rs |
| TSGO response → byte offset | position_to_offset() | crates/verter_lsp/src/tsgo/ipc.rs (ASCII TSX only) |
VS Code negotiates UTF-16. Analysis offsets arrive as UTF-16 code units from file start. JS string indexing is UTF-16 native, so source.charCodeAt(offset) and source.length work directly. Use the shared utf16OffsetToPosition() from packages/vue-vscode/src/utils.ts.
TSGO processes generated TSX which is always ASCII. For ASCII: byte offset == UTF-16 offset == UTF-32 offset. The position_to_offset()/offset_to_position() functions in ipc.rs treat character as byte offset within the line — correct for ASCII. Diagnostics from publishDiagnostics must resolve LSP positions to byte offsets using the TSX content cache.
All file paths are stored internally in canonical ID format. Normalization happens at entry boundaries (receiving paths); denormalization happens at exit boundaries (sending paths to external systems).
| Rule | Example |
|---|---|
| Forward slashes only | c:/Users/dev/App.vue (never c:\Users\dev\App.vue) |
| Lowercase Windows drive | c:/Users/... (never C:/Users/...) |
| No query strings | App.vue (not App.vue?vue&type=script) |
| No virtual suffixes | App.vue (not App.vue._VERTER_.bundle.ts) |
| UTF-8, no percent-encoding | /home/user/my project/App.vue (not my%20project) |
| Source | Function | Location |
|---|---|---|
| LSP client URI | uri_to_canonical_id_from_str() | verter_lsp/src/documents/mod.rs |
| File system / bundler path | canonicalize_id() | verter_host/src/id.rs |
| Bundler plugin | generateComponentId() | packages/unplugin/src/core/compiler.ts |
| CLI args | path_to_file_uri() | verter_lsp/src/main.rs |
| Target | Pattern | Location |
|---|---|---|
| LSP client (file URI) | file:/// + canonical ID | verter_lsp/src/features/definition.rs |
| TSGO type provider | path_to_file_uri() | verter_lsp/src/main.rs |
| File I/O | std::path::Path::new(canonical_id) | OS handles both / and \ on Windows |
canonicalize_id() or uri_to_canonical_id_from_str() before storage or comparisonfile:// URIs or OS paths only when sending to external systems