Merge remote-tracking branch 'origin/master'

This commit is contained in:
Roman Shevchenko
2018-04-10 19:05:56 +02:00
119 changed files with 1441 additions and 1183 deletions
Binary file not shown.
Binary file not shown.
@@ -289,17 +289,19 @@ public class MethodBytecodeUtil {
List<Location> res = new ArrayList<>();
for (Map.Entry<Method, Collection<Location>> entry : byMethod.entrySet()) {
res.addAll(removeMethodSameLineLocations(entry.getKey(), entry.getValue()));
res.addAll(removeMethodSameLineLocations(entry.getKey(), (List<Location>)entry.getValue()));
}
return res;
}
private static Collection<Location> removeMethodSameLineLocations(@NotNull Method method, @NotNull Collection<Location> locations) {
if (locations.size() < 2) {
private static Collection<Location> removeMethodSameLineLocations(@NotNull Method method, @NotNull List<Location> locations) {
int locationsSize = locations.size();
if (locationsSize < 2) {
return locations;
}
//noinspection ConstantConditions
int lineNumber = ContainerUtil.getFirstItem(locations).lineNumber();
List<Boolean> mask = new ArrayList<>();
List<Boolean> mask = new ArrayList<>(locationsSize);
visit(method, new MethodVisitor(Opcodes.API_VERSION) {
boolean myNewBlock = true;
@Override
@@ -322,8 +324,10 @@ public class MethodBytecodeUtil {
myNewBlock = true;
}
}, true);
if (mask.size() == locations.size()) {
List<Location> res = new ArrayList<>();
if (mask.size() == locationsSize) {
locations.sort(Comparator.comparing(Location::codeIndex));
List<Location> res = new ArrayList<>(locationsSize);
int pos = 0;
for (Location location : locations) {
if (mask.get(pos++)) {
@@ -137,7 +137,8 @@ public class AddExceptionToExistingCatchFix extends PsiElementBaseIntentionActio
if (unhandledExceptions.isEmpty()) return null;
boolean containsInCatchOrFinally = containsInCatchOrFinally(element);
List<PsiTryStatement> tryStatements =
PsiTreeUtil.collectParentsOfType(element, PsiTryStatement.class, PsiLambdaExpression.class, PsiClass.class);
PsiTreeUtil.collectParents(element, PsiTryStatement.class, el ->
el instanceof PsiLambdaExpression || el instanceof PsiClass && !(el instanceof PsiAnonymousClass));
if (containsInCatchOrFinally) {
tryStatements.remove(0);
}
@@ -14,6 +14,7 @@ import com.intellij.util.IncorrectOperationException;
import com.intellij.util.SmartList;
import com.siyeh.ig.psiutils.ExpressionUtils;
import gnu.trove.TIntArrayList;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -821,6 +822,15 @@ public class SortContentAction extends PsiElementBaseIntentionAction {
PsiElement lBrace = aClass.getLBrace();
PsiElement rBrace = aClass.getRBrace();
if (lBrace == null || rBrace == null) return;
//PsiEnumConstant holds comments inside, we need codegen to know about this comments to place \n correctly
for (SortableEntry entry : sortableList.myEntries) {
List<PsiComment> comments = StreamEx.ofTree(entry.myElement, el -> StreamEx.of(el.getChildren())).select(PsiComment.class).toList();
for (PsiComment comment : comments) {
entry.myBeforeSeparator.add((PsiComment)comment.copy());
comment.delete();
}
}
StringBuilder sb = new StringBuilder();
sortableList.generate(sb);
PsiElement elementToPreserve = lastElement.getNextSibling();
@@ -40,7 +40,6 @@ import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.*;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Processor;
import java.util.HashSet;
import com.intellij.util.containers.MostlySingularMultiMap;
import com.intellij.util.indexing.IndexingDataKeys;
import org.jetbrains.annotations.NotNull;
@@ -48,6 +47,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJavaFile {
@@ -137,8 +137,8 @@ public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJava
if (stub != null) {
PsiImportList[] nodes = stub.getChildrenByType(JavaStubElementTypes.IMPORT_LIST, PsiImportList.ARRAY_FACTORY);
if (nodes.length == 1) return nodes[0];
if (nodes.length == 0) return null;
reportStubAstMismatch(stub + "; " + stub.getChildrenStubs(), getStubTree());
assert nodes.length == 0;
return null;
}
ASTNode node = calcTreeElement().findChildByType(JavaElementType.IMPORT_LIST);
@@ -0,0 +1,18 @@
// "Add exception to existing catch clause" "true"
import java.io.FileInputStream;
import java.io.FileNotFoundException;
class Test {
void m() {
try {
new Runnable() {
@Override
public void run() { }
InputStream in = new FileInputStream("");
};
} catch (RuntimeException | FileNotFoundException e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,17 @@
// "Add exception to existing catch clause" "true"
import java.io.FileInputStream;
class Test {
void m() {
try {
new Runnable() {
@Override
public void run() { }
InputStream in = new <caret>FileInputStream("");
};
} catch (RuntimeException e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,17 @@
// "Add exception to existing catch clause" "false"
import java.io.FileInputStream;
class Test {
void m() {
try {
class A implements Runnable {
@Override
public void run() { }
InputStream in = new <caret>FileInputStream("");
};
} catch (RuntimeException e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,13 @@
// "Sort content" "true"
public enum OSType {
LINUX// linux
,
MAC//mac
,
WIN//after win
// win
;
}
@@ -0,0 +1,9 @@
// "Sort content" "true"
public enum OSType {
WIN// win
//after win
, LIN<caret>UX // linux
,MAC //mac
;
}
@@ -34,15 +34,13 @@ import com.intellij.psi.stubs.StubTree
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.reference.SoftReference
import com.intellij.testFramework.LeakHunter
import com.intellij.testFramework.PlatformTestUtil
import com.intellij.testFramework.SkipSlowTestLocally
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
import com.intellij.util.ref.GCUtil
import groovy.transform.CompileStatic
import java.util.concurrent.Callable
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Future
import java.util.concurrent.Future
/**
* @author peter
*/
@@ -236,7 +234,7 @@ class B {
LeakHunter.checkLeak(file, StubTree) { candidate -> candidate.root.psi == file }
}
void "test node is not deeply parsed when loaded in green stub presence"() {
void "test node has same PSI when loaded in green stub presence"() {
PsiFileImpl file = (PsiFileImpl)myFixture.addFileToProject("a.java", "class A<T>{}")
def stubTree = file.stubTree
PsiClass psiClass = ((PsiJavaFile)file).classes[0]
@@ -244,7 +242,7 @@ class B {
GCUtil.tryGcSoftlyReachableObjects()
assert stubTree.is(file.greenStubTree)
assert !file.node.parsed
assert file.node.lastChildNode.psi.is(psiClass)
}
void "test load stub from non-file PSI after AST is unloaded"() {
@@ -258,26 +256,22 @@ class B {
assert ((PsiClassImpl) cls).stub
}
void "test load PSI via stub when AST is gc-ed but PSI exists that has never known stub"() {
void "test load PSI via stub when AST is gc-ed but PSI exists that was loaded via AST but knows its stub index"() {
PsiJavaFileImpl file = (PsiJavaFileImpl)myFixture.addFileToProject("a.java", "class A{}")
def cls = file.lastChild
assert cls instanceof PsiClass
GCUtil.tryGcSoftlyReachableObjects()
assert !file.treeElement
assert cls == myFixture.findClass('A')
}
void "test load PSI via stub when AST is gc-ed and PSI remains that never knew stub"() {
PsiJavaFileImpl file = (PsiJavaFileImpl)myFixture.addFileToProject("a.java", "class A{}")
def cls = file.lastChild
assert cls instanceof PsiClass
assert file.treeElement // we still hold a strong reference to AST
assert cls == myFixture.findClass('A')
// now we know stub index and can GC AST
GCUtil.tryGcSoftlyReachableObjects()
assert !file.treeElement
assert cls == myFixture.findClass('A')
assert !file.treeElement
}
void "test bind stubs to AST after AST has been loaded and gc-ed"() {
@@ -335,30 +329,27 @@ class B {
assert stubTree.plainList.find { it.stubType == JavaStubElementTypes.ANONYMOUS_CLASS }
}
@CompileStatic
void "test getStub performance with cached PSI"() {
def text = "class Foo { " + "void bar(int a, int b, int c, int d, int e) { int x = null; }\n" * 1000 + "}"
def file = myFixture.addFileToProject "a.java", text
PsiMethod[] methods = ((PsiJavaFile) file).classes[0].methods
def params = methods.collect { PsiMethod method -> method.parameterList.parameters }
def literal = file.findElementAt(text.indexOf('null')).parent as PsiLiteralExpression // the only cached PSI without stubIndex
void "test stub index is cleared on AST change"() {
def clazz = myFixture.addClass("class Foo { int a; }")
def field = clazz.fields[0]
def file = clazz.containingFile as PsiFileImpl
WriteCommandAction.runWriteCommandAction(project, {
file.viewProvider.document.insertString(0, ' ')
PsiDocumentManager.getInstance(project).commitAllDocuments()
})
assert file.calcStubTree()
WriteCommandAction.runWriteCommandAction(project, {
file.viewProvider.document.insertString(file.text.indexOf('int'), 'void foo();')
PsiDocumentManager.getInstance(project).commitAllDocuments()
})
GCUtil.tryGcSoftlyReachableObjects()
def fileImpl = (PsiFileImpl)file
assert !fileImpl.treeElement
assert !fileImpl.stub
assert file.calcStubTree()
PlatformTestUtil.startPerformanceTest('getStub performance', 100, {
10_000.times {
if (fileImpl.stub != null) {
throw new IllegalStateException("has stub")
}
}
}).assertTiming()
assert params
assert literal
assert field.valid
assert field.name == 'a'
}
}
@@ -38,8 +38,11 @@ import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -50,6 +53,7 @@ import static com.jetbrains.jsonSchema.JsonSchemaConfigurable.isHttpPath;
*/
public class JsonSchemaMappingsView implements Disposable {
private static final String ADD_SCHEMA_MAPPING = "settings.json.schema.add.mapping";
private static final String EDIT_SCHEMA_MAPPING = "settings.json.schema.edit.mapping";
private final Runnable myTreeUpdater;
private TableView<UserDefinedJsonSchemaConfiguration.Item> myTableView;
private JComponent myComponent;
@@ -70,23 +74,20 @@ public class JsonSchemaMappingsView implements Disposable {
myTableView = new TableView<>();
myTableView.getTableHeader().setVisible(false);
final ToolbarDecorator decorator = ToolbarDecorator.createDecorator(myTableView);
decorator.setRemoveAction(new AnActionButtonRunnable() {
final MyEditActionButtonRunnableImpl editAction = new MyEditActionButtonRunnableImpl(project);
decorator.setRemoveAction(new MyRemoveActionButtonRunnable())
.setAddAction(new MyAddActionButtonRunnable(project))
.setEditAction(editAction)
.disableUpDownActions();
myTableView.addMouseListener(new MouseAdapter() {
@Override
public void run(AnActionButton button) {
final int[] rows = myTableView.getSelectedRows();
if (rows != null && rows.length > 0) {
int cnt = 0;
for (int row : rows) {
myTableView.getListTableModel().removeRow(row - cnt);
++cnt;
}
myTableView.getListTableModel().fireTableDataChanged();
myTreeUpdater.run();
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
editAction.execute();
}
}
})
.setAddAction(new MyAddActionButtonRunnable(project))
.disableUpDownActions();
});
mySchemaField = new TextFieldWithBrowseButton();
SwingHelper.installFileCompletionAndBrowseDialog(myProject, mySchemaField, JsonBundle.message("json.schema.add.schema.chooser.title"),
@@ -180,21 +181,30 @@ public class JsonSchemaMappingsView implements Disposable {
return myComponent;
}
private class MyAddActionButtonRunnable implements AnActionButtonRunnable {
private abstract class MyAddOrEditActionButtonRunnableBase implements AnActionButtonRunnable {
private final Project myProject;
public MyAddActionButtonRunnable(Project project) {
public MyAddOrEditActionButtonRunnableBase(Project project) {
myProject = project;
}
@Override
public void run(AnActionButton button) {
public abstract void run(AnActionButton button);
protected void doRun(@Nullable UserDefinedJsonSchemaConfiguration.Item currentItem, int selectedRow) {
assert currentItem == null || selectedRow != -1;
final JBPanel panel = new JBPanel(new GridBagLayout());
final GridBag bag = new GridBag();
final JBTextField patternField = new JBTextField();
final TextFieldWithBrowseButton directoryField = new TextFieldWithBrowseButton();
if (currentItem != null && currentItem.directory) {
directoryField.setText(currentItem.path);
}
final TextFieldWithBrowseButton fileField = new TextFieldWithBrowseButton();
if (currentItem != null && !currentItem.directory && !currentItem.pattern) {
fileField.setText(currentItem.path);
}
bag.setDefaultAnchor(GridBagConstraints.NORTHWEST);
final JBRadioButton radioPattern = new JBRadioButton("Filename pattern:");
@@ -218,17 +228,28 @@ public class JsonSchemaMappingsView implements Disposable {
group.add(radioDirectory);
group.add(radioFile);
radioDirectory.setSelected(true);
if (currentItem == null) {
radioDirectory.setSelected(true);
}
else {
if (currentItem.pattern) radioPattern.setSelected(true);
else if (currentItem.directory) radioDirectory.setSelected(true);
else radioFile.setSelected(true);
}
patternField.setMinimumSize(new Dimension(JBUI.scale(200), UIUtil.getInformationIcon().getIconHeight()));
patternField.getEmptyText().setText("Example: *.config.json");
if (currentItem != null && currentItem.pattern) {
patternField.setText(currentItem.path);
}
final DialogBuilder builder = new DialogBuilder();
builder.setTitle("Add JSON Schema Mapping");
String addOrEdit = currentItem == null ? "Add" : "Edit";
builder.setTitle(addOrEdit + " JSON Schema Mapping");
builder.setNorthPanel(panel);
builder.setPreferredFocusComponent(directoryField);
builder.setDimensionServiceKey("com.jetbrains.jsonSchema.JsonSchemaMappingsView#add");
builder.setHelpId(ADD_SCHEMA_MAPPING);
builder.setHelpId(currentItem == null ? ADD_SCHEMA_MAPPING : EDIT_SCHEMA_MAPPING);
final Getter<String> textGetter = () -> {
if (radioPattern.isSelected()) {
@@ -279,13 +300,66 @@ public class JsonSchemaMappingsView implements Disposable {
final String pattern = textGetter.get();
final UserDefinedJsonSchemaConfiguration.Item item =
new UserDefinedJsonSchemaConfiguration.Item(pattern, radioPattern.isSelected(), radioDirectory.isSelected());
myTableView.getListTableModel().addRow(item);
if (currentItem != null) {
myTableView.getListTableModel().removeRow(selectedRow);
myTableView.getListTableModel().insertRow(selectedRow, item);
myTableView.setSelection(Collections.singleton(item));
}
else {
myTableView.getListTableModel().addRow(item);
}
myTreeUpdater.run();
}
Disposer.dispose(alarm);
}
}
private class MyAddActionButtonRunnable extends MyAddOrEditActionButtonRunnableBase {
public MyAddActionButtonRunnable(Project project) {
super(project);
}
@Override
public void run(AnActionButton button) {
doRun(null, -1);
}
}
private class MyEditActionButtonRunnableImpl extends MyAddOrEditActionButtonRunnableBase {
public MyEditActionButtonRunnableImpl(Project project) {
super(project);
}
@Override
public void run(AnActionButton button) {
execute();
}
public void execute() {
int selectedRow = myTableView.getSelectedRow();
if (selectedRow == -1) return;
UserDefinedJsonSchemaConfiguration.Item item = myTableView.getListTableModel().getItem(selectedRow);
if (item == null) return;
doRun(item, selectedRow);
}
}
private class MyRemoveActionButtonRunnable implements AnActionButtonRunnable {
@Override
public void run(AnActionButton button) {
final int[] rows = myTableView.getSelectedRows();
if (rows != null && rows.length > 0) {
int cnt = 0;
for (int row : rows) {
myTableView.getListTableModel().removeRow(row - cnt);
++cnt;
}
myTableView.getListTableModel().fireTableDataChanged();
myTreeUpdater.run();
}
}
}
private static String getRelativePath(@NotNull Project project, @NotNull String text) {
text = text.trim();
if (project.isDefault() || project.getBasePath() == null) return text;
@@ -457,6 +457,9 @@ class JsonSchemaAnnotatorChecker {
if (!Boolean.TRUE.equals(schema.getAdditionalItemsAllowed())) {
error("Additional items are not allowed", arrayValue.getDelegate());
}
else if (schema.getAdditionalItemsSchema() != null) {
checkObjectBySchemaRecordErrors(schema.getAdditionalItemsSchema(), arrayValue);
}
}
}
}
@@ -9,6 +9,7 @@ import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -28,6 +29,8 @@ import java.util.stream.Collectors;
public class JsonSchemaObject {
@NonNls public static final String DEFINITIONS = "definitions";
@NonNls public static final String PROPERTIES = "properties";
@NonNls public static final String ITEMS = "items";
@NonNls public static final String ADDITIONAL_ITEMS = "additionalItems";
@NonNls public static final String X_INTELLIJ_HTML_DESCRIPTION = "x-intellij-html-description";
@NotNull private final JsonObject myJsonObject;
@Nullable private Map<String, JsonSchemaObject> myDefinitionsMap;
@@ -641,12 +644,42 @@ public class JsonSchemaObject {
current = current.getProperties().get(parts.get(++i));
continue;
}
if (ITEMS.equals(part)) {
if (i == (parts.size() - 1)) {
current = current.getItemsSchema();
}
else {
//noinspection AssignmentToForLoopParameter
Integer next = tryParseInt(parts.get(++i));
List<JsonSchemaObject> itemsSchemaList = current.getItemsSchemaList();
if (itemsSchemaList != null && next != null && next < itemsSchemaList.size()) {
current = itemsSchemaList.get(next);
}
}
continue;
}
if (ADDITIONAL_ITEMS.equals(part)) {
if (i == (parts.size() - 1)) {
current = current.getAdditionalItemsSchema();
}
continue;
}
current = current.getDefinitionsMap() == null ? null : current.getDefinitionsMap().get(part);
}
return current;
}
@Nullable
private static Integer tryParseInt(String s) {
try {
return Integer.parseInt(s);
}
catch (Exception __) {
return null;
}
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
@@ -155,8 +155,8 @@ public class JsonSchemaReader {
READERS_MAP.put("pattern", (element, object, queue) -> {
if (element instanceof JsonStringLiteral) object.setPattern(StringUtil.unquoteString(element.getText()));
});
READERS_MAP.put("additionalItems", createAdditionalItems());
READERS_MAP.put("items", createItems());
READERS_MAP.put(JsonSchemaObject.ADDITIONAL_ITEMS, createAdditionalItems());
READERS_MAP.put(JsonSchemaObject.ITEMS, createItems());
READERS_MAP.put("contains", createContains());
READERS_MAP.put("maxItems", (element, object, queue) -> {
if (element instanceof JsonNumberLiteral) object.setMaxItems((int)((JsonNumberLiteral)element).getValue());
@@ -25,7 +25,7 @@ public class JsonSchemaCatalogManager {
@NotNull private final ConcurrentMap<String, String> myResolvedMappings = ContainerUtil.newConcurrentMap();
private static final String NO_CACHE = "$_$_WS_NO_CACHE_$_$";
private static final String EMPTY = "$_$_WS_EMPTY_$_$";
private static final AtomicBoolean myIsEnabled = new AtomicBoolean(!ApplicationManager.getApplication().isUnitTestMode());
private static final AtomicBoolean myIsEnabled = new AtomicBoolean(true);
public JsonSchemaCatalogManager(@NotNull Project project) {
myProject = project;
@@ -37,6 +37,7 @@ public class JsonSchemaCatalogManager {
}
public void startUpdates() {
if (ApplicationManager.getApplication().isUnitTestMode()) return;
RemoteFileManager instance = RemoteFileManager.getInstance();
instance.addRemoteContentProvider(myRemoteContentProvider);
myCatalog = JsonFileResolver.urlToFile(DEFAULT_CATALOG);
@@ -44,6 +45,7 @@ public class JsonSchemaCatalogManager {
@Nullable
public VirtualFile getSchemaFileForFile(@NotNull VirtualFile file) {
if (ApplicationManager.getApplication().isUnitTestMode()) return null;
if (!myIsEnabled.get()) return null;
String name = file.getName();
@@ -546,6 +546,78 @@ public class JsonSchemaHighlightingTest extends DaemonAnalyzerTestCase {
doTest(schema, "{\"withFormat\": \"localhost\"}");
}
public void testArrayItemReference() throws Exception {
@Language("JSON") final String schema = "{\n" +
" \"items\": [\n" +
" {\n" +
" \"type\": \"integer\"\n" +
" },\n" +
" {\n" +
" \"$ref\": \"#/items/0\"\n" +
" }\n" +
" ]\n" +
"}";
doTest(schema, "[1, 2]");
doTest(schema, "[1, <warning>\"foo\"</warning>]");
}
public void testArrayReference() throws Exception {
@Language("JSON") final String schema = "{\n" +
" \"definitions\": {\n" +
" \"options\": {\n" +
" \"type\": \"array\",\n" +
" \"items\": {\n" +
" \"type\": \"number\"\n" +
" }\n" +
" }\n" +
" },\n" +
" \"items\":{\n" +
" \"$ref\": \"#/definitions/options/items\"\n" +
" }\n" +
" \n" +
"}";
doTest(schema, "[2, 3 ,4]");
doTest(schema, "[2, <warning>\"3\"</warning>]");
}
public void testSelfArrayReferenceDoesNotThrowSOE() throws Exception {
@Language("JSON") final String schema = "{\n" +
" \"items\": [\n" +
" {\n" +
" \"$ref\": \"#/items/0\"\n" +
" }\n" +
" ]\n" +
"}";
doTest(schema, "[]");
}
public void testValidateAdditionalItems() throws Exception {
@Language("JSON") final String schema = "{\n" +
" \"definitions\": {\n" +
" \"options\": {\n" +
" \"type\": \"array\",\n" +
" \"items\": {\n" +
" \"type\": \"number\"\n" +
" }\n" +
" }\n" +
" },\n" +
" \"items\": [\n" +
" {\n" +
" \"type\": \"boolean\"\n" +
" },\n" +
" {\n" +
" \"type\": \"boolean\"\n" +
" }\n" +
" ],\n" +
" \"additionalItems\": {\n" +
" \"$ref\": \"#/definitions/options/items\"\n" +
" }\n" +
"}";
doTest(schema, "[true, true]");
doTest(schema, "[true, true, 1, 2, 3]");
doTest(schema, "[true, true, 1, <warning>\"2\"</warning>]");
}
public static String rootObjectRedefinedSchema() {
return "{\n" +
" \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n" +
@@ -674,8 +674,7 @@ bool CreateJVM()
buf << "Failed to create JVM: error code " << result << ".\n";
buf << "JVM Path: " << jvmPath << "\n";
buf << "If you already have a " BITS_STR " JDK installed, define a JAVA_HOME variable in \n";
buf << "Computer > System Properties > System Settings > Environment Variables.";
buf << "Most likely the cause of the problem is incorrect settings in the VM options file.";
std::string error = LoadStdString(IDS_ERROR_LAUNCHING_APP);
MessageBoxA(NULL, buf.str().c_str(), error.c_str(), MB_OK);
}
@@ -48,6 +48,7 @@ class BuildTasksImpl extends BuildTasks {
}
buildContext.notifyArtifactBuilt(targetFile)
}
logFreeDiskSpace("after building sources archive")
}
@Override
@@ -299,7 +300,9 @@ idea.fatal.error.notification=disabled
copyDependenciesFile()
def patchedApplicationInfo = patchApplicationInfo()
logFreeDiskSpace("before compilation")
def distributionJARsBuilder = compileModulesForDistribution(patchedApplicationInfo)
logFreeDiskSpace("after compilation")
def mavenArtifacts = buildContext.productProperties.mavenArtifacts
if (mavenArtifacts.forIdeModules || !mavenArtifacts.additionalModules.isEmpty()) {
buildContext.executeStep("Generate Maven artifacts", BuildOptions.MAVEN_ARTIFACTS_STEP) {
@@ -354,8 +357,14 @@ idea.fatal.error.notification=disabled
}
}
}
logFreeDiskSpace("after building distributions")
}
private void logFreeDiskSpace(String phase) {
CompilationContextImpl.logFreeDiskSpace(buildContext.messages, buildContext.paths.buildOutputRoot, phase)
}
private def copyDependenciesFile() {
if (buildContext.gradle.forceRun('Preparing dependencies file', 'dependenciesFile')) {
def outputFile = "$buildContext.paths.artifacts/dependencies.txt"
@@ -16,6 +16,7 @@
package org.jetbrains.intellij.build.impl
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil
import com.intellij.util.PathUtilRt
import com.intellij.util.SystemProperties
import groovy.transform.CompileDynamic
@@ -73,6 +74,7 @@ class CompilationContextImpl implements CompilationContext {
}
def dependenciesProjectDir = new File(communityHome, 'build/dependencies')
logFreeDiskSpace(messages, projectHome, "before dwonloading dependencies")
GradleRunner gradle = new GradleRunner(dependenciesProjectDir, messages, SystemProperties.getJavaHome())
if (!options.isInDevelopmentMode) {
setupCompilationDependencies(gradle)
@@ -369,6 +371,11 @@ class CompilationContextImpl implements CompilationContext {
private static String toCanonicalPath(String path) {
FileUtil.toSystemIndependentName(new File(path).canonicalPath)
}
static void logFreeDiskSpace(BuildMessages buildMessages, String directoryPath, String phase) {
def dir = new File(directoryPath)
buildMessages.debug("Free disk space $phase: ${StringUtil.formatFileSize(dir.freeSpace)} (on disk containing $dir)")
}
}
class BuildPathsImpl extends BuildPaths {
@@ -51,7 +51,8 @@ public abstract class StubBase<T extends PsiElement> extends ObjectStubBase<Stub
return myStubList.findChildStubByType(id, elementType);
}
public void setPsi(@NotNull final T psi) {
public void setPsi(@NotNull T psi) {
assert myPsi == null || myPsi == psi;
myPsi = psi;
}
@@ -28,6 +28,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.Predicate;
import static com.intellij.psi.SyntaxTraverser.psiTraverser;
@@ -697,11 +698,11 @@ public class PsiTreeUtil {
return aClass.cast(element);
}
public static <T extends PsiElement> List<T> collectParentsOfType(PsiElement element, Class<T> parent, Class<? extends PsiElement>... stopClasses) {
public static <T extends PsiElement> List<T> collectParents(PsiElement element, Class<T> parent, Predicate<PsiElement> stopCondition) {
element = element.getParent();
List<T> parents = new SmartList<>();
while (element != null) {
if (instanceOf(element, stopClasses)) break;
if (stopCondition.test(element)) break;
if (parent.isInstance(element)) {
parents.add(parent.cast(element));
}
@@ -84,7 +84,6 @@ public class StubBasedPsiElementBase<T extends StubElement> extends ASTDelegateP
public static final Key<String> CREATION_TRACE = Key.create("CREATION_TRACE");
public static final boolean ourTraceStubAstBinding = "true".equals(System.getProperty("trace.stub.ast.binding", "false"));
private volatile SubstrateRef mySubstrateRef;
private volatile int myStubIndex = -1;
private final IElementType myElementType;
public StubBasedPsiElementBase(@NotNull T stub, @NotNull IStubElementType nodeType) {
@@ -238,29 +237,6 @@ public class StubBasedPsiElementBase<T extends StubElement> extends ASTDelegateP
*/
public final void setSubstrateRef(@NotNull SubstrateRef substrateRef) {
mySubstrateRef = substrateRef;
myStubIndex = -1;
}
/**
* Don't invoke this method, it's public for implementation reasons.
*/
public final void setStubIndex(int stubIndex) {
myStubIndex = stubIndex;
}
/**
* Don't invoke this method, it's public for implementation reasons.
*/
public int getStubIndex() {
return myStubIndex;
}
/**
* Don't invoke this method, it's public for implementation reasons.
*/
@NotNull
public final SubstrateRef getSubstrateRef() {
return mySubstrateRef;
}
@NotNull
@@ -383,7 +359,7 @@ public class StubBasedPsiElementBase<T extends StubElement> extends ASTDelegateP
public T getStub() {
ProgressIndicatorProvider.checkCanceled(); // Hope, this is called often
//noinspection unchecked
return (T)mySubstrateRef.getStub(myStubIndex);
return (T)mySubstrateRef.getStub();
}
/**
@@ -395,7 +371,7 @@ public class StubBasedPsiElementBase<T extends StubElement> extends ASTDelegateP
public final T getGreenStub() {
ProgressIndicatorProvider.checkCanceled(); // Hope, this is called often
//noinspection unchecked
return (T)mySubstrateRef.getGreenStub(myStubIndex);
return (T)mySubstrateRef.getGreenStub();
}
/**
@@ -1,153 +0,0 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.intellij.psi.impl.source;
import com.intellij.extapi.psi.StubBasedPsiElementBase;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.LowMemoryWatcher;
import com.intellij.openapi.util.UserDataHolderEx;
import com.intellij.psi.impl.DebugUtil;
import com.intellij.psi.impl.source.tree.AstPath;
import com.intellij.psi.impl.source.tree.CompositeElement;
import com.intellij.reference.SoftReference;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Objects;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Stream;
/**
* A weak cache for all instantiated stub-based PSI to allow {@link CompositeElement#getPsi()} return it when AST is reloaded.<p/>
*
* All methods should be called under an external lock (view provider's PsiLock), except for
* ({@link #getCachedPsi(AstPath)} which can be called without lock.
*
* @author peter
*/
class AstPathPsiMap {
/**
* Not using ConcurrentWeakValueMap because we need to clean each of them up separately, when ASTs in thousands of files are created and gc-ed.
* So we have a per-project single shared reference queue {@link #myQueue} for that.
* Otherwise the files end up retaining lots of maps with all-gc-ed stuff inside, but the maps are still very large.
*/
private final ConcurrentMap<AstPath, MyReference> myMap = ContainerUtil.newConcurrentMap();
private volatile Boolean myHasUnbindableCachedPsi = null;
private static final Key<MyReferenceQueue> STUB_PSI_REFS = Key.create("STUB_PSI_REFS");
private final MyReferenceQueue myQueue;
AstPathPsiMap(@NotNull Project project) {
MyReferenceQueue queue = project.getUserData(STUB_PSI_REFS);
myQueue = queue != null ? queue : ((UserDataHolderEx)project).putUserDataIfAbsent(STUB_PSI_REFS, new MyReferenceQueue(project));
}
void invalidatePsi() {
myQueue.cleanupStaleReferences();
getAllCachedPsi().forEach(psi -> {
DebugUtil.onInvalidated(psi);
psi.setSubstrateRef(SubstrateRef.createInvalidRef(psi));
});
myMap.clear();
myHasUnbindableCachedPsi = false;
}
void switchToStrongRefs() {
myQueue.cleanupStaleReferences();
getAllCachedPsi().forEach(psi -> {
CompositeElement node = (CompositeElement)psi.getNode();
node.setPsi(psi);
psi.setSubstrateRef(SubstrateRef.createAstStrongRef(node));
});
myMap.clear();
myHasUnbindableCachedPsi = false;
}
@Nullable
StubBasedPsiElementBase<?> getCachedPsi(@NotNull AstPath ref) {
return SoftReference.dereference(myMap.get(ref));
}
@NotNull
StubBasedPsiElementBase<?> cachePsi(@NotNull AstPath key, @NotNull StubBasedPsiElementBase psi) {
myQueue.cleanupStaleReferences();
// ensure PSI will use AST path before making it available to other threads
// otherwise another thread could invoke StubRef.getNode and fail since file's AST isn't set yet
psi.setSubstrateRef(key);
myMap.put(key, new MyReference(psi, key, myQueue));
clearStubIndexCache();
return psi;
}
Stream<? extends StubBasedPsiElementBase<?>> getAllCachedPsi() {
myQueue.cleanupStaleReferences();
if (myMap.isEmpty()) return Stream.empty();
return myMap.values().stream().map(Reference::get).filter(Objects::nonNull);
}
boolean hasUnbindableCachedPsi() {
Boolean answer = myHasUnbindableCachedPsi;
if (answer == null) {
myHasUnbindableCachedPsi = answer = calcHasUnbindableCachedPsi();
}
return answer;
}
private boolean calcHasUnbindableCachedPsi() {
myQueue.cleanupStaleReferences();
if (myMap.isEmpty()) return false;
return getAllCachedPsi().anyMatch(e -> e.getStubIndex() < 0);
}
void clearStubIndexCache() {
myHasUnbindableCachedPsi = null;
}
private static class MyReference extends WeakReference<StubBasedPsiElementBase<?>> {
final AstPath pathRef;
MyReference(StubBasedPsiElementBase psi, AstPath ref, ReferenceQueue<StubBasedPsiElementBase<?>> queue) {
super(psi, queue);
pathRef = ref;
}
}
private static class MyReferenceQueue extends ReferenceQueue<StubBasedPsiElementBase<?>> {
MyReferenceQueue(Project project) {
LowMemoryWatcher.register(() -> cleanupStaleReferences(), project);
}
void cleanupStaleReferences() {
while (true) {
MyReference reference = (MyReference)poll();
if (reference == null) break;
AstPath key = reference.pathRef;
AstPathPsiMap refToPsi = key.getContainingFile().getRefToPsi();
refToPsi.myMap.remove(key, reference);
refToPsi.clearStubIndexCache();
}
}
}
}
@@ -15,32 +15,48 @@
*/
package com.intellij.psi.impl.source;
import com.intellij.extapi.psi.StubBasedPsiElementBase;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Getter;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.DebugUtil;
import com.intellij.psi.impl.source.tree.CompositeElement;
import com.intellij.psi.impl.source.tree.FileElement;
import com.intellij.psi.stubs.PsiFileStubImpl;
import com.intellij.psi.stubs.StubTree;
import com.intellij.psi.stubs.*;
import com.intellij.reference.SoftReference;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.Reference;
import java.util.Set;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
/**
* @author peter
*/
final class FileTrees {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.FileTrees");
private static final int firstNonFilePsiIndex = 1;
private final PsiFileImpl myFile;
private final Reference<StubTree> myStub;
private final Getter<FileElement> myTreeElementPointer; // SoftReference/WeakReference to ASTNode or a strong reference to a tree if the file is a DummyHolder
final boolean astLoaded;
final boolean useStrongRefs;
/** Keeps references to all alive stubbed PSI (using {@link SpineRef}) to ensure PSI identity is preserved after AST/stubs are gc-ed and reloaded */
@Nullable private final List<Reference<StubBasedPsiElementBase>> myRefToPsi;
private FileTrees(@Nullable Reference<StubTree> stub, @Nullable Getter<FileElement> ast, boolean astLoaded, boolean useStrongRefs) {
private FileTrees(@NotNull PsiFileImpl file,
@Nullable Reference<StubTree> stub,
@Nullable Getter<FileElement> ast,
@Nullable List<Reference<StubBasedPsiElementBase>> refToPsi) {
this.myFile = file;
this.myStub = stub;
this.myTreeElementPointer = ast;
this.astLoaded = astLoaded;
this.useStrongRefs = useStrongRefs;
this.myRefToPsi = refToPsi;
}
@Nullable
@@ -54,7 +70,41 @@ final class FileTrees {
}
FileTrees switchToStrongRefs() {
return new FileTrees(myStub, myTreeElementPointer, astLoaded, true);
if (myRefToPsi == null) return this;
getAllCachedPsi(myRefToPsi).forEach(psi -> {
ASTNode node = psi.getNode();
LOG.assertTrue(node.getPsi() == psi);
psi.setSubstrateRef(SubstrateRef.createAstStrongRef(node));
});
return new FileTrees(myFile, myStub, myTreeElementPointer, null);
}
private static Stream<StubBasedPsiElementBase> getAllCachedPsi(@NotNull List<Reference<StubBasedPsiElementBase>> refToPsi) {
return refToPsi.stream().map(SoftReference::dereference).filter(Objects::nonNull);
}
boolean useSpineRefs() {
return myRefToPsi != null;
}
FileTrees switchToSpineRefs(@NotNull StubbedSpine srcSpine) {
List<Reference<StubBasedPsiElementBase>> refToPsi = myRefToPsi;
if (refToPsi == null) refToPsi = new ArrayList<>(Collections.nCopies(srcSpine.getStubCount(), null));
for (int i = firstNonFilePsiIndex; i < refToPsi.size(); i++) {
StubBasedPsiElementBase psi = (StubBasedPsiElementBase)srcSpine.getStubPsi(i);
assert psi != null;
psi.setSubstrateRef(new SpineRef(myFile, i));
StubBasedPsiElementBase existing = SoftReference.dereference(refToPsi.get(i));
if (existing != null) {
assert existing == psi : "Duplicate PSI found";
} else {
refToPsi.set(i, new WeakReference<>(psi));
}
}
return new FileTrees(myFile, myStub, myTreeElementPointer, refToPsi);
}
FileTrees clearStub(@NotNull String reason) {
@@ -62,29 +112,104 @@ final class FileTrees {
if (stubHolder != null) {
((PsiFileStubImpl<?>)stubHolder.getRoot()).clearPsi(reason);
}
return new FileTrees(null, myTreeElementPointer, astLoaded, useStrongRefs);
if (myRefToPsi != null) {
DebugUtil.performPsiModification("clearStub", () -> getAllCachedPsi(myRefToPsi).forEach(psi -> {
DebugUtil.onInvalidated(psi);
psi.setSubstrateRef(SubstrateRef.createInvalidRef(psi));
}));
}
return new FileTrees(myFile, null, myTreeElementPointer, null);
}
FileTrees withAst(@NotNull Getter<FileElement> ast) {
return new FileTrees(myStub, ast, true, useStrongRefs);
return new FileTrees(myFile, myStub, ast, myRefToPsi).reconcilePsi(derefStub(), ast.get(), true);
}
FileTrees withExclusiveStub(@NotNull StubTree stub, Set<PsiFileImpl> allRoots) {
if (derefTreeElement() != null || useStrongRefs) {
throw new RuntimeException(toString() + "; roots=" + allRoots + "; root trees=" + ContainerUtil.map(allRoots, PsiFileImpl::getFileTrees));
}
return new FileTrees(new SoftReference<>(stub), null, false, false);
}
FileTrees withGreenStub(@NotNull StubTree stub, @NotNull PsiFileImpl file) {
if (derefTreeElement() == null || !astLoaded) {
throw new RuntimeException("No AST in file " + file + " of " + file.getClass() + "; " + this);
}
return new FileTrees(new SoftReference<>(stub), myTreeElementPointer, true, useStrongRefs);
FileTrees withStub(@NotNull StubTree stub, @Nullable FileElement ast) {
assert derefTreeElement() == ast;
return new FileTrees(myFile, new SoftReference<>(stub), myTreeElementPointer, myRefToPsi)
.reconcilePsi(stub, ast, false);
}
static FileTrees noStub(@Nullable FileElement ast, @NotNull PsiFileImpl file) {
return new FileTrees(null, ast, ast != null, file instanceof DummyHolder);
return new FileTrees(file, null, ast, null);
}
/**
* Ensures {@link #myRefToPsi}, stubs and AST all have the same PSI at corresponding indices.
* In case several sources already have PSI (e.g. created during AST parsing), overwrites them with the "correct" one,
* which is taken from {@link #myRefToPsi} if exists, otherwise from either stubs or AST depending on {@code takePsiFromStubs}.
*/
private FileTrees reconcilePsi(@Nullable StubTree stubTree, @Nullable FileElement astRoot, boolean takePsiFromStubs) {
assert stubTree != null || astRoot != null;
if ((stubTree == null || astRoot == null) && (myRefToPsi == null || !getAllCachedPsi(myRefToPsi).findFirst().isPresent())) {
// there's only one source of PSI, nothing to reconcile
return new FileTrees(myFile, myStub, myTreeElementPointer, null);
}
List<StubElement<?>> stubList = stubTree == null ? null : stubTree.getPlainList();
List<CompositeElement> nodeList = astRoot == null ? null : astRoot.getStubbedSpine().getSpineNodes();
try {
return DebugUtil.performPsiModification("reconcilePsi", () -> {
if (myRefToPsi != null) {
assert myRefToPsi.size() == (stubList != null ? stubList.size() : nodeList.size()) : "Cached PSI count doesn't match actual one";
bindSubstratesToCachedPsi(stubList, nodeList);
}
if (stubList != null && nodeList != null) {
assert stubList.size() == nodeList.size() : "Stub count doesn't match stubbed node length";
StubbedSpine srcSpine = takePsiFromStubs ? stubTree.getSpine() : astRoot.getStubbedSpine();
FileTrees result = switchToSpineRefs(srcSpine);
bindStubsWithAst(srcSpine, stubList, nodeList, takePsiFromStubs);
return result;
}
return this;
});
}
catch (Throwable e) {
LOG.error(e);
myFile.onContentReload();
myFile.rebuildStub();
throw StubTreeLoader.getInstance().stubTreeAndIndexDoNotMatch(e.getMessage(), stubTree, myFile);
}
}
private void bindSubstratesToCachedPsi(List<StubElement<?>> stubList, List<CompositeElement> nodeList) {
assert myRefToPsi != null;
for (int i = firstNonFilePsiIndex; i < myRefToPsi.size(); i++) {
StubBasedPsiElementBase cachedPsi = SoftReference.dereference(myRefToPsi.get(i));
if (cachedPsi != null) {
if (stubList != null) {
//noinspection unchecked
((StubBase)stubList.get(i)).setPsi(cachedPsi);
}
if (nodeList != null) {
nodeList.get(i).setPsi(cachedPsi);
}
}
}
}
private static void bindStubsWithAst(StubbedSpine srcSpine, List<StubElement<?>> stubList, List<CompositeElement> nodeList, boolean takePsiFromStubs) {
for (int i = firstNonFilePsiIndex; i < stubList.size(); i++) {
StubElement<?> stub = stubList.get(i);
CompositeElement node = nodeList.get(i);
assert stub.getStubType() == node.getElementType() : "Stub type mismatch";
PsiElement psi = srcSpine.getStubPsi(i);
assert psi != null;
if (takePsiFromStubs) {
node.setPsi(psi);
} else {
//noinspection unchecked
((StubBase)stub).setPsi(psi);
}
}
}
@Override
@@ -92,8 +217,7 @@ final class FileTrees {
return "FileTrees{" +
"stub=" + (myStub == null ? "noRef" : derefStub()) +
", AST=" + (myTreeElementPointer == null ? "noRef" : derefTreeElement()) +
", astLoaded=" + astLoaded +
", useStrongRefs=" + useStrongRefs +
", useSpineRefs=" + useSpineRefs() +
'}' ;
}
}
@@ -2,7 +2,6 @@
package com.intellij.psi.impl.source;
import com.intellij.extapi.psi.StubBasedPsiElementBase;
import com.intellij.ide.util.PsiNavigationSupport;
import com.intellij.lang.*;
import com.intellij.navigation.ItemPresentation;
@@ -14,8 +13,10 @@ import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Queryable;
import com.intellij.openapi.util.Factory;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.Getter;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileWithId;
@@ -40,7 +41,6 @@ import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.PatchedWeakReference;
import com.intellij.util.concurrency.AtomicFieldUpdater;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -66,8 +66,6 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
private volatile FileTrees myTrees = FileTrees.noStub(null, this);
private boolean myInvalidated;
@SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized")
private AstPathPsiMap myRefToPsi;
private final ThreadLocal<FileElement> myFileElementBeingLoaded = new ThreadLocal<>();
protected final PsiManagerEx myManager;
public static final Key<Boolean> BUILDING_STUB = new Key<>("Don't use stubs mark!");
private final PsiLock myPsiLock;
@@ -80,7 +78,6 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
protected PsiFileImpl(@NotNull FileViewProvider provider ) {
myManager = (PsiManagerEx)provider.getManager();
myViewProvider = provider;
myRefToPsi = new AstPathPsiMap(getProject());
myPsiLock = ((AbstractFileViewProvider) provider).getFilePsiLock();
}
@@ -179,43 +176,16 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
}
}
FileElement treeElement = createFileElement(viewProvider.getContents());
treeElement.setPsi(this);
myFileElementBeingLoaded.set(treeElement);
try {
while (true) {
FileTrees trees = myTrees;
List<Pair<StubBasedPsiElementBase, AstPath>> bindings = calcStubAstBindings(treeElement, trees);
FileElement savedTree = ensureTreeElement(viewProvider, treeElement, trees, bindings);
if (savedTree != null) {
return savedTree;
}
}
}
finally {
myFileElementBeingLoaded.remove();
}
}
@Nullable
private FileElement ensureTreeElement(@NotNull FileViewProvider viewProvider,
@NotNull FileElement treeElement,
@NotNull FileTrees trees,
@NotNull List<Pair<StubBasedPsiElementBase, AstPath>> bindings) {
synchronized (myPsiLock) {
FileElement existing = derefTreeElement();
if (existing != null) {
return existing;
FileElement treeElement = derefTreeElement();
if (treeElement != null) {
return treeElement;
}
if (trees != myTrees) {
return null; // try again
}
treeElement = createFileElement(viewProvider.getContents());
treeElement.setPsi(this);
switchFromStubToAst(bindings, trees);
updateTrees(trees.withAst(createTreeElementPointer(treeElement)));
updateTrees(myTrees.withAst(createTreeElementPointer(treeElement)));
if (LOG.isDebugEnabled() && viewProvider.isPhysical()) {
LOG.debug("Loaded text for file " + viewProvider.getVirtualFile().getPresentableUrl());
@@ -229,49 +199,15 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
@Override
public StubbedSpine getStubbedSpine() {
StubTree tree = getGreenStubTree();
return tree != null ? tree.getSpine() : calcTreeElement().getStubbedSpine();
}
private void switchFromStubToAst(List<Pair<StubBasedPsiElementBase, AstPath>> bindings, FileTrees trees) {
if (!bindings.isEmpty() && trees.useStrongRefs) {
List<String> psiStrings = ContainerUtil.map(bindings, pair -> pair.first.getClass().getName());
LOG.error(this + " of " + getClass() + "; " + psiStrings);
}
for (int i = 0; i < bindings.size(); i++) {
Pair<StubBasedPsiElementBase, AstPath> pair = bindings.get(i);
StubBasedPsiElementBase psi = pair.first;
AstPath path = pair.second;
path.getNode().setPsi(psi);
myRefToPsi.cachePsi(path, psi);
psi.setStubIndex(i + 1);
}
myRefToPsi.clearStubIndexCache();
}
private List<Pair<StubBasedPsiElementBase, AstPath>> calcStubAstBindings(@NotNull FileElement root, FileTrees trees) {
final StubTree stubTree = trees.derefStub();
if (stubTree == null || trees.astLoaded) { // don't bind green stub to AST: the PSI should already be cached in myRefToPsi
return Collections.emptyList();
}
try {
List<Pair<StubBase, TreeElement>> result = TreeUtil.calcStubAstBindings(stubTree, root);
if (tree != null) return tree.getSpine();
AstSpine astSpine = calcTreeElement().getStubbedSpine();
if (!myTrees.useSpineRefs()) {
synchronized (myPsiLock) {
return ContainerUtil.map(result, pair -> {
StubElement stub = pair.first;
PsiElement psi = stub.getPsi();
assert psi != null : "Stub " + stub + " (" + stub.getClass() + ") has returned null PSI";
AstPath path = AstPath.getNodePath((CompositeElement)pair.second);
assert path != null : "Null path";
return Pair.create((StubBasedPsiElementBase)psi, path);
});
updateTrees(myTrees.switchToSpineRefs(astSpine));
}
}
catch (TreeUtil.StubBindingException e) {
reportStubAstMismatch(e.getMessage(), stubTree);
return Collections.emptyList();
}
return astSpine;
}
@Nullable
@@ -281,15 +217,6 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
return type instanceof IStubFileElementType ? (IStubFileElementType)type : null;
}
void reportStubAstMismatch(String message, StubTree stubTree) {
rebuildStub();
synchronized (myPsiLock) {
updateTrees(myTrees.clearStub(STUB_PSI_MISMATCH));
}
throw StubTreeLoader.getInstance().stubTreeAndIndexDoNotMatch(message, stubTree, this);
}
@NotNull
protected FileElement createFileElement(CharSequence docText) {
final FileElement treeElement;
@@ -396,12 +323,13 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
PsiFileImpl clone = BlockSupportImpl.getFileCopy(this, providerCopy);
copyCopyableDataTo(clone);
clone.myRefToPsi = new AstPathPsiMap(getProject());
if (getTreeElement() != null) {
// not set by provider in clone
final FileElement treeClone = (FileElement)calcTreeElement().clone();
clone.setTreeElementPointer(treeClone); // should not use setTreeElement here because cloned file still have VirtualFile (SCR17963)
treeClone.setPsi(clone);
} else {
clone.setTreeElementPointer(null);
}
if (viewProvider.isEventSystemEnabled()) {
@@ -583,8 +511,6 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
DebugUtil.performPsiModification("onContentReload", () -> {
synchronized (myPsiLock) {
myRefToPsi.invalidatePsi();
FileElement treeElement = derefTreeElement();
if (treeElement != null) {
treeElement.detachFromFile();
@@ -628,13 +554,12 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
public StubTree getStubTree() {
assertReadAccessAllowed();
if (myTrees.astLoaded && !mayReloadStub()) return null;
if (Boolean.TRUE.equals(getUserData(BUILDING_STUB))) return null;
if (getTreeElement() != null) return null;
final StubTree derefd = derefStub();
if (derefd != null) return derefd;
if (getElementTypeForStubBuilder() == null) return null;
if (Boolean.TRUE.equals(getUserData(BUILDING_STUB)) || getElementTypeForStubBuilder() == null) return null;
final VirtualFile vFile = getVirtualFile();
if (!(vFile instanceof VirtualFileWithId) || !vFile.isValid()) return null;
@@ -645,7 +570,7 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
final List<Pair<IStubFileElementType, PsiFile>> roots = StubTreeBuilder.getStubbedRoots(viewProvider);
synchronized (myPsiLock) {
if (!mayLoadExclusiveStub()) return null;
if (getTreeElement() != null) return null;
final StubTree derefdOnLock = derefStub();
if (derefdOnLock != null) return derefdOnLock;
@@ -664,66 +589,31 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
return null;
}
// first, set all references from stubs to existing PSI (in AST or AstPathPsiMap)
Map<PsiFileImpl, StubTree> bindings = prepareAllStubTrees(roots, stubRoots);
StubTree result = bindings.get(this);
assert result != null : "Current file not in root list: " + roots + ", vp=" + viewProvider;
// now stubs can be safely published
for (PsiFileImpl eachPsiRoot : bindings.keySet()) {
FileTrees trees = eachPsiRoot.myTrees;
StubTree stub = bindings.get(eachPsiRoot);
FileElement ast = trees.derefTreeElement();
eachPsiRoot.updateTrees(ast == null ? trees.withExclusiveStub(stub, bindings.keySet()) : trees.withGreenStub(stub, eachPsiRoot));
StubTree result = null;
for (int i = 0; i < roots.size(); i++) {
PsiFileImpl eachPsiRoot = (PsiFileImpl)roots.get(i).second;
if (eachPsiRoot.derefStub() == null) {
StubTree stubTree = eachPsiRoot.setStubTree(stubRoots[i]);
if (eachPsiRoot == this) {
result = stubTree;
}
}
}
assert result != null : "Current file not in root list: " + roots + ", vp=" + viewProvider;
return result;
}
}
private boolean mayLoadExclusiveStub() {
return getTreeElement() == null && !hasUnbindableCachedPsi();
}
private static Map<PsiFileImpl, StubTree> prepareAllStubTrees(List<Pair<IStubFileElementType, PsiFile>> roots, PsiFileStub[] rootStubs) {
Map<PsiFileImpl, StubTree> bindings = ContainerUtil.newIdentityHashMap();
for (int i = 0; i < roots.size(); i++) {
PsiFileImpl eachPsiRoot = (PsiFileImpl)roots.get(i).second;
//noinspection unchecked
((StubBase)rootStubs[i]).setPsi(eachPsiRoot);
StubTree stubTree = new StubTree(rootStubs[i]);
FileElement fileElement = eachPsiRoot.getTreeElement();
stubTree.setDebugInfo("created in getStubTree(), with AST = " + (fileElement != null));
if (fileElement != null) {
// Set references from these stubs to AST, because:
// Stub index might call getStubTree on main PSI file, but then use getPlainListFromAllRoots and return stubs from another file.
// Even if that file already has AST, stub.getPsi() should be the same as in AST
TreeUtil.bindStubsToTree(stubTree, fileElement);
eachPsiRoot.myRefToPsi.clearStubIndexCache();
bindings.put(eachPsiRoot, stubTree);
} else if (eachPsiRoot.derefStub() == null && eachPsiRoot.mayLoadExclusiveStub()) {
eachPsiRoot.bindStubsToCachedPsi(stubTree);
bindings.put(eachPsiRoot, stubTree);
}
}
return bindings;
}
private boolean mayReloadStub() {
if (getTreeElement() != null || useStrongRefs()) {
return false;
}
StubTreeLoader loader = StubTreeLoader.getInstance();
if (loader != null && loader.isStubReloadingProhibited()) {
return false;
}
return !hasUnbindableCachedPsi();
}
private boolean hasUnbindableCachedPsi() {
synchronized (myPsiLock) {
return ContainerUtil.exists(myViewProvider.getAllFiles(),
file -> file instanceof PsiFileImpl && ((PsiFileImpl)file).myRefToPsi.hasUnbindableCachedPsi());
}
@NotNull
private StubTree setStubTree(PsiFileStub root) {
//noinspection unchecked
((StubBase)root).setPsi(this);
StubTree stubTree = new StubTree(root);
FileElement fileElement = getTreeElement();
stubTree.setDebugInfo("created in getStubTree(), with AST = " + (fileElement != null));
updateTrees(myTrees.withStub(stubTree, fileElement));
return stubTree;
}
@Nullable
@@ -738,23 +628,8 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
}
}
FileTrees getFileTrees() {
return myTrees;
}
private void bindStubsToCachedPsi(StubTree stubTree) {
myRefToPsi.getAllCachedPsi().forEach(psi -> {
int index = psi.getStubIndex();
if (index >= 0) {
//noinspection unchecked
((StubBase)stubTree.getPlainList().get(index)).setPsi(psi);
}
});
}
protected PsiFileImpl cloneImpl(FileElement treeElementClone) {
PsiFileImpl clone = (PsiFileImpl)super.clone();
clone.myRefToPsi = new AstPathPsiMap(getProject());
clone.setTreeElementPointer(treeElementClone); // should not use setTreeElement here because cloned file still have VirtualFile (SCR17963)
treeElementClone.setPsi(clone);
return clone;
@@ -791,14 +666,8 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
@NotNull
public final FileElement calcTreeElement() {
// Attempt to find (loaded) tree element without taking lock first.
FileElement treeElement = getTreeElement();
if (treeElement != null) return treeElement;
treeElement = myFileElementBeingLoaded.get();
if (treeElement != null) return treeElement;
return loadTreeElement();
return treeElement != null ? treeElement : loadTreeElement();
}
@Override
@@ -1026,7 +895,6 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
if (tree != null) {
return tree;
}
assert myFileElementBeingLoaded.get() == null : "non-empty thread-local";
FileElement fileElement = calcTreeElement();
synchronized (myPsiLock) {
tree = derefStub();
@@ -1052,23 +920,14 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
tree = new StubTree((PsiFileStub)currentStubTree);
tree.setDebugInfo("created in calcStubTree");
try {
TreeUtil.bindStubsToTree(tree, fileElement);
myRefToPsi.clearStubIndexCache();
}
catch (TreeUtil.StubBindingException e) {
rebuildStub();
throw new RuntimeException("Stub and PSI element type mismatch in " + getName(), e);
}
updateTrees(myTrees.withGreenStub(tree, this));
updateTrees(myTrees.withStub(tree, fileElement));
}
return tree;
}
}
private void rebuildStub() {
final void rebuildStub() {
ApplicationManager.getApplication().invokeLater(() -> {
if (!myManager.isDisposed()) {
myManager.dropPsiCaches();
@@ -1104,13 +963,10 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
public final void beforeAstChange() {
checkWritable();
if (!useStrongRefs()) {
synchronized (myPsiLock) {
for (PsiFile root : myViewProvider.getAllFiles()) {
if (root instanceof PsiFileImpl) {
((PsiFileImpl)root).switchToStrongRefs();
}
}
synchronized (myPsiLock) {
FileTrees updated = myTrees.switchToStrongRefs();
if (updated != myTrees) {
updateTrees(updated);
}
}
}
@@ -1123,42 +979,4 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
CheckUtil.checkWritable(this);
}
}
private void switchToStrongRefs() {
FileElement node = calcTreeElement();
updateTrees(myTrees.switchToStrongRefs());
myRefToPsi.switchToStrongRefs();
AstPath.invalidatePaths(node);
}
@Nullable
public StubBasedPsiElementBase<?> obtainPsi(@NotNull AstPath path, @NotNull Factory<StubBasedPsiElementBase<?>> creator) {
if (useStrongRefs()) {
return null;
}
StubBasedPsiElementBase<?> psi = myRefToPsi.getCachedPsi(path);
if (psi != null) return psi;
synchronized (myPsiLock) {
if (useStrongRefs()) {
return null;
}
psi = myRefToPsi.getCachedPsi(path);
return psi != null ? psi : myRefToPsi.cachePsi(path, creator.create());
}
}
final AstPathPsiMap getRefToPsi() {
return myRefToPsi;
}
public final boolean useStrongRefs() {
return myTrees.useStrongRefs;
}
public boolean mayCacheAst() {
return myFileElementBeingLoaded.get() == null;
}
}
@@ -0,0 +1,53 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.psi.impl.source;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiFile;
import com.intellij.psi.stubs.Stub;
import com.intellij.psi.stubs.StubTree;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author peter
*/
class SpineRef extends SubstrateRef {
private final PsiFileImpl myFile;
private final int myIndex;
SpineRef(@NotNull PsiFileImpl file, int index) {
myFile = file;
myIndex = index;
}
@NotNull
@Override
public ASTNode getNode() {
return myFile.calcTreeElement().getStubbedSpine().getSpineNodes().get(myIndex);
}
@Nullable
@Override
public Stub getStub() {
StubTree tree = myFile.getStubTree();
return tree == null ? null : tree.getPlainList().get(myIndex);
}
@Nullable
@Override
public Stub getGreenStub() {
StubTree tree = myFile.getGreenStubTree();
return tree == null ? null : tree.getPlainList().get(myIndex);
}
@Override
public boolean isValid() {
return myFile.isValid();
}
@NotNull
@Override
public PsiFile getContainingFile() {
return myFile;
}
}
@@ -42,13 +42,13 @@ public abstract class SubstrateRef {
public abstract ASTNode getNode();
@Nullable
public Stub getStub(int stubIndex) {
public Stub getStub() {
return null;
}
@Nullable
public Stub getGreenStub(int index) {
return getStub(index);
public Stub getGreenStub() {
return getStub();
}
public abstract boolean isValid();
@@ -119,7 +119,7 @@ public abstract class SubstrateRef {
@NotNull
@Override
public Stub getStub(int stubIndex) {
public Stub getStub() {
return myStub;
}
@@ -1,311 +0,0 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.intellij.psi.impl.source.tree;
import com.intellij.extapi.psi.StubBasedPsiElementBase;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.VirtualFileWithId;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.PsiFileImpl;
import com.intellij.psi.impl.source.SubstrateRef;
import com.intellij.psi.stubs.IStubElementType;
import com.intellij.psi.stubs.Stub;
import com.intellij.psi.stubs.StubTree;
import com.intellij.reference.SoftReference;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.WeakReference;
import java.util.List;
/**
* A lightweight object representing a chain of node indices (among all lazy-parseable and stub-based elements)
* allowing to restore a specific node after it's been garbage-collected and recreated.
*
* @author peter
*/
public abstract class AstPath extends SubstrateRef {
@SuppressWarnings("ConstantForZeroLengthArrayAllocation") // need a different instance to distinguish removed children for diagnostics
private static final CompositeElement[] REMOVED_PATH_CHILDREN = new CompositeElement[0];
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.AstPath");
private static final Key<CompositeElement[]> PATH_CHILDREN = Key.create("PATH_CHILDREN");
private static final Key<AstPath> NODE_PATH = Key.create("NODE_PATH");
@NotNull
public abstract PsiFileImpl getContainingFile();
@NotNull
public abstract CompositeElement getNode();
@Override
public boolean isValid() {
return getContainingFile().isValid();
}
protected abstract int getDepth();
@Nullable
@Override
public Stub getStub(int stubIndex) {
if (stubIndex < 0) return null;
StubTree stubTree = getFileStubTree();
return stubTree == null ? null : stubTree.getPlainList().get(stubIndex);
}
@Nullable
protected abstract StubTree getFileStubTree();
@Nullable
@Override
public Stub getGreenStub(int stubIndex) {
if (stubIndex < 0) return null;
StubTree stubTree = getContainingFile().getGreenStubTree();
return stubTree == null ? null : stubTree.getPlainList().get(stubIndex);
}
@Nullable
public static AstPath getNodePath(@NotNull CompositeElement node) {
if (node instanceof FileElement) {
PsiElement psi = node.getCachedPsi();
if (!(psi instanceof PsiFileImpl)) return null;
PsiFileImpl file = (PsiFileImpl)psi;
if (!(file.getVirtualFile() instanceof VirtualFileWithId) || file.getElementTypeForStubBuilder() == null) {
return null;
}
return new RootPath(file);
}
return node.getUserData(NODE_PATH);
}
static void cacheNodePaths(@NotNull LazyParseableElement parent, @Nullable TreeElement child, @Nullable AstPath parentPath) {
if (parentPath == null) {
return;
}
final int depth = parentPath.getDepth() + 1;
final List<CompositeElement> children = ContainerUtil.newArrayList();
while (child != null) {
child.acceptTree(new RecursiveTreeElementWalkingVisitor(false) {
@Override
public void visitComposite(CompositeElement composite) {
boolean lazy = composite instanceof LazyParseableElement;
if (lazy || composite.getElementType() instanceof IStubElementType) {
int index = children.size();
composite.putUserData(NODE_PATH, depth % 4 == 0 ? new MilestoneChildPath(parentPath, index, depth) : new ChildPath(parentPath, index));
children.add(composite);
}
if (!lazy) {
super.visitComposite(composite);
}
}
});
child = child.getTreeNext();
}
parent.putUserData(PATH_CHILDREN, children.isEmpty() ? CompositeElement.EMPTY_ARRAY : children.toArray(CompositeElement.EMPTY_ARRAY));
for (CompositeElement each : children) {
if (each instanceof LazyParseableElement && ((LazyParseableElement)each).isParsed()) {
cacheNodePaths((LazyParseableElement)each, each.getFirstChildNode(), getNodePath(each));
}
}
}
public static void invalidatePaths(@NotNull LazyParseableElement scope) {
CompositeElement[] children = scope.getUserData(PATH_CHILDREN);
if (children == null) return;
scope.putUserData(PATH_CHILDREN, REMOVED_PATH_CHILDREN);
for (CompositeElement child : children) {
child.putUserData(NODE_PATH, null);
assertConsistency(child.getCachedPsi());
if (child instanceof LazyParseableElement) {
invalidatePaths((LazyParseableElement)child);
}
}
}
private static void assertConsistency(PsiElement cachedPsi) {
if (cachedPsi instanceof StubBasedPsiElementBase &&
((StubBasedPsiElementBase)cachedPsi).getSubstrateRef() instanceof AstPath) {
LOG.error("Expected strong reference at " + cachedPsi +
" of " + cachedPsi.getClass() +
" and " + ((StubBasedPsiElementBase)cachedPsi).getElementType());
}
}
private static class ChildPath extends AstPath {
private final AstPath myParent;
private final int myIndex;
ChildPath(@NotNull AstPath parent, int index) {
myParent = parent;
myIndex = index;
}
@NotNull
@Override
public PsiFileImpl getContainingFile() {
return myParent.getContainingFile();
}
@NotNull
@Override
public CompositeElement getNode() {
CompositeElement parentNode = myParent.getNode();
//noinspection ResultOfMethodCallIgnored
parentNode.getFirstChildNode(); // expand chameleons, populate PATH_CHILDREN array
CompositeElement[] children = parentNode.getUserData(PATH_CHILDREN);
boolean removed = children == REMOVED_PATH_CHILDREN;
if (children == null || removed) {
throw reportMissingChildren(parentNode, removed);
}
if (myIndex >= children.length) {
throw new AssertionError(myIndex + " >= " + children.length + "; " + parentNode + " of " + parentNode.getClass());
}
return children[myIndex];
}
private AssertionError reportMissingChildren(CompositeElement parentNode, boolean removed) {
String message = "No path children in " + parentNode + " of " + parentNode.getClass() + "; removed=" + removed;
PsiFileImpl file = getContainingFile();
message += "\n file: " + file + " of " + file.getClass() + "; physical=" + file.isPhysical() + "; useStrongRefs=" + file.useStrongRefs();
FileElement fileElement = file.getTreeElement();
message += "\n ast=" + fileElement;
if (fileElement != null) {
CompositeElement[] rootChildren = fileElement.getUserData(PATH_CHILDREN);
message += "; root.children=" + (rootChildren == REMOVED_PATH_CHILDREN ? "removed" : rootChildren == null ? "null" : "nonNull");
}
return new AssertionError(message);
}
@Override
protected int getDepth() {
return 1 + myParent.getDepth();
}
@Override
protected StubTree getFileStubTree() {
return myParent.getFileStubTree();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ChildPath)) return false;
ChildPath path = (ChildPath)o;
return myIndex == path.myIndex && myParent.equals(path.myParent);
}
@Override
public int hashCode() {
return 31 * myParent.hashCode() + myIndex;
}
}
private static class MilestoneChildPath extends ChildPath {
private final int myDepth;
private final PsiFileImpl myFile;
private volatile WeakReference<CompositeElement> myNode;
MilestoneChildPath(@NotNull AstPath parent, int index, int depth) {
super(parent, index);
myDepth = depth;
myFile = parent.getContainingFile();
}
@NotNull
@Override
public CompositeElement getNode() {
CompositeElement node = SoftReference.dereference(myNode);
if (node == null) {
node = super.getNode();
if (myFile.mayCacheAst()) {
myNode = new WeakReference<>(node);
}
}
return node;
}
@Override
protected StubTree getFileStubTree() {
return SoftReference.dereference(myNode) == null ? myFile.getStubTree() : null;
}
@NotNull
@Override
public PsiFileImpl getContainingFile() {
return myFile;
}
@Override
protected int getDepth() {
return myDepth;
}
}
private static class RootPath extends AstPath {
private final PsiFileImpl myFile;
RootPath(@NotNull PsiFileImpl file) {
myFile = file;
}
@Override
public boolean equals(Object o) {
return this == o || o instanceof RootPath && myFile.equals(((RootPath)o).myFile);
}
@Override
public int hashCode() {
return myFile.hashCode();
}
@NotNull
@Override
public PsiFileImpl getContainingFile() {
return myFile;
}
@NotNull
@Override
public CompositeElement getNode() {
return myFile.calcTreeElement();
}
@Override
protected int getDepth() {
return 0;
}
@Override
protected StubTree getFileStubTree() {
return myFile.getStubTree();
}
}
}
@@ -57,7 +57,8 @@ public class AstSpine implements StubbedSpine {
return index >= myNodes.size() ? null : myNodes.get(index).getElementType();
}
List<CompositeElement> getNodes() {
@NotNull
public List<CompositeElement> getSpineNodes() {
return myNodes;
}
}
@@ -18,7 +18,6 @@ package com.intellij.psi.impl.source.tree;
import com.intellij.diagnostic.ThreadDumper;
import com.intellij.extapi.psi.ASTDelegatePsiElement;
import com.intellij.extapi.psi.StubBasedPsiElementBase;
import com.intellij.lang.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
@@ -29,9 +28,10 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.DebugUtil;
import com.intellij.psi.impl.FreeThreadedFileViewProvider;
import com.intellij.psi.impl.source.*;
import com.intellij.psi.impl.source.DummyHolder;
import com.intellij.psi.impl.source.DummyHolderFactory;
import com.intellij.psi.impl.source.SourceTreeToPsiMap;
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
import com.intellij.psi.stubs.IStubElementType;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.util.PsiUtilCore;
@@ -723,21 +723,10 @@ public class CompositeElement extends TreeElement {
PsiElement wrapper = myWrapper;
if (wrapper != null) return wrapper;
wrapper = obtainStubBasedPsi();
if (wrapper == null) wrapper = createPsiNoLock();
wrapper = createPsiNoLock();
return ourPsiUpdater.compareAndSet(this, null, wrapper) ? wrapper : ObjectUtils.assertNotNull(myWrapper);
}
/**
* If AST has been gced and recreated, but someone still holds a reference to a PSI, then {@link #getPsi()} should return the very same PSI object.
* So we try to find that PSI in file's {@link AstPathPsiMap}.
*/
@Nullable
private PsiElement obtainStubBasedPsi() {
AstPath path = getElementType() instanceof IStubElementType ? AstPath.getNodePath(this) : null;
return path == null ? null : path.getContainingFile().obtainPsi(path, () -> (StubBasedPsiElementBase<?>)createPsiNoLock());
}
@Override
public <T extends PsiElement> T getPsi(@NotNull Class<T> clazz) {
return LeafElement.getPsi(clazz, getPsi(), LOG);
@@ -755,6 +744,10 @@ public class CompositeElement extends TreeElement {
}
public void setPsi(@NotNull PsiElement psi) {
PsiElement prev = myWrapper;
if (prev != null && prev != psi) {
DebugUtil.onInvalidated(prev);
}
myWrapper = psi;
}
@@ -769,13 +762,6 @@ public class CompositeElement extends TreeElement {
}
public void rawAddChildrenWithoutNotifications(@NotNull TreeElement first) {
if (DebugUtil.DO_EXPENSIVE_CHECKS && !(this instanceof LazyParseableElement)) {
PsiFileImpl file = getCachedFile(this);
if (file != null && !file.useStrongRefs()) {
throw new AssertionError("Attempt to modify PSI in a file with weakly-referenced AST. Possible cause: missing PomTransaction.");
}
}
final TreeElement last = getLastChildNode();
if (last == null){
TreeElement chainLast = rawSetParents(first, this);
@@ -18,6 +18,7 @@ package com.intellij.psi.impl.source.tree;
import com.intellij.lang.*;
import com.intellij.openapi.util.Getter;
import com.intellij.openapi.util.RecursionManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.StubBuilder;
import com.intellij.psi.impl.PsiManagerEx;
@@ -27,12 +28,9 @@ import com.intellij.psi.stubs.IStubElementType;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.ILightStubFileElementType;
import com.intellij.psi.tree.IStubFileElementType;
import com.intellij.reference.SoftReference;
import com.intellij.util.CharTable;
import org.jetbrains.annotations.NotNull;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -40,7 +38,7 @@ public class FileElement extends LazyParseableElement implements FileASTNode, Ge
public static final FileElement[] EMPTY_ARRAY = new FileElement[0];
private volatile CharTable myCharTable = new CharTableImpl();
private volatile boolean myDetached;
private volatile Reference<AstSpine> myStubbedSpine;
private volatile AstSpine myStubbedSpine;
@Override
protected PsiElement createPsiNoLock() {
@@ -108,13 +106,17 @@ public class FileElement extends LazyParseableElement implements FileASTNode, Ge
@NotNull
public final AstSpine getStubbedSpine() {
AstSpine result = SoftReference.dereference(myStubbedSpine);
AstSpine result = myStubbedSpine;
if (result == null) {
IStubFileElementType type = ((PsiFileImpl)getPsi()).getElementTypeForStubBuilder();
PsiFileImpl file = (PsiFileImpl)getPsi();
IStubFileElementType type = file.getElementTypeForStubBuilder();
if (type == null) return AstSpine.EMPTY_SPINE;
result = new AstSpine(calcStubbedDescendants(type.getBuilder()));
myStubbedSpine = getManager().isBatchFilesProcessingMode() ? new WeakReference<>(result) : new SoftReference<>(result);
result = RecursionManager.doPreventingRecursion(file, false, () -> new AstSpine(calcStubbedDescendants(type.getBuilder())));
if (result == null) {
throw new StackOverflowError("Endless recursion prevented");
}
myStubbedSpine = result;
}
return result;
}
@@ -19,7 +19,6 @@
*/
package com.intellij.psi.impl.source.tree;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Attachment;
import com.intellij.openapi.diagnostic.LogUtil;
import com.intellij.openapi.diagnostic.Logger;
@@ -200,7 +199,7 @@ public class LazyParseableElement extends CompositeElement {
}
if (parsedNode != null) {
setChildren(parsedNode, AstPath.getNodePath(this));
setChildren(parsedNode);
}
myParsed = true;
@@ -220,10 +219,9 @@ public class LazyParseableElement extends CompositeElement {
}
}
private void setChildren(@NotNull TreeElement parsedNode, @Nullable AstPath thisPath) {
private void setChildren(@NotNull TreeElement parsedNode) {
ProgressManager.getInstance().executeNonCancelableSection(() -> {
try {
AstPath.cacheNodePaths(this, parsedNode, thisPath);
TreeElement last = rawSetParents(parsedNode, this);
super.setFirstChildNode(parsedNode);
super.setLastChildNode(last);
@@ -16,21 +16,14 @@
package com.intellij.psi.impl.source.tree;
import com.intellij.extapi.psi.StubBasedPsiElementBase;
import com.intellij.lang.ASTNode;
import com.intellij.lexer.Lexer;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.impl.DebugUtil;
import com.intellij.psi.stubs.StubBase;
import com.intellij.psi.stubs.StubElement;
import com.intellij.psi.stubs.StubTree;
import com.intellij.psi.templateLanguages.OuterLanguageElement;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.IStrongWhitespaceHolderElementType;
@@ -38,7 +31,9 @@ import com.intellij.psi.tree.TokenSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
public class TreeUtil {
@@ -441,45 +436,6 @@ public class TreeUtil {
boolean isStrongElementOnRisingSlope = true;
}
public static class StubBindingException extends RuntimeException {
StubBindingException(String message) {
super(message);
}
}
public static void bindStubsToTree(@NotNull StubTree stubTree, @NotNull FileElement tree) throws StubBindingException {
List<Pair<StubBase, TreeElement>> bindings = calcStubAstBindings(stubTree, tree);
for (int i = 0; i < bindings.size(); i++) {
Pair<StubBase, TreeElement> pair = bindings.get(i);
StubBasedPsiElementBase psi = (StubBasedPsiElementBase)pair.second.getPsi();
//noinspection unchecked
pair.first.setPsi(psi);
psi.setStubIndex(i + 1);
}
}
@NotNull
public static List<Pair<StubBase, TreeElement>> calcStubAstBindings(@NotNull StubTree stubTree, @NotNull FileElement tree) throws StubBindingException {
PsiFile file = (PsiFile)tree.getPsi();
List<CompositeElement> nodes = tree.getStubbedSpine().getNodes();
List<StubElement<?>> stubs = stubTree.getPlainList();
if (stubs.size() != nodes.size()) {
throw new StubBindingException("Stub list in " + file.getName() + " length differs from PSI");
}
List<Pair<StubBase, TreeElement>> bindings = new ArrayList<>();
for (int i = 1; i < stubs.size(); i++) { // start from 1 to skip file root stub
StubBase<?> stub = (StubBase<?>)stubs.get(i);
CompositeElement node = nodes.get(i);
if (stub.getStubType() != node.getElementType()) {
throw new StubBindingException("stub:" + stub + ", AST:" + node.getElementType());
}
bindings.add(Pair.create(stub, node));
}
return bindings;
}
@Nullable
public static ASTNode skipWhitespaceAndComments(final ASTNode node, boolean forward) {
return skipWhitespaceCommentsAndTokens(node, TokenSet.EMPTY, forward);
@@ -51,10 +51,6 @@ public abstract class StubTreeLoader {
@Nullable
public abstract ObjectStubTree readFromVFile(Project project, final VirtualFile vFile);
public boolean isStubReloadingProhibited() {
return false;
}
public abstract void rebuildStubTree(VirtualFile virtualFile);
public abstract boolean canHaveStub(VirtualFile file);
@@ -97,7 +97,10 @@ public abstract class LineStatusMarkerPopupRenderer extends LineStatusMarkerRend
}
@Nullable
protected JComponent createAdditionalInfoPanel(@NotNull Editor editor, @NotNull Range range, @Nullable Point mousePosition) {
protected JComponent createAdditionalInfoPanel(@NotNull Editor editor,
@NotNull Range range,
@Nullable Point mousePosition,
@NotNull Disposable disposable) {
return null;
}
@@ -142,7 +145,7 @@ public abstract class LineStatusMarkerPopupRenderer extends LineStatusMarkerRend
toolbar.updateActionsImmediately(); // we need valid ActionToolbar.getPreferredSize() to calc size of popup
toolbar.setReservePlaceAutoPopupIcon(false);
JComponent additionalInfoPanel = createAdditionalInfoPanel(editor, range, mousePosition);
JComponent additionalInfoPanel = createAdditionalInfoPanel(editor, range, mousePosition, disposable);
PopupPanel popupPanel = new PopupPanel(editor, toolbar, editorComponent, additionalInfoPanel);
@@ -53,7 +53,7 @@ public interface TreeStructureProvider {
* @see com.intellij.openapi.actionSystem.DataProvider
*/
@Nullable
default Object getData(Collection<AbstractTreeNode> selected, String dataName) {
default Object getData(@NotNull Collection<AbstractTreeNode> selected, String dataName) {
return null;
}
}
@@ -64,8 +64,11 @@ public class TodoConfiguration implements PersistentStateComponent<Element> {
buildIndexPatterns();
}
/**
* Returns the list of default TO_DO patterns. Can be customized in other IDEs (and is customized in Rider).
*/
@NotNull
private static TodoPattern[] getDefaultPatterns() {
protected TodoPattern[] getDefaultPatterns() {
//noinspection SpellCheckingInspection
return new TodoPattern[]{
new TodoPattern("\\btodo\\b.*", TodoAttributesUtil.createDefault(), false),
@@ -229,7 +229,12 @@ public class ExecutionUtil {
return getLiveIndicator(base, 13, 13);
}
@SuppressWarnings("UseJBColor")
public static Icon getLiveIndicator(@Nullable final Icon base, int emptyIconWidth, int emptyIconHeight) {
return getIndicator(base, emptyIconWidth, emptyIconHeight, Color.GREEN);
}
public static Icon getIndicator(@Nullable final Icon base, int emptyIconWidth, int emptyIconHeight, Color color) {
return new LayeredIcon(base, new Icon() {
@SuppressWarnings("UseJBColor")
@Override
@@ -238,7 +243,7 @@ public class ExecutionUtil {
Graphics2D g2d = (Graphics2D)g.create();
try {
GraphicsUtil.setupAAPainting(g2d);
g2d.setColor(Color.GREEN);
g2d.setColor(color);
Ellipse2D.Double shape =
new Ellipse2D.Double(x + getIconWidth() - iSize, y + getIconHeight() - iSize, iSize, iSize);
g2d.fill(shape);
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.application.options.editor.EditorSmartKeysConfigurable">
<grid id="27dc6" binding="myRootPanel" layout-manager="GridLayoutManager" row-count="13" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="0">
<grid id="27dc6" binding="myRootPanel" layout-manager="GridLayoutManager" row-count="14" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="0">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="437"/>
@@ -10,7 +10,7 @@
<children>
<vspacer id="a9cd">
<constraints>
<grid row="12" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
<grid row="13" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<component id="18e81" class="javax.swing.JCheckBox" binding="myCbSmartHome">
@@ -60,7 +60,7 @@
<grid id="6ebf" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="5" right="5"/>
<constraints>
<grid row="10" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="1" fill="3" indent="0" use-parent-layout="false"/>
<grid row="11" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="1" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
@@ -88,7 +88,7 @@
</grid>
<grid id="b08a8" binding="myAddonPanel" layout-manager="GridBagLayout">
<constraints>
<grid row="11" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="8" fill="2" indent="0" use-parent-layout="false"/>
<grid row="12" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="8" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
@@ -114,7 +114,7 @@
<grid id="e1eb4" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="9" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
<grid row="10" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<clientProperties>
@@ -146,7 +146,7 @@
<grid id="5344e" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="8" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
<grid row="9" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<clientProperties>
@@ -184,7 +184,7 @@
</grid>
<hspacer id="6cc57">
<constraints>
<grid row="8" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
<grid row="9" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
</hspacer>
<component id="892b" class="javax.swing.JCheckBox" binding="myCbEnableAddingCaretsOnDoubleCtrlArrows">
@@ -196,6 +196,15 @@
<text resource-bundle="messages/ApplicationBundle" key="checkbox.enable.double.ctrl"/>
</properties>
</component>
<component id="db94d" class="javax.swing.JCheckBox" binding="myCbTabExistsBracketsAndQuotes">
<constraints>
<grid row="8" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<selected value="false"/>
<text resource-bundle="messages/ApplicationBundle" key="checkbox.tab.exists.brackets.and.quotes"/>
</properties>
</component>
</children>
</grid>
</form>
@@ -1,18 +1,4 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.application.options.editor;
@@ -71,6 +57,7 @@ public class EditorSmartKeysConfigurable extends CompositeConfigurable<UnnamedCo
private JCheckBox myCbReformatBlockOnTypingRBrace;
private JComboBox mySmartBackspaceCombo;
private JCheckBox myCbEnableAddingCaretsOnDoubleCtrlArrows;
private JCheckBox myCbTabExistsBracketsAndQuotes;
private boolean myAddonsInitialized = false;
private static final String NO_REFORMAT = ApplicationBundle.message("combobox.paste.reformat.none");
@@ -180,7 +167,7 @@ public class EditorSmartKeysConfigurable extends CompositeConfigurable<UnnamedCo
myCbCamelWords.setSelected(editorSettings.isCamelWords());
myCbSurroundSelectionOnTyping.setSelected(codeInsightSettings.SURROUND_SELECTION_ON_QUOTE_TYPED);
myCbTabExistsBracketsAndQuotes.setSelected(codeInsightSettings.TAB_EXITS_BRACKETS_AND_QUOTES);
myCbEnableAddingCaretsOnDoubleCtrlArrows.setSelected(editorSettings.addCaretsOnDoubleCtrl());
SmartBackspaceMode backspaceMode = codeInsightSettings.getBackspaceMode();
@@ -215,6 +202,7 @@ public class EditorSmartKeysConfigurable extends CompositeConfigurable<UnnamedCo
codeInsightSettings.AUTOINSERT_PAIR_QUOTE = myCbInsertPairQuote.isSelected();
codeInsightSettings.REFORMAT_BLOCK_ON_RBRACE = myCbReformatBlockOnTypingRBrace.isSelected();
codeInsightSettings.SURROUND_SELECTION_ON_QUOTE_TYPED = myCbSurroundSelectionOnTyping.isSelected();
codeInsightSettings.TAB_EXITS_BRACKETS_AND_QUOTES = myCbTabExistsBracketsAndQuotes.isSelected();
editorSettings.setCamelWords(myCbCamelWords.isSelected());
codeInsightSettings.REFORMAT_ON_PASTE = getReformatPastedBlockValue();
codeInsightSettings.setBackspaceMode(getSmartBackspaceModeValue());
@@ -248,6 +236,8 @@ public class EditorSmartKeysConfigurable extends CompositeConfigurable<UnnamedCo
isModified |= (getSmartBackspaceModeValue() != codeInsightSettings.getBackspaceMode());
isModified |= isModified(myCbTabExistsBracketsAndQuotes, codeInsightSettings.TAB_EXITS_BRACKETS_AND_QUOTES);
return isModified;
}
@@ -149,6 +149,8 @@ public class CodeInsightSettings implements PersistentStateComponent<Element>, C
public boolean ADD_MEMBER_IMPORTS_ON_THE_FLY = true;
public boolean JSP_ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY;
public boolean TAB_EXITS_BRACKETS_AND_QUOTES;
/**
* Names of classes and packages excluded from (Java) auto-import and completion. These are only IDE-specific settings
* and don't take project-specific settings into account.
@@ -479,7 +479,7 @@ class PassExecutorService implements Disposable {
@NotNull final AtomicInteger threadsToStartCountdown,
@NotNull Runnable callbackOnApplied) {
ApplicationManager.getApplication().invokeLater((DumbAwareRunnable)() -> {
if (isDisposed() || myProject.isDisposed()) {
if (isDisposed() || myProject.isDisposed() || !fileEditor.isValid()) {
updateProgress.cancel();
}
if (updateProgress.isCanceled()) {
@@ -0,0 +1,71 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight.editorActions;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.highlighting.BraceMatcher;
import com.intellij.codeInsight.highlighting.BraceMatchingUtil;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Caret;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.actionSystem.EditorAction;
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiUtilBase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class BraceOrQuoteOutAction extends EditorAction {
public BraceOrQuoteOutAction() {
super(new Handler());
}
private static class Handler extends EditorActionHandler {
private Handler() {
super(true);
}
@Override
protected boolean isEnabledForCaret(@NotNull Editor editor, @NotNull Caret caret, DataContext dataContext) {
return getCaretShift(editor, caret) != 0;
}
@Override
protected void doExecute(@NotNull Editor editor, @Nullable Caret caret, DataContext dataContext) {
assert caret != null;
int caretShift = getCaretShift(editor, caret);
if (caretShift != 0) {
caret.moveToOffset(caret.getOffset() + caretShift);
}
}
private static int getCaretShift(@NotNull Editor editor, @NotNull Caret caret) {
if (!CodeInsightSettings.getInstance().TAB_EXITS_BRACKETS_AND_QUOTES || !(editor instanceof EditorEx)) return 0;
Project project = editor.getProject();
if (project == null) return 0;
PsiFile psiFile = PsiUtilBase.getPsiFileInEditor(editor, project);
if (psiFile == null) return 0;
FileType fileType = TypedHandler.getFileType(psiFile, editor);
int caretOffset = caret.getOffset();
HighlighterIterator iterator = ((EditorEx)editor).getHighlighter().createIterator(caretOffset);
BraceMatcher braceMatcher = BraceMatchingUtil.getBraceMatcher(fileType, iterator);
if (caretOffset == iterator.getStart() &&
braceMatcher.isRBraceToken(iterator, editor.getDocument().getImmutableCharSequence(), fileType)) {
return iterator.getEnd() - caretOffset;
}
else {
QuoteHandler quoteHandler = TypedHandler.getQuoteHandler(psiFile, editor);
if (quoteHandler != null && quoteHandler.isClosingQuote(iterator, caretOffset)) {
return 1;
}
}
return 0;
}
}
}
@@ -0,0 +1,24 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight.editorActions;
import com.intellij.openapi.actionSystem.ActionPromoter;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.actions.TabAction;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class TabActionsPromoter implements ActionPromoter {
@Override
public List<AnAction> promote(List<AnAction> actions, DataContext context) {
List<AnAction> newList = new ArrayList<>(actions);
newList.sort(Comparator.comparingInt(action -> {
if (action instanceof BraceOrQuoteOutAction) return 0;
else if (action instanceof TabAction) return 1;
else return -1;
}));
return newList;
}
}
@@ -1,18 +1,4 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight.editorActions;
@@ -101,7 +87,7 @@ public class TypedHandler extends TypedActionHandlerBase {
}
@NotNull
private static FileType getFileType(@NotNull PsiFile file, @NotNull Editor editor) {
static FileType getFileType(@NotNull PsiFile file, @NotNull Editor editor) {
FileType fileType = file.getFileType();
Language language = PsiUtilBase.getLanguageInEditor(editor, file.getProject());
if (language != null && language != PlainTextLanguage.INSTANCE) {
@@ -27,8 +27,6 @@ import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.psi.impl.source.tree.injected.InjectedCaret;
import com.intellij.psi.util.PsiUtilBase;
@@ -46,14 +44,12 @@ import java.util.List;
import java.util.Map;
public class CommentByLineCommentHandler extends MultiCaretCodeInsightActionHandler {
private Project myProject;
private final List<Block> myBlocks = new ArrayList<>();
@Override
// first pass - adjacent carets are grouped into blocks
public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull Caret caret, @NotNull PsiFile file) {
myProject = project;
file = file.getViewProvider().getPsi(file.getViewProvider().getBaseLanguage());
PsiElement context = InjectedLanguageManager.getInstance(file.getProject()).getInjectionHost(file);
@@ -135,8 +131,6 @@ public class CommentByLineCommentHandler extends MultiCaretCodeInsightActionHand
public void postInvoke() {
FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.comment.line");
CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getSettings(myProject);
// second pass - determining whether we need to comment or to uncomment
boolean allLinesCommented = true;
for (Block block : myBlocks) {
@@ -158,7 +152,7 @@ public class CommentByLineCommentHandler extends MultiCaretCodeInsightActionHand
block.blockSuitableCommenter = getBlockSuitableCommenter(psiFile, offset, endOffset);
Language lineStartLanguage = getLineStartLanguage(block.editor, psiFile, startLine);
CommonCodeStyleSettings languageSettings = codeStyleSettings.getCommonSettings(lineStartLanguage);
CommonCodeStyleSettings languageSettings = CodeStyle.getLanguageSettings(psiFile, lineStartLanguage);
block.commentWithIndent = !languageSettings.LINE_COMMENT_AT_FIRST_COLUMN;
block.addSpace = languageSettings.LINE_COMMENT_ADD_SPACE;
@@ -52,6 +52,10 @@ public class SilentCodeCleanupAction extends AnAction {
runInspections(project, analysisScope);
}
@SuppressWarnings("WeakerAccess")
@Nullable
protected Runnable getPostRunnable() { return null; }
@SuppressWarnings("WeakerAccess")
protected void runInspections(@NotNull Project project, @NotNull AnalysisScope scope) {
InspectionProfile profile = getProfileForSilentCleanup(project);
@@ -60,7 +64,7 @@ public class SilentCodeCleanupAction extends AnAction {
}
InspectionManager managerEx = InspectionManager.getInstance(project);
GlobalInspectionContextBase globalContext = (GlobalInspectionContextBase) managerEx.createNewGlobalContext(false);
globalContext.codeCleanup(scope, profile, getTemplatePresentation().getText(), null, false);
globalContext.codeCleanup(scope, profile, getTemplatePresentation().getText(), getPostRunnable(), false);
}
@SuppressWarnings("WeakerAccess")
@@ -672,7 +672,7 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA
UIManager.getColor("SearchEverywhere.shortcutForeground") : foregroundColor;
StringBuilder cbText = new StringBuilder("<html>");
cbText.append(IdeBundle.message("checkbox.include.non.project.items"));
cbText.append(ProjectUICustomization.replaceProjectConceptName(IdeBundle.message("checkbox.include.non.project.items")));
cbText.append(" ");
if (!UIUtil.isUnderWin10LookAndFeel()) cbText.append("<b>");
cbText.append("<font color=#").append(ColorUtil.toHex(shortcutColor)).append(">").append(getShortcut()).append("</font>");
@@ -15,6 +15,7 @@
*/
package com.intellij.ide.fileTemplates;
import com.intellij.application.options.CodeStyle;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.fileTemplates.impl.CustomFileTemplate;
import com.intellij.openapi.application.ApplicationManager;
@@ -27,7 +28,6 @@ import com.intellij.openapi.fileTypes.FileTypes;
import com.intellij.openapi.fileTypes.ex.FileTypeManagerEx;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.project.ex.ProjectManagerEx;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.ClassLoaderUtil;
import com.intellij.openapi.util.ThrowableComputable;
@@ -240,7 +240,7 @@ public class FileTemplateUtil {
final String result = stringWriter.toString();
if (useSystemLineSeparators) {
final String newSeparator = CodeStyleSettingsManager.getSettings(ProjectManagerEx.getInstanceEx().getDefaultProject()).getLineSeparator();
final String newSeparator = CodeStyle.getDefaultSettings().getLineSeparator();
if (!"\n".equals(newSeparator)) {
return StringUtil.convertLineSeparators(result, newSeparator);
}
@@ -363,7 +363,7 @@ public class FileTemplateUtil {
}
public static String indent(String methodText, Project project, FileType fileType) {
int indent = CodeStyleSettingsManager.getSettings(project).getIndentSize(fileType);
int indent = CodeStyle.getSettings(project).getIndentSize(fileType);
return methodText.replaceAll("\n", "\n" + StringUtil.repeatSymbol(' ', indent));
}
@@ -297,6 +297,12 @@ public abstract class AbstractProjectViewPane implements DataProvider, Disposabl
@Override
public Object getData(String dataId) {
Object data =
myTreeStructure instanceof AbstractTreeStructureBase ?
((AbstractTreeStructureBase)myTreeStructure).getDataFromProviders(getSelectedNodes(AbstractTreeNode.class), dataId) : null;
if (data != null) {
return data;
}
if (CommonDataKeys.NAVIGATABLE_ARRAY.is(dataId)) {
TreePath[] paths = getSelectionPaths();
if (paths == null) return null;
@@ -318,9 +324,6 @@ public abstract class AbstractProjectViewPane implements DataProvider, Disposabl
return navigatables.toArray(new Navigatable[0]);
}
}
if (myTreeStructure instanceof AbstractTreeStructureBase) {
return ((AbstractTreeStructureBase) myTreeStructure).getDataFromProviders(getSelectedNodes(AbstractTreeNode.class), dataId);
}
return null;
}
@@ -73,8 +73,8 @@ public final class CompoundTreeStructureProvider implements TreeStructureProvide
@Nullable
@Override
public Object getData(Collection<AbstractTreeNode> selection, String id) {
if (id != null && selection != null && !selection.isEmpty()) {
public Object getData(@NotNull Collection<AbstractTreeNode> selection, String id) {
if (id != null && !selection.isEmpty()) {
for (TreeStructureProvider provider : providers) {
try {
Object data = provider.getData(selection, id);
@@ -27,6 +27,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.ui.ProjectUICustomization;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.indexing.FileBasedIndex;
@@ -68,12 +69,12 @@ public class GotoClassModel2 extends FilteringGotoByModel<Language> {
@Override
public String getCheckBoxName() {
return IdeBundle.message("checkbox.include.non.project.classes");
return ProjectUICustomization.replaceProjectConceptName(IdeBundle.message("checkbox.include.non.project.classes"));
}
@Override
public String getNotInMessage() {
return IdeBundle.message("label.no.matches.found.in.project");
return ProjectUICustomization.replaceProjectConceptName(IdeBundle.message("label.no.matches.found.in.project"));
}
@Override
@@ -24,6 +24,7 @@ import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileSystemItem;
import com.intellij.psi.codeStyle.MinusculeMatcher;
import com.intellij.psi.codeStyle.NameUtil;
import com.intellij.ui.ProjectUICustomization;
import com.intellij.util.containers.JBIterable;
import com.intellij.util.indexing.FileBasedIndex;
import org.jetbrains.annotations.NotNull;
@@ -94,7 +95,7 @@ public class GotoFileModel extends FilteringGotoByModel<FileType> implements Dum
if (NonProjectScopeDisablerEP.isSearchInNonProjectDisabled()) {
return null;
}
return IdeBundle.message("checkbox.include.non.project.files");
return ProjectUICustomization.replaceProjectConceptName(IdeBundle.message("checkbox.include.non.project.files"));
}
@Override
@@ -26,6 +26,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.psi.PsiElement;
import com.intellij.psi.presentation.java.SymbolPresentationUtil;
import com.intellij.ui.ProjectUICustomization;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -66,12 +67,12 @@ public class GotoSymbolModel2 extends FilteringGotoByModel<Language> {
@Override
public String getCheckBoxName() {
return IdeBundle.message("checkbox.include.non.project.symbols");
return ProjectUICustomization.replaceProjectConceptName(IdeBundle.message("checkbox.include.non.project.symbols"));
}
@Override
public String getNotInMessage() {
return IdeBundle.message("label.no.matches.found.in.project");
return ProjectUICustomization.replaceProjectConceptName(IdeBundle.message("label.no.matches.found.in.project"));
}
@Override
@@ -106,6 +106,7 @@ class EditorSettingsStatisticsCollector extends ApplicationUsagesCollector {
addBoolIfDiffers(set, cis, cisDefault, s -> s.SHOW_PARAMETER_NAME_HINTS_ON_COMPLETION, "completionHints");
addBoolIfDiffers(set, cis, cisDefault, s -> s.SHOW_EXTERNAL_ANNOTATIONS_INLINE, "externalAnnotationsInline");
addBoolIfDiffers(set, cis, cisDefault, s -> s.SHOW_INFERRED_ANNOTATIONS_INLINE, "inferredAnnotationsInline");
addBoolIfDiffers(set, cis, cisDefault, s -> s.TAB_EXITS_BRACKETS_AND_QUOTES, "tabExitsBracketsAndQuotes");
return set;
}
@@ -113,6 +113,7 @@ class LegacyEditorSettingsStatisticsCollector extends UsagesCollector {
addBoolIfDiffers(set, cis, cisDefault, s -> s.SHOW_PARAMETER_NAME_HINTS_ON_COMPLETION, "completionHints");
addBoolIfDiffers(set, cis, cisDefault, s -> s.SHOW_EXTERNAL_ANNOTATIONS_INLINE, "externalAnnotationsInline");
addBoolIfDiffers(set, cis, cisDefault, s -> s.SHOW_INFERRED_ANNOTATIONS_INLINE, "inferredAnnotationsInline");
addBoolIfDiffers(set, cis, cisDefault, s -> s.TAB_EXITS_BRACKETS_AND_QUOTES, "tabExitsBracketsAndQuotes");
return set;
}
@@ -189,11 +189,6 @@ public class StubTreeLoaderImpl extends StubTreeLoader {
}
}
@Override
public boolean isStubReloadingProhibited() {
return ourStubReloadingProhibited;
}
private static int getCurrentTextContentLength(Project project, VirtualFile vFile, Document document, PsiFile psiFile) {
if (vFile.getFileType().isBinary()) {
return -1;
@@ -70,10 +70,10 @@ public class PtyCommandLine extends GeneralCommandLine {
logContent = "Unable to retrieve log: " + e.getMessage();
}
LOG.error("Couldn't run process with PTY", t, logContent);
LOG.debug("Couldn't run process with PTY", t, logContent);
}
else {
LOG.error("Couldn't run process with PTY", t);
LOG.debug("Couldn't run process with PTY", t);
}
}
@@ -8,6 +8,7 @@ import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.PlatformUtils;
import com.intellij.util.xmlb.XmlSerializerUtil;
import com.intellij.util.xmlb.annotations.OptionTag;
@@ -33,12 +34,15 @@ public class GeneralSettings implements PersistentStateComponent<GeneralSettings
public static final String PROP_INACTIVE_TIMEOUT = "inactiveTimeout";
public static final String PROP_SUPPORT_SCREEN_READERS = "supportScreenReaders";
public static final String SUPPORT_SCREEN_READERS = "ide.support.screenreaders.enabled";
private static final Boolean SUPPORT_SCREEN_READERS_OVERRIDEN = getSupportScreenReadersOverriden();
static final UINumericRange SAVE_FILES_AFTER_IDLE_SEC = new UINumericRange(15, 1, 300);
private String myBrowserPath = BrowserUtil.getDefaultAlternativeBrowserPath();
private boolean myShowTipsOnStartup = true;
private boolean myReopenLastProject = true;
private boolean mySupportScreenReaders = false;
private boolean mySupportScreenReaders = ObjectUtils.chooseNotNull(SUPPORT_SCREEN_READERS_OVERRIDEN, Boolean.FALSE);
private boolean mySyncOnFrameActivation = true;
private boolean mySaveOnFrameDeactivation = true;
private boolean myAutoSaveIfInactive = false; // If true the IDEA automatically saves files if it is inactive for some seconds
@@ -121,6 +125,19 @@ public class GeneralSettings implements PersistentStateComponent<GeneralSettings
myReopenLastProject = reopenLastProject;
}
@Nullable
private static Boolean getSupportScreenReadersOverriden() {
String prop = System.getProperty(SUPPORT_SCREEN_READERS);
if (prop != null) {
return Boolean.parseBoolean(prop);
}
return null;
}
public static boolean isSupportScreenReadersOverriden() {
return SUPPORT_SCREEN_READERS_OVERRIDEN != null;
}
public boolean isSupportScreenReaders() {
return mySupportScreenReaders;
}
@@ -1,18 +1,4 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.openapi.actionSystem;
import org.jetbrains.annotations.NonNls;
@@ -344,4 +330,6 @@ public interface IdeActions {
@NonNls String ACTION_METHOD_OVERLOAD_SWITCH_DOWN = "MethodOverloadSwitchDown";
@NonNls String ACTION_UPDATE_RUNNING_APPLICATION = "UpdateRunningApplication";
@NonNls String ACTION_BRACE_OR_QUOTE_OUT = "BraceOrQuoteOut";
}
@@ -0,0 +1,49 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ui;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.util.text.StringUtil;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
/**
* Allows to apply IDE-specific customizations to the terms used in platform UI features.
*/
public class ProjectUICustomization {
public static ProjectUICustomization getInstance() {
return ServiceManager.getService(ProjectUICustomization.class);
}
/**
* Returns the name to be displayed in the UI for the "project" concept (Rider changes this to "solution").
*/
public String getProjectConceptName() {
return "project";
}
public static String replaceProjectConceptName(String text) {
return text.replace("project", getInstance().getProjectConceptName());
}
public static void replaceProjectConceptName(AbstractButton component) {
component.setText(component.getText().replace("project", getInstance().getProjectConceptName()));
}
public static void replaceProjectConceptName(Border border) {
if (border instanceof TitledBorder) {
TitledBorder titledBorder = (TitledBorder)border;
titledBorder.setTitle(titledBorder.getTitle().replace("project", getInstance().getProjectConceptName()));
}
}
public static String replaceProjectConceptNameForAction(String actionName) {
String name = getInstance().getProjectConceptName();
if (name.equals("project")) {
return actionName;
}
return actionName.replace("_", "").replace("project", name).replace("Project", StringUtil.capitalize(name));
}
}
@@ -28,7 +28,9 @@ public class OSProcessUtil {
if (SystemInfo.isWindows) {
try {
if (process instanceof WinPtyProcess) {
boolean res = WinProcessManager.kill(((WinPtyProcess)process).getChildProcessId(), true);
int pid = ((WinPtyProcess) process).getChildProcessId();
if (pid == -1) return true;
boolean res = WinProcessManager.kill(pid, true);
process.destroy();
return res;
}
@@ -25,6 +25,7 @@ import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.options.SearchableConfigurable;
import com.intellij.openapi.options.ex.ConfigurableWrapper;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.ui.ProjectUICustomization;
import com.intellij.ui.components.JBRadioButton;
import com.intellij.util.PlatformUtils;
import org.jetbrains.annotations.NotNull;
@@ -152,6 +153,11 @@ public class GeneralSettingsConfigurable extends CompositeConfigurable<Searchabl
GeneralSettings settings = GeneralSettings.getInstance();
myComponent.myChkReopenLastProject.setSelected(settings.isReopenLastProject());
myComponent.myChkSupportScreenReaders.setSelected(settings.isSupportScreenReaders());
if (GeneralSettings.isSupportScreenReadersOverriden()) {
myComponent.myChkSupportScreenReaders.setEnabled(false);
myComponent.myChkSupportScreenReaders.setToolTipText(
"The option is overriden by the JVM property: \"" + GeneralSettings.SUPPORT_SCREEN_READERS + "\"");
}
myComponent.myChkSyncOnFrameActivation.setSelected(settings.isSyncOnFrameActivation());
myComponent.myChkSaveOnFrameDeactivation.setSelected(settings.isSaveOnFrameDeactivation());
myComponent.myChkAutoSaveIfInactive.setSelected(settings.isAutoSaveIfInactive());
@@ -216,8 +222,15 @@ public class GeneralSettingsConfigurable extends CompositeConfigurable<Searchabl
private JBRadioButton myDisconnectJBRadioButton;
private JBRadioButton myAskJBRadioButton;
private TextFieldWithBrowseButton myProjectDirectoryTextField;
private JPanel myProjectOpeningPanel;
public MyComponent() { }
public MyComponent() {
ProjectUICustomization.replaceProjectConceptName(myChkReopenLastProject);
ProjectUICustomization.replaceProjectConceptName(myProjectOpeningPanel.getBorder());
ProjectUICustomization.replaceProjectConceptName(myOpenProjectInNewWindow);
ProjectUICustomization.replaceProjectConceptName(myOpenProjectInSameWindow);
ProjectUICustomization.replaceProjectConceptName(myConfirmWindowToOpenProject);
}
private void createUIComponents() {
myProjectDirectoryTextField = new TextFieldWithBrowseButton();
@@ -181,7 +181,7 @@
<border type="none"/>
<children/>
</grid>
<grid id="dab62" layout-manager="GridLayoutManager" row-count="5" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="dab62" binding="myProjectOpeningPanel" layout-manager="GridLayoutManager" row-count="5" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
@@ -26,6 +26,7 @@ import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeFrame;
import com.intellij.projectImport.ProjectAttachProcessor;
import com.intellij.ui.ProjectUICustomization;
public class CloseProjectAction extends AnAction implements DumbAware {
@Override
@@ -43,10 +44,10 @@ public class CloseProjectAction extends AnAction implements DumbAware {
Project project = event.getData(CommonDataKeys.PROJECT);
presentation.setEnabled(project != null);
if (ProjectAttachProcessor.canAttachToProject() && project != null && ModuleManager.getInstance(project).getModules().length > 1) {
presentation.setText(IdeBundle.message("action.close.projects.in.current.window"));
presentation.setText(ProjectUICustomization.replaceProjectConceptNameForAction(IdeBundle.message("action.close.projects.in.current.window")));
}
else {
presentation.setText(IdeBundle.message("action.close.project"));
presentation.setText(ProjectUICustomization.replaceProjectConceptNameForAction(IdeBundle.message("action.close.project")));
}
}
}
@@ -744,6 +744,10 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
}
public void exit(boolean force, boolean exitConfirmed, boolean restart, @NotNull String[] beforeRestart) {
exit(force, exitConfirmed, restart, false, beforeRestart);
}
public void exit(boolean force, boolean exitConfirmed, boolean restart, boolean elevate, @NotNull String[] beforeRestart) {
if (!force) {
if (myExitInProgress) return;
if (!exitConfirmed && getDefaultModalityState() != ModalityState.NON_MODAL) return;
@@ -751,14 +755,14 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
myExitInProgress = true;
if (isDispatchThread()) {
doExit(force, exitConfirmed, restart, beforeRestart);
doExit(force, exitConfirmed, restart, elevate, beforeRestart);
}
else {
invokeLater(() -> doExit(force, exitConfirmed, restart, beforeRestart), ModalityState.NON_MODAL);
invokeLater(() -> doExit(force, exitConfirmed, restart, elevate, beforeRestart), ModalityState.NON_MODAL);
}
}
private void doExit(boolean force, boolean exitConfirmed, boolean restart, String[] beforeRestart) {
private void doExit(boolean force, boolean exitConfirmed, boolean restart, boolean elevate, String[] beforeRestart) {
try {
if (!force && !confirmExitIfNeeded(exitConfirmed)) {
return;
@@ -786,7 +790,7 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
int exitCode = 0;
if (restart && Restarter.isSupported()) {
try {
Restarter.scheduleRestart(beforeRestart);
Restarter.scheduleRestart(elevate, beforeRestart);
}
catch (Throwable t) {
LOG.error("Restart failed", t);
@@ -24,6 +24,9 @@ import java.util.Set;
@State(name = "EditorSettings", storages = @Storage("editor.xml"))
public class EditorSettingsExternalizable implements PersistentStateComponent<EditorSettingsExternalizable.OptionSet> {
@NonNls
public static String PROP_VIRTUAL_SPACE = "VirtualSpace";
public static final UINumericRange BLINKING_RANGE = new UINumericRange(500, 10, 1500);
public static final UINumericRange QUICK_DOC_DELAY_RANGE = new UINumericRange(500, 1, 5000);
@@ -374,7 +377,9 @@ public class EditorSettingsExternalizable implements PersistentStateComponent<Ed
}
public void setVirtualSpace(boolean val) {
boolean oldValue = myOptions.IS_VIRTUAL_SPACE;
myOptions.IS_VIRTUAL_SPACE = val;
myPropertyChangeSupport.firePropertyChange(PROP_VIRTUAL_SPACE, oldValue, val);
}
public boolean isCaretInsideTabs() {
@@ -446,7 +446,7 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec
return file != null ? myEditorManager.getSelectedEditorWithProvider(file) : null;
}
private PlaceInfo createPlaceInfo(@NotNull final FileEditor fileEditor, final FileEditorProvider fileProvider) {
protected PlaceInfo createPlaceInfo(@NotNull final FileEditor fileEditor, final FileEditorProvider fileProvider) {
if (!fileEditor.isValid()) return null;
final VirtualFile file = myEditorManager.getFile(fileEditor);
@@ -485,13 +485,13 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec
return myFileDocumentManager;
}
private static final class PlaceInfo {
protected static final class PlaceInfo {
private final VirtualFile myFile;
private final FileEditorState myNavigationState;
private final String myEditorTypeId;
private final Reference<EditorWindow> myWindow;
PlaceInfo(@NotNull VirtualFile file,
public PlaceInfo(@NotNull VirtualFile file,
@NotNull FileEditorState navigationState,
@NotNull String editorTypeId,
@Nullable EditorWindow window) {
@@ -177,6 +177,7 @@ public class MovablePopup {
if (owner != null) {
if (myHeavyWeight) {
Window view = new JWindow(owner);
view.setType(Window.Type.POPUP);
setAlwaysOnTop(view, myAlwaysOnTop);
setWindowFocusable(view, myWindowFocusable);
setWindowShadow(view, myWindowShadow);
@@ -104,9 +104,13 @@ public class Restarter {
}
public static void scheduleRestart(@NotNull String... beforeRestart) throws IOException {
scheduleRestart(false, beforeRestart);
}
public static void scheduleRestart(Boolean elevate, @NotNull String... beforeRestart) throws IOException {
Logger.getInstance(Restarter.class).info("restart: " + Arrays.toString(beforeRestart));
if (SystemInfo.isWindows) {
restartOnWindows(beforeRestart);
restartOnWindows(elevate, beforeRestart);
}
else if (SystemInfo.isMac) {
restartOnMac(beforeRestart);
@@ -119,7 +123,7 @@ public class Restarter {
}
}
private static void restartOnWindows(String... beforeRestart) throws IOException {
private static void restartOnWindows(Boolean elevate, String... beforeRestart) throws IOException {
Kernel32 kernel32 = Native.loadLibrary("kernel32", Kernel32.class);
Shell32 shell32 = Native.loadLibrary("shell32", Shell32.class);
@@ -146,7 +150,13 @@ public class Restarter {
args.add(String.valueOf(pid));
args.add(String.valueOf(beforeRestart.length));
Collections.addAll(args, beforeRestart);
args.add(String.valueOf(argv.length));
if (elevate) {
args.add(String.valueOf(argv.length + 1));
args.add(new File(PathManager.getBinPath(), "launcher.exe").getPath());
}
else {
args.add(String.valueOf(argv.length));
}
Collections.addAll(args, argv);
runRestarter(new File(PathManager.getBinPath(), "restarter.exe"), args);
@@ -1630,4 +1630,6 @@ action.TogglePresentationMode.enter=Enter Presentation Mode
action.EmojiAndSymbols.description=Input special characters using macOS system palette
action.MethodOverloadSwitchUp.text=Previous Method Overload
action.MethodOverloadSwitchDown.text=Next Method Overload
action.MethodOverloadSwitchDown.text=Next Method Overload
action.BraceOrQuoteOut.text=Exit Current Braces/Quotes
@@ -453,6 +453,7 @@ checkbox.honor.camelhumps.words.settings.on.double.click=<html>Honor "CamelHumps
group.smart.keys=Smart Keys
checkbox.use.camelhumps.words=Use "CamelHumps" words
checkbox.enable.double.ctrl=Add multiple carets on double {0} with arrow keys
checkbox.tab.exists.brackets.and.quotes=Tab exits brackets/quotes
checkbox.insert.pair.quote=Insert pair quote
checkbox.insert.pair.bracket=Insert paired brackets (), [], {}, <>
checkbox.javadoc.stub.after.slash.star.star=Insert documentation comment stub
@@ -274,6 +274,8 @@ commit.dialog.refresh.files=Synchronizing files...
commit.dialog.rejected.commit.template=Rejected commit: {0}
commit.dialog.changelist.label=Changelis&t:
commit.dialog.changed.files.label=Changed &Files
commit.dialog.partial.commit.warning.title=Partial Commit Is Not Supported
commit.dialog.partial.commit.warning.body=Partial commit is not supported for ''{0}''.\nAll changes in the selected files will be committed.
commit.wait.util.synced.message=Please wait until VCS synchronization is finished.
commit.wait.util.synced.title=Finishing VCS refresh
change.list.manager.wait.lists.synchronization={0}: Local Changes Refresh
@@ -991,6 +991,7 @@
<elementPreviewProvider implementation="com.intellij.codeInsight.preview.ElementPreviewHintProvider"/>
<actionPromoter implementation="com.intellij.ui.EditorTextFieldActionPromoter"/>
<actionPromoter implementation="com.intellij.codeInsight.editorActions.TabActionsPromoter"/>
<stepsBeforeRunProvider implementation="com.intellij.execution.impl.RunConfigurationBeforeRunProvider"/>
@@ -294,6 +294,8 @@
<postStartupActivity implementation="com.intellij.internal.IconsLoadTime$StartupLoadTime"/>
<applicationService serviceImplementation="com.intellij.ui.ProjectUICustomization"/>
<!-- General -->
<applicationConfigurable groupId="appearance" groupWeight="120" key="title.general" bundle="messages.IdeBundle" id="preferences.general"
instance="com.intellij.ide.GeneralSettingsConfigurable"/>
@@ -48,6 +48,8 @@
<action id="EditorIndentLineOrSelection" class="com.intellij.openapi.editor.actions.IndentLineOrSelectionAction"/>
<action id="BraceOrQuoteOut" class="com.intellij.codeInsight.editorActions.BraceOrQuoteOutAction"/>
<group id="CodeInsightEditorActions">
<reference ref="LookupActions"/>
<action id="EmacsStyleIndent" class="com.intellij.codeInsight.editorActions.EmacsStyleIndentAction"/>
@@ -593,6 +593,12 @@
<action id="ChangesView.Revert">
<keyboard-shortcut first-keystroke="control alt Z"/>
</action>
<action id="Vcs.MoveChangedLinesToChangelist">
<keyboard-shortcut first-keystroke="alt shift M"/>
</action>
<action id="ChangesView.Move">
<keyboard-shortcut first-keystroke="alt shift M"/>
</action>
<action id="GotoNextError">
<keyboard-shortcut first-keystroke="F2"/>
@@ -1081,4 +1087,8 @@
<action id="RunDashboard.ShowConfigurations">
<keyboard-shortcut first-keystroke="control shift T"/>
</action>
<action id="BraceOrQuoteOut">
<keyboard-shortcut first-keystroke="TAB"/>
</action>
</keymap>
@@ -360,4 +360,6 @@
<action id="RunDashboard.ShowConfigurations">
<keyboard-shortcut first-keystroke="shift alt H"/>
</action>
<action id="Vcs.MoveChangedLinesToChangelist"/>
<action id="ChangesView.Move"/>
</keymap>
@@ -310,4 +310,6 @@
<action id="GotoImplementation"/>
<action id="EditorScrollUp"/>
<action id="EditorScrollDown"/>
<action id="Vcs.MoveChangedLinesToChangelist"/>
<action id="ChangesView.Move"/>
</keymap>
@@ -266,6 +266,12 @@
<action id="CallHierarchy">
<keyboard-shortcut first-keystroke="control alt H"/>
</action>
<action id="Vcs.MoveChangedLinesToChangelist">
<keyboard-shortcut first-keystroke="meta shift M"/>
</action>
<action id="ChangesView.Move">
<keyboard-shortcut first-keystroke="meta shift M"/>
</action>
<action id="CommentByBlockComment">
<keyboard-shortcut first-keystroke="meta alt SLASH"/>
@@ -289,6 +289,12 @@
<action id="CallHierarchy">
<keyboard-shortcut first-keystroke="control alt H"/>
</action>
<action id="Vcs.MoveChangedLinesToChangelist">
<keyboard-shortcut first-keystroke="meta shift M"/>
</action>
<action id="ChangesView.Move">
<keyboard-shortcut first-keystroke="meta shift M"/>
</action>
<action id="CommentByBlockComment">
<keyboard-shortcut first-keystroke="control shift SLASH"/>
@@ -401,4 +407,6 @@
<action id="GotoPrevElementUnderCaretUsage">
<keyboard-shortcut first-keystroke="ctrl alt UP"/>
</action>
<action id="Vcs.MoveChangedLinesToChangelist"/>
<action id="ChangesView.Move"/>
</keymap>
@@ -384,4 +384,6 @@
<action id="ShelvedChanges.Rename">
<keyboard-shortcut first-keystroke="control R"/>
</action>
<action id="Vcs.MoveChangedLinesToChangelist"/>
<action id="ChangesView.Move"/>
</keymap>
@@ -0,0 +1,30 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.openapi.editor.actions;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.editor.impl.AbstractEditorTest;
public class EditorTabOutTest extends AbstractEditorTest {
public void testMethodCall() {
doTest("class C { void m() { System.getenv(\"a\"<caret>) } }", 1);
}
public void testStringLiteral() {
doTest("class C { void m() { String s = \"a<caret>\" } }", 1);
}
private void doTest(String fileText, int expectedCaretShift) {
boolean savedSetting = CodeInsightSettings.getInstance().TAB_EXITS_BRACKETS_AND_QUOTES;
CodeInsightSettings.getInstance().TAB_EXITS_BRACKETS_AND_QUOTES = true;
try {
configureFromFileText(getTestName(false) + ".java", fileText);
int originalCaretOffset = myEditor.getCaretModel().getOffset();
executeAction(IdeActions.ACTION_BRACE_OR_QUOTE_OUT);
assertEquals("Unexpected caret offset", originalCaretOffset + expectedCaretShift, myEditor.getCaretModel().getOffset());
}
finally {
CodeInsightSettings.getInstance().TAB_EXITS_BRACKETS_AND_QUOTES = savedSetting;
}
}
}
@@ -4,6 +4,7 @@ package com.intellij.openapi.updateSettings
import com.intellij.configurationStore.deserialize
import com.intellij.openapi.updateSettings.impl.UpdateOptions
import com.intellij.testFramework.ProjectRule
import com.intellij.testFramework.assertions.Assertions.assertThat
import com.intellij.util.loadElement
import org.junit.ClassRule
import org.junit.Test
@@ -15,6 +16,12 @@ class UpdateOptionsTest {
val projectRule = ProjectRule()
}
@Test
fun `auto check is enabled`() {
// it is quite important default value (true), so, ensure that it is not modified without changing test expectation (as an additional check)
assertThat(UpdateOptions().isCheckNeeded).isTrue()
}
@Test
fun test() {
loadElement("""
@@ -209,21 +209,25 @@ public class GeneralIdBasedToSMTRunnerEventsConvertor extends GeneralTestEventsP
}
testProxy.setFrameworkOutputFile(testFinishedEvent.getOutputFile());
testProxy.setFinished();
if (node.getState() != State.FAILED) {
LOG.debug("onTestFinished: node.getState() != State.FAILED");
// Don't count the same test twice if 'testFailed' message is followed by 'testFinished' message
// which may happen if generated TeamCity messages adhere rules from
// https://confluence.jetbrains.com/display/TCD10/Build+Script+Interaction+with+TeamCity
// Anyway, this id-based converter already breaks TeamCity protocol by expecting messages with
// non-standard TeamCity attributes: 'nodeId'/'parentNodeId' instead of 'name'.
fireOnTestFinished(testProxy);
}
fireOnTestFinishedIfNeeded(testProxy, node);
terminateNode(node, State.FINISHED);
}
});
}
private void fireOnTestFinishedIfNeeded(@NotNull SMTestProxy testProxy, @NotNull Node node) {
// allow clients to omit sending 'testFinished' messages after 'testFailed'/'testIgnored' messages
if (node.getState() != State.FINISHED && node.getState() != State.FAILED && node.getState() != State.IGNORED) {
LOG.debug("onTestFinished: state != FINISHED && state != FAILED && state != IGNORED");
// Don't count the same test twice if 'testFailed' or 'testIgnored' message is followed by 'testFinished' message
// which may happen if generated TeamCity messages adhere rules from
// https://confluence.jetbrains.com/display/TCD10/Build+Script+Interaction+with+TeamCity
// Anyway, this id-based converter already breaks TeamCity protocol by expecting messages with
// non-standard TeamCity attributes: 'nodeId'/'parentNodeId' instead of 'name'.
fireOnTestFinished(testProxy);
}
}
public void onSuiteFinished(@NotNull final TestSuiteFinishedEvent suiteFinishedEvent) {
if (LOG.isDebugEnabled()) {
LOG.debug("onSuiteFinished " + suiteFinishedEvent.getId());
@@ -328,10 +332,9 @@ public class GeneralIdBasedToSMTRunnerEventsConvertor extends GeneralTestEventsP
if (duration >= 0) {
testProxy.setDuration(duration);
}
fireOnTestFinished(testProxy);
// fire event
fireOnTestFailed(testProxy);
fireOnTestFinishedIfNeeded(testProxy, node);
terminateNode(node, State.FAILED);
});
@@ -349,8 +352,10 @@ public class GeneralIdBasedToSMTRunnerEventsConvertor extends GeneralTestEventsP
}
SMTestProxy testProxy = node.getProxy();
testProxy.setTestIgnored(testIgnoredEvent.getIgnoreComment(), testIgnoredEvent.getStacktrace());
// fire event
fireOnTestIgnored(testProxy);
fireOnTestFinishedIfNeeded(testProxy, node);
terminateNode(node, State.IGNORED);
}
});
@@ -26,14 +26,16 @@ public class TestIgnoredEvent extends TreeNodeEvent {
private final String myStacktrace;
public TestIgnoredEvent(@NotNull String testName, @NotNull String ignoreComment, @Nullable String stacktrace) {
super(testName, null);
myIgnoreComment = ignoreComment;
myStacktrace = stacktrace;
this(testName, null, ignoreComment, stacktrace);
}
public TestIgnoredEvent(@NotNull TestIgnored testIgnored, @Nullable String stacktrace) {
super(testIgnored.getTestName(), TreeNodeEvent.getNodeId(testIgnored));
myIgnoreComment = testIgnored.getIgnoreComment();
this(testIgnored.getTestName(), TreeNodeEvent.getNodeId(testIgnored), testIgnored.getIgnoreComment(), stacktrace);
}
public TestIgnoredEvent(@Nullable String name, @Nullable String id, @Nullable String ignoreComment, @Nullable String stacktrace) {
super(name, id);
myIgnoreComment = ignoreComment;
myStacktrace = stacktrace;
}
@@ -15,23 +15,32 @@
*/
package com.intellij.execution.testframework.sm.runner;
import com.intellij.execution.testframework.sm.runner.events.TestFailedEvent;
import com.intellij.execution.testframework.sm.runner.events.TestStartedEvent;
import com.intellij.execution.testframework.sm.runner.events.TestSuiteStartedEvent;
import com.intellij.execution.testframework.sm.runner.events.TreeNodeEvent;
import com.intellij.execution.testframework.TestConsoleProperties;
import com.intellij.execution.testframework.sm.runner.events.*;
import com.intellij.execution.testframework.sm.runner.ui.SMTRunnerConsoleView;
import com.intellij.execution.testframework.sm.runner.ui.SMTestRunnerResultsForm;
import com.intellij.openapi.util.Disposer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class GeneralIdBasedToSMTRunnerEventsConvertorTest extends BaseSMTRunnerTestCase {
private SMTRunnerConsoleView myConsole;
private GeneralIdBasedToSMTRunnerEventsConvertor myEventsProcessor;
private SMTestProxy.SMRootTestProxy myRootProxy;
private SMTestRunnerResultsForm myResultsViewer;
@Override
protected void setUp() throws Exception {
super.setUp();
TestConsoleProperties consoleProperties = createConsoleProperties();
myConsole = new SMTRunnerConsoleView(consoleProperties);
myConsole.initUI();
myResultsViewer = myConsole.getResultsViewer();
myRootProxy = new SMTestProxy.SMRootTestProxy();
myEventsProcessor = new GeneralIdBasedToSMTRunnerEventsConvertor(getProject(), myRootProxy, "test");
myEventsProcessor.addEventsListener(myResultsViewer);
myEventsProcessor.onStartTesting();
}
@@ -39,6 +48,7 @@ public class GeneralIdBasedToSMTRunnerEventsConvertorTest extends BaseSMTRunnerT
protected void tearDown() throws Exception {
try {
Disposer.dispose(myEventsProcessor);
Disposer.dispose(myConsole);
}
finally {
super.tearDown();
@@ -84,6 +94,27 @@ public class GeneralIdBasedToSMTRunnerEventsConvertorTest extends BaseSMTRunnerT
assertTrue(suiteProxy.isInProgress());
}
public void testIgnoredEvent() {
onSuiteStarted("Suite", null, "1", TreeNodeEvent.ROOT_NODE_ID);
SMTestProxy suite = validateSuite("1", "Suite", null, myRootProxy);
onTestStarted("testA", null, "A", "1", true);
SMTestProxy testA = validateTest("A", "testA", null, true, suite);
onTestIgnored("A");
validateTestIgnored("A", testA);
assertFalse(testA.isInProgress());
assertTrue(suite.isInProgress());
assertEquals(1, myResultsViewer.getFinishedTestCount());
onTestFinished("A", null);
assertEquals(1, myResultsViewer.getFinishedTestCount());
onTestStarted("testB", null, "B", "1", true);
SMTestProxy testB = validateTest("B", "testB", null, true, suite);
assertEquals(1, myResultsViewer.getFinishedTestCount());
onTestIgnored("B");
assertEquals(2, myResultsViewer.getFinishedTestCount());
validateTestIgnored("B", testB);
}
@NotNull
private SMTestProxy validateSuite(@NotNull String id,
@NotNull String expectedName,
@@ -129,6 +160,14 @@ public class GeneralIdBasedToSMTRunnerEventsConvertorTest extends BaseSMTRunnerT
return test;
}
private void validateTestIgnored(@NotNull String id, @NotNull SMTestProxy expectedTestProxy) {
SMTestProxy test = myEventsProcessor.findProxyById(id);
assertEquals(expectedTestProxy, test);
assertFalse(test.isSuite());
assertTrue(test.isFinal());
assertTrue(test.isIgnored());
}
private void onSuiteStarted(@NotNull String suiteName, @Nullable String metainfo, @NotNull String id, @NotNull String parentId) {
myEventsProcessor.onSuiteStarted(new TestSuiteStartedEvent(suiteName, id, parentId, null, metainfo, null, null, false));
}
@@ -141,9 +180,16 @@ public class GeneralIdBasedToSMTRunnerEventsConvertorTest extends BaseSMTRunnerT
myEventsProcessor.onTestStarted(new TestStartedEvent(testName, id, parentId, null, metainfo, null, null, running));
}
private void onTestFinished(@NotNull String id, @Nullable Long duration) {
myEventsProcessor.onTestFinished(new TestFinishedEvent(null, id, duration));
}
private void onTestFailed(@NotNull String id, @NotNull String errorMessage, int durationMillis) {
myEventsProcessor.onTestFailure(new TestFailedEvent(null, id, errorMessage, null, false, null,
null, null, null, false, false, durationMillis));
}
private void onTestIgnored(@NotNull String id) {
myEventsProcessor.onTestIgnored(new TestIgnoredEvent(null, id, null, null));
}
}
@@ -66,6 +66,7 @@ public abstract class KeymapsTestCase extends KeymapsTestCaseBase {
{ "shift F7", "PreviousDiff", "SmartStepInto"},
{ "shift TAB", "PreviousTemplateVariable", "PrevParameter", "EditorUnindentSelection", "PrevTemplateParameter"},
{ "shift alt L", "org.jetbrains.plugins.ruby.console.LoadInIrbConsoleAction", "context.load"},
{ "shift alt M", "ChangesView.Move", "Vcs.MoveChangedLinesToChangelist"},
{ "shift control D", "TagDocumentationNavigation", "Diff.ShowSettingsPopup", "Uml.ShowDiff", "Console.TableResult.CompareCells"},
{ "shift control DOWN", "ResizeToolWindowDown", "MoveStatementDown"},
{ "shift control ENTER", "EditorCompleteStatement", "Console.Jpa.GenerateSql"},
@@ -105,6 +106,7 @@ public abstract class KeymapsTestCase extends KeymapsTestCaseBase {
{ "meta N", "FileChooser.NewFolder", "Generate", "NewElement"},
{ "meta O", "GotoClass", "GotoChangedFile"},
{ "shift meta G", "ClassTemplateNavigation", "GoToClass", "FindPrevious"},
{ "shift meta M", "ChangesView.Move", "Vcs.MoveChangedLinesToChangelist"},
{ "shift meta LEFT", "EditorLineStartWithSelection", "ResizeToolWindowLeft", },
{ "shift meta RIGHT", "EditorLineEndWithSelection", "ResizeToolWindowRight", },
{ "alt R", "Django.RunManageTaskAction", "org.jetbrains.plugins.ruby.tasks.rake.actions.RakeTasksPopupAction"},
@@ -3,6 +3,7 @@ package com.intellij.testFramework
import com.intellij.ide.highlighter.ProjectFileType
import com.intellij.idea.IdeaTestApplication
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.runUndoTransparentWriteAction
import com.intellij.openapi.components.ComponentManager
@@ -16,6 +17,7 @@ import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.project.ex.ProjectEx
import com.intellij.openapi.project.ex.ProjectManagerEx
import com.intellij.openapi.project.impl.ProjectManagerImpl
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS
import com.intellij.openapi.vfs.newvfs.persistent.PersistentFSImpl
@@ -305,4 +307,17 @@ fun createOrLoadProject(tempDirManager: TemporaryDirectory, projectCreator: ((Vi
fun ComponentManager.saveStore() {
stateStore.save(SmartList(), true)
}
class DisposableRule : ExternalResource() {
private var _disposable = lazy { Disposer.newDisposable() }
val disposable: Disposable
get() = _disposable.value
override fun after() {
if (_disposable.isInitialized()) {
Disposer.dispose(_disposable.value)
}
}
}
@@ -115,7 +115,11 @@ public class UnixProcessManager {
public static boolean sendSignalToProcessTree(int processId, int signal) {
checkCLib();
final int our_pid = C_LIB.getpid();
final int ourPid = C_LIB.getpid();
return sendSignalToProcessTree(processId, signal, ourPid);
}
public static boolean sendSignalToProcessTree(int processId, int signal, int ourPid) {
if (LOG.isDebugEnabled()) {
LOG.debug("Sending signal " + signal + " to process tree with root PID " + processId);
}
@@ -124,7 +128,7 @@ public class UnixProcessManager {
final ProcessInfo processInfo = new ProcessInfo();
final List<Integer> childrenPids = new ArrayList<Integer>();
findChildProcesses(our_pid, processId, foundPid, processInfo, childrenPids);
findChildProcesses(ourPid, processId, foundPid, processInfo, childrenPids);
// result is true if signal was sent to at least one process
final boolean result;
@@ -22,6 +22,10 @@ public interface CommitExecutor {
return null;
}
default boolean supportsPartialCommit() {
return false;
}
@NotNull
CommitSession createCommitSession();
}
@@ -15,39 +15,54 @@
*/
package com.intellij.openapi.vcs.actions;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.ActionPromoter;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.diff.actions.DiffWalkerAction;
import com.intellij.openapi.vcs.ex.MoveChangesLineStatusAction;
import com.intellij.openapi.vcs.ex.RollbackLineStatusAction;
import com.intellij.util.containers.ContainerUtil;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author Konstantin Bulenkov
*/
public class VcsActionPromoter implements ActionPromoter {
@Override
public List<AnAction> promote(List<AnAction> actions, DataContext context) {
List<AnAction> list = new ArrayList<>(0);
ActionManager am = ActionManager.getInstance();
List<AnAction> reorderedActions = new ArrayList<>(actions);
List<String> reorderedIds = ContainerUtil.map(reorderedActions, it -> am.getId(it));
for (AnAction action : actions) {
if (action instanceof RollbackLineStatusAction) {
list.add(action);
}
if (action instanceof MoveChangesLineStatusAction) {
list.add(action);
}
if (action instanceof ShowMessageHistoryAction) {
list.add(action);
}
if (action instanceof DiffWalkerAction) {
list.add(action);
}
}
reorderActionPair(reorderedActions, reorderedIds, "Vcs.MoveChangedLinesToChangelist", "ChangesView.Move");
reorderActionPair(reorderedActions, reorderedIds, "Vcs.RollbackChangedLines", "ChangesView.Revert");
return list;
Set<AnAction> promoted = new HashSet<>(ContainerUtil.filter(actions, action -> {
return action instanceof ShowMessageHistoryAction ||
action instanceof DiffWalkerAction;
}));
reorderedActions.removeAll(promoted);
reorderedActions.addAll(0, promoted);
return reorderedActions;
}
/**
* Ensures that one global action has priority over another global action.
* But is not pushing it ahead of other actions (ex: of some local action with same shortcut).
*/
private static void reorderActionPair(List<AnAction> reorderedActions, List<String> reorderedIds,
String highPriority, String lowPriority) {
int highPriorityIndex = reorderedIds.indexOf(highPriority);
int lowPriorityIndex = reorderedIds.indexOf(lowPriority);
if (highPriorityIndex == -1 || lowPriorityIndex == -1) return;
if (highPriorityIndex < lowPriorityIndex) return;
String id = reorderedIds.remove(highPriorityIndex);
AnAction action = reorderedActions.remove(highPriorityIndex);
reorderedIds.add(lowPriorityIndex, id);
reorderedActions.add(lowPriorityIndex, action);
}
}
@@ -76,6 +76,11 @@ public class CreatePatchCommitExecutor extends LocalCommitExecutor implements Pr
return "reference.dialogs.vcs.patch.create";
}
@Override
public boolean supportsPartialCommit() {
return true;
}
@Override
@NotNull
public CommitSession createCommitSession() {
@@ -55,6 +55,11 @@ public class ShelveChangesCommitExecutor extends LocalCommitExecutor {
return "reference.dialogs.vcs.shelve";
}
@Override
public boolean supportsPartialCommit() {
return true;
}
private class ShelveChangesCommitSession implements CommitSession, CommitSessionContextAware {
@Override
public void setContext(CommitContext context) {
@@ -1,6 +1,7 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.openapi.vcs.changes.ui;
import com.intellij.CommonBundle;
import com.intellij.diff.util.DiffPlaces;
import com.intellij.diff.util.DiffUserDataKeysEx;
import com.intellij.diff.util.DiffUtil;
@@ -29,6 +30,7 @@ import com.intellij.openapi.vcs.changes.*;
import com.intellij.openapi.vcs.changes.actions.ScheduleForAdditionAction;
import com.intellij.openapi.vcs.changes.actions.diff.lst.LocalChangeListDiffTool;
import com.intellij.openapi.vcs.checkin.*;
import com.intellij.openapi.vcs.ex.PartialLocalLineStatusTracker;
import com.intellij.openapi.vcs.impl.CheckinHandlersManager;
import com.intellij.openapi.vcs.impl.LineStatusTrackerManager;
import com.intellij.openapi.vcs.impl.PartialChangesUtil;
@@ -99,6 +101,8 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj
@NotNull private final List<CommitExecutor> myExecutors;
@NotNull private final List<CheckinHandler> myHandlers = newArrayList();
private final boolean myAllOfDefaultChangeListChangesIncluded;
@NotNull private final String myCommitActionName;
private final boolean myEnablePartialCommit;
@NotNull private final Map<String, String> myListComments;
@NotNull private final List<CommitExecutorAction> myExecutorActions;
@@ -289,7 +293,6 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj
myIsAlien = isAlien;
myResultHandler = customResultHandler;
myListComments = newHashMap();
myDiffDetails = new MyChangeProcessor(myProject);
if (!myShowVcsCommit && isEmpty(executors)) {
throw new IllegalArgumentException("nothing found to execute commit with");
@@ -297,6 +300,25 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj
myAllOfDefaultChangeListChangesIncluded = newHashSet(changes).containsAll(newHashSet(defaultChangeList.getChanges()));
myHandlers.addAll(createCheckinHandlers(project, this, myCommitContext));
setTitle(myShowVcsCommit ? TITLE : getExecutorPresentableText(executors.get(0)));
myCommitActionName = getCommitActionName(myAffectedVcses);
myExecutorActions = createExecutorActions(executors);
if (myShowVcsCommit) {
myCommitAction = new CommitAction(myCommitActionName);
myCommitAction.setOptions(myExecutorActions);
}
else {
myCommitAction = null;
myExecutorActions.get(0).putValue(DEFAULT_ACTION, Boolean.TRUE);
}
myHelpId = myShowVcsCommit ? HELP_ID : getHelpId(executors);
myEnablePartialCommit = ContainerUtil.exists(getAffectedVcses(), AbstractVcs::arePartialChangelistsSupported) &&
(myShowVcsCommit || ContainerUtil.exists(myExecutors, executor -> executor.supportsPartialCommit()));
myDiffDetails = new MyChangeProcessor(myProject, myEnablePartialCommit);
myCommitMessageArea = new CommitMessage(project, true, true, myShowVcsCommit);
if (myIsAlien) {
@@ -312,7 +334,8 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj
else {
LineStatusTrackerManager.getInstanceImpl(myProject).resetExcludedFromCommitMarkers();
MultipleLocalChangeListsBrowser browser = new MultipleLocalChangeListsBrowser(project, true, true, myShowVcsCommit);
MultipleLocalChangeListsBrowser browser = new MultipleLocalChangeListsBrowser(project, true, true,
myShowVcsCommit, myEnablePartialCommit);
myBrowser = browser;
if (initialSelection != null) browser.setSelectedChangeList(initialSelection);
@@ -352,21 +375,9 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj
setComment(initialSelection, comment);
}
initCheckinHandlers(project);
myCommitOptions = new CommitOptionsPanel(this, myHandlers, getAffectedVcses());
restoreState();
setTitle(myShowVcsCommit ? TITLE : trimEllipsis(executors.get(0).getActionText()));
myCommitAction = myShowVcsCommit ? new CommitAction(getCommitActionName()) : null;
myExecutorActions = createExecutorActions(executors);
if (myCommitAction != null) {
myCommitAction.setOptions(myExecutorActions);
}
else {
myExecutorActions.get(0).putValue(DEFAULT_ACTION, Boolean.TRUE);
}
myHelpId = myCommitAction != null ? HELP_ID : getHelpId(executors);
myWarningLabel = new JBLabel();
myWarningLabel.setForeground(JBColor.RED);
myWarningLabel.setBorder(JBUI.Borders.empty(5, 5, 0, 5));
@@ -444,13 +455,18 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj
showDetailsIfSaved();
}
private void initCheckinHandlers(@NotNull Project project) {
@NotNull
private static List<CheckinHandler> createCheckinHandlers(@NotNull Project project,
@NotNull CheckinProjectPanel checkinPanel,
@NotNull CommitContext commitContext) {
List<CheckinHandler> handlers = new ArrayList<>();
for (BaseCheckinHandlerFactory factory : getCheckInFactories(project)) {
CheckinHandler handler = factory.createHandler(this, myCommitContext);
CheckinHandler handler = factory.createHandler(checkinPanel, commitContext);
if (!CheckinHandler.DUMMY.equals(handler)) {
myHandlers.add(handler);
handlers.add(handler);
}
}
return handlers;
}
@NotNull
@@ -503,7 +519,7 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj
private void updateOnListSelection() {
updateComment();
myCommitOptions.onChangeListSelected((LocalChangeList)myBrowser.getSelectedChangeList(),
myCommitOptions.onChangeListSelected(myBrowser.getSelectedChangeList(),
ChangeListManagerImpl.getInstanceImpl(myProject).getUnversionedFiles());
}
@@ -617,6 +633,7 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj
return;
}
if (!checkCommitOptionsSupported(commitExecutor)) return;
if (!saveDialogState()) return;
saveComments(true);
@@ -628,7 +645,7 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj
JComponent configurationUI = SessionDialog.createConfigurationUI(session, getIncludedChanges(), getCommitMessage());
if (configurationUI != null) {
DialogWrapper sessionDialog =
new SessionDialog(commitExecutor.getActionText(), getProject(), session, getIncludedChanges(), getCommitMessage(),
new SessionDialog(getExecutorPresentableText(commitExecutor), getProject(), session, getIncludedChanges(), getCommitMessage(),
configurationUI);
if (!sessionDialog.showAndGet()) {
session.executionCanceled();
@@ -703,8 +720,8 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj
private void updateComment() {
if (myVcsConfiguration.CLEAR_INITIAL_COMMIT_MESSAGE) return;
LocalChangeList list = (LocalChangeList)myBrowser.getSelectedChangeList();
if (list == null || list.getName().equals(myLastSelectedListName)) {
LocalChangeList list = myBrowser.getSelectedChangeList();
if (list.getName().equals(myLastSelectedListName)) {
return;
} else if (myLastSelectedListName != null) {
saveCommentIntoChangeList();
@@ -746,19 +763,22 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj
PropertiesComponent.getInstance().setValue(DETAILS_SHOW_OPTION, myDetailsSplitter.isOn(), DETAILS_SHOW_OPTION_DEFAULT);
}
@NotNull
@Override
public String getCommitActionName() {
String name = null;
for (AbstractVcs vcs : myAffectedVcses) {
return myCommitActionName;
}
@NotNull
private static String getCommitActionName(@NotNull Collection<AbstractVcs> affectedVcses) {
Set<String> names = map2SetNotNull(affectedVcses, vcs -> {
CheckinEnvironment checkinEnvironment = vcs.getCheckinEnvironment();
if (name == null && checkinEnvironment != null) {
name = checkinEnvironment.getCheckinOperationName();
}
else {
name = VcsBundle.getString("commit.dialog.default.commit.operation.name");
}
return checkinEnvironment != null ? checkinEnvironment.getCheckinOperationName() : null;
});
if (names.size() == 1) {
return notNull(ContainerUtil.getFirstItem(names));
}
return name != null ? name : VcsBundle.getString("commit.dialog.default.commit.operation.name");
return VcsBundle.getString("commit.dialog.default.commit.operation.name");
}
private boolean checkComment() {
@@ -883,6 +903,25 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj
myListComments.forEach((changeListName, comment) -> changeListManager.editComment(changeListName, comment));
}
private boolean checkCommitOptionsSupported(@NotNull CommitExecutor commitExecutor) {
if (myIsAlien) return true;
if (!commitExecutor.supportsPartialCommit()) {
boolean hasPartialChanges = ContainerUtil.exists(getIncludedChanges(), change -> {
PartialLocalLineStatusTracker tracker = PartialChangesUtil.getPartialTracker(myProject, change);
return tracker != null && tracker.hasPartialChangesToCommit();
});
if (hasPartialChanges) {
return Messages.YES ==
Messages.showYesNoDialog(myProject,
message("commit.dialog.partial.commit.warning.body", getExecutorPresentableText(commitExecutor)),
message("commit.dialog.partial.commit.warning.title"),
commitExecutor.getActionText(), CommonBundle.getCancelButtonText(), Messages.getWarningIcon());
}
}
return true;
}
@Override
public void doCancelAction() {
myCommitOptions.saveChangeListComponentsState();
@@ -1071,9 +1110,14 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj
return myBrowser.getData(dataId);
}
@NotNull
static String getExecutorPresentableText(@NotNull CommitExecutor executor) {
return trimEllipsis(removeMnemonic(executor.getActionText()));
}
@NotNull
static String trimEllipsis(@NotNull String title) {
return StringUtil.trimEnd(title, "...");
return StringUtil.trimEnd(StringUtil.trimEnd(title, "..."), "\u2026");
}
private void ensureDataIsActual(@NotNull Runnable runnable) {
@@ -1133,11 +1177,11 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj
}
private class MyChangeProcessor extends ChangeViewDiffRequestProcessor {
public MyChangeProcessor(@NotNull Project project) {
public MyChangeProcessor(@NotNull Project project, boolean enablePartialCommit) {
super(project, DiffPlaces.COMMIT_DIALOG);
putContextUserData(DiffUserDataKeysEx.SHOW_READ_ONLY_LOCK, true);
putContextUserData(LocalChangeListDiffTool.ALLOW_EXCLUDE_FROM_COMMIT, true);
putContextUserData(LocalChangeListDiffTool.ALLOW_EXCLUDE_FROM_COMMIT, enablePartialCommit);
}
@Override
@@ -1160,7 +1204,7 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj
@Override
protected void selectChange(@NotNull Wrapper change) {
//noinspection unchecked
myBrowser.selectEntries((List)singletonList(change.getUserObject()));
myBrowser.selectEntries(singletonList(change.getUserObject()));
}
@NotNull
@@ -62,6 +62,7 @@ public class MultipleLocalChangeListsBrowser extends CommitDialogChangesBrowser
new MergingUpdateQueue("MultipleLocalChangeListsBrowser", 300, true, ANY_COMPONENT, this);
private final boolean myEnableUnversioned;
private final boolean myEnablePartialCommit;
@Nullable private JComponent myBottomDiffComponent;
@NotNull private final ChangeListChooser myChangeListChooser;
@@ -78,9 +79,11 @@ public class MultipleLocalChangeListsBrowser extends CommitDialogChangesBrowser
public MultipleLocalChangeListsBrowser(@NotNull Project project,
boolean showCheckboxes,
boolean highlightProblems,
boolean enableUnversioned) {
boolean enableUnversioned,
boolean enablePartialCommit) {
super(project, showCheckboxes, highlightProblems);
myEnableUnversioned = enableUnversioned;
myEnablePartialCommit = enablePartialCommit;
myChangeList = ChangeListManager.getInstance(project).getDefaultChangeList();
myChangeListChooser = new ChangeListChooser();
@@ -151,7 +154,7 @@ public class MultipleLocalChangeListsBrowser extends CommitDialogChangesBrowser
protected void updateDiffContext(@NotNull DiffRequestChain chain) {
super.updateDiffContext(chain);
chain.putUserData(DiffUserDataKeysEx.BOTTOM_PANEL, myBottomDiffComponent);
chain.putUserData(LocalChangeListDiffTool.ALLOW_EXCLUDE_FROM_COMMIT, true);
chain.putUserData(LocalChangeListDiffTool.ALLOW_EXCLUDE_FROM_COMMIT, myEnablePartialCommit);
}
@@ -17,6 +17,8 @@ package com.intellij.openapi.vcs.ex
import com.intellij.diff.util.Side
import com.intellij.openapi.Disposable
import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.DefaultActionGroup
import com.intellij.openapi.actionSystem.Separator
import com.intellij.openapi.application.ModalityState
@@ -31,6 +33,8 @@ import com.intellij.openapi.editor.Document
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.event.DocumentEvent
import com.intellij.openapi.editor.event.DocumentListener
import com.intellij.openapi.keymap.KeymapUtil
import com.intellij.openapi.project.DumbAwareAction
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.registry.Registry
@@ -517,7 +521,10 @@ class PartialLocalLineStatusTracker(project: Project,
}
}
override fun createAdditionalInfoPanel(editor: Editor, range: Range, mousePosition: Point?): JComponent? {
override fun createAdditionalInfoPanel(editor: Editor,
range: Range,
mousePosition: Point?,
disposable: Disposable): JComponent? {
if (range !is LocalRange) return null
val changeLists = ChangeListManager.getInstance(tracker.project).changeLists
@@ -536,6 +543,18 @@ class PartialLocalLineStatusTracker(project: Project,
val link = ActionGroupLink(rangeList.name, null, group)
val moveChangesShortcutSet = ActionManager.getInstance().getAction("Vcs.MoveChangedLinesToChangelist").shortcutSet
object : DumbAwareAction() {
override fun actionPerformed(e: AnActionEvent?) {
link.linkLabel.doClick()
}
}.registerCustomShortcutSet(moveChangesShortcutSet, editor.component, disposable)
val shortcuts = moveChangesShortcutSet.shortcuts
if (shortcuts.isNotEmpty()) {
link.linkLabel.toolTipText = "Move lines to another changelist (${KeymapUtil.getShortcutText(shortcuts.first())})"
}
val panel = JPanel(BorderLayout())
panel.add(link, BorderLayout.CENTER)
panel.border = JBUI.Borders.emptyLeft(7)
@@ -19,6 +19,7 @@ package com.intellij.xdebugger.breakpoints;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import com.intellij.xdebugger.XDebuggerBundle;
import com.intellij.xdebugger.XSourcePosition;
import com.intellij.xdebugger.breakpoints.ui.XBreakpointCustomPropertiesPanel;
import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroupingRule;
@@ -245,6 +246,10 @@ public abstract class XBreakpointType<B extends XBreakpoint<P>, P extends XBreak
return getDisplayText(breakpoint);
}
public String getLogMessageLabelText() {
return XDebuggerBundle.message("xbreakpoints.log.message.label");
}
public interface XBreakpointCreator<P extends XBreakpointProperties> {
@NotNull
XBreakpoint<P> createBreakpoint(@Nullable P properties);

Some files were not shown because too many files have changed in this diff Show More