Skip to content

Conversation

@kekavc24
Copy link
Contributor

@kekavc24 kekavc24 commented Nov 7, 2025

Issue(s)

Changes

  • Calling YamlEditor.remove on a block map/list now removes the entire node completely and leaves no dangling comments or line breaks.
  • _removeFromBlockMap and _removeFromBlockList have been refactored to have shared logic. The heuristics for performing their edits are quite similar.

Disclaimers

This is a positive but breaking change. I have not changed the failing tests to showcase difference in output once/if this change lands.

Rationale

While YamlEditor itself doesn't support inserting ScalarStyle.FOLDED and ScalarStyle.LITERAL strings with trailing line breaks, there is a probability (even if it's low) that it might get a yaml string with these styles. It would be nice if it can handle this.

It uses a "if-not-first-then-discard-all" approach. It scans around the node and checks the next node (if present) for context before performing the edit. Looking ahead is easier than backtracking. See examples.

Case 1: Top level list

--- # Before edit
- value
- >+
  folded keep chomp

- removed # Funky comment
          # More indented
            # Messes with literal & folded.
- next

--- # After edit. Block scalar is safe
- value
- >+
  folded keep chomp

- next

Case 2: Top level maps

--- # Before edit
key: value
block: >+
  folded keep chomp

removed: entry # Funky comment
                    # More indented
                      # Messes with literal & folded.
next: entry

--- # After edit. Block scalar is safe
key: value
block: >+
  folded keep chomp

next: entry

Case 3: Compact lists

Same logic applies to block maps.

  • Variant 1
--- # Before edit

- - removed # My comment
  - next

--- # After edit

- - next
  • Variant 2 - Lenient when making nodes compact. This is the first node. We have the next as the buffer.
--- # Before edit

- - removed # Comment
            # No man's land
  - next

--- # After edit

-          # No man's land
  - next
  • Variant 3 - Strict. Who is before? Can check (is it worth it?), truncates instead.
--- # Before edit

- - |+
      literal keep chomp

  - removed # Comment
            # No man's land
  - next

--- # After edit

- - |+
      literal keep chomp

  - next
  • Variant 4 - Okay. Who is before? Can check (*BUT* is it worth it?), truncates instead.
--- # Before edit

- - Who is here

# Who? me?
# Just hanging around
# https://youtube.com/shorts/GPIwgAUwXYw?si=B8RzE6B0uXJQWHk-

  - removed # Comment
            # No man's land

  - next

--- # After edit

- - Who is here

# Who? me?
# Just hanging around
# https://youtube.com/shorts/GPIwgAUwXYw?si=B8RzE6B0uXJQWHk-

  - next
  • Variant 5 - Trailing. Removed.
--- # Before edit
- value
- another
- removed # Funky comment
          # More indented
            # Messes with literal & folded.

--- # After edit. Block scalar is safe
- value
- another

  • I’ve reviewed the contributor guide and applied the relevant portions to this PR.
Contribution guidelines:

Many Dart repos have a weekly cadence for reviewing PRs - please allow for some latency before initial review feedback.

Note: The Dart team is trialing Gemini Code Assist. Don't take its comments as final Dart team feedback. Use the suggestions if they're helpful; otherwise, wait for a human reviewer.

@kekavc24 kekavc24 requested a review from a team as a code owner November 7, 2025 11:58
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant and well-reasoned refactoring to how YAML block collection entries are removed, aiming to prevent unintended effects on block scalars. The new removeBlockCollectionEntry function is a great addition for centralizing this complex logic. The changes are thoroughly commented and include relevant test cases. I've identified a couple of areas for improvement in the new utility functions to enhance correctness and clarity.


// We scanned till the end of the string and found nothing.
if (currentOffset == -1) {
lineEndingIndex = currentOffset;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There seems to be a bug when indexOf returns -1. In that case, currentOffset becomes -1, and lineEndingIndex is also set to -1. This causes the caller, removeBlockCollectionEntry, to calculate an incorrect end offset for the source edit, potentially leading to incorrect document modifications.

When currentOffset is -1, it signifies that the rest of the string contains only whitespace. To ensure this trailing whitespace is included in the removal, the end of the removal range should extend to the end of the string. Setting lineEndingIndex to lastOffset will allow the caller to correctly calculate the end of the removal range as yaml.length.

Suggested change
lineEndingIndex = currentOffset;
lineEndingIndex = lastOffset;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. Missed that while renaming stuff.

default:
break scanner;
}
} while (start >= 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The loop condition while (start >= 0) is a bit confusing as the start variable is not modified within the loop. The loop correctly terminates due to break or return statements, but the condition itself is misleading. Using while (true) would more clearly communicate the intent that this is a loop that is exited from within its body, improving code readability and maintainability.

Suggested change
} while (start >= 0);
} while (true);

* Refactor `_removeFromBlockList` and `_removeFromBlockMap` and remove dangling line breaks which may affect how block scalars are intepreted by the `package: yaml`.
* Prefer obtaining an entry in one pass from `YamlMap`.
* Add tests
@kekavc24 kekavc24 force-pushed the fix-line-ending-bug branch from 01a4b9d to bf49685 Compare November 7, 2025 12:25
@github-actions
Copy link

github-actions bot commented Nov 7, 2025

Package publishing

Package Version Status Publish tag (post-merge)
package:bazel_worker 1.1.4 already published at pub.dev
package:benchmark_harness 2.4.0-wip WIP (no publish necessary)
package:boolean_selector 2.1.2 already published at pub.dev
package:browser_launcher 1.1.3 already published at pub.dev
package:cli_config 0.2.1-wip WIP (no publish necessary)
package:cli_util 0.5.0-wip WIP (no publish necessary)
package:clock 1.1.3-wip WIP (no publish necessary)
package:code_builder 4.11.0 already published at pub.dev
package:coverage 1.15.0 already published at pub.dev
package:csslib 1.0.2 already published at pub.dev
package:extension_discovery 2.1.0 already published at pub.dev
package:file 7.0.2-wip WIP (no publish necessary)
package:file_testing 3.1.0-wip WIP (no publish necessary)
package:glob 2.1.3 already published at pub.dev
package:graphs 2.3.3-wip WIP (no publish necessary)
package:html 0.15.7-wip WIP (no publish necessary)
package:io 1.1.0-wip WIP (no publish necessary)
package:json_rpc_2 4.0.0 already published at pub.dev
package:markdown 7.3.1-wip WIP (no publish necessary)
package:mime 2.1.0-wip WIP (no publish necessary)
package:oauth2 2.0.5 already published at pub.dev
package:package_config 2.3.0-wip WIP (no publish necessary)
package:pool 1.5.2 already published at pub.dev
package:process 5.0.5 already published at pub.dev
package:pub_semver 2.2.0 already published at pub.dev
package:pubspec_parse 1.5.1-wip WIP (no publish necessary)
package:source_map_stack_trace 2.1.3-wip WIP (no publish necessary)
package:source_maps 0.10.14-wip WIP (no publish necessary)
package:source_span 1.10.1 already published at pub.dev
package:sse 4.1.8 already published at pub.dev
package:stack_trace 1.12.2-wip (error) pubspec version (1.12.2-wip) and changelog (1.12.2-dev) don't agree
package:stream_channel 2.1.4 already published at pub.dev
package:stream_transform 2.1.2-wip WIP (no publish necessary)
package:string_scanner 1.4.1 already published at pub.dev
package:term_glyph 1.2.3-wip WIP (no publish necessary)
package:test_reflective_loader 0.5.0 ready to publish test_reflective_loader-v0.5.0
package:timing 1.0.2 already published at pub.dev
package:unified_analytics 8.0.8 ready to publish unified_analytics-v8.0.8
package:watcher 1.1.5-wip WIP (no publish necessary)
package:yaml 3.1.3 already published at pub.dev
package:yaml_edit 2.2.2 already published at pub.dev

Documentation at https://github.com/dart-lang/ecosystem/wiki/Publishing-automation.

@github-actions
Copy link

github-actions bot commented Nov 7, 2025

PR Health

License Headers ✔️
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
Files
no missing headers

All source files should start with a license header.

Unrelated files missing license headers
Files
pkgs/bazel_worker/benchmark/benchmark.dart
pkgs/benchmark_harness/integration_test/perf_benchmark_test.dart
pkgs/boolean_selector/example/example.dart
pkgs/clock/lib/clock.dart
pkgs/clock/lib/src/clock.dart
pkgs/clock/lib/src/default.dart
pkgs/clock/lib/src/stopwatch.dart
pkgs/clock/lib/src/utils.dart
pkgs/clock/test/clock_test.dart
pkgs/clock/test/default_test.dart
pkgs/clock/test/stopwatch_test.dart
pkgs/clock/test/utils.dart
pkgs/coverage/lib/src/coverage_options.dart
pkgs/html/example/main.dart
pkgs/html/lib/dom.dart
pkgs/html/lib/dom_parsing.dart
pkgs/html/lib/html_escape.dart
pkgs/html/lib/parser.dart
pkgs/html/lib/src/constants.dart
pkgs/html/lib/src/encoding_parser.dart
pkgs/html/lib/src/html_input_stream.dart
pkgs/html/lib/src/list_proxy.dart
pkgs/html/lib/src/query_selector.dart
pkgs/html/lib/src/token.dart
pkgs/html/lib/src/tokenizer.dart
pkgs/html/lib/src/treebuilder.dart
pkgs/html/lib/src/utils.dart
pkgs/html/test/dom_test.dart
pkgs/html/test/parser_feature_test.dart
pkgs/html/test/parser_test.dart
pkgs/html/test/query_selector_test.dart
pkgs/html/test/selectors/level1_baseline_test.dart
pkgs/html/test/selectors/level1_lib.dart
pkgs/html/test/selectors/selectors.dart
pkgs/html/test/support.dart
pkgs/html/test/tokenizer_test.dart
pkgs/html/test/trie_test.dart
pkgs/html/tool/generate_trie.dart
pkgs/pubspec_parse/test/git_uri_test.dart
pkgs/stack_trace/example/example.dart
pkgs/watcher/test/custom_watcher_factory_test.dart
pkgs/yaml_edit/example/example.dart

This check can be disabled by tagging the PR with skip-license-check.

API leaks ✔️

The following packages contain symbols visible in the public API, but not exported by the library. Export these symbols or remove them from your publicly visible API.

Package Leaked API symbol Leaking sources

This check can be disabled by tagging the PR with skip-leaking-check.

Breaking changes ✔️
Package Change Current Version New Version Needed Version Looking good?
yaml_edit None 2.2.2 2.2.2 2.2.2 ✔️

This check can be disabled by tagging the PR with skip-breaking-check.

Coverage ⚠️
File Coverage
pkgs/yaml_edit/lib/src/equality.dart 💔 88 % ⬇️ 13 %
pkgs/yaml_edit/lib/src/list_mutations.dart 💔 99 % ⬇️ 0 %
pkgs/yaml_edit/lib/src/map_mutations.dart 💚 100 %
pkgs/yaml_edit/lib/src/utils.dart 💔 98 % ⬇️ 0 %

This check for test coverage is informational (issues shown here will not fail the PR).

This check can be disabled by tagging the PR with skip-coverage-check.

Changelog Entry
Package Changed Files
package:yaml_edit pkgs/yaml_edit/lib/src/equality.dart
pkgs/yaml_edit/lib/src/list_mutations.dart
pkgs/yaml_edit/lib/src/map_mutations.dart
pkgs/yaml_edit/lib/src/utils.dart

Changes to files need to be accounted for in their respective changelogs.

This check can be disabled by tagging the PR with skip-changelog-check.

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.

Comment causes issue with ScalarStyle.FOLDED

1 participant