Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(127)

Unified Diff: pkg/analyzer_experimental/lib/src/services/formatter_impl.dart

Issue 16562012: Dart formatter babysteps. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/analyzer_experimental/lib/src/services/formatter_impl.dart
===================================================================
--- pkg/analyzer_experimental/lib/src/services/formatter_impl.dart (revision 0)
+++ pkg/analyzer_experimental/lib/src/services/formatter_impl.dart (revision 0)
@@ -0,0 +1,291 @@
+// Copyright (c) 2013, 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.
+
+library formatter_impl;
+
+
+import 'dart:io';
Bob Nystrom 2013/06/06 22:23:06 Nit, but Nathan and I put organize our imports lik
pquitslund 2013/06/07 19:45:01 Done.
+import 'package:analyzer_experimental/src/generated/scanner.dart';
+import 'package:analyzer_experimental/analyzer.dart';
+import 'package:analyzer_experimental/src/generated/parser.dart';
+import 'package:analyzer_experimental/src/generated/source.dart';
Bob Nystrom 2013/06/06 22:23:06 These are fine, but you can also use relative impo
pquitslund 2013/06/07 19:45:01 I thought this would lead to woes if I ever wanted
Bob Nystrom 2013/06/10 21:15:46 It's very confusing, unfortunately. The basic rule
+
+
+/// OS line separator. (TODO: may not be necessary)
messick 2013/06/07 13:51:36 TODO(ldap) everywhere? It probably seems redundant
pquitslund 2013/06/07 19:45:01 Done.
+const String NEW_LINE = '\n' ; //Platform.pathSeparator;
Bob Nystrom 2013/06/06 22:23:06 Ditch the type annotation.
pquitslund 2013/06/07 19:45:01 Oh my. This is taking some getting used to. :) D
+
+/// Formatter options.
+class FormatterOptions {
+
+ /// Create formatter options with defaults derived (where defined) from
+ /// the style guide: <http://www.dartlang.org/articles/style-guide/>.
+ const FormatterOptions({this.initialIndentationLevel:0,
Bob Nystrom 2013/06/06 22:23:06 Space after ":".
pquitslund 2013/06/07 19:45:01 Done. If only I had a formatter to do this for me
+ this.indentPerLevel:2,
+ this.lineSeparator:NEW_LINE,
+ this.pageWidth:80,
+ this.tabSize:2});
+
+ final String lineSeparator;
+ final int initialIndentationLevel;
+ final int indentPerLevel;
+ final int tabSize;
+ final int pageWidth;
+}
+
+
+/// Thrown when an error occurs in formatting.
+class FormatterException implements Exception {
+
+ /// A message describing the error.
+ final String message;
+
+ /// Creates a new FormatterException with an optional error [message].
+ const FormatterException([this.message = '']);
+
+ FormatterException.forError(List<AnalysisError> errors) :
+ // TODO: add descriptive message based on errors
+ message = 'an analysis error occured during format';
+
+ String toString() => 'FormatterException: $message';
+
+}
+
+/// Specifies the kind of code snippet to format.
+class CodeKind {
+
+ final int index;
+
+ const CodeKind(this.index);
+
+ /// A compilation unit snippet.
+ static const COMPILATION_UNIT = const CodeKind(0);
+
+ /// A statement snippet.
+ static const STATEMENT = const CodeKind(1);
+
+}
+
+/// Dart source code formatter.
+abstract class CodeFormatter {
messick 2013/06/07 13:51:36 I'm not clear what your plan is but I wasn't expec
pquitslund 2013/06/07 19:45:01 See response to Bob below.
+
+ factory CodeFormatter([FormatterOptions options = const FormatterOptions()])
+ => new CodeFormatterImpl(options);
+
+ /// Format the specified portion (from [offset] with [length]) of the given
+ /// [source] string, optionally providing an [indentationLevel].
+ String format(CodeKind kind, String source, {int offset, int end,
+ int indentationLevel:0});
Bob Nystrom 2013/06/06 22:23:06 Indent a line continuation +4, not +2.
pquitslund 2013/06/07 19:45:01 Done.
+
+}
+
+class CodeFormatterImpl implements CodeFormatter, AnalysisErrorListener {
Bob Nystrom 2013/06/06 22:23:06 Unify this with CodeFormatter. Simpler = better. :
pquitslund 2013/06/07 19:45:01 OK. I *might*. My thinking here is that I really
Brian Wilkerson 2013/06/07 19:55:11 Another alternative would be to not implement Anal
+
+ final FormatterOptions options;
+ final List<AnalysisError> errors;
+
+ CodeFormatterImpl(this.options) : errors = new List<AnalysisError>();
+
+ String format(CodeKind kind, String source, {int offset, int end,
+ int indentationLevel:0}) {
+
+ var start = tokenize(source);
+ _checkForErrors();
+
+ var node = parse(kind, start);
+ _checkForErrors();
+
+ // To be continued...
+
+ return source;
+ }
+
+ ASTNode parse(CodeKind kind, Token start) {
Bob Nystrom 2013/06/06 22:23:06 The style guide would name this "AstNode".
messick 2013/06/07 13:51:36 We should talk to Konstantin to see if we can get
pquitslund 2013/06/07 19:45:01 Aha. Something for Konstantin (or Brian). Thanks
pquitslund 2013/06/07 19:45:01 Good point!
+
+ var parser = new Parser(null, this);
+
+ switch (kind) {
+ case CodeKind.COMPILATION_UNIT:
+ return parser.parseCompilationUnit(start);
+ case CodeKind.STATEMENT:
+ return parser.parseStatement(start);
messick 2013/06/07 13:51:36 At a guess, I'd expect refactoring to want a few m
pquitslund 2013/06/07 19:45:01 Absolutely. Need to crawl before I can walk thoug
+ }
+
+ throw new FormatterException('Unsupported format kind: $kind');
+ }
+
+ _checkForErrors() {
+ if (errors.length > 0) {
+ throw new FormatterException.forError(errors);
+ }
+ }
+
+ void onError(AnalysisError error){
Bob Nystrom 2013/06/06 22:23:06 Space before "{".
pquitslund 2013/06/07 19:45:01 Eagle eye. Fixed!
+ errors.add(error);
+ }
+
+ Token tokenize(String source) {
+ var scanner = new StringScanner(null, source, this);
+ return scanner.tokenize();
Brian Wilkerson 2013/06/06 22:21:58 I noticed that you're testing for new-lines in the
pquitslund 2013/06/07 19:45:01 Cool. Great tip. I'll look into that. Thanks!
+ }
+
+}
+
+/// Placeholder class to hold a reference to the Class object representing
+/// the Dart keyword void.
+class Void extends Object {
messick 2013/06/07 13:51:36 I hope you'll be able to use Keyword.VOID instead
pquitslund 2013/06/07 19:45:01 The issue is that I really like to use VOID as a t
+
+}
+
+
+/// Records a sequence of edits to a source string that will cause the string
+/// to be formatted when applied.
+class EditRecorder {
+
+ final FormatterOptions options;
+
+ int column = 0;
+
+ int sourceIndex = 0;
+ String source = '';
+
+ Token currentToken;
+
+ int indentationLevel = 0;
+ int numberOfIndentations = 0;
+
+ bool isIndentNeeded = false;
+
+ EditRecorder(this.options);
+
+ /// Count the number of whitespace chars beginning at the current
+ /// [sourceIndex].
+ int countWhitespace() {
+ var count = 0;
+ for (var i = sourceIndex; i < source.length; ++i) {
+ if (isIndentChar(source[i])) {
+ ++count;
+ } else {
+ break;
+ }
+ }
+ return count;
+ }
+
+ /// Indent.
+ void indent() {
+ indentationLevel += options.indentPerLevel;
messick 2013/06/07 13:51:36 These names are confusing. What's a "level"? inden
pquitslund 2013/06/07 19:45:01 Working on it. Thanks!
+ numberOfIndentations++;
+ }
+
+ /// Test if there is a newline at the given source [index].
+ bool isNewlineAt(int index) {
+ if (index < 0 || index + NEW_LINE.length > source.length) {
+ return false;
+ }
+ for (var i = 0; i < NEW_LINE.length; i++) {
Bob Nystrom 2013/06/06 22:23:06 Do you need to increment index here too?
pquitslund 2013/06/07 19:45:01 Time for some more tests! :)
+ if (source[index] != NEW_LINE[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
+
+final String SPACE = ' ';
Bob Nystrom 2013/06/06 22:23:06 "final String" -> "const".
pquitslund 2013/06/07 19:45:01 Done.
+
+bool isIndentChar(String ch) => ch == SPACE; // TODO(pquitslund) also check tab
+
+
+/// Manages stored [Edit]s.
+abstract class EditStore {
Brian Wilkerson 2013/06/06 22:21:58 Edits and edit stores sound like a re-usable piece
messick 2013/06/07 13:51:36 I think so, too.
+
+ /// Add an [Edit] that describes a textual [replacement] of a text interval
+ /// starting at the given [offset] spanning the given [length].
+ void addEdit(int offset, int length, String replacement);
+
+ /// Get the index of the current edit (for use in caching location
+ /// information).
+ int getCurrentEditIndex();
+
+ /// Get the underlying sequence of [Edit]s.
+ List<Edit> get edits;
+
+ /// Get the last edit.
+ Edit getLastEdit();
Bob Nystrom 2013/06/06 22:23:06 You'll already have edits.last, so I'd ditch this.
pquitslund 2013/06/07 19:45:01 Right but for some reason I convinced myself that
Bob Nystrom 2013/06/10 21:15:46 They could just do edits.isEmpty to check for that
+
+ /// Add an [Edit] that describes an insertion of text starting at the given
+ /// [offset].
+ void insert(int offset, String insertedString);
+
+ /// Reset cached state.
+ void reset();
+
+}
+
+ /// Basic un-optimized [EditStore] suitable for subclassing.
+ class BasicEditStore implements EditStore {
Bob Nystrom 2013/06/06 22:23:06 You can unify this with EditStore. If users don't
pquitslund 2013/06/07 19:45:01 Ah yes. Good point! Done.
+
+ final List<Edit> edits = new List<Edit>();
Bob Nystrom 2013/06/06 22:23:06 final edits = <Edit>[];
pquitslund 2013/06/07 19:45:01 Done.
+
+ /// Add the given [Edit] to the end of the edit sequence.
+ void add(Edit edit) {
+ edits.add(edit);
+ }
+
+ void addEdit(int offset, int length, String replacement) {
+ add(new Edit(offset, length, replacement));
+ }
+
+ //TODO(pquitslund): verify that this should be "last" vs. "next"
messick 2013/06/07 13:51:36 I wonder if this comment is in the right place?
pquitslund 2013/06/07 19:45:01 Done.
+ int getCurrentEditIndex() => edits.length - 1;
+
+ Edit getLastEdit() => edits.isEmpty ? null : edits.last;
+
+ void insert(int offset, String insertedString) {
+ addEdit(offset, 0, insertedString);
+ }
+
+ void reset() {
+ edits.clear();
+ }
+
+ String toString() => 'EditStore( ${edits.toString()} )';
+
+ }
Bob Nystrom 2013/06/06 22:23:06 Unindent.
pquitslund 2013/06/07 19:45:01 Nothing escapes you!
+
+
+
+/// Describes a text edit.
+class Edit {
+
+ /// The offset at which to apply the edit.
+ final int offset;
+
+ /// The length of the text interval to replace.
+ final int length;
+
+ /// The replacement text.
+ final String replacement;
+
+ /// Create an edit.
+ const Edit(this.offset, this.length, this.replacement);
+
+ /// Create an edit for the given [range].
+ Edit.forRange(SourceRange range, String replacement):
+ this(range.offset, range.length, replacement);
+
+ String toString() => '${offset < 0 ? '(' : 'X('} offset: ${offset} , '
+ 'length ${length}, replacement :> ${replacement} <:)';
+
+}
+
+/// An AST visitor that drives formatting heuristics.
+class FormattingEngine extends RecursiveASTVisitor<Void> {
+
+ final FormatterOptions options;
+
+ FormattingEngine(this.options);
+
+}

Powered by Google App Engine
This is Rietveld 408576698