/* * Copyright (c) 2013, the Dart project authors. * * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.google.dart.engine.parser; import com.google.dart.engine.EngineTestCase; import com.google.dart.engine.ast.CompilationUnit; import com.google.dart.engine.error.GatheringErrorListener; import com.google.dart.engine.scanner.CharSequenceReader; import com.google.dart.engine.scanner.IncrementalScanner; import com.google.dart.engine.scanner.Scanner; import com.google.dart.engine.scanner.Token; import com.google.dart.engine.source.Source; import com.google.dart.engine.source.TestSource; import com.google.dart.engine.utilities.ast.AstComparator; public class IncrementalParserTest extends EngineTestCase { public void fail_replace_identifier_with_functionLiteral_in_initializer() { // Function literals aren't allowed inside initializers; incremental parsing needs to gather // the appropriate context. // // "class A { var a; A(b) : a = b ? b : 0 { } }" // "class A { var a; A(b) : a = b ? () {} : 0 { } }" assertParse("class A { var a; A(b) : a = b ? ", "b", "() {}", " : 0 { } }"); } public void test_delete_everything() { // "f() => a + b;" // "" assertParse("", "f() => a + b;", "", ""); } public void test_delete_identifier_beginning() { // "f() => abs + b;" // "f() => s + b;" assertParse("f() => ", "ab", "", "s + b;"); } public void test_delete_identifier_end() { // "f() => abs + b;" // "f() => a + b;" assertParse("f() => a", "bs", "", " + b;"); } public void test_delete_identifier_middle() { // "f() => abs + b;" // "f() => as + b;" assertParse("f() => a", "b", "", "s + b;"); } public void test_delete_mergeTokens() { // "f() => a + b + c;" // "f() => ac;" assertParse("f() => a", " + b + ", "", "c;"); } public void test_insert_afterIdentifier1() { // "f() => a + b;" // "f() => abs + b;" assertParse("f() => a", "", "bs", " + b;"); } public void test_insert_afterIdentifier2() { // "f() => a + b;" // "f() => a + bar;" assertParse("f() => a + b", "", "ar", ";"); } public void test_insert_beforeIdentifier1() { // "f() => a + b;" // "f() => xa + b;" assertParse("f() => ", "", "x", "a + b;"); } public void test_insert_beforeIdentifier2() { // "f() => a + b;" // "f() => a + xb;" assertParse("f() => a + ", "", "x", "b;"); } public void test_insert_convertOneFunctionToTwo() { // "f() {}" // "f() => 0; g() {}" assertParse("f()", "", " => 0; g()", " {}"); } public void test_insert_end() { // "class A {}" // "class A {} class B {}" assertParse("class A {}", "", " class B {}", ""); } public void test_insert_insideClassBody() { // "class C {C(); }" // "class C { C(); }" assertParse("class C {", "", " ", "C(); }"); } public void test_insert_insideIdentifier() { // "f() => cob;" // "f() => cow.b;" assertParse("f() => co", "", "w.", "b;"); } public void test_insert_newIdentifier1() { // "f() => a; c;" // "f() => a; b c;" assertParse("f() => a;", "", " b", " c;"); } public void test_insert_newIdentifier2() { // "f() => a; c;" // "f() => a;b c;" assertParse("f() => a;", "", "b", " c;"); } public void test_insert_newIdentifier3() { // "/** A simple function. */ f() => a; c;" // "/** A simple function. */ f() => a; b c;" assertParse("/** A simple function. */ f() => a;", "", " b", " c;"); } public void test_insert_newIdentifier4() { // "/** An [A]. */ class A {} class B { m() { return 1; } }" // "/** An [A]. */ class A {} class B { m() { return 1 + 2; } }" assertParse("/** An [A]. */ class A {} class B { m() { return 1", "", " + 2", "; } }"); } public void test_insert_period() { // "f() => a + b;" // "f() => a + b.;" assertParse("f() => a + b", "", ".", ";"); } public void test_insert_period_betweenIdentifiers1() { // "f() => a b;" // "f() => a. b;" assertParse("f() => a", "", ".", " b;"); } public void test_insert_period_betweenIdentifiers2() { // "f() => a b;" // "f() => a .b;" assertParse("f() => a ", "", ".", "b;"); } public void test_insert_period_betweenIdentifiers3() { // "f() => a b;" // "f() => a . b;" assertParse("f() => a ", "", ".", " b;"); } public void test_insert_period_insideExistingIdentifier() { // "f() => ab;" // "f() => a.b;" assertParse("f() => a", "", ".", "b;"); } public void test_insert_periodAndIdentifier() { // "f() => a + b;" // "f() => a + b.x;" assertParse("f() => a + b", "", ".x", ";"); } public void test_insert_simpleToComplexExression() { // "/** An [A]. */ class A {} class B { m() => 1; }" // "/** An [A]. */ class A {} class B { m() => 1 + 2; }" assertParse("/** An [A]. */ class A {} class B { m() => 1", "", " + 2", "; }"); } public void test_insert_whitespace_end() { // "f() => a + b;" // "f() => a + b; " assertParse("f() => a + b;", "", " ", ""); } public void test_insert_whitespace_end_multiple() { // "f() => a + b;" // "f() => a + b; " assertParse("f() => a + b;", "", " ", ""); } public void test_insert_whitespace_middle() { // "f() => a + b;" // "f() => a + b;" assertParse("f() => a", "", " ", " + b;"); } public void test_replace_identifier_beginning() { // "f() => bell + b;" // "f() => fell + b;" assertParse("f() => ", "b", "f", "ell + b;"); } public void test_replace_identifier_end() { // "f() => bell + b;" // "f() => belt + b;" assertParse("f() => bel", "l", "t", " + b;"); } public void test_replace_identifier_middle() { // "f() => first + b;" // "f() => frost + b;" assertParse("f() => f", "ir", "ro", "st + b;"); } public void test_replace_multiple_partialFirstAndLast() { // "f() => aa + bb;" // "f() => ab * ab;" assertParse("f() => a", "a + b", "b * a", "b;"); } public void test_replace_operator_oneForMany() { // "f() => a + b;" // "f() => a * c - b;" assertParse("f() => a ", "+", "* c -", " b;"); } public void test_replace_operator_oneForOne() { // "f() => a + b;" // "f() => a * b;" assertParse("f() => a ", "+", "*", " b;"); } /** * Given a description of the original and modified contents, perform an incremental scan of the * two pieces of text. * * @param prefix the unchanged text before the edit region * @param removed the text that was removed from the original contents * @param added the text that was added to the modified contents * @param suffix the unchanged text after the edit region */ private void assertParse(String prefix, String removed, String added, String suffix) { // // Compute the information needed to perform the test. // String originalContents = prefix + removed + suffix; String modifiedContents = prefix + added + suffix; int replaceStart = prefix.length(); Source source = new TestSource(); // // Parse the original contents. // GatheringErrorListener originalListener = new GatheringErrorListener(); Scanner originalScanner = new Scanner( source, new CharSequenceReader(originalContents), originalListener); Token originalTokens = originalScanner.tokenize(); assertNotNull(originalTokens); Parser originalParser = new Parser(source, originalListener); CompilationUnit originalUnit = originalParser.parseCompilationUnit(originalTokens); assertNotNull(originalUnit); // // Parse the modified contents. // GatheringErrorListener modifiedListener = new GatheringErrorListener(); Scanner modifiedScanner = new Scanner( source, new CharSequenceReader(modifiedContents), modifiedListener); Token modifiedTokens = modifiedScanner.tokenize(); assertNotNull(modifiedTokens); Parser modifiedParser = new Parser(source, modifiedListener); CompilationUnit modifiedUnit = modifiedParser.parseCompilationUnit(modifiedTokens); assertNotNull(modifiedUnit); // // Incrementally parse the modified contents. // GatheringErrorListener incrementalListener = new GatheringErrorListener(); IncrementalScanner incrementalScanner = new IncrementalScanner(source, new CharSequenceReader( modifiedContents), incrementalListener); Token incrementalTokens = incrementalScanner.rescan( originalTokens, replaceStart, removed.length(), added.length()); assertNotNull(incrementalTokens); IncrementalParser incrementalParser = new IncrementalParser( source, incrementalScanner.getTokenMap(), incrementalListener); CompilationUnit incrementalUnit = incrementalParser.reparse( originalUnit, incrementalScanner.getLeftToken(), incrementalScanner.getRightToken(), replaceStart, prefix.length() + removed.length()); assertNotNull(incrementalUnit); // // Validate that the results of the incremental parse are the same as the full parse of the // modified source. // assertTrue(AstComparator.equalNodes(modifiedUnit, incrementalUnit)); // TODO(brianwilkerson) Verify that the errors are correct? } }