• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dart-lang / yaml_edit / 4521889217

pending completion
4521889217

push

github

GitHub
Require Dart 2.19, update to latest lints

28 of 30 new or added lines in 6 files covered. (93.33%)

12 existing lines in 4 files now uncovered.

624 of 654 relevant lines covered (95.41%)

56.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.7
/lib/src/utils.dart
1
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2
// for details. All rights reserved. Use of this source code is governed by a
3
// BSD-style license that can be found in the LICENSE file.
4

5
import 'package:source_span/source_span.dart';
6
import 'package:yaml/yaml.dart';
7

8
import 'editor.dart';
9

10
/// Determines if [string] is dangerous by checking if parsing the plain string
11
/// can return a result different from [string].
12
///
13
/// This function is also capable of detecting if non-printable characters are
14
/// in [string].
15
bool isDangerousString(String string) {
63✔
16
  try {
30✔
17
    if (loadYamlNode(string).value != string) {
129✔
18
      return true;
15✔
19
    }
20

21
    // [string] should also not contain the `[`, `]`, `,`, `{` and `}` indicator
22
    // characters.
23
    return string.contains(RegExp(r'\{|\[|\]|\}|,'));
96✔
24
  } catch (e) {
25
    /// This catch statement catches [ArgumentError] in `loadYamlNode` when
26
    /// a string can be interpreted as a URI tag, but catches for other
27
    /// [YamlException]s
28
    return true;
12✔
29
  }
30
}
30✔
31

32
/// Asserts that [value] is a valid scalar according to YAML.
33
///
34
/// A valid scalar is a number, String, boolean, or null.
35
void assertValidScalar(Object? value) {
99✔
36
  if (value is num || value is String || value is bool || value == null) {
180✔
37
    return;
48✔
38
  }
39

40
  throw ArgumentError.value(value, 'value', 'Not a valid scalar type!');
12✔
41
}
48✔
42

43
/// Checks if [node] is a [YamlNode] with block styling.
44
///
45
/// [ScalarStyle.ANY] and [CollectionStyle.ANY] are considered to be block
46
/// styling by default for maximum flexibility.
47
bool isBlockNode(YamlNode node) {
75✔
48
  if (node is YamlScalar) {
75✔
49
    if (node.style == ScalarStyle.LITERAL ||
114✔
50
        node.style == ScalarStyle.FOLDED ||
114✔
51
        node.style == ScalarStyle.ANY) {
114✔
52
      return true;
36✔
53
    }
54
  }
55

56
  if (node is YamlList &&
51✔
57
      (node.style == CollectionStyle.BLOCK ||
69✔
58
          node.style == CollectionStyle.ANY)) return true;
69✔
59
  if (node is YamlMap &&
45✔
60
      (node.style == CollectionStyle.BLOCK ||
60✔
61
          node.style == CollectionStyle.ANY)) return true;
60✔
62

63
  return false;
15✔
64
}
36✔
65

66
/// Returns the content sensitive ending offset of [yamlNode] (i.e. where the
67
/// last meaningful content happens)
68
int getContentSensitiveEnd(YamlNode yamlNode) {
69✔
69
  if (yamlNode is YamlList) {
69✔
70
    if (yamlNode.style == CollectionStyle.FLOW) {
39✔
71
      return yamlNode.span.end.offset;
24✔
72
    } else {
73
      return getContentSensitiveEnd(yamlNode.nodes.last);
45✔
74
    }
75
  } else if (yamlNode is YamlMap) {
69✔
76
    if (yamlNode.style == CollectionStyle.FLOW) {
45✔
77
      return yamlNode.span.end.offset;
48✔
78
    } else {
79
      return getContentSensitiveEnd(yamlNode.nodes.values.last);
45✔
80
    }
81
  }
82

83
  return yamlNode.span.end.offset;
141✔
84
}
33✔
85

86
/// Checks if the item is a Map or a List
87
bool isCollection(Object item) => item is Map || item is List;
129✔
88

89
/// Checks if [index] is [int], >=0, < [length]
90
bool isValidIndex(Object? index, int length) {
42✔
91
  return index is int && index >= 0 && index < length;
117✔
92
}
12✔
93

94
/// Checks if the item is empty, if it is a List or a Map.
95
///
96
/// Returns `false` if [item] is not a List or Map.
97
bool isEmpty(Object item) {
45✔
98
  if (item is Map) return item.isEmpty;
63✔
99
  if (item is List) return item.isEmpty;
60✔
100

101
  return false;
102
}
21✔
103

104
/// Creates a [SourceSpan] from [sourceUrl] with no meaningful location
105
/// information.
106
///
107
/// Mainly used with [wrapAsYamlNode] to allow for a reasonable
108
/// implementation of [SourceSpan.message].
109
SourceSpan shellSpan(Object? sourceUrl) {
51✔
110
  final shellSourceLocation = SourceLocation(0, sourceUrl: sourceUrl);
99✔
111
  return SourceSpanBase(shellSourceLocation, shellSourceLocation, '');
99✔
112
}
113

114
/// Returns if [value] is a [YamlList] or [YamlMap] with [CollectionStyle.FLOW].
115
bool isFlowYamlCollectionNode(Object value) =>
24✔
116
    value is YamlNode && value.collectionStyle == CollectionStyle.FLOW;
93✔
117

118
/// Determines the index where [newKey] will be inserted if the keys in [map]
119
/// are in alphabetical order when converted to strings.
120
///
121
/// Returns the length of [map] if the keys in [map] are not in alphabetical
122
/// order.
123
int getMapInsertionIndex(YamlMap map, Object newKey) {
21✔
124
  final keys = map.nodes.keys.map((k) => k.toString()).toList();
96✔
125

126
  for (var i = 1; i < keys.length; i++) {
42✔
127
    if (keys[i].compareTo(keys[i - 1]) < 0) {
51✔
128
      return map.length;
12✔
129
    }
130
  }
131

132
  final insertionIndex =
133
      keys.indexWhere((key) => key.compareTo(newKey as String) > 0);
72✔
134

135
  if (insertionIndex != -1) return insertionIndex;
33✔
136

137
  return map.length;
15✔
138
}
9✔
139

140
/// Returns the detected indentation step used in [yaml], or
141
/// defaults to a value of `2` if no indentation step can be detected.
142
///
143
/// Indentation step is determined by the difference in indentation of the
144
/// first block-styled yaml collection in the second level as compared to the
145
/// top-level elements. In the case where there are multiple possible
146
/// candidates, we choose the candidate closest to the start of [yaml].
147
int getIndentation(YamlEditor editor) {
63✔
148
  final node = editor.parseAt([]);
66✔
149
  Iterable<YamlNode>? children;
150
  var indentation = 2;
3✔
151

152
  if (node is YamlMap && node.style == CollectionStyle.BLOCK) {
123✔
153
    children = node.nodes.values;
87✔
154
  } else if (node is YamlList && node.style == CollectionStyle.BLOCK) {
105✔
155
    children = node.nodes;
51✔
156
  }
157

158
  if (children != null) {
30✔
159
    for (final child in children) {
96✔
160
      var indent = 0;
161
      if (child is YamlList) {
63✔
162
        indent = getListIndentation(editor.toString(), child);
54✔
163
      } else if (child is YamlMap) {
63✔
164
        indent = getMapIndentation(editor.toString(), child);
60✔
165
      }
166

167
      if (indent != 0) indentation = indent;
63✔
168
    }
169
  }
170
  return indentation;
30✔
171
}
30✔
172

173
/// Gets the indentation level of [list]. This is 0 if it is a flow list,
174
/// but returns the number of spaces before the hyphen of elements for
175
/// block lists.
176
///
177
/// Throws [UnsupportedError] if an empty block map is passed in.
178
int getListIndentation(String yaml, YamlList list) {
57✔
179
  if (list.style == CollectionStyle.FLOW) return 0;
87✔
180

181
  /// An empty block map doesn't really exist.
182
  if (list.isEmpty) {
30✔
UNCOV
183
    throw UnsupportedError('Unable to get indentation for empty block list');
×
184
  }
185

186
  final lastSpanOffset = list.nodes.last.span.start.offset;
177✔
187
  final lastNewLine = yaml.lastIndexOf('\n', lastSpanOffset - 1);
87✔
188
  final lastHyphen = yaml.lastIndexOf('-', lastSpanOffset - 1);
87✔
189

190
  if (lastNewLine == -1) return lastHyphen;
87✔
191

192
  return lastHyphen - lastNewLine - 1;
87✔
193
}
27✔
194

195
/// Gets the indentation level of [map]. This is 0 if it is a flow map,
196
/// but returns the number of spaces before the keys for block maps.
197
int getMapIndentation(String yaml, YamlMap map) {
51✔
198
  if (map.style == CollectionStyle.FLOW) return 0;
78✔
199

200
  /// An empty block map doesn't really exist.
201
  if (map.isEmpty) {
27✔
UNCOV
202
    throw UnsupportedError('Unable to get indentation for empty block map');
×
203
  }
204

205
  /// Use the number of spaces between the last key and the newline as
206
  /// indentation.
207
  final lastKey = map.nodes.keys.last as YamlNode;
105✔
208
  final lastSpanOffset = lastKey.span.start.offset;
105✔
209
  final lastNewLine = yaml.lastIndexOf('\n', lastSpanOffset);
51✔
210
  final lastQuestionMark = yaml.lastIndexOf('?', lastSpanOffset);
51✔
211

212
  if (lastQuestionMark == -1) {
78✔
213
    if (lastNewLine == -1) return lastSpanOffset;
78✔
214
    return lastSpanOffset - lastNewLine - 1;
69✔
215
  }
216

217
  /// If there is a question mark, it might be a complex key. Check if it
218
  /// is on the same line as the key node to verify.
219
  if (lastNewLine == -1) return lastQuestionMark;
33✔
220
  if (lastQuestionMark > lastNewLine) {
21✔
221
    return lastQuestionMark - lastNewLine - 1;
18✔
222
  }
223

224
  return lastSpanOffset - lastNewLine - 1;
15✔
225
}
24✔
226

227
/// Returns the detected line ending used in [yaml], more specifically, whether
228
/// [yaml] appears to use Windows `\r\n` or Unix `\n` line endings.
229
///
230
/// The heuristic used is to count all `\n` in the text and if strictly more
231
/// than half of them are preceded by `\r` we report that windows line endings
232
/// are used.
233
String getLineEnding(String yaml) {
75✔
234
  var index = -1;
75✔
235
  var unixNewlines = 0;
236
  var windowsNewlines = 0;
237
  while ((index = yaml.indexOf('\n', index + 1)) != -1) {
192✔
238
    if (index != 0 && yaml[index - 1] == '\r') {
147✔
239
      windowsNewlines++;
6✔
240
    } else {
241
      unixNewlines++;
51✔
242
    }
243
  }
244

245
  return windowsNewlines > unixNewlines ? '\r\n' : '\n';
75✔
246
}
36✔
247

248
extension YamlNodeExtension on YamlNode {
249
  /// Returns the [CollectionStyle] of `this` if `this` is [YamlMap] or
250
  /// [YamlList].
251
  ///
252
  /// Otherwise, returns `null`.
253
  CollectionStyle? get collectionStyle {
51✔
254
    final me = this;
255
    if (me is YamlMap) return me.style;
75✔
256
    if (me is YamlList) return me.style;
75✔
257
    return null;
12✔
258
  }
24✔
259
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc