1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Eclipse Public License, Version 1.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.eclipse.org/org/documents/epl-v10.php 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.ide.eclipse.adt.internal.editors; 17 18 import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.AdtProjectTest; 19 20 import org.eclipse.core.resources.IFile; 21 import org.eclipse.jface.text.DocumentCommand; 22 import org.eclipse.jface.text.IDocument; 23 import org.eclipse.jface.text.source.ISourceViewer; 24 import org.eclipse.swt.graphics.Point; 25 import org.eclipse.ui.IEditorPart; 26 import org.eclipse.ui.IWorkbenchPage; 27 import org.eclipse.ui.PlatformUI; 28 import org.eclipse.ui.ide.IDE; 29 30 @SuppressWarnings("javadoc") 31 public class AndroidXmlAutoEditStrategyTest extends AdtProjectTest { 32 33 public void checkInsertNewline(String before, String after) throws Exception { 34 AndroidXmlAutoEditStrategy s = new AndroidXmlAutoEditStrategy(); 35 36 // All tests just operate on the "edithandling" document; the contents are 37 // ignored and replaced with the before-document passed in 38 IFile file = getLayoutFile(getProject(), "edithandling.xml"); 39 40 IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); 41 assertNotNull(page); 42 IEditorPart editor = IDE.openEditor(page, file); 43 assertTrue(editor instanceof AndroidXmlEditor); 44 AndroidXmlEditor layoutEditor = (AndroidXmlEditor) editor; 45 ISourceViewer viewer = layoutEditor.getStructuredSourceViewer(); 46 47 String newDocumentContent = stripCaret(before); 48 IDocument document = viewer.getDocument(); 49 document.replace(0, document.getLength(), newDocumentContent); 50 51 // Determine the offset, and possibly make text range selections as well 52 int offset = updateCaret(viewer, before); 53 54 DocumentCommand c = new TestDocumentCommand(); 55 c.doit = true; 56 c.offset = offset; 57 c.caretOffset = -1; 58 c.length = 0; 59 c.text = "\n"; 60 61 s.customizeDocumentCommand(document, c); 62 63 if (c.doit) { 64 if (c.length == 0 && c.text == null) { 65 return; 66 } 67 68 document.replace(c.offset, c.length, c.text); 69 70 int caretOffset = c.offset + c.text.length(); 71 72 // The shiftsCaret flag doesn't behave the way it's documented to 73 //if (c.shiftsCaret && c.caretOffset != -1) { 74 if (c.caretOffset != -1) { 75 caretOffset = c.caretOffset; 76 } 77 viewer.setSelectedRange(caretOffset, 0); 78 } 79 80 String text = document.get(); 81 Point selectedRange = viewer.getSelectedRange(); 82 assert selectedRange.y == 0; 83 String textWithCaret = text; 84 if (selectedRange.x >= 0) { 85 textWithCaret = text.substring(0, selectedRange.x) + "^" + 86 text.substring(selectedRange.x); 87 } 88 89 assertEquals(after, textWithCaret); 90 } 91 92 private static String stripCaret(String s) { 93 int index = s.indexOf('^'); 94 assertTrue(index != -1); 95 return s.substring(0, index) + s.substring(index + 1); 96 } 97 98 public void testCornerCase1() throws Exception { 99 checkInsertNewline("^", "\n^"); 100 } 101 102 public void testCornerCase2() throws Exception { 103 checkInsertNewline( 104 "\n^", 105 106 "\n\n^"); 107 } 108 109 public void testCornerCase3() throws Exception { 110 checkInsertNewline( 111 " ^", 112 113 " \n" + 114 " ^"); 115 } 116 117 public void testSimpleIndentation1() throws Exception { 118 checkInsertNewline( 119 " ^ ", 120 121 " \n" + 122 " ^ "); 123 } 124 125 public void testSimpleIndentation2() throws Exception { 126 checkInsertNewline( 127 "\n" + 128 " foo^\n", 129 130 "\n" + 131 " foo\n" + 132 " ^\n"); 133 } 134 135 public void testSimpleIndentation3() throws Exception { 136 checkInsertNewline( 137 "\n" + 138 " <newtag>^\n", 139 140 "\n" + 141 " <newtag>\n" + 142 " ^\n"); 143 } 144 145 public void testSimpleIndentation4() throws Exception { 146 checkInsertNewline( 147 "\n" + 148 " <newtag/>^\n", 149 150 "\n" + 151 " <newtag/>\n" + 152 " ^\n"); 153 } 154 155 public void testSimpleIndentation5() throws Exception { 156 checkInsertNewline( 157 "\n" + 158 " <newtag^\n", 159 "\n" + 160 " <newtag\n" + 161 " ^\n"); 162 } 163 164 public void testSplitAttribute() throws Exception { 165 checkInsertNewline( 166 "\n" + 167 " <newtag ^attribute='value'/>\n", 168 169 "\n" + 170 " <newtag \n" + 171 " ^attribute='value'/>\n"); 172 } 173 174 public void testIndentationInComments1() throws Exception { 175 // Make sure that inside a comment we ignore tags etc 176 checkInsertNewline( 177 "<!--\n foo^\n--->\n", 178 179 "<!--\n foo\n ^\n--->\n"); 180 } 181 182 public void testIndentationInComments2() throws Exception { 183 // Make sure that inside a comment we ignore tags etc 184 checkInsertNewline( 185 "\n" + 186 "<!--\n" + 187 "<foo><^\n" + 188 "-->\n", 189 190 "\n" + 191 "<!--\n" + 192 "<foo><\n" + 193 "^\n" + 194 "-->\n"); 195 } 196 197 public void testSurroundCaret() throws Exception { 198 checkInsertNewline( 199 "\n" + 200 " <item>^</item>\n", 201 202 "\n" + 203 " <item>\n" + 204 " ^\n" + 205 " </item>\n"); 206 } 207 208 public void testSurroundCaret2() throws Exception { 209 // This test combines both surround with and continuing earlier lines (where 210 // it searches for a matching tag) 211 checkInsertNewline( 212 "\n" + 213 " <foo\n" + 214 " name='value'>^</foo>\n", 215 216 "\n" + 217 " <foo\n" + 218 " name='value'>\n" + 219 " ^\n" + 220 " </foo>\n"); 221 } 222 223 public void testContinueEarlierLine1() throws Exception { 224 // Here we need to indent to the exact location of an earlier line 225 checkInsertNewline( 226 "\n" + 227 " <foo\n" + 228 " name='value'/>^\n", 229 230 "\n" + 231 " <foo\n" + 232 " name='value'/>\n" + 233 " ^\n"); 234 } 235 236 public void testContinueEarlierLine2() throws Exception { 237 checkInsertNewline( 238 "\n" + 239 " <foo\n" + 240 " name='value'></foo>^\n", 241 242 "\n" + 243 " <foo\n" + 244 " name='value'></foo>\n" + 245 " ^\n"); 246 // Note that 247 // <foo 248 // > 249 // </foo> 250 // does not require special handling, this only happens with the closing tag is sharing 251 // a line. 252 } 253 254 public void testContinueEarlierLine3() throws Exception { 255 // Make sure the code to look up the corresponding opening tag works properly 256 checkInsertNewline( 257 "\n" + 258 " <foo\n" + 259 " name='value'><bar></bar><baz/></foo>^\n", 260 261 "\n" + 262 " <foo\n" + 263 " name='value'><bar></bar><baz/></foo>\n" + 264 " ^\n"); 265 } 266 267 public void testContinueEarlierLine4() throws Exception { 268 checkInsertNewline( 269 " <Button\n" + 270 " android:id=\"@+id/button1\"\n" + 271 " android:layout_width=\"wrap_content\"\n" + 272 " android:layout_height=\"wrap_content\"\n" + 273 " android:text=\"Button\" >^\n" + 274 " </Button>\n", 275 276 " <Button\n" + 277 " android:id=\"@+id/button1\"\n" + 278 " android:layout_width=\"wrap_content\"\n" + 279 " android:layout_height=\"wrap_content\"\n" + 280 " android:text=\"Button\" >\n" + 281 " ^\n" + 282 " </Button>\n"); 283 } 284 285 public void testIndent() throws Exception { 286 checkInsertNewline( 287 " <Button\n" + 288 " attr=\"value\"></Button>^\n", 289 290 " <Button\n" + 291 " attr=\"value\"></Button>\n" + 292 " ^\n" + 293 ""); 294 } 295 296 public void testLineBeginning1() throws Exception { 297 // Test that if you insert on a blank line, we just add a newline and indent 298 checkInsertNewline( 299 "<foo>\n" + 300 "^\n" + 301 "</foo>", 302 303 "<foo>\n" + 304 "\n" + 305 " ^\n" + 306 "</foo>"); 307 } 308 309 public void testLineBeginning2() throws Exception { 310 // Test that if you insert with the caret on the beginning of a line that has 311 // content, we insert an indent correctly 312 checkInsertNewline( 313 "<foo>\n" + 314 "^ <bar/>\n" + 315 "</foo>", 316 317 "<foo>\n" + 318 "\n" + 319 " ^<bar/>\n" + 320 "</foo>"); 321 } 322 323 public void testLineBeginning3() throws Exception { 324 checkInsertNewline( 325 "<foo>\n" + 326 " <bar>\n" + 327 "^\n" + 328 " <baz/>\n" + 329 " </bar>\n" + 330 "</foo>", 331 332 "<foo>\n" + 333 " <bar>\n" + 334 "\n" + 335 " ^\n" + 336 " <baz/>\n" + 337 " </bar>\n" + 338 "</foo>"); 339 340 } 341 342 public void testLineBeginning4() throws Exception { 343 // Test that if you insert with the caret on the beginning of a line that has 344 // content, we insert an indent correctly 345 checkInsertNewline( 346 "<foo>\n" + 347 " <bar>\n" + 348 "\n" + 349 "^ <baz/>\n" + 350 " </bar>\n" + 351 "</foo>", 352 353 "<foo>\n" + 354 " <bar>\n" + 355 "\n" + 356 "\n" + 357 " ^<baz/>\n" + 358 " </bar>\n" + 359 "</foo>"); 360 } 361 362 public void testLineBeginning5() throws Exception { 363 // Test that if you insert with the caret on the beginning of a line that has 364 // content, we insert an indent correctly 365 checkInsertNewline( 366 "<foo>\n" + 367 " <bar>\n" + 368 "\n" + 369 " ^ <baz/>\n" + 370 " </bar>\n" + 371 "</foo>", 372 373 "<foo>\n" + 374 " <bar>\n" + 375 "\n" + 376 " \n" + 377 " ^<baz/>\n" + 378 " </bar>\n" + 379 "</foo>"); 380 } 381 382 public void testLineBeginning6() throws Exception { 383 384 checkInsertNewline( 385 " <foo>\n" + 386 " <bar>\n" + 387 " \n" + 388 " \n" + 389 "^ </bar>\n" + 390 " </foo>\n", 391 392 " <foo>\n" + 393 " <bar>\n" + 394 " \n" + 395 " \n" + 396 "\n" + 397 " ^</bar>\n" + 398 " </foo>\n"); 399 } 400 401 public void testBlankContinuation() throws Exception { 402 403 checkInsertNewline( 404 " <foo>\n" + 405 " <bar>\n" + 406 " ^\n" + 407 " </bar>\n" + 408 " </foo>\n" + 409 "", 410 411 " <foo>\n" + 412 " <bar>\n" + 413 " \n" + 414 " ^\n" + 415 " </bar>\n" + 416 " </foo>\n" + 417 ""); 418 } 419 420 public void testIssue22332a() throws Exception { 421 checkInsertNewline( 422 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + 423 "<resources>\n" + 424 "\n" + 425 " <string name=\"hello\">Hello World, MainActivity!</string>^\n" + 426 "\n" + 427 "</resources>", 428 429 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + 430 "<resources>\n" + 431 "\n" + 432 " <string name=\"hello\">Hello World, MainActivity!</string>\n" + 433 " ^\n" + 434 "\n" + 435 "</resources>"); 436 } 437 438 public void testIssue22332b() throws Exception { 439 checkInsertNewline( 440 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + 441 "<resources>\n" + 442 "\n" + 443 " <string name=\"hello\">Hello World, MainActivity!</string>\n" + 444 " ^\n" + 445 "\n" + 446 "</resources>", 447 448 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + 449 "<resources>\n" + 450 "\n" + 451 " <string name=\"hello\">Hello World, MainActivity!</string>\n" + 452 " \n" + 453 " ^\n" + 454 "\n" + 455 "</resources>"); 456 } 457 458 /** 459 * To test 460 * When you press / after < I should reindent the current line. For example, 461 * if you have 462 * <foo> 463 * <bar> 464 * </ the moment you've typed this we should dedent it back out 465 * When you press newline we need to reindent 466 */ 467 468 /** Subclassed for test usage since constructor is protected */ 469 private class TestDocumentCommand extends DocumentCommand { 470 public TestDocumentCommand() { 471 } 472 } 473 } 474