Skip to content

feat: subscript access, setter methods, def self.method, heredoc fix#21

Merged
jeanpaulsio merged 10 commits into
mainfrom
feat/edge-cases
Mar 26, 2026
Merged

feat: subscript access, setter methods, def self.method, heredoc fix#21
jeanpaulsio merged 10 commits into
mainfrom
feat/edge-cases

Conversation

@jeanpaulsio

@jeanpaulsio jeanpaulsio commented Mar 26, 2026

Copy link
Copy Markdown
Owner

Summary

Tackles the remaining edge cases from the known limitations list and adds comprehensive indentation support.

New features

  • Subscript expressions: arr[0], hash[:key], matrix[i][j]
  • Subscript assignment: hash[:key] = value
  • Setter method definitions: def name=(value)
  • Class method definitions: def self.method_name
  • Tab key support in demo editor
  • Cmd+Shift+Enter inserts line above in demo editor

Indentation improvements

  • Rebuilt indentation engine using indentService for robust handling of incomplete code
  • Backward-scanning deindent logic correctly matches end/else/elsif/when/rescue/ensure to their opening keywords at any nesting depth
  • Single-line forms (class Foo; end, def foo; body; end, endless methods) no longer incorrectly indent the next line
  • Intermediate keywords (else, elsif, when, rescue, ensure) correctly indent their body
  • Empty lines preserve surrounding indent context

Tests

  • 76 programmatic indentation tests covering:
    • Block openers (def, class, module, if, unless, while, until, for, case, begin, do, braces)
    • Mid-block keywords (else, elsif, when, rescue, ensure)
    • End alignment at all nesting levels
    • Modifier forms (no indent change)
    • Single-line bodies (no indent change)
    • Complex real-world patterns (rescue in def, nested case, begin inside if)
  • All 89 grammar tests continue to pass

Test plan

  • npm run build && npm test — 89 grammar tests pass
  • node test/indent-test.mjs — 76 indentation tests pass
  • Manual testing in demo editor

Subscript expressions:
- arr[0], hash[:key], matrix[i][j] via SubscriptExpression
- Subscript assignment: arr[0] = 42, HASH[key] ||= []

Method definition improvements:
- Setter methods: def name=(val) ... end
- Class methods: def self.method(args) ... end (with and without parens)

Heredoc vs left-shift fix:
- Bare identifier heredocs (<<FILE) now reject non-whitespace after
  the delimiter on the opening line, preventing <<File.expand_path()
  from being misread as a heredoc

Real-world accuracy improvement:
- Devise: 98.0% → 99.1%
- All 7 repos above 95%

88 tests passing (5 new).
Indentation rules now check context.textAfter for closing keywords.
When typing `end`, `else`, `elsif`, `when`, `rescue`, `ensure` on a
new line inside a block, the line deindents to align with the opening
keyword (def, class, if, begin, etc.).

Previously the indent functions always added one unit regardless of
what was being typed, so `end` would stay indented until manually
adjusted.
Hash shorthand:
- {name: "Alice", age: 30} works again
- Moved Symbol from inline token to external tokenizer (symbolTokenizer)
  to eliminate : vs Symbol overlap with Block in expressions
- colonOp handles : in ternary and hash shorthand contexts
- Same pattern as /, <, % external tokenizers

Indentation fix:
- Indent functions now check context.textAfter for closing keywords
- Typing `end`, `else`, `elsif`, `when`, `rescue`, `ensure` immediately
  deindents to match the opening keyword
- After `end`, pressing enter starts at column 0 (not indented)
- Intermediate keywords (elsif, else, rescue) indent their bodies

89 tests passing.
The previous approach used indentNodeProp which depends on complete
parse tree nodes (MethodBody, ClassBody, etc.). These don't exist
while typing because the code is incomplete (no `end` yet).

New approach: text-based indentService that runs BEFORE tree-based
indentation. It checks the previous line's content:
- After def/class/module/if/while/etc. → indent one level
- After else/elsif/rescue/ensure/when → indent one level
- After end → stay at end's level
- On end/else/elsif/rescue/ensure → deindent to match opener
- Default → maintain previous line's indentation

Tree-based indentation (indentNodeProp) is kept only for bracket
delimited constructs ([], {}, ()) where delimitedIndent works well.

89 tests passing.
Single-line forms like `class Foo; end`, `def foo; body; end`, and
endless methods `def foo(x) = x + 1` no longer incorrectly indent
the next line. Added 76 programmatic indentation tests covering
block openers, mid-block keywords, end alignment, nesting, modifier
forms, and real-world patterns. Also adds Tab key support and
Cmd+Shift+Enter for insert-line-above in the demo editor.
Rewrote indent service to handle:
- Closing delimiters (}, ], )) via backward bracket scanning
- Continuation lines (trailing +, -, *, &&, ||, \, comma)
- Method chaining with leading dots (.bar, .baz)
- Assignment with block openers (x = if, x = case, @foo ||= begin)
- Chain-end detection: returns to base indent when chain/continuation ends
- Lambda/Proc blocks (-> (x) {, -> (x) do)
- Multi-line method def arguments

119 programmatic indent tests covering all 18 sections from the spec:
sections 1-8 (block openers, mid-block, end, delimiters, nesting,
modifiers, single-line), 9 (heredocs), 10 (continuations + chaining),
11 (lambdas), 12 (strings), 13 (assignment blocks), 14 (real-world),
15 (edge cases), 16 (when/in style), 17 (multi-line args), 18 (conditional assign).
The insert-line-above keybinding now calculates the proper indent
level for the new line instead of always placing the cursor at
column 0. Also rebuilt demo bundle with latest indentation fixes.
Updated README with honest parse accuracy numbers from larger
real-world files (87.4% overall). Documented known limitations
from error pattern analysis. Bumped version to 0.2.0 for the
comprehensive indentation overhaul and edge case fixes.
@jeanpaulsio jeanpaulsio merged commit c1efd3b into main Mar 26, 2026
1 check passed
@jeanpaulsio jeanpaulsio deleted the feat/edge-cases branch March 26, 2026 21:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant