Skip to content

v0.10

Choose a tag to compare

@dsisco11 dsisco11 released this 07 Jan 03:08
· 8 commits to master since this release
28aca6c

Release Notes: v0.10.0

Release Date: January 6, 2026
Previous Version: v0.9 (January 4, 2026)

Overview

This release contains significant improvements to the SyntaxEditor and Query system, fixing fundamental issues with multi-node replacements and range queries. The internal architecture has been refactored to properly handle queries that match multiple consecutive siblings.

🚀 New Features

Query Region System

A new internal QueryRegion abstraction enables queries to correctly resolve to contiguous ranges of slots within parent containers. This powers correct behavior for:

  • Query.Between(start, end) - Now correctly replaces/removes/inserts around entire matched ranges
  • Block boundary queries (.Start(), .End(), .Inner()) - Properly resolve to slot positions for accurate editing
  • Sequence queries - Multi-node matches are handled atomically

Block Boundary API Improvements

The IBlockContainerNode interface now uses a cleaner API:

// Old API (removed)
Query.Syntax<FunctionSyntax>().InnerStart("body")
Query.Syntax<FunctionSyntax>().InnerEnd("body")

// New API
Query.Syntax<FunctionSyntax>().Block("body").Start()
Query.Syntax<FunctionSyntax>().Block("body").End()
Query.Syntax<FunctionSyntax>().Block("body").Inner()

Green Node Trivia Access

New methods on GreenNode for efficient trivia access:

  • GetLeadingTrivia() / GetTrailingTrivia() - Access trivia via first/last leaf descendant
  • GetLeadingTriviaWidth() / GetTrailingTriviaWidth() - Precomputed width access
  • GetFirstLeaf() / GetLastLeaf() - Navigate to boundary leaves in O(depth)

⚡ Performance Improvements

PathTrackingWalker

A new internal PathTrackingWalker tracks paths incrementally during tree traversal:

  • O(1) per step instead of O(depth) for NodePath.FromNode()
  • Pushes sibling index on descent, pops on ascent
  • Used by all SelectRegions implementations

Lazy Path Resolution

QueryRegion.ParentPath is computed lazily on first access, avoiding tree-walking overhead when regions are enumerated but not all are used for editing.

Optimized Trivia Extraction

  • SyntaxEditor.GetTrivia() now delegates to green node methods (2 lines vs 30+ lines)
  • Uses precomputed LeadingTriviaWidth/TrailingTriviaWidth instead of runtime summation
  • GetContentWithoutTrivia() uses span-based slicing

Inlined Query Matching

  • KindNodeQuery.SelectRegions inlines Kind check and predicate evaluation
  • BlockNodeQuery.SelectRegions inlines block type check
  • Avoids virtual TryMatch dispatch in hot loops

🐛 Bug Fixes

Fixed: Query.Between with SyntaxEditor.Replace()

Previously, Replace() with range queries like Query.Between(start, end) would only replace the first node. Now the entire matched range is replaced atomically.

Fixed: Block Inner Content Replacement

Replacing content inside empty blocks (e.g., { }) now works correctly. The zero-width inner region is properly resolved.

Fixed: Multi-Position Insertions

InsertAfter and InsertBefore with queries matching multiple positions now correctly insert at all matched positions.

📊 New Benchmarks

Added SyntaxEditorBenchmarks.cs with comprehensive benchmarks:

Category Benchmarks
Replace Replace_SingleToken, Replace_MultipleTokens, Replace_AllIdents
BlockInner Replace_BlockInner_Empty, Replace_BlockInner_WithContent, Replace_BlockInner_Larger
Insert InsertAfter_SinglePosition, InsertAfter_ManyPositions, InsertBefore_ManyPositions
SelectRegions SelectRegions_DeepTree (20/50 levels), SelectRegions_DeepTree_Blocks, SelectRegions_DeepTree_First
Transform Edit_Transform_SingleToken, Replace_WithNodeTransformer
UndoRedo Edit_ThenUndo, MultipleEdits_ThenUndoAll

🔧 Breaking Changes

None for public API. All changes are internal implementation details.

Internal API Changes (for library contributors)

  • Removed InsertionQuery and InsertionPoint enum - replaced by BoundaryQuery + IRegionQuery
  • IBlockContainerNode.InnerStart()/InnerEnd() replaced by .Block().Start()/.End()
  • Query types now implement IRegionQuery interface

📁 Files Changed

+2,751 / -891 lines across 21 files

New Files:
  QueryRegion.cs (245 lines)
  SyntaxEditorBenchmarks.cs (375 lines)

Major Changes:
  SyntaxEditor.cs (+530 lines refactored)
  NodeQueryTypes.cs (+386 lines)
  QueryCombinators.cs (+181 lines)
  GreenBlock.cs (+203 lines)
  IBlockContainerNode.cs (+171 lines)
  GreenNode.cs (+88 lines)

🧪 Test Coverage

  • 15+ new tests for Query.Between with all editor operations
  • Block inner content replacement tests (empty and non-empty)
  • Deep tree traversal tests (20-50 levels)
  • Multi-position insertion tests

Upgrade Guide

No action required for most users. If you were using internal APIs:

// If you implemented IBlockContainerNode, update usage:
// Before:
editor.InsertAfter(query.InnerStart("body"), "code")

// After:
editor.InsertAfter(query.Block("body").Start(), "code")