| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 import 'package:path/path.dart' as p; | 5 import 'package:path/path.dart' as p; |
| 6 import 'package:collection/collection.dart'; | 6 import 'package:collection/collection.dart'; |
| 7 | 7 |
| 8 import 'utils.dart'; | 8 import 'utils.dart'; |
| 9 | 9 |
| 10 const _SEPARATOR = 0x2F; // "/" | 10 const _SEPARATOR = 0x2F; // "/" |
| 11 | 11 |
| 12 /// A node in the abstract syntax tree for a glob. | 12 /// A node in the abstract syntax tree for a glob. |
| 13 abstract class AstNode { | 13 abstract class AstNode { |
| 14 /// The cached regular expression that this AST was compiled into. | 14 /// The cached regular expression that this AST was compiled into. |
| 15 RegExp _regExp; | 15 RegExp _regExp; |
| 16 | 16 |
| 17 /// Whether this node matches case-sensitively or not. | 17 /// Whether this node matches case-sensitively or not. |
| 18 final bool caseSensitive; | 18 final bool caseSensitive; |
| 19 | 19 |
| 20 /// Whether this glob could match an absolute path. | 20 /// Whether this glob could match an absolute path. |
| 21 /// | 21 /// |
| 22 /// Either this or [canMatchRelative] or both will be true. | 22 /// Either this or [canMatchRelative] or both will be true. |
| 23 final bool canMatchAbsolute = false; | 23 bool get canMatchAbsolute => false; |
| 24 | 24 |
| 25 /// Whether this glob could match a relative path. | 25 /// Whether this glob could match a relative path. |
| 26 /// | 26 /// |
| 27 /// Either this or [canMatchRelative] or both will be true. | 27 /// Either this or [canMatchRelative] or both will be true. |
| 28 final bool canMatchRelative = true; | 28 bool get canMatchRelative => true; |
| 29 | 29 |
| 30 AstNode._(this.caseSensitive); | 30 AstNode._(this.caseSensitive); |
| 31 | 31 |
| 32 /// Returns a new glob with all the options bubbled to the top level. | 32 /// Returns a new glob with all the options bubbled to the top level. |
| 33 /// | 33 /// |
| 34 /// In particular, this returns a glob AST with two guarantees: | 34 /// In particular, this returns a glob AST with two guarantees: |
| 35 /// | 35 /// |
| 36 /// 1. There are no [OptionsNode]s other than the one at the top level. | 36 /// 1. There are no [OptionsNode]s other than the one at the top level. |
| 37 /// 2. It matches the same set of paths as [this]. | 37 /// 2. It matches the same set of paths as [this]. |
| 38 /// | 38 /// |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 79 var nextSequences = node.flattenOptions().options; | 79 var nextSequences = node.flattenOptions().options; |
| 80 sequences = sequences.expand((sequence) { | 80 sequences = sequences.expand((sequence) { |
| 81 return nextSequences.map((nextSequence) { | 81 return nextSequences.map((nextSequence) { |
| 82 return sequence.toList()..addAll(nextSequence.nodes); | 82 return sequence.toList()..addAll(nextSequence.nodes); |
| 83 }); | 83 }); |
| 84 }); | 84 }); |
| 85 } | 85 } |
| 86 | 86 |
| 87 return new OptionsNode(sequences.map((sequence) { | 87 return new OptionsNode(sequences.map((sequence) { |
| 88 // Combine any adjacent LiteralNodes in [sequence]. | 88 // Combine any adjacent LiteralNodes in [sequence]. |
| 89 return new SequenceNode(sequence.fold([], (combined, node) { | 89 return new SequenceNode(sequence.fold/*<List<AstNode>>*/([], (combined, no
de) { |
| 90 if (combined.isEmpty || combined.last is! LiteralNode || | 90 if (combined.isEmpty || combined.last is! LiteralNode || |
| 91 node is! LiteralNode) { | 91 node is! LiteralNode) { |
| 92 return combined..add(node); | 92 return combined..add(node); |
| 93 } | 93 } |
| 94 | 94 |
| 95 combined[combined.length - 1] = new LiteralNode( | 95 combined[combined.length - 1] = new LiteralNode( |
| 96 combined.last.text + node.text, caseSensitive: caseSensitive); | 96 // TODO(nweiz): Avoid casting when sdk#25565 is fixed. |
| 97 (combined.last as LiteralNode).text + (node as LiteralNode).text, |
| 98 caseSensitive: caseSensitive); |
| 97 return combined; | 99 return combined; |
| 98 }), caseSensitive: caseSensitive); | 100 }), caseSensitive: caseSensitive); |
| 99 }), caseSensitive: caseSensitive); | 101 }), caseSensitive: caseSensitive); |
| 100 } | 102 } |
| 101 | 103 |
| 102 /// Splits this glob into components along its path separators. | 104 /// Splits this glob into components along its path separators. |
| 103 /// | 105 /// |
| 104 /// For example, given the glob `foo/*/*.dart`, this would return three globs: | 106 /// For example, given the glob `foo/*/*.dart`, this would return three globs: |
| 105 /// `foo`, `*`, and `*.dart`. | 107 /// `foo`, `*`, and `*.dart`. |
| 106 /// | 108 /// |
| 107 /// Path separators within options nodes are not split. For example, | 109 /// Path separators within options nodes are not split. For example, |
| 108 /// `foo/{bar,baz/bang}/qux` will return three globs: `foo`, `{bar,baz/bang}`, | 110 /// `foo/{bar,baz/bang}/qux` will return three globs: `foo`, `{bar,baz/bang}`, |
| 109 /// and `qux`. | 111 /// and `qux`. |
| 110 /// | 112 /// |
| 111 /// [context] is used to determine what absolute roots look like for this | 113 /// [context] is used to determine what absolute roots look like for this |
| 112 /// glob. | 114 /// glob. |
| 113 List<SequenceNode> split(p.Context context) { | 115 List<SequenceNode> split(p.Context context) { |
| 114 var componentsToReturn = []; | 116 var componentsToReturn = <SequenceNode>[]; |
| 115 var currentComponent; | 117 List<AstNode> currentComponent; |
| 116 | 118 |
| 117 addNode(node) { | 119 addNode(AstNode node) { |
| 118 if (currentComponent == null) currentComponent = []; | 120 if (currentComponent == null) currentComponent = []; |
| 119 currentComponent.add(node); | 121 currentComponent.add(node); |
| 120 } | 122 } |
| 121 | 123 |
| 122 finishComponent() { | 124 finishComponent() { |
| 123 if (currentComponent == null) return; | 125 if (currentComponent == null) return; |
| 124 componentsToReturn.add( | 126 componentsToReturn.add( |
| 125 new SequenceNode(currentComponent, caseSensitive: caseSensitive)); | 127 new SequenceNode(currentComponent, caseSensitive: caseSensitive)); |
| 126 currentComponent = null; | 128 currentComponent = null; |
| 127 } | 129 } |
| 128 | 130 |
| 129 for (var node in nodes) { | 131 for (var node in nodes) { |
| 130 if (node is! LiteralNode || !node.text.contains('/')) { | 132 if (node is! LiteralNode) { |
| 131 addNode(node); | 133 addNode(node); |
| 132 continue; | 134 continue; |
| 133 } | 135 } |
| 134 | 136 |
| 135 var text = node.text; | 137 // TODO(nweiz): Avoid casting when sdk#25565 is fixed. |
| 138 var literal = node as LiteralNode; |
| 139 if (!literal.text.contains('/')) { |
| 140 addNode(literal); |
| 141 continue; |
| 142 } |
| 143 |
| 144 var text = literal.text; |
| 136 if (context.style == p.Style.windows) text = text.replaceAll("/", "\\"); | 145 if (context.style == p.Style.windows) text = text.replaceAll("/", "\\"); |
| 137 var components = context.split(text); | 146 var components = context.split(text); |
| 138 | 147 |
| 139 // If the first component is absolute, that means it's a separator (on | 148 // If the first component is absolute, that means it's a separator (on |
| 140 // Windows some non-separator things are also absolute, but it's invalid | 149 // Windows some non-separator things are also absolute, but it's invalid |
| 141 // to have "C:" show up in the middle of a path anyway). | 150 // to have "C:" show up in the middle of a path anyway). |
| 142 if (context.isAbsolute(components.first)) { | 151 if (context.isAbsolute(components.first)) { |
| 143 // If this is the first component, it's the root. | 152 // If this is the first component, it's the root. |
| 144 if (componentsToReturn.isEmpty && currentComponent == null) { | 153 if (componentsToReturn.isEmpty && currentComponent == null) { |
| 145 var root = components.first; | 154 var root = components.first; |
| (...skipping 14 matching lines...) Expand all Loading... |
| 160 // For each component except the last one, add a separate sequence to | 169 // For each component except the last one, add a separate sequence to |
| 161 // [sequences] containing only that component. | 170 // [sequences] containing only that component. |
| 162 for (var component in components.take(components.length - 1)) { | 171 for (var component in components.take(components.length - 1)) { |
| 163 addNode(new LiteralNode(component, caseSensitive: caseSensitive)); | 172 addNode(new LiteralNode(component, caseSensitive: caseSensitive)); |
| 164 finishComponent(); | 173 finishComponent(); |
| 165 } | 174 } |
| 166 | 175 |
| 167 // For the final component, only end its sequence (by adding a new empty | 176 // For the final component, only end its sequence (by adding a new empty |
| 168 // sequence) if it ends with a separator. | 177 // sequence) if it ends with a separator. |
| 169 addNode(new LiteralNode(components.last, caseSensitive: caseSensitive)); | 178 addNode(new LiteralNode(components.last, caseSensitive: caseSensitive)); |
| 170 if (node.text.endsWith('/')) finishComponent(); | 179 if (literal.text.endsWith('/')) finishComponent(); |
| 171 } | 180 } |
| 172 | 181 |
| 173 finishComponent(); | 182 finishComponent(); |
| 174 return componentsToReturn; | 183 return componentsToReturn; |
| 175 } | 184 } |
| 176 | 185 |
| 177 String _toRegExp() => nodes.map((node) => node._toRegExp()).join(); | 186 String _toRegExp() => nodes.map((node) => node._toRegExp()).join(); |
| 178 | 187 |
| 179 bool operator==(Object other) => other is SequenceNode && | 188 bool operator==(Object other) => other is SequenceNode && |
| 180 const IterableEquality().equals(nodes, other.nodes); | 189 const IterableEquality().equals(nodes, other.nodes); |
| (...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 381 super._(caseSensitive); | 390 super._(caseSensitive); |
| 382 | 391 |
| 383 String _toRegExp() => regExpQuote(text); | 392 String _toRegExp() => regExpQuote(text); |
| 384 | 393 |
| 385 bool operator==(Object other) => other is LiteralNode && other.text == text; | 394 bool operator==(Object other) => other is LiteralNode && other.text == text; |
| 386 | 395 |
| 387 int get hashCode => text.hashCode; | 396 int get hashCode => text.hashCode; |
| 388 | 397 |
| 389 String toString() => text; | 398 String toString() => text; |
| 390 } | 399 } |
| OLD | NEW |