Merge remote-tracking branch 'origin/master'

This commit is contained in:
Dmitry Batkovich
2019-02-05 14:57:47 +03:00
18 changed files with 227 additions and 257 deletions
@@ -6,7 +6,6 @@ import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.keymap.KeymapUtil
import com.intellij.testGuiFramework.fixtures.IdeFrameFixture
import com.intellij.testGuiFramework.impl.GuiTestCase
import com.intellij.testGuiFramework.impl.LogActionsDuringTest
import com.intellij.testGuiFramework.impl.ScreenshotsDuringTest
import com.intellij.testGuiFramework.tests.community.CommunityProjectCreator
import com.intellij.testGuiFramework.util.Key.ESCAPE
@@ -1,24 +1,28 @@
// 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.
// Copyright 2000-2019 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.uast;
import com.intellij.lang.Language;
import com.intellij.lang.MetaLanguage;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.uast.UastLanguagePlugin;
import java.util.Collection;
import java.util.HashSet;
import java.util.Collections;
import java.util.Set;
/**
* @author yole
*/
public class UastMetaLanguage extends MetaLanguage {
private final Set<Language> myLanguages = new HashSet<>();
private final Set<Language> myLanguages;
protected UastMetaLanguage() {
super("UAST");
for (UastLanguagePlugin plugin: UastLanguagePlugin.Companion.getInstances()) {
Collection<UastLanguagePlugin> languagePlugins = UastLanguagePlugin.Companion.getInstances();
myLanguages = new THashSet<>(languagePlugins.size());
for (UastLanguagePlugin plugin: languagePlugins) {
myLanguages.add(plugin.getLanguage());
}
}
@@ -31,6 +35,6 @@ public class UastMetaLanguage extends MetaLanguage {
@NotNull
@Override
public Collection<Language> getMatchingLanguages() {
return myLanguages;
return Collections.unmodifiableSet(myLanguages);
}
}
@@ -5,7 +5,6 @@ import com.intellij.openapi.util.io.FileAttributes;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileSystem;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -13,12 +12,12 @@ import org.jetbrains.annotations.Nullable;
* @author max
*/
public class VFileCreateEvent extends VFileEvent {
@NotNull private final VirtualFile myParent;
private final @NotNull VirtualFile myParent;
private final @NotNull String myChildName;
private final boolean myDirectory;
private final boolean myEmptyDirectory;
@NotNull private final String myChildName;
private final FileAttributes myAttributes;
private final String mySymlinkTarget;
private final boolean myEmptyDirectory;
private VirtualFile myCreatedFile;
public VFileCreateEvent(Object requestor,
@@ -30,8 +29,8 @@ public class VFileCreateEvent extends VFileEvent {
boolean isFromRefresh,
boolean isEmptyDirectory) {
super(requestor, isFromRefresh);
myChildName = childName;
myParent = parent;
myChildName = childName;
myDirectory = isDirectory;
myAttributes = attributes;
mySymlinkTarget = symlinkTarget;
@@ -47,13 +46,6 @@ public class VFileCreateEvent extends VFileEvent {
return myDirectory;
}
/**
* @return true if the newly created file is a directory which has no children.
*/
public boolean isEmptyDirectory() {
return isDirectory() && myEmptyDirectory;
}
@NotNull
public VirtualFile getParent() {
return myParent;
@@ -69,11 +61,9 @@ public class VFileCreateEvent extends VFileEvent {
return mySymlinkTarget;
}
@NonNls
@Override
public String toString() {
return "VfsEvent[create " + (myDirectory ? (isEmptyDirectory() ? "(empty) " :"") + "dir "
: "file ") + myChildName + " in " + myParent.getUrl() + "]";
/** @return true if the newly created file is a directory which has no children. */
public boolean isEmptyDirectory() {
return isDirectory() && myEmptyDirectory;
}
@NotNull
@@ -105,12 +95,7 @@ public class VFileCreateEvent extends VFileEvent {
@Override
public boolean isValid() {
if (myParent.isValid()) {
boolean childExists = myParent.findChild(myChildName) != null;
return !childExists;
}
return false;
return myParent.isValid() && myParent.findChild(myChildName) == null;
}
@Override
@@ -133,4 +118,10 @@ public class VFileCreateEvent extends VFileEvent {
result = 31 * result + myChildName.hashCode();
return result;
}
@Override
public String toString() {
String kind = myDirectory ? (isEmptyDirectory() ? "(empty) " : "") + "dir " : "file ";
return "VfsEvent[create " + kind + myChildName + " in " + myParent.getUrl() + "]";
}
}
@@ -197,7 +197,7 @@ public class ShowIntentionActionsHandler implements CodeInsightActionHandler {
}
private static void invokeIntention(@NotNull IntentionAction action, @Nullable Editor editor, @NotNull PsiFile file) {
IntentionsCollector.getInstance().record(action, file.getLanguage());
IntentionsCollector.getInstance().record(file.getProject(), action, file.getLanguage());
PsiElement elementToMakeWritable = action.getElementToMakeWritable(file);
if (elementToMakeWritable != null && !FileModificationService.getInstance().preparePsiElementsForWrite(elementToMakeWritable)) {
return;
@@ -4,6 +4,7 @@ package com.intellij.ide.actions;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import com.intellij.util.xmlb.annotations.MapAnnotation;
import com.intellij.util.xmlb.annotations.Tag;
import org.jetbrains.annotations.NotNull;
@@ -18,18 +19,10 @@ import java.util.Map;
*/
public abstract class ActionsCollector {
/**
* Only actions from platform and JB plugins are recorded.
* If no context class is provided then nothing will be recorded.
* @deprecated use {@link #record(String, Class)} instead
* @deprecated use {@link #record(Project, AnAction, AnActionEvent)} instead
*/
@Deprecated
public void record(String actionId) {}
/**
* @deprecated use {@link #record(AnAction, AnActionEvent)} instead
*/
@Deprecated
public void record(@Nullable String actionId, @NotNull Class context, @Nullable AnActionEvent event) {}
public void record(@Nullable AnAction action, @Nullable AnActionEvent event) {}
/**
* Records explicitly whitelisted actions
@@ -47,7 +40,7 @@ public abstract class ActionsCollector {
* Records action id for global actions or action class name for actions generated on runtime.
* Only actions from platform and JB plugins are recorded.
*/
public abstract void record(@Nullable AnAction action, @Nullable AnActionEvent event);
public abstract void record(@Nullable Project project, @Nullable AnAction action, @Nullable AnActionEvent event);
public abstract State getState();
@@ -16,6 +16,7 @@ import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.RoamingType;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
@@ -53,11 +54,11 @@ public class ActionsCollectorImpl extends ActionsCollector implements Persistent
}
@Override
public void record(@Nullable AnAction action, @Nullable AnActionEvent event) {
public void record(@Nullable Project project, @Nullable AnAction action, @Nullable AnActionEvent event) {
if (action == null) return;
final PluginInfo info = PluginInfoDetectorKt.getPluginInfo(action.getClass());
final FeatureUsageData data = new FeatureUsageData().addOS().addPluginInfo(info);
final FeatureUsageData data = new FeatureUsageData().addOS().addProject(project).addPluginInfo(info);
if (event != null) {
data.addInputEvent(event).
@@ -12,6 +12,7 @@ import com.intellij.internal.statistic.utils.PluginInfo;
import com.intellij.internal.statistic.utils.PluginInfoDetectorKt;
import com.intellij.lang.Language;
import com.intellij.openapi.components.*;
import com.intellij.openapi.project.Project;
import com.intellij.util.xmlb.annotations.MapAnnotation;
import com.intellij.util.xmlb.annotations.Tag;
import org.jetbrains.annotations.NotNull;
@@ -41,10 +42,15 @@ public class IntentionsCollector implements PersistentStateComponent<IntentionsC
}
public void record(@NotNull IntentionAction action, @NotNull Language language) {
record(null, action, language);
}
public void record(@Nullable Project project, @NotNull IntentionAction action, @NotNull Language language) {
final Class<?> clazz = getOriginalHandlerClass(action);
final PluginInfo info = PluginInfoDetectorKt.getPluginInfo(clazz);
final FeatureUsageData data = new FeatureUsageData().addOS().
addProject(project).
addPluginInfo(info).
addLanguage(language);
@@ -17,7 +17,7 @@ import java.util.prefs.Preferences
object EventLogConfiguration {
private val LOG = Logger.getInstance(EventLogConfiguration::class.java)
private const val SALT_PREFERENCE_KEY = "feature_usage_event_log_salt"
const val version: Int = 7
const val version: Int = 8
val sessionId: String = UUID.randomUUID().toString().shortedUUID()
@@ -1191,7 +1191,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Disposab
}
//noinspection AssignmentToStaticFieldFromInstanceMethod
IdeaLogger.ourLastActionId = myLastPreformedActionId;
ActionsCollector.getInstance().record(action, event);
ActionsCollector.getInstance().record(CommonDataKeys.PROJECT.getData(dataContext), action, event);
for (AnActionListener listener : myActionListeners) {
listener.beforeActionPerformed(action, dataContext, event);
}
@@ -3,7 +3,6 @@ package com.intellij.openapi.vfs.newvfs.persistent;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileAttributes;
@@ -35,9 +34,9 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
class LocalFileSystemRefreshWorker {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vfs.newvfs.persistent.RefreshWorker");
import static com.intellij.openapi.vfs.newvfs.persistent.VfsEventGenerationHelper.LOG;
class LocalFileSystemRefreshWorker {
private final boolean myIsRecursive;
private final Queue<NewVirtualFile> myRefreshQueue = new Queue<>(100);
private final VfsEventGenerationHelper myHelper = new VfsEventGenerationHelper();
@@ -89,9 +88,7 @@ class LocalFileSystemRefreshWorker {
while (!myRefreshQueue.isEmpty()) {
NewVirtualFile file = myRefreshQueue.pullFirst();
boolean fileDirty = file.isDirty();
if (LOG.isTraceEnabled()) LOG.trace("file=" + file + " dirty=" + fileDirty);
if (!fileDirty) continue;
if (!myHelper.checkDirty(file)) continue;
checkCancelled(file);
@@ -3,12 +3,10 @@ package com.intellij.openapi.vfs.newvfs.persistent;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileAttributes;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VFileProperty;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
@@ -36,14 +34,13 @@ import java.util.List;
import java.util.Set;
import static com.intellij.openapi.util.Pair.pair;
import static com.intellij.openapi.vfs.newvfs.persistent.VfsEventGenerationHelper.LOG;
import static com.intellij.util.containers.ContainerUtil.newTroveSet;
/**
* @author max
*/
public class RefreshWorker {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vfs.newvfs.persistent.RefreshWorker");
private final boolean myIsRecursive;
private final Queue<Pair<NewVirtualFile, FileAttributes>> myRefreshQueue = new Queue<>(100);
private final VfsEventGenerationHelper myHelper = new VfsEventGenerationHelper();
@@ -74,6 +71,7 @@ public class RefreshWorker {
myLocalFileSystemRefreshWorker.scan();
return;
}
NewVirtualFile root = myRefreshQueue.peekFirst().first;
NewVirtualFileSystem fs = root.getFileSystem();
if (root.isDirectory()) {
@@ -93,9 +91,7 @@ public class RefreshWorker {
while (!myRefreshQueue.isEmpty()) {
Pair<NewVirtualFile, FileAttributes> pair = myRefreshQueue.pullFirst();
NewVirtualFile file = pair.first;
boolean fileDirty = file.isDirty();
if (LOG.isTraceEnabled()) LOG.trace("file=" + file + " dirty=" + fileDirty);
if (!fileDirty) continue;
if (!myHelper.checkDirty(file)) continue;
checkCancelled(file);
@@ -216,18 +212,11 @@ public class RefreshWorker {
return true;
});
if (hasEvents) {
break;
}
if (hasEvents) break;
}
}
@NotNull
private static Path appendPath(@NotNull String parent, @NotNull String name) {
return StringUtil.endsWith(parent, "/") ? Paths.get(parent + name) : Paths.get(parent + "/" + name);
}
private void partialDirRefresh(@NotNull NewVirtualFileSystem fs,
@NotNull TObjectHashingStrategy<String> strategy,
@NotNull VirtualDirectoryImpl dir) {
@@ -289,9 +278,8 @@ public class RefreshWorker {
return true;
});
if (hasEvents) {
break;
}
if (hasEvents) break;
}
}
@@ -314,21 +302,10 @@ public class RefreshWorker {
FileAttributes attributes = fs.getAttributes(file);
if (attributes == null) return null;
String symlinkTarget = attributes.isSymLink() ? fs.resolveSymLink(file) : null;
return new NewChildRecord(name, appendPath(dir.getPath(), name), attributes, symlinkTarget);
return new NewChildRecord(name, Paths.get(dir.getPath() + '/' + name), attributes, symlinkTarget);
}
private void checkAndScheduleFileNameChange(@Nullable OpenTHashSet<String> actualNames, @NotNull VirtualFile child) {
if (actualNames != null) {
String currentName = child.getName();
String actualName = actualNames.get(currentName);
if (actualName != null && !currentName.equals(actualName)) {
myHelper.scheduleAttributeChange(child, VirtualFile.PROP_NAME, currentName, actualName);
}
}
}
private static class RefreshCancelledException extends RuntimeException {
}
private static class RefreshCancelledException extends RuntimeException { }
private void checkCancelled(@NotNull NewVirtualFile stopAt) {
if (myCancelled || ourCancellingCondition != null && ourCancellingCondition.fun(stopAt)) {
@@ -373,13 +350,23 @@ public class RefreshWorker {
if (currentIsDirectory != upToDateIsDirectory || currentIsSymlink != upToDateIsSymlink || currentIsSpecial != upToDateIsSpecial) {
myHelper.scheduleDeletion(child);
String symlinkTarget = upToDateIsSymlink ? fs.resolveSymLink(child) : null;
myHelper.scheduleCreation(parent, child.getName(), appendPath(parent.getPath(), child.getName()), childAttributes, symlinkTarget);
myHelper.scheduleCreation(parent, child.getName(), Paths.get(parent.getPath() + '/' + child.getName()), childAttributes, symlinkTarget);
return true;
}
return false;
}
private void checkAndScheduleFileNameChange(@Nullable OpenTHashSet<String> actualNames, @NotNull VirtualFile child) {
if (actualNames != null) {
String currentName = child.getName();
String actualName = actualNames.get(currentName);
if (actualName != null && !currentName.equals(actualName)) {
myHelper.scheduleAttributeChange(child, VirtualFile.PROP_NAME, currentName, actualName);
}
}
}
private static Function<? super VirtualFile, Boolean> ourCancellingCondition;
@TestOnly
@@ -7,6 +7,7 @@ import com.intellij.openapi.util.io.FileAttributes;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.impl.local.LocalFileSystemBase;
import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
import com.intellij.openapi.vfs.newvfs.events.*;
import org.jetbrains.annotations.NotNull;
@@ -15,8 +16,8 @@ import java.util.ArrayList;
import java.util.List;
class VfsEventGenerationHelper {
protected static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vfs.newvfs.persistent.RefreshWorker");
private static final Logger LOG_ATTRIBUTES = Logger.getInstance("#com.intellij.openapi.vfs.newvfs.persistent.RefreshWorker_Attributes");
static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vfs.newvfs.persistent.RefreshWorker");
private final List<VFileEvent> myEvents = new ArrayList<>();
@NotNull
@@ -24,26 +25,20 @@ class VfsEventGenerationHelper {
return myEvents;
}
void scheduleAttributeChange(@NotNull VirtualFile file, @VirtualFile.PropName @NotNull String property, Object current, Object upToDate) {
if (LOG.isTraceEnabled()) LOG.trace("update '" + property + "' file=" + file);
myEvents.add(new VFilePropertyChangeEvent(null, file, property, current, upToDate, true));
boolean checkDirty(@NotNull NewVirtualFile file) {
boolean fileDirty = file.isDirty();
if (LOG.isTraceEnabled()) LOG.trace("file=" + file + " dirty=" + fileDirty);
return fileDirty;
}
void checkContentChanged(@NotNull VirtualFile file, long oldTimestamp, long newTimestamp, long oldLength, long newLength) {
if (oldTimestamp != newTimestamp || oldLength != newLength) {
scheduleUpdateContent(file, oldTimestamp, newTimestamp, oldLength, newLength);
}
}
void scheduleUpdateContent(@NotNull VirtualFile file, long oldTimestamp, long newTimestamp, long oldLength, long newLength) {
if (LOG.isTraceEnabled()) {
LOG.trace(
if (LOG.isTraceEnabled()) LOG.trace(
"update file=" + file +
(oldTimestamp != newTimestamp ? ", oldtimestamp=" + oldTimestamp + ", newtimestamp=" + newTimestamp : "") +
(oldLength != newLength ? ", oldlength=" + oldLength + ", length=" + newLength : "")
);
(oldTimestamp != newTimestamp ? " TS=" + oldTimestamp + "->" + newTimestamp : "") +
(oldLength != newLength ? " len=" + oldLength + "->" + newLength : ""));
myEvents.add(new VFileContentChangeEvent(null, file, file.getModificationStamp(), -1, oldTimestamp, newTimestamp, oldLength, newLength, true));
}
myEvents.add(new VFileContentChangeEvent(null, file, file.getModificationStamp(), -1, oldTimestamp, newTimestamp, oldLength, newLength, true));
}
void scheduleCreation(@NotNull VirtualFile parent,
@@ -51,9 +46,8 @@ class VfsEventGenerationHelper {
@NotNull Path path,
@NotNull FileAttributes attributes,
String symlinkTarget) {
boolean isEmptyDir = attributes.isDirectory() && !LocalFileSystemBase.hasChildren(path);
if (LOG.isTraceEnabled()) LOG.trace("create parent=" + parent + " name=" + childName + " attr=" + attributes);
boolean isEmptyDir = attributes.isDirectory() && !LocalFileSystemBase.hasChildren(path);
myEvents.add(new VFileCreateEvent(null, parent, childName, attributes.isDirectory(), attributes, symlinkTarget, true, isEmptyDir));
}
@@ -76,14 +70,16 @@ class VfsEventGenerationHelper {
}
void checkWritableAttributeChange(@NotNull VirtualFile file, boolean oldWritable, boolean newWritable) {
if (LOG_ATTRIBUTES.isTraceEnabled()) {
LOG_ATTRIBUTES.trace("file=" + file + " writable vfs=" + file.isWritable() + " persistence=" + oldWritable + " real=" + newWritable);
}
if (oldWritable != newWritable) {
scheduleAttributeChange(file, VirtualFile.PROP_WRITABLE, oldWritable, newWritable);
}
}
void scheduleAttributeChange(@NotNull VirtualFile file, @VirtualFile.PropName @NotNull String property, Object current, Object upToDate) {
if (LOG.isTraceEnabled()) LOG.trace("update file=" + file + ' ' + property + '=' + current + "->" + upToDate);
myEvents.add(new VFilePropertyChangeEvent(null, file, property, current, upToDate, true));
}
void addAllEventsFrom(@NotNull VfsEventGenerationHelper otherHelper) {
myEvents.addAll(otherHelper.myEvents);
}
@@ -88,10 +88,6 @@ open class GuiTestCase {
@JvmField
val testMethod = TestName()
@Rule
@JvmField
val logActionsDuringTest = LogActionsDuringTest()
val projectFolder: String by lazy {
val dir = File(projectsFolder, testMethod.methodName)
if (!dir.mkdirs()) {
@@ -1,41 +0,0 @@
// 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.testGuiFramework.impl
import com.intellij.application.subscribe
import com.intellij.openapi.Disposable
import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.actionSystem.ex.AnActionListener
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.util.Disposer
import org.junit.rules.TestWatcher
import org.junit.runner.Description
private val LOG = logger<LogActionsDuringTest>()
/**
* Rule that logs all actions during the test.
*/
class LogActionsDuringTest : TestWatcher() {
private var disposable: Disposable? = null
private val actionListener = object : AnActionListener {
override fun beforeActionPerformed(action: AnAction, dataContext: DataContext, event: AnActionEvent) {
LOG.info("Action: $action (actionId: ${ActionManager.getInstance().getId(action)}); DataContext: $dataContext; Event: $event")
}
}
override fun starting(description: Description) {
disposable = Disposer.newDisposable()
AnActionListener.TOPIC.subscribe(disposable!!, actionListener)
}
override fun finished(description: Description) {
disposable?.let {
this.disposable = null
Disposer.dispose(it)
}
}
}
@@ -1,6 +1,4 @@
// Copyright 2000-2017 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.
// Copyright 2000-2019 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.codeInspection;
import com.intellij.codeInspection.booleanIsAlwaysInverted.BooleanMethodIsAlwaysInvertedInspection;
@@ -9,7 +7,6 @@ import com.intellij.testFramework.IdeaTestUtil;
import com.siyeh.ig.IGInspectionTestCase;
public class BooleanMethodIsAlwaysInvertedInspectionTest extends IGInspectionTestCase {
public void testUnusedMethod() {
doTest();
}
@@ -1,26 +1,24 @@
// Copyright 2000-2019 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 org.jetbrains.plugins.github.pullrequest.data.service
import com.intellij.icons.AllIcons
import com.intellij.openapi.Disposable
import com.intellij.openapi.progress.EmptyProgressIndicator
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.progress.Task
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.popup.JBPopupFactory
import com.intellij.openapi.ui.popup.JBPopupListener
import com.intellij.openapi.ui.popup.LightweightWindowEvent
import com.intellij.openapi.ui.popup.PopupChooserBuilder
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.Pair
import com.intellij.openapi.util.text.StringUtil
import com.intellij.ui.CollectionListModel
import com.intellij.ui.ColorUtil
import com.intellij.ui.SimpleTextAttributes
import com.intellij.ui.*
import com.intellij.ui.components.JBList
import com.intellij.util.ui.ColorIcon
import com.intellij.util.ui.JBUI
import com.intellij.util.ui.StatusText
import com.intellij.util.ui.UIUtil
import com.intellij.ui.speedSearch.NameFilteringListModel
import com.intellij.ui.speedSearch.SpeedSearch
import com.intellij.util.ui.*
import com.intellij.util.ui.JBUI.Panels.simplePanel
import com.intellij.util.ui.components.BorderLayoutPanel
import org.jetbrains.plugins.github.api.GithubApiRequestExecutor
import org.jetbrains.plugins.github.api.GithubApiRequests
@@ -36,10 +34,16 @@ import org.jetbrains.plugins.github.pullrequest.data.GithubPullRequestsBusyState
import org.jetbrains.plugins.github.pullrequest.data.GithubPullRequestsDataLoader
import org.jetbrains.plugins.github.util.*
import java.awt.Component
import java.awt.Cursor
import java.awt.event.ActionListener
import java.awt.event.KeyEvent
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
import java.util.function.Function
import javax.swing.*
import javax.swing.event.DocumentEvent
class GithubPullRequestsMetadataServiceImpl internal constructor(private val project: Project,
private val progressManager: ProgressManager,
@@ -101,8 +105,7 @@ class GithubPullRequestsMetadataServiceImpl internal constructor(private val pro
override fun adjustLabels(pullRequest: Long, parentComponent: JComponent) {
showChooser(pullRequest, "Labels", parentComponent,
{ SelectionListCellRenderer.Labels() }, { it.name },
{ _, _ -> repoIssuesLabels }, { it.labels.orEmpty() })
{ SelectionListCellRenderer.Labels() }, { _, _ -> repoIssuesLabels }, { it.labels.orEmpty() })
.handleOnEdt(getAdjustmentHandler(pullRequest, "label") { delta, indicator ->
requestExecutor.execute(indicator,
GithubApiRequests.Repos.Issues.Labels
@@ -120,89 +123,152 @@ class GithubPullRequestsMetadataServiceImpl internal constructor(private val pro
return showChooser(pullRequest, popupTitle, parentComponent, { list ->
val avatarIconsProvider = avatarIconsProviderFactory.create(JBUI.uiIntValue("GitHub.Avatars.Size", 20), list)
SelectionListCellRenderer.Users(avatarIconsProvider)
}, { it.login }, availableListProvider, currentListExtractor)
}, availableListProvider, currentListExtractor)
}
private fun <T> showChooser(pullRequest: Long,
popupTitle: String,
parentComponent: JComponent,
cellRendererFactory: (JList<SelectableWrapper<T>>) -> ListCellRenderer<SelectableWrapper<T>>,
speedSearchNamer: (T) -> String,
cellRendererFactory: (JList<SelectableWrapper<T>>) -> SelectionListCellRenderer<T>,
availableListProvider: (ProgressIndicator, GithubPullRequestDetailed) -> List<T>,
currentListExtractor: (GithubPullRequestDetailed) -> List<T>)
: CompletableFuture<CollectionDelta<T>> {
val listModel = CollectionListModel<SelectableWrapper<T>>()
val list = JBList<SelectableWrapper<T>>(listModel)
val list = JBList<SelectableWrapper<T>>(listModel).apply {
visibleRowCount = 7
isFocusable = false
selectionMode = ListSelectionModel.SINGLE_SELECTION
}
val listCellRenderer = cellRendererFactory(list)
list.cellRenderer = listCellRenderer
val builder = PopupChooserBuilder<SelectableWrapper<T>>(list)
.setTitle(popupTitle)
.setResizable(true)
.setMovable(true)
.setNamerForFiltering { speedSearchNamer(it.value) }
.setAutoSelectIfEmpty(false)
.setCloseOnEnter(false)
.setRenderer(cellRendererFactory(list))
.setItemsChosenCallback {
for (item in it) {
item.selected = !item.selected
val speedSearch = SpeedSearch()
val filteringListModel = NameFilteringListModel<SelectableWrapper<T>>(list,
{ listCellRenderer.getText(it.value) },
speedSearch::shouldBeShowing,
speedSearch)
speedSearch.addChangeListener {
val prevSelection = list.selectedValue // save to restore the selection on filter drop
filteringListModel.refilter()
if (filteringListModel.size > 0) {
val fullMatchIndex = if (speedSearch.isHoldingFilter) filteringListModel.closestMatchIndex
else filteringListModel.getElementIndex(prevSelection)
if (fullMatchIndex != -1) {
list.selectedIndex = fullMatchIndex
}
if (filteringListModel.size <= list.selectedIndex || !filteringListModel.contains(list.selectedValue)) {
list.selectedIndex = 0
}
list.repaint()
}
val popup = builder.createPopup()
}
val updater = builder.backgroundUpdater
updater.paintBusy(true)
list.emptyText.text = "Loading..."
val scrollPane = ScrollPaneFactory.createScrollPane(list, true).apply {
viewport.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)
isFocusable = false
}
var originalSelection: Set<T> = setOf()
val progressIndicator = EmptyProgressIndicator()
Disposer.register(popup, Disposable { progressIndicator.cancel() })
val loadingFuture = GithubAsyncUtil
.futureOfMutable { dataLoader.getDataProvider(pullRequest).detailsRequest }
.thenComposeAsync(Function { details: GithubPullRequestDetailedWithHtml ->
originalSelection = currentListExtractor(details).toHashSet()
progressManager.submitBackgroundTask(project, "Load List Of Possibilities", true, progressIndicator) {
availableListProvider(it, details)
val searchField = SearchTextField(false).apply {
border = IdeBorderFactory.createBorder(SideBorder.BOTTOM)
UIUtil.setBackgroundRecursively(this, UIUtil.getListBackground())
textEditor.border = JBUI.Borders.empty()
//focus dark magic, otherwise focus shifts to searchfield panel
isFocusable = false
addDocumentListener(object : DocumentAdapter() {
override fun textChanged(e: DocumentEvent) {
speedSearch.updatePattern(text)
}
})
.thenAcceptAsync(Consumer { possibilities ->
listModel.replaceAll(possibilities
.map { SelectableWrapper(it, originalSelection.contains(it)) }
.sortedBy { !it.selected })
}
updater.paintBusy(false)
list.emptyText.text = StatusText.DEFAULT_EMPTY_TEXT
val panel = simplePanel(scrollPane).addToTop(searchField)
ScrollingUtil.installActions(list, panel)
ListUtil.installAutoSelectOnMouseMove(list)
popup.pack(true, true)
}, EDT_EXECUTOR)
.exceptionally {
updater.paintBusy(false)
list.emptyText.clear()
list.emptyText.appendText("Can't load the list", SimpleTextAttributes.ERROR_ATTRIBUTES)
list.emptyText.appendSecondaryText(it.message.orEmpty(), SimpleTextAttributes.ERROR_ATTRIBUTES, null)
throw it
fun toggleSelection() {
for (item in list.selectedValuesList) {
item.selected = !item.selected
}
list.repaint()
}
Disposer.register(popup, Disposable {
loadingFuture.cancel(true)
list.addMouseListener(object : MouseAdapter() {
override fun mouseReleased(e: MouseEvent) {
if (UIUtil.isActionClick(e, MouseEvent.MOUSE_RELEASED) && !UIUtil.isSelectionButtonDown(e) && !e.isConsumed) toggleSelection()
}
})
val result = CompletableFuture<CollectionDelta<T>>()
popup.addListener(object : JBPopupListener {
override fun onClosed(event: LightweightWindowEvent) {
if (!loadingFuture.isDone || loadingFuture.isCancelled || loadingFuture.isCompletedExceptionally) {
result.cancel(true)
return
JBPopupFactory.getInstance().createComponentPopupBuilder(panel, searchField)
.setRequestFocus(true)
.setCancelOnClickOutside(true)
.setTitle(popupTitle)
.setResizable(true)
.setMovable(true)
.setKeyboardActions(listOf(Pair.create(ActionListener { toggleSelection() }, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0))))
.addListener(object : JBPopupListener {
private lateinit var loadingFuture: CompletableFuture<Void>
private lateinit var originalSelection: Set<T>
override fun beforeShown(event: LightweightWindowEvent) {
val popup = event.asPopup()
list.setPaintBusy(true)
list.emptyText.text = "Loading..."
val progressIndicator = EmptyProgressIndicator()
loadingFuture = GithubAsyncUtil
.futureOfMutable { dataLoader.getDataProvider(pullRequest).detailsRequest }
.thenComposeAsync(Function { details: GithubPullRequestDetailedWithHtml ->
originalSelection = currentListExtractor(details).toHashSet()
progressManager.submitBackgroundTask(project, "Load List Of Possibilities", true, progressIndicator) {
availableListProvider(it, details)
}
})
.thenAcceptAsync(Consumer { possibilities ->
listModel.replaceAll(possibilities
.map { SelectableWrapper(it, originalSelection.contains(it)) }
.sortedBy { !it.selected })
list.setPaintBusy(false)
list.emptyText.text = UIBundle.message("message.noMatchesFound")
popup.pack(true, true)
if (list.selectedIndex == -1) {
list.selectedIndex = 0
}
}, EDT_EXECUTOR)
.exceptionally {
list.setPaintBusy(false)
list.emptyText.clear()
list.emptyText.appendText("Can't load the list", SimpleTextAttributes.ERROR_ATTRIBUTES)
list.emptyText.appendSecondaryText(it.message.orEmpty(), SimpleTextAttributes.ERROR_ATTRIBUTES, null)
throw it
}
Disposer.register(popup, Disposable {
progressIndicator.cancel()
loadingFuture.cancel(true)
})
}
val selected = listModel.items.filter { it.selected }.map { it.value }
result.complete(CollectionDelta(originalSelection, selected))
}
})
popup.showUnderneathOf(parentComponent)
override fun onClosed(event: LightweightWindowEvent) {
if (!loadingFuture.isDone || loadingFuture.isCancelled || loadingFuture.isCompletedExceptionally) {
result.cancel(true)
return
}
val selected = listModel.items.filter { it.selected }.map { it.value }
result.complete(CollectionDelta(originalSelection, selected))
}
})
.createPopup()
.showUnderneathOf(parentComponent)
return result
}
@@ -240,19 +306,19 @@ class GithubPullRequestsMetadataServiceImpl internal constructor(private val pro
private data class SelectableWrapper<T>(val value: T, var selected: Boolean = false)
private sealed class SelectionListCellRenderer<T>
: ListCellRenderer<SelectableWrapper<T>>, BorderLayoutPanel() {
private sealed class SelectionListCellRenderer<T> : ListCellRenderer<SelectableWrapper<T>>, BorderLayoutPanel() {
private val mainLabel = JLabel()
private val checkIconLabel = JLabel()
init {
checkIconLabel.iconTextGap = JBUI.scale(UIUtil.DEFAULT_VGAP)
checkIconLabel.border = JBUI.Borders.empty(0, 4)
addToLeft(checkIconLabel)
addToCenter(mainLabel)
border = JBUI.Borders.empty(2)
border = JBUI.Borders.empty(4, 0)
}
override fun getListCellRendererComponent(list: JList<out SelectableWrapper<T>>,
@@ -260,9 +326,8 @@ class GithubPullRequestsMetadataServiceImpl internal constructor(private val pro
index: Int,
isSelected: Boolean,
cellHasFocus: Boolean): Component {
font = list.font
foreground = UIUtil.getListForeground(isSelected, list.hasFocus())
background = UIUtil.getListBackground(isSelected, list.hasFocus())
foreground = UIUtil.getListForeground(isSelected, true)
background = UIUtil.getListBackground(isSelected, true)
mainLabel.foreground = foreground
mainLabel.font = font
@@ -270,14 +335,8 @@ class GithubPullRequestsMetadataServiceImpl internal constructor(private val pro
mainLabel.text = getText(value.value)
mainLabel.icon = getIcon(value.value)
if (value.selected) {
checkIconLabel.icon = AllIcons.Actions.Checked
checkIconLabel.border = JBUI.Borders.empty(0, 4)
}
else {
checkIconLabel.icon = null
checkIconLabel.border = JBUI.Borders.empty(0, 10)
}
val icon = LafIconLookup.getIcon("checkmark", isSelected, false)
checkIconLabel.icon = if (value.selected) icon else EmptyIcon.create(icon)
return this
}
@@ -72,7 +72,7 @@ public abstract class AbstractModelBuilderTest {
{"2.6"}, /*{"2.7"}, {"2.8"}, {"2.9"}, {"2.10"}, {"2.11"}, {"2.12"}, {"2.13"}, */{"2.14.1"},
{"3.0"}, /*{"3.1"}, {"3.2"}, {"3.3"}, {"3.4"},*/ {"3.5"},
{"4.0"}, /*{"4.1"}, {"4.2"}, {"4.3"}, {"4.4"}, {"4.5.1"}, {"4.6"}, {"4.7"}, {"4.8"}, {"4.9"},*/ {"4.10.3"},
{"5.0"}
{"5.0"}, {"5.2"}
};
public static final String BASE_GRADLE_VERSION = String.valueOf(SUPPORTED_GRADLE_VERSIONS[SUPPORTED_GRADLE_VERSIONS.length - 1][0]);
@@ -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-2019 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 org.jetbrains.uast
import com.intellij.lang.Language
@@ -21,13 +7,12 @@ import com.intellij.psi.*
interface UastLanguagePlugin {
companion object {
val extensionPointName: ExtensionPointName<UastLanguagePlugin> =
ExtensionPointName.create<UastLanguagePlugin>("org.jetbrains.uast.uastLanguagePlugin")
private val extensionArray: Array<UastLanguagePlugin> by lazy(LazyThreadSafetyMode.PUBLICATION) { extensionPointName.extensions }
val extensionPointName = ExtensionPointName<UastLanguagePlugin>("org.jetbrains.uast.uastLanguagePlugin")
private val extensions by lazy(LazyThreadSafetyMode.PUBLICATION) { extensionPointName.extensionList }
fun getInstances(): Collection<UastLanguagePlugin> = extensionArray.toList()
fun getInstances(): Collection<UastLanguagePlugin> = extensions
fun byLanguage(language: Language): UastLanguagePlugin? = extensionArray.firstOrNull { it.language === language }
fun byLanguage(language: Language): UastLanguagePlugin? = extensions.firstOrNull { it.language === language }
}
data class ResolvedMethod(val call: UCallExpression, val method: PsiMethod)