package com.jetbrains.dart.analysisServer;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.command.undo.UndoManager;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
import com.intellij.openapi.fileEditor.FileEditorManagerListener;
import com.intellij.openapi.ide.CopyPasteManager;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.testFramework.fixtures.CodeInsightFixtureTestCase;
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl;
import com.jetbrains.lang.dart.DartFileType;
import com.jetbrains.lang.dart.analyzer.DartAnalysisServerService;
import com.jetbrains.lang.dart.analyzer.DartServerData;
import com.jetbrains.lang.dart.analyzer.DartServerData.DartNavigationRegion;
import com.jetbrains.lang.dart.analyzer.DartServerData.DartRegion;
import com.jetbrains.lang.dart.util.DartTestUtils;
import org.jetbrains.annotations.NotNull;
import java.awt.datatransfer.StringSelection;
import java.util.List;
public class DartServerHighlightingTest extends CodeInsightFixtureTestCase {
@Override
public void setUp() throws Exception {
super.setUp();
DartTestUtils.configureDartSdk(myModule, myFixture.getTestRootDisposable(), true);
myFixture.setTestDataPath(DartTestUtils.BASE_TEST_DATA_PATH + getBasePath());
((CodeInsightTestFixtureImpl)myFixture).canChangeDocumentDuringHighlighting(true);
}
@Override
protected String getBasePath() {
return "/analysisServer/highlighting";
}
private void doHighlightingTest() {
myFixture.configureByFile(getTestName(false) + ".dart");
myFixture.checkHighlighting();
}
private static void checkRegions(final List<? extends DartRegion> regions, final TextRange... ranges) {
assertEquals("Incorrect regions amount", ranges.length, regions.size());
int i = 0;
for (DartServerData.DartRegion region : regions) {
assertEquals("Mismatched region " + i, ranges[i++], TextRange.create(region.getOffset(), region.getOffset() + region.getLength()));
}
}
private void undoAndUpdateHighlighting(@NotNull final VirtualFile file) {
// to make sure that navigation and highlighting regions are reset we have to send new document contents both before Undo and after
DartAnalysisServerService.getInstance(getProject()).updateFilesContent();
UndoManager.getInstance(getProject()).undo(FileEditorManager.getInstance(getProject()).getSelectedEditor(file));
myFixture.doHighlighting();
checkServerDataInitialState(file);
}
public void testErrorsHighlighting() throws Exception {
doHighlightingTest();
}
public void testErrorsAfterEOF() {
doHighlightingTest();
}
public void testErrorAtZeroOffset() {
myFixture.addFileToProject("bar.dart", "part of x;");
doHighlightingTest();
}
private void initServerDataTest() {
myFixture.configureByText(DartFileType.INSTANCE, "import 'dart:core';\n" +
"import 'dart:core';\n" +
"import 'dart:core';\n");
myFixture.doHighlighting();
}
private void checkServerDataInitialState(@NotNull final VirtualFile file) {
final DartAnalysisServerService service = DartAnalysisServerService.getInstance(getProject());
// references to 'dart:core'
checkRegions(service.getNavigation(file), TextRange.create(7, 18), TextRange.create(27, 38), TextRange.create(47, 58));
checkRegions(service.getHighlight(file),
TextRange.create(0, 19), TextRange.create(0, 6), TextRange.create(7, 18),
TextRange.create(20, 39), TextRange.create(20, 26), TextRange.create(27, 38),
TextRange.create(40, 59), TextRange.create(40, 46), TextRange.create(47, 58));
}
public void testServerDataUpdateOnTyping() {
// navigation region must be deleted when touched by editing and updated otherwise
initServerDataTest();
final DartAnalysisServerService service = DartAnalysisServerService.getInstance(getProject());
final VirtualFile file = getFile().getVirtualFile();
checkServerDataInitialState(file);
// typing at the beginning of the region
getEditor().getCaretModel().moveToOffset(27);
myFixture.type('a');
checkRegions(service.getNavigation(file), TextRange.create(7, 18), TextRange.create(28, 39), TextRange.create(48, 59));
checkRegions(service.getHighlight(file),
TextRange.create(0, 19), TextRange.create(0, 6), TextRange.create(7, 18),
TextRange.create(20, 40), TextRange.create(20, 26), TextRange.create(28, 39),
TextRange.create(41, 60), TextRange.create(41, 47), TextRange.create(48, 59));
undoAndUpdateHighlighting(file);
// typing in the middle of the region
getEditor().getCaretModel().moveToOffset(29);
myFixture.type('a');
checkRegions(service.getNavigation(file), TextRange.create(7, 18), /*TextRange.create(28, 39),*/ TextRange.create(48, 59));
checkRegions(service.getHighlight(file),
TextRange.create(0, 19), TextRange.create(0, 6), TextRange.create(7, 18),
TextRange.create(20, 40), TextRange.create(20, 26), TextRange.create(27, 39),
TextRange.create(41, 60), TextRange.create(41, 47), TextRange.create(48, 59));
undoAndUpdateHighlighting(file);
// typing at the end of the region
getEditor().getCaretModel().moveToOffset(38);
myFixture.type('a');
checkRegions(service.getNavigation(file), TextRange.create(7, 18), TextRange.create(27, 38), TextRange.create(48, 59));
checkRegions(service.getHighlight(file),
TextRange.create(0, 19), TextRange.create(0, 6), TextRange.create(7, 18),
TextRange.create(20, 40), TextRange.create(20, 26), TextRange.create(27, 38),
TextRange.create(41, 60), TextRange.create(41, 47), TextRange.create(48, 59));
}
public void testServerDataUpdateOnPaste() {
initServerDataTest();
final DartAnalysisServerService service = DartAnalysisServerService.getInstance(getProject());
final VirtualFile file = getFile().getVirtualFile();
checkServerDataInitialState(file);
CopyPasteManager.getInstance().setContents(new StringSelection("long text 012345678901234567890123456789"));
// paste a lot at the beginning of the region
getEditor().getCaretModel().moveToOffset(27);
myFixture.performEditorAction(IdeActions.ACTION_EDITOR_PASTE);
checkRegions(service.getNavigation(file), TextRange.create(7, 18), TextRange.create(67, 78), TextRange.create(87, 98));
checkRegions(service.getHighlight(file),
TextRange.create(0, 19), TextRange.create(0, 6), TextRange.create(7, 18),
TextRange.create(20, 79), TextRange.create(20, 26), TextRange.create(67, 78),
TextRange.create(80, 99), TextRange.create(80, 86), TextRange.create(87, 98));
undoAndUpdateHighlighting(file);
// paste a lot in the middle of the region
getEditor().getCaretModel().moveToOffset(29);
myFixture.performEditorAction(IdeActions.ACTION_EDITOR_PASTE);
checkRegions(service.getNavigation(file), TextRange.create(7, 18), /*TextRange.create(28, 39),*/ TextRange.create(87, 98));
checkRegions(service.getHighlight(file),
TextRange.create(0, 19), TextRange.create(0, 6), TextRange.create(7, 18),
TextRange.create(20, 79), TextRange.create(20, 26), TextRange.create(27, 78),
TextRange.create(80, 99), TextRange.create(80, 86), TextRange.create(87, 98));
undoAndUpdateHighlighting(file);
// paste a lot at the end of the region
getEditor().getCaretModel().moveToOffset(38);
myFixture.performEditorAction(IdeActions.ACTION_EDITOR_PASTE);
checkRegions(service.getNavigation(file), TextRange.create(7, 18), TextRange.create(27, 38), TextRange.create(87, 98));
checkRegions(service.getHighlight(file),
TextRange.create(0, 19), TextRange.create(0, 6), TextRange.create(7, 18),
TextRange.create(20, 79), TextRange.create(20, 26), TextRange.create(27, 38),
TextRange.create(80, 99), TextRange.create(80, 86), TextRange.create(87, 98));
}
public void testServerDataUpdateOnBackspace() {
initServerDataTest();
final DartAnalysisServerService service = DartAnalysisServerService.getInstance(getProject());
final VirtualFile file = getFile().getVirtualFile();
checkServerDataInitialState(file);
// backspace at the beginning of the region
getEditor().getCaretModel().moveToOffset(27);
myFixture.type('\b');
checkRegions(service.getNavigation(file), TextRange.create(7, 18), TextRange.create(26, 37), TextRange.create(46, 57));
checkRegions(service.getHighlight(file),
TextRange.create(0, 19), TextRange.create(0, 6), TextRange.create(7, 18),
TextRange.create(20, 38), TextRange.create(20, 26), TextRange.create(26, 37),
TextRange.create(39, 58), TextRange.create(39, 45), TextRange.create(46, 57));
undoAndUpdateHighlighting(file);
// backspace in the middle of the region
getEditor().getCaretModel().moveToOffset(29);
myFixture.type('\b');
checkRegions(service.getNavigation(file), TextRange.create(7, 18), /*TextRange.create(27, 38),*/ TextRange.create(46, 57));
checkRegions(service.getHighlight(file),
TextRange.create(0, 19), TextRange.create(0, 6), TextRange.create(7, 18),
TextRange.create(20, 38), TextRange.create(20, 26), TextRange.create(27, 37),
TextRange.create(39, 58), TextRange.create(39, 45), TextRange.create(46, 57));
undoAndUpdateHighlighting(file);
// backspace at the end of the region
getEditor().getCaretModel().moveToOffset(39);
myFixture.type('\b');
checkRegions(service.getNavigation(file), TextRange.create(7, 18), TextRange.create(27, 38), TextRange.create(46, 57));
checkRegions(service.getHighlight(file),
TextRange.create(0, 19), TextRange.create(0, 6), TextRange.create(7, 18),
TextRange.create(20, 38), TextRange.create(20, 26), TextRange.create(27, 38),
TextRange.create(39, 58), TextRange.create(39, 45), TextRange.create(46, 57));
}
public void testServerDataUpdateOnSelectionDelete() {
initServerDataTest();
final DartAnalysisServerService service = DartAnalysisServerService.getInstance(getProject());
final VirtualFile file = getFile().getVirtualFile();
checkServerDataInitialState(file);
// delete exactly the region
getEditor().getSelectionModel().setSelection(27, 38);
getEditor().getCaretModel().moveToOffset(38);
myFixture.type('\b');
checkRegions(service.getNavigation(file), TextRange.create(7, 18), /*TextRange.create(26, 37),*/ TextRange.create(47 - 11, 58 - 11));
checkRegions(service.getHighlight(file),
TextRange.create(0, 19), TextRange.create(0, 6), TextRange.create(7, 18),
TextRange.create(20, 39 - 11), TextRange.create(20, 26), /*TextRange.create(27, 38),*/
TextRange.create(40 - 11, 59 - 11), TextRange.create(40 - 11, 46 - 11), TextRange.create(47 - 11, 58 - 11));
undoAndUpdateHighlighting(file);
// delete selection in the middle of the region
getEditor().getSelectionModel().setSelection(29, 36);
getEditor().getCaretModel().moveToOffset(36);
myFixture.type('\b');
checkRegions(service.getNavigation(file), TextRange.create(7, 18), /*TextRange.create(28, 39),*/ TextRange.create(47 - 7, 58 - 7));
checkRegions(service.getHighlight(file),
TextRange.create(0, 19), TextRange.create(0, 6), TextRange.create(7, 18),
TextRange.create(20, 39 - 7), TextRange.create(20, 26), TextRange.create(27, 38 - 7),
TextRange.create(40 - 7, 59 - 7), TextRange.create(40 - 7, 46 - 7), TextRange.create(47 - 7, 58 - 7));
undoAndUpdateHighlighting(file);
// delete selection that includes region and selection start/end touch other regions
getEditor().getSelectionModel().setSelection(18, 47);
getEditor().getCaretModel().moveToOffset(47);
myFixture.type('\b');
checkRegions(service.getNavigation(file), TextRange.create(7, 18), /*TextRange.create(28, 39),*/ TextRange.create(47 - 29, 58 - 29));
checkRegions(service.getHighlight(file),
/*TextRange.create(0, 19),*/ TextRange.create(0, 6), TextRange.create(7, 18),
/*TextRange.create(20, 39), TextRange.create(20, 26), TextRange.create(27, 38),*/
/*TextRange.create(40, 59), TextRange.create(40, 46),*/ TextRange.create(47 - 29, 58 - 29));
undoAndUpdateHighlighting(file);
// delete selection that has start in one region and end in another
getEditor().getSelectionModel().setSelection(17, 28);
getEditor().getCaretModel().moveToOffset(28);
myFixture.type('\b');
checkRegions(service.getNavigation(file), /*TextRange.create(7, 18), TextRange.create(28, 39),*/ TextRange.create(36, 47));
checkRegions(service.getHighlight(file),
/*TextRange.create(0, 19),*/ TextRange.create(0, 6), /*TextRange.create(7, 18),*/
/*TextRange.create(20, 39), TextRange.create(20, 26), TextRange.create(27, 38),*/
TextRange.create(40 - 11, 59 - 11), TextRange.create(40 - 11, 46 - 11), TextRange.create(47 - 11, 58 - 11));
}
public void testNavigationTargetOffsetUpdated() {
myFixture.configureByText(DartFileType.INSTANCE, "var a = 1; var b = a;");
myFixture.doHighlighting();
final DartAnalysisServerService service = DartAnalysisServerService.getInstance(getProject());
final VirtualFile file = getFile().getVirtualFile();
final List<DartNavigationRegion> regions = service.getNavigation(file);
checkRegions(regions, TextRange.create(4, 5), TextRange.create(15, 16), TextRange.create(19, 20));
assertEquals(4, regions.get(2).getTargets().get(0).getOffset(getProject(), file));
getEditor().getCaretModel().moveToOffset(0);
myFixture.type("foo \b");
checkRegions(regions, TextRange.create(4 + 3, 5 + 3), TextRange.create(15 + 3, 16 + 3), TextRange.create(19 + 3, 20 + 3));
assertEquals(4 + 3, regions.get(2).getTargets().get(0).getOffset(getProject(), file));
}
public void testSyntaxHighlighting() throws Exception {
myFixture.configureByFile(getTestName(false) + ".dart");
myFixture.checkHighlighting(true, true, true);
}
public void testServerDataLifecycle() throws Exception {
myFixture.configureByText("firstFile.dart", "class Foo { toString(){ return super.toString(); } }");
final VirtualFile firstFile = getFile().getVirtualFile();
final VirtualFile secondFile =
myFixture.addFileToProject("secondFile.dart", "class Bar { toString(){ return super.toString(); } }").getVirtualFile();
final DartAnalysisServerService service = DartAnalysisServerService.getInstance(getProject());
myFixture.doHighlighting();
assertNotEmpty(service.getHighlight(firstFile));
assertNotEmpty(service.getNavigation(firstFile));
assertNotEmpty(service.getOverrideMembers(firstFile));
myFixture.openFileInEditor(secondFile);
// TestFileEditorManager doesn't notify listeners itself;
// we need any notification to trigger DartAnalysisserverService.updateVisibleFiles()
final FileEditorManagerEvent event =
new FileEditorManagerEvent(FileEditorManager.getInstance(getProject()), firstFile, null, secondFile, null);
getProject().getMessageBus().syncPublisher(FileEditorManagerListener.FILE_EDITOR_MANAGER).selectionChanged(event);
myFixture.doHighlighting();
assertNotEmpty(service.getHighlight(firstFile));
assertNotEmpty(service.getNavigation(firstFile));
assertNotEmpty(service.getOverrideMembers(firstFile));
assertNotEmpty(service.getHighlight(secondFile));
assertNotEmpty(service.getNavigation(secondFile));
assertNotEmpty(service.getOverrideMembers(secondFile));
getProject().getMessageBus().syncPublisher(FileEditorManagerListener.FILE_EDITOR_MANAGER)
.fileClosed(FileEditorManager.getInstance(getProject()), firstFile);
assertNotEmpty(service.getHighlight(firstFile));
assertNotEmpty(service.getNavigation(firstFile));
assertNotEmpty(service.getOverrideMembers(firstFile));
assertNotEmpty(service.getHighlight(secondFile));
assertNotEmpty(service.getNavigation(secondFile));
assertNotEmpty(service.getOverrideMembers(secondFile));
FileEditorManager.getInstance(getProject()).closeFile(firstFile);
getProject().getMessageBus().syncPublisher(FileEditorManagerListener.FILE_EDITOR_MANAGER)
.fileClosed(FileEditorManager.getInstance(getProject()), firstFile);
assertEmpty(service.getHighlight(firstFile));
assertEmpty(service.getNavigation(firstFile));
assertEmpty(service.getOverrideMembers(firstFile));
assertNotEmpty(service.getHighlight(secondFile));
assertNotEmpty(service.getNavigation(secondFile));
assertNotEmpty(service.getOverrideMembers(secondFile));
}
public void testRespectErrorLocationFile() throws Exception {
// test workaround for https://github.com/dart-lang/sdk/issues/25034
myFixture.addFileToProject("main_part.dart", "class A{}");
myFixture.configureByText("main.dart",
"library test;\n" +
"part <error descr=\"The included part ''main_part.dart'' must have a part-of directive.\">'main_part.dart'</error>;\n" +
"class A {}");
myFixture.checkHighlighting();
}
public void _testAnalysisOptionsFile() throws Exception {
// do nt use configureByText(), because that method creates file with different name (___.analysis_options)
final PsiFile file = myFixture.addFileToProject(".analysis_options",
"analyzer:\n" +
" errors:\n" +
" <warning>invalid-option</warning>: <warning>invalid-value</warning>");
myFixture.openFileInEditor(file.getVirtualFile());
myFixture.checkHighlighting();
}
public void testErrorsUpdatedOnTypingAndUndo() throws Exception {
myFixture.configureByText("foo.dart", "main(){\n" +
" <warning>Ra<caret>ndom</warning> <warning>r</warning> = new <warning>Random</warning>();\n" +
"}");
myFixture.checkHighlighting();
final List<HighlightInfo> highlighting = myFixture.doHighlighting(HighlightSeverity.WARNING);
myFixture.type(" ");
myFixture.type('\b'); // backspace
assertSameElements(highlighting, myFixture.doHighlighting(HighlightSeverity.WARNING));
}
public void testErrorsRemoved() throws Exception {
myFixture.configureByText("foo.dart", "<caret>import <warning>'dart:math'</warning>;");
myFixture.checkHighlighting();
myFixture.type("//");
assertEmpty(myFixture.doHighlighting(HighlightSeverity.WEAK_WARNING));
}
}