Skip to content

feat: add dart-main tag and fix flutter test/group button priority#70

Open
tsinis wants to merge 5 commits intozed-extensions:mainfrom
tsinis:feat/dart-main-runnable
Open

feat: add dart-main tag and fix flutter test/group button priority#70
tsinis wants to merge 5 commits intozed-extensions:mainfrom
tsinis:feat/dart-main-runnable

Conversation

@tsinis
Copy link
Copy Markdown

@tsinis tsinis commented Mar 16, 2026

Hi Team!

This PR introduces dart run support for standalone Dart files and fixes several issues with Flutter test/group play buttons in the gutter.

Changes

dart-main — new tag for dart run

  • Matches main() functions in files with dart: SDK imports
  • Uses the same pattern structure as flutter-main so tree-sitter returns it with the same step count — since dart format places dart: imports before package: imports, dart-main is always processed first and overwritten by more specific tags in Flutter/test files.

ERROR root fallback variants

  • Added (ERROR ...) variants for dart-main, flutter-main, and flutter-test-main.
  • Files using Dart 3 shorthand constructors (e.g. .all(.circular(4))) parse as ERROR root instead of program — without these fallbacks, those files get no play buttons at all.

Arrow-body test fixes

  • Removed arrow-body group patterns (flutter-test-group, dart-test-group) — when void main() => group(...), both main and group identifiers share the same row, so group overwrites the more useful test-main tag.
  • Added arrow-body test-single patterns — test() calls inside main() => group("name", () { test(...) }) are on separate rows, so they work correctly.

Cleanup

  • Removed redundant #not-match? predicates on flutter-main (a string matching package:flutter/material.dart can never also match flutter_test or test).
  • Wrapped all patterns in explicit (program ...) / (ERROR ...) root nodes.

Known limitations

  • dart-main only matches files with dart: imports (e.g. dart:io, dart:convert). Pure Dart files importing only package: dependencies won't get a play button. This is unavoidable: patterns matching non-dart: imports have later capture positions than flutter imports, so they'd overwrite flutter-main in Zed's HashMap. Most real CLI tools use dart: imports, so this is a narrow edge case.
  • Tree-sitter step-count ordering means a simple fallback pattern (without import matching) always has the fewest steps, is returned last by QueryMatches, and overwrites all specific tags — making a universal dart-main impossible without changes to Zed's runnable resolution logic.

A note on testing

This extension currently has no test infrastructure for verifying runnables behavior. The tree-sitter CLI (tree-sitter query) confirms pattern matching and ordering, but Zed's WASM runtime can behave differently — I observed cases where CLI results were correct, but Zed didn't render the expected play button. Introducing a test harness that validates runnables against Zed's actual SyntaxMapMatches ordering would prevent regressions and remove the guesswork from future changes.

Verification

Tested with tree-sitter query against grammar revision 80e23c0:

File type Expected tag Result
Standalone Dart (dart:io import) dart-main
Flutter app (package:flutter/material.dart) flutter-main
Flutter test (block body main() { group() }) flutter-test-main + flutter-test-group
Flutter test (arrow body main() => group()) flutter-test-main + flutter-test-single per test
Dart test (package:test/test.dart) dart-test-file
ERROR root Flutter app (Dart 3 shorthand) flutter-main

tsinis added 2 commits March 16, 2026 19:23
Added a new task for running Dart applications with the label
"dart run $ZED_STEM".
This change introduces a fallback pattern for standalone Dart files, ensuring
that more specific patterns for Flutter and test files take priority.
@cla-bot
Copy link
Copy Markdown

cla-bot bot commented Mar 16, 2026

We require contributors to sign our Contributor License Agreement, and we don't have @tsinis on file. You can sign our CLA at https://zed.dev/cla. Once you've signed, post a comment here that says '@cla-bot check'.

@tsinis
Copy link
Copy Markdown
Author

tsinis commented Mar 16, 2026

@cla-bot check

@cla-bot cla-bot bot added the cla-signed label Mar 16, 2026
@cla-bot
Copy link
Copy Markdown

cla-bot bot commented Mar 16, 2026

The cla-bot has been summoned, and re-checked this pull request!

@kingwill101
Copy link
Copy Markdown

@tsinis I assume this covers #53 ?

@tsinis
Copy link
Copy Markdown
Author

tsinis commented Mar 20, 2026

@tsinis I assume this covers #53 ?

Hi @kingwill101, yes, I believe it does.

Added new tasks for running flutter tests with specific line numbers for
group and single tests, improving the testing workflow for developers.
@tsinis
Copy link
Copy Markdown
Author

tsinis commented Mar 21, 2026

Update: Why the simple dart-main fallback doesn't work

After extensive testing, I discovered that the initial approach (placing a simple (function_signature) pattern first in the file) does not work as expected. Here's why:

The assumption: Patterns listed earlier in runnables.scm are returned earlier by tree-sitter, so "last target wins" means later patterns (flutter-main, etc.) override the earlier dart-main.

The reality: Tree-sitter's QueryMatches iterator orders matches by matching complexity (step count), not by pattern position in the query file. Patterns with fewer matching steps are returned LAST, not first. Since the simple dart-main has the fewest steps (just function_signature, no import matching), it is always the last match for the main row — it overwrites flutter-main, flutter-test-main, and dart-test-file in every file.

Evidence (from tree-sitter query CLI on a Flutter test file):

pattern: 0 (flutter-main)     — import at row 4 + function_signature
pattern: 2 (flutter-test-main) — import at row 5 + function_signature  
pattern: 11 (dart-main)        — function_signature only ← ALWAYS LAST

The fix: dart-main must match dart: SDK imports specifically, giving it the same structure and step count as other patterns. Since dart format places dart: imports before package: imports, the @_import capture position for dart-main is always earlier in the file, so it's processed first and overwritten by more specific patterns. Trade-off: pure Dart files without dart: imports don't get a button (extremely rare for CLI tools).

I'm also restructuring all patterns to use explicit (program ...) / (ERROR ...) root matching and adding ERROR fallback variants for files with Dart 3 shorthand constructors that cause parse errors.

I will update the PR title and description too 😟

@tsinis tsinis changed the title feat: add dart run task for standalone main() functions feat: add dart-main tag and fix flutter test/group button priority Mar 21, 2026
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.

2 participants