From 5b6f6bdf975207d58c1c56ed18b77c8f32bdb0f0 Mon Sep 17 00:00:00 2001 From: thomashii Date: Tue, 16 Mar 2021 08:14:28 +0800 Subject: [PATCH] Added package code_buffer --- CHANGELOG.md | 9 +- packages/code_buffer/.gitignore | 85 ++++++++ packages/code_buffer/.travis.yml | 1 + packages/code_buffer/CHANGELOG.md | 2 + packages/code_buffer/LICENSE | 21 ++ packages/code_buffer/README.md | 63 ++++++ packages/code_buffer/analysis_options.yaml | 3 + packages/code_buffer/example/main.dart | 45 ++++ packages/code_buffer/lib/code_buffer.dart | 229 +++++++++++++++++++++ packages/code_buffer/pubspec.yaml | 12 ++ packages/code_buffer/test/copy_test.dart | 45 ++++ packages/code_buffer/test/span_test.dart | 44 ++++ packages/code_buffer/test/write_test.dart | 87 ++++++++ packages/http_exception/pubspec.yaml | 2 +- packages/pretty_logging/pubspec.yaml | 6 +- 15 files changed, 646 insertions(+), 8 deletions(-) create mode 100644 packages/code_buffer/.gitignore create mode 100644 packages/code_buffer/.travis.yml create mode 100644 packages/code_buffer/CHANGELOG.md create mode 100644 packages/code_buffer/LICENSE create mode 100644 packages/code_buffer/README.md create mode 100644 packages/code_buffer/analysis_options.yaml create mode 100644 packages/code_buffer/example/main.dart create mode 100644 packages/code_buffer/lib/code_buffer.dart create mode 100644 packages/code_buffer/pubspec.yaml create mode 100644 packages/code_buffer/test/copy_test.dart create mode 100644 packages/code_buffer/test/span_test.dart create mode 100644 packages/code_buffer/test/write_test.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index c8746a60..d02adae7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,9 @@ -# 3.0.1 (NNBD) +# 4.0.0 (NNBD) * Changed Dart SDK requirements for all packages to ">=2.12.0 <3.0.0" to support NNBD. -* Updated pretty_logging to 2.0.0 -* Updated angel_http_exception to 2.0.0 -* Updated angel_cli to 3.0.0. (Rename not working) +* Updated pretty_logging to 3.0.0 +* Updated angel_http_exception to 3.0.0 +* Moved to https://github.com/dukefirehawk/cli +* Updated angel_route to 5.0.0 # 3.0.0 (Non NNBD) * Changed Dart SDK requirements for all packages to ">=2.10.0 <3.0.0" diff --git a/packages/code_buffer/.gitignore b/packages/code_buffer/.gitignore new file mode 100644 index 00000000..0e12a575 --- /dev/null +++ b/packages/code_buffer/.gitignore @@ -0,0 +1,85 @@ +# See https://www.dartlang.org/tools/private-files.html + +# Files and directories created by pub +.packages +.pub/ +build/ +# If you're building an application, you may want to check-in your pubspec.lock +pubspec.lock + +# Directory created by dartdoc +# If you don't generate documentation locally you can remove this line. +doc/api/ +### Dart template +# See https://www.dartlang.org/tools/private-files.html + +# Files and directories created by pub + +# SDK 1.20 and later (no longer creates packages directories) + +# Older SDK versions +# (Include if the minimum SDK version specified in pubsepc.yaml is earlier than 1.20) +.project +.buildlog +**/packages/ + + +# Files created by dart2js +# (Most Dart developers will use pub build to compile Dart, use/modify these +# rules if you intend to use dart2js directly +# Convention is to use extension '.dart.js' for Dart compiled to Javascript to +# differentiate from explicit Javascript files) +*.dart.js +*.part.js +*.js.deps +*.js.map +*.info.json + +# Directory created by dartdoc + +# Don't commit pubspec lock file +# (Library packages only! Remove pattern if developing an application package) +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties diff --git a/packages/code_buffer/.travis.yml b/packages/code_buffer/.travis.yml new file mode 100644 index 00000000..de2210c9 --- /dev/null +++ b/packages/code_buffer/.travis.yml @@ -0,0 +1 @@ +language: dart \ No newline at end of file diff --git a/packages/code_buffer/CHANGELOG.md b/packages/code_buffer/CHANGELOG.md new file mode 100644 index 00000000..a9c6ebb1 --- /dev/null +++ b/packages/code_buffer/CHANGELOG.md @@ -0,0 +1,2 @@ +# 1.0.1 +* Added `CodeBuffer.noWhitespace()`. \ No newline at end of file diff --git a/packages/code_buffer/LICENSE b/packages/code_buffer/LICENSE new file mode 100644 index 00000000..3de28325 --- /dev/null +++ b/packages/code_buffer/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Tobe O + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/code_buffer/README.md b/packages/code_buffer/README.md new file mode 100644 index 00000000..d556c428 --- /dev/null +++ b/packages/code_buffer/README.md @@ -0,0 +1,63 @@ +# code_buffer +[![Pub](https://img.shields.io/pub/v/code_buffer.svg)](https://pub.dartlang.org/packages/code_buffer) +[![build status](https://travis-ci.org/thosakwe/code_buffer.svg)](https://travis-ci.org/thosakwe/code_buffer) + +An advanced StringBuffer geared toward generating code, and source maps. + +# Installation +In your `pubspec.yaml`: + +```yaml +dependencies: + code_buffer: ^1.0.0 +``` + +# Usage +Use a `CodeBuffer` just like any regular `StringBuffer`: + +```dart +String someFunc() { + var buf = new CodeBuffer(); + buf + ..write('hello ') + ..writeln('world!'); + return buf.toString(); +} +``` + +However, a `CodeBuffer` supports indentation. + +```dart +void someOtherFunc() { + var buf = new CodeBuffer(); + // Custom options... + var buf = new CodeBuffer(newline: '\r\n', space: '\t', trailingNewline: true); + + // Any following lines will have an incremented indentation level... + buf.indent(); + + // And vice-versa: + buf.outdent(); +} +``` + +`CodeBuffer` instances keep track of every `SourceSpan` they create. +This makes them useful for codegen tools, or to-JS compilers. + +```dart +void someFunc(CodeBuffer buf) { + buf.write('hello'); + expect(buf.lastLine.text, 'hello'); + + buf.writeln('world'); + expect(buf.lastLine.lastSpan.start.column, 5); +} +``` + +You can copy a `CodeBuffer` into another, heeding indentation rules: + +```dart +void yetAnotherFunc(CodeBuffer a, CodeBuffer b) { + b.copyInto(a); +} +``` \ No newline at end of file diff --git a/packages/code_buffer/analysis_options.yaml b/packages/code_buffer/analysis_options.yaml new file mode 100644 index 00000000..eae1e42a --- /dev/null +++ b/packages/code_buffer/analysis_options.yaml @@ -0,0 +1,3 @@ +analyzer: + strong-mode: + implicit-casts: false \ No newline at end of file diff --git a/packages/code_buffer/example/main.dart b/packages/code_buffer/example/main.dart new file mode 100644 index 00000000..c02b9674 --- /dev/null +++ b/packages/code_buffer/example/main.dart @@ -0,0 +1,45 @@ +import 'package:code_buffer/code_buffer.dart'; +import 'package:test/test.dart'; + +/// Use a `CodeBuffer` just like any regular `StringBuffer`: +String someFunc() { + var buf = new CodeBuffer(); + buf + ..write('hello ') + ..writeln('world!'); + return buf.toString(); +} + +/// However, a `CodeBuffer` supports indentation. +void someOtherFunc() { + var buf = new CodeBuffer(); + + // Custom options... + // ignore: unused_local_variable + var customBuf = new CodeBuffer(newline: '\r\n', space: '\t', trailingNewline: true); + + // Without whitespace.. + // ignore: unused_local_variable + var minifyingBuf = new CodeBuffer.noWhitespace(); + + // Any following lines will have an incremented indentation level... + buf.indent(); + + // And vice-versa: + buf.outdent(); +} + +/// `CodeBuffer` instances keep track of every `SourceSpan` they create. +//This makes them useful for codegen tools, or to-JS compilers. +void yetAnotherOtherFunc(CodeBuffer buf) { + buf.write('hello'); + expect(buf.lastLine!.text, 'hello'); + + buf.writeln('world'); + expect(buf.lastLine!.lastSpan!.start.column, 5); +} + +/// You can copy a `CodeBuffer` into another, heeding indentation rules: +void yetEvenAnotherFunc(CodeBuffer a, CodeBuffer b) { + b.copyInto(a); +} \ No newline at end of file diff --git a/packages/code_buffer/lib/code_buffer.dart b/packages/code_buffer/lib/code_buffer.dart new file mode 100644 index 00000000..68406bdc --- /dev/null +++ b/packages/code_buffer/lib/code_buffer.dart @@ -0,0 +1,229 @@ +import 'package:source_span/source_span.dart'; + +/// An advanced StringBuffer geared toward generating code, and source maps. +class CodeBuffer implements StringBuffer { + /// The character sequence used to represent a line break. + final String newline; + + /// The character sequence used to represent a space/tab. + final String space; + + /// The source URL to be applied to all generated [SourceSpan] instances. + final sourceUrl; + + /// If `true` (default: `false`), then an additional [newline] will be inserted at the end of the generated string. + final bool trailingNewline; + + final List _lines = []; + CodeBufferLine? _currentLine, _lastLine; + int _indentationLevel = 0; + int _length = 0; + + CodeBuffer( + {this.space: ' ', + this.newline: '\n', + this.trailingNewline: false, + this.sourceUrl}); + + /// Creates a [CodeBuffer] that does not emit additional whitespace. + factory CodeBuffer.noWhitespace({sourceUrl}) => CodeBuffer( + space: '', newline: '', trailingNewline: false, sourceUrl: sourceUrl); + + /// The last line created within this buffer. + CodeBufferLine? get lastLine => _lastLine; + + /// Returns an immutable collection of the [CodeBufferLine]s within this instance. + List get lines => List.unmodifiable(_lines); + + @override + bool get isEmpty => _lines.isEmpty; + + @override + bool get isNotEmpty => _lines.isNotEmpty; + + @override + int get length => _length; + + CodeBufferLine _createLine() { + var start = SourceLocation( + _length, + sourceUrl: sourceUrl, + line: _lines.length, + column: _indentationLevel * space.length, + ); + var line = CodeBufferLine._(_indentationLevel, start).._end = start; + _lines.add(_lastLine = line); + return line; + } + + /// Increments the indentation level. + void indent() { + _indentationLevel++; + } + + /// Decrements the indentation level, if it is greater than `0`. + void outdent() { + if (_indentationLevel > 0) _indentationLevel--; + } + + /// Copies the contents of this [CodeBuffer] into another, preserving indentation and source mapping information. + void copyInto(CodeBuffer other) { + if (_lines.isEmpty) return; + int i = 0; + + for (var line in _lines) { + // To compute offset: + // 1. Find current length of other + // 2. Add length of its newline + // 3. Add indentation + var column = (other._indentationLevel + line.indentationLevel) * + other.space.length; + var offset = other._length + other.newline.length + column; + + // Re-compute start + end + var start = SourceLocation( + offset, + sourceUrl: other.sourceUrl, + line: other._lines.length + i, + column: column, + ); + + var end = SourceLocation( + offset + line.span.length, + sourceUrl: other.sourceUrl, + line: start.line, + column: column + line._buf.length, + ); + + var clone = CodeBufferLine._( + line.indentationLevel + other._indentationLevel, start) + .._end = end + .._buf.write(line._buf.toString()); + + // Adjust lastSpan + if (line._lastSpan != null) { + var s = line._lastSpan!.start; + var lastSpanColumn = + ((line.indentationLevel + other._indentationLevel) * + other.space.length) + + line.text.indexOf(line._lastSpan!.text); + clone._lastSpan = SourceSpan( + SourceLocation( + offset + s.offset, + sourceUrl: other.sourceUrl, + line: clone.span.start.line, + column: lastSpanColumn, + ), + SourceLocation( + offset + s.offset + line._lastSpan!.length, + sourceUrl: other.sourceUrl, + line: clone.span.end.line, + column: lastSpanColumn + line._lastSpan!.length, + ), + line._lastSpan!.text, + ); + } + + other._lines.add(other._currentLine = other._lastLine = clone); + + // Adjust length accordingly... + other._length = offset + clone.span.length; + i++; + } + + other.writeln(); + } + + @override + void clear() { + _lines.clear(); + _length = _indentationLevel = 0; + _currentLine = null; + } + + @override + void writeCharCode(int charCode) { + _currentLine ??= _createLine(); + + _currentLine!._buf.writeCharCode(charCode); + var end = _currentLine!._end; + _currentLine!._end = SourceLocation( + end.offset + 1, + sourceUrl: end.sourceUrl, + line: end.line, + column: end.column + 1, + ); + _length++; + _currentLine!._lastSpan = + SourceSpan(end, _currentLine!._end, String.fromCharCode(charCode)); + } + + @override + void write(Object? obj) { + var msg = obj.toString(); + _currentLine ??= _createLine(); + _currentLine!._buf.write(msg); + var end = _currentLine!._end; + _currentLine!._end = SourceLocation( + end.offset + msg.length, + sourceUrl: end.sourceUrl, + line: end.line, + column: end.column + msg.length, + ); + _length += msg.length; + _currentLine!._lastSpan = SourceSpan(end, _currentLine!._end, msg); + } + + @override + void writeln([Object? obj = ""]) { + if (obj != null && obj != '') write(obj); + _currentLine = null; + _length++; + } + + @override + void writeAll(Iterable objects, [String separator = ""]) { + write(objects.join(separator)); + } + + @override + String toString() { + var buf = StringBuffer(); + int i = 0; + + for (var line in lines) { + if (i++ > 0) buf.write(newline); + for (int j = 0; j < line.indentationLevel; j++) buf.write(space); + buf.write(line._buf.toString()); + } + + if (trailingNewline == true) buf.write(newline); + + return buf.toString(); + } +} + +/// Represents a line of text within a [CodeBuffer]. +class CodeBufferLine { + /// Mappings from one [SourceSpan] to another, to aid with generating dynamic source maps. + final Map sourceMappings = {}; + + /// The level of indentation preceding this line. + final int indentationLevel; + + final SourceLocation _start; + final StringBuffer _buf = StringBuffer(); + late SourceLocation _end; + SourceSpan? _lastSpan; + + CodeBufferLine._(this.indentationLevel, this._start); + + /// The [SourceSpan] corresponding to the last text written to this line. + SourceSpan? get lastSpan => _lastSpan; + + /// The [SourceSpan] corresponding to this entire line. + SourceSpan get span => SourceSpan(_start, _end, _buf.toString()); + + /// The text within this line. + String get text => _buf.toString(); +} diff --git a/packages/code_buffer/pubspec.yaml b/packages/code_buffer/pubspec.yaml new file mode 100644 index 00000000..ae4fa8e9 --- /dev/null +++ b/packages/code_buffer/pubspec.yaml @@ -0,0 +1,12 @@ +name: code_buffer +version: 2.0.0 +description: An advanced StringBuffer geared toward generating code, and source maps. +author: Tobe O +homepage: https://github.com/thosakwe/code_buffer +environment: + sdk: '>=2.12.0 <3.0.0' +dependencies: + charcode: ^1.2.0 + source_span: ^1.8.1 +dev_dependencies: + test: ^1.16.8 \ No newline at end of file diff --git a/packages/code_buffer/test/copy_test.dart b/packages/code_buffer/test/copy_test.dart new file mode 100644 index 00000000..9d0170f0 --- /dev/null +++ b/packages/code_buffer/test/copy_test.dart @@ -0,0 +1,45 @@ +import 'package:code_buffer/code_buffer.dart'; +import 'package:test/test.dart'; + +main() { + var a = new CodeBuffer(), b = new CodeBuffer(); + + setUp(() { + a.writeln('outer block 1'); + b..writeln('inner block 1')..writeln('inner block 2'); + b.copyInto(a..indent()); + a + ..outdent() + ..writeln('outer block 2'); + }); + + tearDown(() { + a.clear(); + b.clear(); + }); + + test('sets correct text', () { + expect( + a.toString(), + [ + 'outer block 1', + ' inner block 1', + ' inner block 2', + 'outer block 2', + ].join('\n')); + }); + + test('sets lastLine+lastSpan', () { + var c = new CodeBuffer() + ..indent() + ..write('>') + ..writeln('innermost'); + c.copyInto(a); + expect(a.lastLine!.text, '>innermost'); + expect(a.lastLine!.span.start.column, 2); + expect(a.lastLine!.lastSpan!.start.line, 4); + expect(a.lastLine!.lastSpan!.start.column, 3); + expect(a.lastLine!.lastSpan!.end.line, 4); + expect(a.lastLine!.lastSpan!.end.column, 12); + }); +} diff --git a/packages/code_buffer/test/span_test.dart b/packages/code_buffer/test/span_test.dart new file mode 100644 index 00000000..c96acffd --- /dev/null +++ b/packages/code_buffer/test/span_test.dart @@ -0,0 +1,44 @@ +import 'package:charcode/charcode.dart'; +import 'package:code_buffer/code_buffer.dart'; +import 'package:test/test.dart'; + +main() { + var buf = new CodeBuffer(); + tearDown(buf.clear); + + test('writeCharCode', () { + buf.writeCharCode($x); + expect(buf.lastLine!.lastSpan!.start.column, 0); + expect(buf.lastLine!.lastSpan!.start.line, 0); + expect(buf.lastLine!.lastSpan!.end.column, 1); + expect(buf.lastLine!.lastSpan!.end.line, 0); + }); + + test('write', () { + buf.write('foo'); + expect(buf.lastLine!.lastSpan!.start.column, 0); + expect(buf.lastLine!.lastSpan!.start.line, 0); + expect(buf.lastLine!.lastSpan!.end.column, 3); + expect(buf.lastLine!.lastSpan!.end.line, 0); + }); + + test('multiple writes in one line', () { + buf..write('foo')..write('baz'); + expect(buf.lastLine!.lastSpan!.start.column, 3); + expect(buf.lastLine!.lastSpan!.start.line, 0); + expect(buf.lastLine!.lastSpan!.end.column, 6); + expect(buf.lastLine!.lastSpan!.end.line, 0); + }); + + test('multiple lines', () { + buf + ..writeln('foo') + ..write('bar') + ..write('+') + ..writeln('baz'); + expect(buf.lastLine!.lastSpan!.start.column, 4); + expect(buf.lastLine!.lastSpan!.start.line, 1); + expect(buf.lastLine!.lastSpan!.end.column, 7); + expect(buf.lastLine!.lastSpan!.end.line, 1); + }); +} diff --git a/packages/code_buffer/test/write_test.dart b/packages/code_buffer/test/write_test.dart new file mode 100644 index 00000000..31238980 --- /dev/null +++ b/packages/code_buffer/test/write_test.dart @@ -0,0 +1,87 @@ +import 'package:charcode/charcode.dart'; +import 'package:code_buffer/code_buffer.dart'; +import 'package:test/test.dart'; + +main() { + var buf = new CodeBuffer(); + tearDown(buf.clear); + + test('writeCharCode', () { + buf.writeCharCode($x); + expect(buf.toString(), 'x'); + }); + + test('write', () { + buf.write('hello world'); + expect(buf.toString(), 'hello world'); + }); + + test('custom space', () { + var b = new CodeBuffer(space: '+') + ..writeln('foo') + ..indent() + ..writeln('baz'); + expect(b.toString(), 'foo\n+baz'); + }); + + test('custom newline', () { + var b = new CodeBuffer(newline: 'N') + ..writeln('foo') + ..indent() + ..writeln('baz'); + expect(b.toString(), 'fooN baz'); + }); + + test('trailing newline', () { + var b = new CodeBuffer(trailingNewline: true)..writeln('foo'); + expect(b.toString(), 'foo\n'); + }); + + group('multiple lines', () { + setUp(() { + buf..writeln('foo')..writeln('bar')..writeln('baz'); + expect(buf.lines, hasLength(3)); + expect(buf.lines[0].text, 'foo'); + expect(buf.lines[1].text, 'bar'); + expect(buf.lines[2].text, 'baz'); + }); + }); + + test('indent', () { + buf + ..writeln('foo') + ..indent() + ..writeln('bar') + ..indent() + ..writeln('baz') + ..outdent() + ..writeln('quux') + ..outdent() + ..writeln('end'); + expect(buf.toString(), 'foo\n bar\n baz\n quux\nend'); + }); + + group('sets lastLine text', () { + test('writeCharCode', () { + buf.writeCharCode($x); + expect(buf.lastLine!.text, 'x'); + }); + + test('write', () { + buf.write('hello world'); + expect(buf.lastLine!.text, 'hello world'); + }); + }); + + group('sets lastLine lastSpan', () { + test('writeCharCode', () { + buf.writeCharCode($x); + expect(buf.lastLine!.lastSpan!.text, 'x'); + }); + + test('write', () { + buf.write('hello world'); + expect(buf.lastLine!.lastSpan!.text, 'hello world'); + }); + }); +} diff --git a/packages/http_exception/pubspec.yaml b/packages/http_exception/pubspec.yaml index ea88a684..a75358bb 100644 --- a/packages/http_exception/pubspec.yaml +++ b/packages/http_exception/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_http_exception -version: 2.1.0 +version: 3.0.0 description: Exception class that can be serialized to JSON and serialized to clients. #author: Tobe O homepage: https://github.com/dukefirehawk/angel/packages/http_exception diff --git a/packages/pretty_logging/pubspec.yaml b/packages/pretty_logging/pubspec.yaml index 5f693722..96a714f5 100644 --- a/packages/pretty_logging/pubspec.yaml +++ b/packages/pretty_logging/pubspec.yaml @@ -1,8 +1,8 @@ name: pretty_logging -version: 2.1.0 +version: 4.0.0 description: Standalone helper for colorful logging output, using pkg:io AnsiCode. -author: Tobe Osakwe -homepage: https://github.com/angel-dart/pretty_logging +#author: Tobe Osakwe +homepage: https://github.com/dukefirehawk/angel environment: sdk: '>=2.12.0 <3.0.0' dependencies: