| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 library polymer.src.utils; | 5 library polymer.src.utils; |
| 6 | 6 |
| 7 import 'dart:async'; | |
| 8 import 'package:path/path.dart' show Builder; | |
| 9 export 'utils_observe.dart' show toCamelCase, toHyphenedName; | |
| 10 | |
| 11 /** | 7 /** |
| 12 * An instance of the pathos library builder. We could just use the default | 8 * Converts a string name with hyphens into an identifier, by removing hyphens |
| 13 * builder in pathos, but we add this indirection to make it possible to run | 9 * and capitalizing the following letter. Optionally [startUppercase] to |
| 14 * unittest for windows paths. | 10 * captialize the first letter. |
| 15 */ | 11 */ |
| 16 Builder path = new Builder(); | 12 String toCamelCase(String hyphenedName, {bool startUppercase: false}) { |
| 17 | 13 var segments = hyphenedName.split('-'); |
| 18 /** Convert a OS specific path into a url. */ | 14 int start = startUppercase ? 0 : 1; |
| 19 String pathToUrl(String relPath) => | 15 for (int i = start; i < segments.length; i++) { |
| 20 (path.separator == '/') ? relPath : path.split(relPath).join('/'); | 16 var segment = segments[i]; |
| 21 | 17 if (segment.length > 0) { |
| 22 /** | 18 // Character between 'a'..'z' mapped to 'A'..'Z' |
| 23 * Invokes [callback], logs how long it took to execute in ms, and returns | 19 segments[i] = '${segment[0].toUpperCase()}${segment.substring(1)}'; |
| 24 * whatever [callback] returns. The log message will be printed if [printTime] | 20 } |
| 25 * is true. | |
| 26 */ | |
| 27 time(String logMessage, callback(), | |
| 28 {bool printTime: false, bool useColors: false}) { | |
| 29 final watch = new Stopwatch(); | |
| 30 watch.start(); | |
| 31 var result = callback(); | |
| 32 watch.stop(); | |
| 33 final duration = watch.elapsedMilliseconds; | |
| 34 if (printTime) { | |
| 35 _printMessage(logMessage, duration, useColors); | |
| 36 } | 21 } |
| 37 return result; | 22 return segments.join(''); |
| 38 } | 23 } |
| 39 | 24 |
| 40 /** | 25 /** Reverse of [toCamelCase]. */ |
| 41 * Invokes [callback], logs how long it takes from the moment [callback] is | 26 String toHyphenedName(String word) { |
| 42 * executed until the future it returns is completed. Returns the future | 27 var sb = new StringBuffer(); |
| 43 * returned by [callback]. The log message will be printed if [printTime] | 28 for (int i = 0; i < word.length; i++) { |
| 44 * is true. | 29 var lower = word[i].toLowerCase(); |
| 45 */ | 30 if (word[i] != lower && i > 0) sb.write('-'); |
| 46 Future asyncTime(String logMessage, Future callback(), | 31 sb.write(lower); |
| 47 {bool printTime: false, bool useColors: false}) { | 32 } |
| 48 final watch = new Stopwatch(); | 33 return sb.toString(); |
| 49 watch.start(); | |
| 50 return callback()..then((_) { | |
| 51 watch.stop(); | |
| 52 final duration = watch.elapsedMilliseconds; | |
| 53 if (printTime) { | |
| 54 _printMessage(logMessage, duration, useColors); | |
| 55 } | |
| 56 }); | |
| 57 } | 34 } |
| 58 | |
| 59 void _printMessage(String logMessage, int duration, bool useColors) { | |
| 60 var buf = new StringBuffer(); | |
| 61 buf.write(logMessage); | |
| 62 for (int i = logMessage.length; i < 60; i++) buf.write(' '); | |
| 63 buf.write(' -- '); | |
| 64 if (useColors) { | |
| 65 buf.write(GREEN_COLOR); | |
| 66 } | |
| 67 if (duration < 10) buf.write(' '); | |
| 68 if (duration < 100) buf.write(' '); | |
| 69 buf..write(duration)..write(' ms'); | |
| 70 if (useColors) { | |
| 71 buf.write(NO_COLOR); | |
| 72 } | |
| 73 print(buf.toString()); | |
| 74 } | |
| 75 | |
| 76 // Color constants used for generating messages. | |
| 77 final String GREEN_COLOR = '\u001b[32m'; | |
| 78 final String RED_COLOR = '\u001b[31m'; | |
| 79 final String MAGENTA_COLOR = '\u001b[35m'; | |
| 80 final String NO_COLOR = '\u001b[0m'; | |
| 81 | |
| 82 /** A future that waits until all added [Future]s complete. */ | |
| 83 // TODO(sigmund): this should be part of the futures/core libraries. | |
| 84 class FutureGroup { | |
| 85 static const _FINISHED = -1; | |
| 86 | |
| 87 int _pending = 0; | |
| 88 Future _failedTask; | |
| 89 final Completer<List> _completer = new Completer<List>(); | |
| 90 final List results = []; | |
| 91 | |
| 92 /** Gets the task that failed, if any. */ | |
| 93 Future get failedTask => _failedTask; | |
| 94 | |
| 95 /** | |
| 96 * Wait for [task] to complete. | |
| 97 * | |
| 98 * If this group has already been marked as completed, you'll get a | |
| 99 * [StateError]. | |
| 100 * | |
| 101 * If this group has a [failedTask], new tasks will be ignored, because the | |
| 102 * error has already been signaled. | |
| 103 */ | |
| 104 void add(Future task) { | |
| 105 if (_failedTask != null) return; | |
| 106 if (_pending == _FINISHED) throw new StateError("Future already completed"); | |
| 107 | |
| 108 _pending++; | |
| 109 var i = results.length; | |
| 110 results.add(null); | |
| 111 task.then((res) { | |
| 112 results[i] = res; | |
| 113 if (_failedTask != null) return; | |
| 114 _pending--; | |
| 115 if (_pending == 0) { | |
| 116 _pending = _FINISHED; | |
| 117 _completer.complete(results); | |
| 118 } | |
| 119 }, onError: (e) { | |
| 120 if (_failedTask != null) return; | |
| 121 _failedTask = task; | |
| 122 _completer.completeError(e, getAttachedStackTrace(e)); | |
| 123 }); | |
| 124 } | |
| 125 | |
| 126 Future<List> get future => _completer.future; | |
| 127 } | |
| 128 | |
| 129 | |
| 130 /** | |
| 131 * Escapes [text] for use in a Dart string. | |
| 132 * [single] specifies single quote `'` vs double quote `"`. | |
| 133 * [triple] indicates that a triple-quoted string, such as `'''` or `"""`. | |
| 134 */ | |
| 135 String escapeDartString(String text, {bool single: true, bool triple: false}) { | |
| 136 // Note: don't allocate anything until we know we need it. | |
| 137 StringBuffer result = null; | |
| 138 | |
| 139 for (int i = 0; i < text.length; i++) { | |
| 140 int code = text.codeUnitAt(i); | |
| 141 var replace = null; | |
| 142 switch (code) { | |
| 143 case 92/*'\\'*/: replace = r'\\'; break; | |
| 144 case 36/*r'$'*/: replace = r'\$'; break; | |
| 145 case 34/*'"'*/: if (!single) replace = r'\"'; break; | |
| 146 case 39/*"'"*/: if (single) replace = r"\'"; break; | |
| 147 case 10/*'\n'*/: if (!triple) replace = r'\n'; break; | |
| 148 case 13/*'\r'*/: if (!triple) replace = r'\r'; break; | |
| 149 | |
| 150 // Note: we don't escape unicode characters, under the assumption that | |
| 151 // writing the file in UTF-8 will take care of this. | |
| 152 | |
| 153 // TODO(jmesserly): do we want to replace any other non-printable | |
| 154 // characters (such as \f) for readability? | |
| 155 } | |
| 156 | |
| 157 if (replace != null && result == null) { | |
| 158 result = new StringBuffer(text.substring(0, i)); | |
| 159 } | |
| 160 | |
| 161 if (result != null) result.write(replace != null ? replace : text[i]); | |
| 162 } | |
| 163 | |
| 164 return result == null ? text : result.toString(); | |
| 165 } | |
| 166 | |
| 167 /** Iterates through an infinite sequence, starting from zero. */ | |
| 168 class IntIterator implements Iterator<int> { | |
| 169 int _next = -1; | |
| 170 | |
| 171 int get current => _next < 0 ? null : _next; | |
| 172 | |
| 173 bool moveNext() { | |
| 174 _next++; | |
| 175 return true; | |
| 176 } | |
| 177 } | |
| OLD | NEW |