mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-15 02:59:33 +07:00
KTNB-790: Fix gist creation for Jupyter Notebooks
Add a relevant test GitOrigin-RevId: 481a423a37307c978032fe8dcde75e81df134074
This commit is contained in:
committed by
intellij-monorepo-bot
parent
2216387276
commit
9f35bc136e
@@ -10,6 +10,9 @@
|
||||
<extensionPoint qualifiedName="intellij.vcs.github.titleAndDescriptionGenerator"
|
||||
interface="org.jetbrains.plugins.github.pullrequest.ui.toolwindow.create.GHPRTitleAndDescriptionGeneratorExtension"
|
||||
dynamic="true"/>
|
||||
<extensionPoint qualifiedName="com.intellij.vcs.github.gistContentsCollector"
|
||||
interface="org.jetbrains.plugins.github.GithubGistContentsCollector"
|
||||
dynamic="true"/>
|
||||
</extensionPoints>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
@@ -62,6 +65,11 @@
|
||||
<registryKey defaultValue="5000"
|
||||
description="Milliseconds margin used when comparing known last seen date with last updated at date for PRs"
|
||||
key="github.last.seen.state.margin.millis"/>
|
||||
|
||||
<vcs.github.gistContentsCollector
|
||||
implementation="org.jetbrains.plugins.github.DefaultGithubGistContentsCollector"
|
||||
id="default"
|
||||
order="last"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="Git4Idea">
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.plugins.github
|
||||
|
||||
import com.intellij.openapi.application.ReadAction
|
||||
import com.intellij.openapi.application.WriteAction
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.openapi.fileTypes.FileTypeManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.intellij.openapi.vcs.changes.ChangeListManager
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.plugins.github.api.data.request.GithubGistRequest
|
||||
import org.jetbrains.plugins.github.i18n.GithubBundle
|
||||
import org.jetbrains.plugins.github.util.GithubNotificationIdsHolder
|
||||
import org.jetbrains.plugins.github.util.GithubNotifications
|
||||
import org.jetbrains.plugins.github.util.GithubUtil
|
||||
import java.io.IOException
|
||||
|
||||
open class DefaultGithubGistContentsCollector : GithubGistContentsCollector {
|
||||
override fun collectContents(gistEventData: GithubGistContentsCollector.GistEventData): List<GithubGistRequest.FileContent> {
|
||||
val (project, editor, file, files) = gistEventData
|
||||
|
||||
if (editor != null) {
|
||||
val contents = getContentFromEditor(editor, file)
|
||||
if (contents != null) {
|
||||
return contents
|
||||
}
|
||||
}
|
||||
|
||||
val myFiles: Array<VirtualFile>? = files ?: file?.let { arrayOf(it) }
|
||||
if (myFiles != null) {
|
||||
return buildList {
|
||||
for (vf in myFiles) {
|
||||
addAll(getContentFromFile(vf, project, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG.error("File, files and editor can't be null all at once!")
|
||||
throw IllegalStateException("File, files and editor can't be null all at once!")
|
||||
}
|
||||
|
||||
protected open fun getContentFromEditor(editor: Editor, file: VirtualFile?): List<GithubGistRequest.FileContent>? {
|
||||
val text: String = ReadAction.compute<String?, java.lang.RuntimeException> { editor.selectionModel.selectedText } ?: editor.document.text
|
||||
if (text.isBlank()) {
|
||||
return null
|
||||
}
|
||||
|
||||
val fileName = file?.name.orEmpty()
|
||||
return listOf(GithubGistRequest.FileContent(fileName, text))
|
||||
}
|
||||
|
||||
private fun getContentFromFile(file: VirtualFile, project: Project, prefix: String?): List<GithubGistRequest.FileContent> {
|
||||
if (file.isDirectory) {
|
||||
return getContentFromDirectory(file, project, prefix)
|
||||
}
|
||||
if (file.fileType.isBinary) {
|
||||
GithubNotifications.showWarning(project, GithubNotificationIdsHolder.GIST_CANNOT_CREATE,
|
||||
GithubBundle.message("cannot.create.gist"),
|
||||
GithubBundle.message("create.gist.error.binary.file", file.name))
|
||||
return emptyList()
|
||||
}
|
||||
val content = WriteAction.computeAndWait<String?, RuntimeException> { getFileContents(file) }
|
||||
if (content == null) {
|
||||
GithubNotifications.showWarning(project,
|
||||
GithubNotificationIdsHolder.GIST_CANNOT_CREATE,
|
||||
GithubBundle.message("cannot.create.gist"),
|
||||
GithubBundle.message("create.gist.error.content.read", file.name))
|
||||
return emptyList()
|
||||
}
|
||||
if (content.isBlank()) {
|
||||
return emptyList()
|
||||
}
|
||||
val filename = addPrefix(file.name, prefix, false)
|
||||
return listOf(GithubGistRequest.FileContent(filename, content))
|
||||
}
|
||||
|
||||
private fun getFileContents(file: VirtualFile): @NlsSafe String? {
|
||||
try {
|
||||
return getFileContentInternal(file)
|
||||
}
|
||||
catch (e: IOException) {
|
||||
LOG.info("Couldn't read contents of the file $file", e)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun getFileContentInternal(file: VirtualFile): @NlsSafe String? {
|
||||
val fileDocumentManager = FileDocumentManager.getInstance()
|
||||
val document = fileDocumentManager.getDocument(file)
|
||||
if (document != null) {
|
||||
fileDocumentManager.saveDocument(document)
|
||||
return document.text
|
||||
}
|
||||
else {
|
||||
return String(file.contentsToByteArray(), file.charset)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getContentFromDirectory(dir: VirtualFile, project: Project, prefix: String?): List<GithubGistRequest.FileContent> {
|
||||
val contents: MutableList<GithubGistRequest.FileContent> = ArrayList()
|
||||
for (file in dir.children) {
|
||||
if (!isFileIgnored(file, project)) {
|
||||
val pref = addPrefix(dir.name, prefix, true)
|
||||
contents.addAll(getContentFromFile(file, project, pref))
|
||||
}
|
||||
}
|
||||
return contents
|
||||
}
|
||||
|
||||
private fun addPrefix(name: String, prefix: String?, addTrailingSlash: Boolean): String {
|
||||
var pref = prefix ?: ""
|
||||
pref += name
|
||||
if (addTrailingSlash) {
|
||||
pref += "_"
|
||||
}
|
||||
return pref
|
||||
}
|
||||
|
||||
private fun isFileIgnored(file: VirtualFile, project: Project): Boolean {
|
||||
val manager = ChangeListManager.getInstance(project)
|
||||
return manager.isIgnoredFile(file) || FileTypeManager.getInstance().isFileIgnored(file)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG = GithubUtil.LOG
|
||||
}
|
||||
}
|
||||
@@ -2,18 +2,11 @@
|
||||
package org.jetbrains.plugins.github;
|
||||
|
||||
import com.intellij.ide.BrowserUtil;
|
||||
import com.intellij.notebook.editor.BackedVirtualFile;
|
||||
import com.intellij.openapi.actionSystem.ActionUpdateThread;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.openapi.application.WriteAction;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.fileTypes.FileTypeManager;
|
||||
import com.intellij.openapi.ide.CopyPasteManager;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.Task;
|
||||
@@ -21,8 +14,6 @@ import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vcs.changes.ChangeListManager;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -39,14 +30,12 @@ import org.jetbrains.plugins.github.util.*;
|
||||
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
public class GithubCreateGistAction extends DumbAwareAction {
|
||||
private static final Logger LOG = GithubUtil.LOG;
|
||||
private static final Condition<@Nullable VirtualFile> FILE_WITH_CONTENT = f -> f != null && !(f.getFileType().isBinary());
|
||||
|
||||
@Override
|
||||
@@ -134,7 +123,7 @@ public class GithubCreateGistAction extends DumbAwareAction {
|
||||
if (token == null) return;
|
||||
GithubApiRequestExecutor requestExecutor = GithubApiRequestExecutor.Factory.getInstance().create(account.getServer(), token);
|
||||
|
||||
List<FileContent> contents = collectContents(project, editor, file, files);
|
||||
List<FileContent> contents = GithubGistContentsCollector.Companion.collectContents(project, editor, file, files);
|
||||
if (contents.isEmpty()) return;
|
||||
|
||||
String gistUrl = createGist(project, requestExecutor, indicator, account.getServer(),
|
||||
@@ -176,39 +165,6 @@ public class GithubCreateGistAction extends DumbAwareAction {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static List<FileContent> collectContents(@NotNull Project project,
|
||||
@Nullable Editor editor,
|
||||
@Nullable VirtualFile file,
|
||||
VirtualFile @Nullable [] files) {
|
||||
boolean isBackedFile = file instanceof BackedVirtualFile;
|
||||
if (editor != null) {
|
||||
String content = getContentFromEditor(editor, isBackedFile);
|
||||
if (content != null) {
|
||||
if (file != null) {
|
||||
return Collections.singletonList(new FileContent(file.getName(), content));
|
||||
}
|
||||
else {
|
||||
return Collections.singletonList(new FileContent("", content));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (files != null) {
|
||||
List<FileContent> contents = new ArrayList<>();
|
||||
for (VirtualFile vf : files) {
|
||||
contents.addAll(getContentFromFile(vf, project, null));
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
|
||||
if (file != null) {
|
||||
return getContentFromFile(file, project, null);
|
||||
}
|
||||
|
||||
LOG.error("File, files and editor can't be null all at once!");
|
||||
throw new IllegalStateException("File, files and editor can't be null all at once!");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static String createGist(@NotNull Project project,
|
||||
@NotNull GithubApiRequestExecutor executor,
|
||||
@@ -240,88 +196,4 @@ public class GithubCreateGistAction extends DumbAwareAction {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getContentFromEditor(@NotNull final Editor editor, boolean onlySelection) {
|
||||
String text = ReadAction.compute(() -> editor.getSelectionModel().getSelectedText());
|
||||
if (text == null && !onlySelection) {
|
||||
text = editor.getDocument().getText();
|
||||
}
|
||||
|
||||
if (StringUtil.isEmptyOrSpaces(text)) {
|
||||
return null;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<FileContent> getContentFromFile(@NotNull final VirtualFile file, @NotNull Project project, @Nullable String prefix) {
|
||||
final VirtualFile realFile = BackedVirtualFile.getOriginFileIfBacked(file);
|
||||
if (realFile.isDirectory()) {
|
||||
return getContentFromDirectory(realFile, project, prefix);
|
||||
}
|
||||
if (realFile.getFileType().isBinary()) {
|
||||
GithubNotifications
|
||||
.showWarning(project, GithubNotificationIdsHolder.GIST_CANNOT_CREATE,
|
||||
GithubBundle.message("cannot.create.gist"),
|
||||
GithubBundle.message("create.gist.error.binary.file", realFile.getName()));
|
||||
return Collections.emptyList();
|
||||
}
|
||||
String content = WriteAction.computeAndWait(() -> {
|
||||
try {
|
||||
FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
|
||||
Document document = fileDocumentManager.getDocument(realFile);
|
||||
if (document != null) {
|
||||
fileDocumentManager.saveDocument(document);
|
||||
return document.getText();
|
||||
}
|
||||
else {
|
||||
return new String(realFile.contentsToByteArray(), realFile.getCharset());
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.info("Couldn't read contents of the file " + realFile, e);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
if (content == null) {
|
||||
GithubNotifications
|
||||
.showWarning(project,
|
||||
GithubNotificationIdsHolder.GIST_CANNOT_CREATE,
|
||||
GithubBundle.message("cannot.create.gist"),
|
||||
GithubBundle.message("create.gist.error.content.read", realFile.getName()));
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (StringUtil.isEmptyOrSpaces(content)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
String filename = addPrefix(realFile.getName(), prefix, false);
|
||||
return Collections.singletonList(new FileContent(filename, content));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<FileContent> getContentFromDirectory(@NotNull VirtualFile dir, @NotNull Project project, @Nullable String prefix) {
|
||||
List<FileContent> contents = new ArrayList<>();
|
||||
for (VirtualFile file : dir.getChildren()) {
|
||||
if (!isFileIgnored(file, project)) {
|
||||
String pref = addPrefix(dir.getName(), prefix, true);
|
||||
contents.addAll(getContentFromFile(file, project, pref));
|
||||
}
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
|
||||
private static String addPrefix(@NotNull String name, @Nullable String prefix, boolean addTrailingSlash) {
|
||||
String pref = prefix == null ? "" : prefix;
|
||||
pref += name;
|
||||
if (addTrailingSlash) {
|
||||
pref += "_";
|
||||
}
|
||||
return pref;
|
||||
}
|
||||
|
||||
private static boolean isFileIgnored(@NotNull VirtualFile file, @NotNull Project project) {
|
||||
ChangeListManager manager = ChangeListManager.getInstance(project);
|
||||
return manager.isIgnoredFile(file) || FileTypeManager.getInstance().isFileIgnored(file);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.plugins.github
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.plugins.github.api.data.request.GithubGistRequest
|
||||
|
||||
interface GithubGistContentsCollector {
|
||||
data class GistEventData(
|
||||
val project: Project,
|
||||
val editor: Editor?,
|
||||
val file: VirtualFile?,
|
||||
val files: Array<VirtualFile>?,
|
||||
)
|
||||
|
||||
/**
|
||||
* Collects the contents of a GitHub Gist based on the provided event data.
|
||||
*
|
||||
* @param gistEventData Data related to the Gist event, including project reference, editor instance, and target files.
|
||||
* @return A list of file contents for the Gist request.
|
||||
* Null indicates that this collector isn't responsible for this type of data, and the content should be passed to the next collector.
|
||||
* Empty list means that the collector is aware of this type of data but wasn't able to collect any content.
|
||||
*/
|
||||
fun collectContents(gistEventData: GistEventData): List<GithubGistRequest.FileContent>?
|
||||
|
||||
companion object {
|
||||
val EP = ExtensionPointName.create<GithubGistContentsCollector>("com.intellij.vcs.github.gistContentsCollector")
|
||||
|
||||
fun collectContents(
|
||||
project: Project,
|
||||
editor: Editor?,
|
||||
file: VirtualFile?,
|
||||
files: Array<VirtualFile>?,
|
||||
): List<GithubGistRequest.FileContent> {
|
||||
val eventData = GistEventData(project, editor, file, files)
|
||||
return EP.extensionList.firstNotNullOf { it.collectContents(eventData) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ class GithubCreateGistContentTest : GithubCreateGistContentTestBase() {
|
||||
val file = projectRoot.findFileByRelativePath("file.txt")
|
||||
assertNotNull(file)
|
||||
|
||||
val actual = GithubCreateGistAction.collectContents(myProject, null, file, null)
|
||||
val actual = collectContents(myProject, null, file, null)
|
||||
|
||||
checkEquals(expected, actual)
|
||||
}
|
||||
@@ -40,7 +40,7 @@ class GithubCreateGistContentTest : GithubCreateGistContentTestBase() {
|
||||
val file = projectRoot.findFileByRelativePath("folder")
|
||||
assertNotNull(file)
|
||||
|
||||
val actual = GithubCreateGistAction.collectContents(myProject, null, file, null)
|
||||
val actual = collectContents(myProject, null, file, null)
|
||||
|
||||
checkEquals(expected, actual)
|
||||
}
|
||||
@@ -51,7 +51,7 @@ class GithubCreateGistContentTest : GithubCreateGistContentTestBase() {
|
||||
val file = projectRoot.findFileByRelativePath("folder/empty_folder")
|
||||
assertNotNull(file)
|
||||
|
||||
val actual = GithubCreateGistAction.collectContents(myProject, null, file, null)
|
||||
val actual = collectContents(myProject, null, file, null)
|
||||
|
||||
checkEquals(expected, actual)
|
||||
}
|
||||
@@ -62,7 +62,7 @@ class GithubCreateGistContentTest : GithubCreateGistContentTestBase() {
|
||||
val file = projectRoot.findFileByRelativePath("folder/empty_file")
|
||||
assertNotNull(file)
|
||||
|
||||
val actual = GithubCreateGistAction.collectContents(myProject, null, file, null)
|
||||
val actual = collectContents(myProject, null, file, null)
|
||||
|
||||
checkEquals(expected, actual)
|
||||
}
|
||||
@@ -73,15 +73,13 @@ class GithubCreateGistContentTest : GithubCreateGistContentTestBase() {
|
||||
expected.add(FileContent("file2", "file2 content"))
|
||||
expected.add(FileContent("file3", "file3 content"))
|
||||
|
||||
val files = arrayOfNulls<VirtualFile>(3)
|
||||
files[0] = projectRoot.findFileByRelativePath("file.txt")
|
||||
files[1] = projectRoot.findFileByRelativePath("folder/file2")
|
||||
files[2] = projectRoot.findFileByRelativePath("folder/dir/file3")
|
||||
assertNotNull(files[0])
|
||||
assertNotNull(files[1])
|
||||
assertNotNull(files[2])
|
||||
val files = arrayOf(
|
||||
projectRoot.findFileByRelativePath("file.txt")!!,
|
||||
projectRoot.findFileByRelativePath("folder/file2")!!,
|
||||
projectRoot.findFileByRelativePath("folder/dir/file3")!!,
|
||||
)
|
||||
|
||||
val actual = GithubCreateGistAction.collectContents(myProject, null, null, files)
|
||||
val actual = collectContents(myProject, null, null, files)
|
||||
|
||||
checkEquals(expected, actual)
|
||||
}
|
||||
@@ -91,7 +89,7 @@ class GithubCreateGistContentTest : GithubCreateGistContentTestBase() {
|
||||
|
||||
val files = VirtualFile.EMPTY_ARRAY
|
||||
|
||||
val actual = GithubCreateGistAction.collectContents(myProject, null, null, files)
|
||||
val actual = collectContents(myProject, null, null, files)
|
||||
|
||||
checkEquals(expected, actual)
|
||||
}
|
||||
@@ -109,7 +107,7 @@ class GithubCreateGistContentTest : GithubCreateGistContentTestBase() {
|
||||
val expected = ArrayList<FileContent>()
|
||||
expected.add(FileContent("file.txt", "file.txt content"))
|
||||
|
||||
val actual = GithubCreateGistAction.collectContents(myProject, editor, file, null)
|
||||
val actual = collectContents(myProject, editor, file, null)
|
||||
|
||||
checkEquals(expected, actual)
|
||||
}
|
||||
@@ -127,7 +125,7 @@ class GithubCreateGistContentTest : GithubCreateGistContentTestBase() {
|
||||
val expected = ArrayList<FileContent>()
|
||||
expected.add(FileContent("", "file.txt content"))
|
||||
|
||||
val actual = GithubCreateGistAction.collectContents(myProject, editor, null, null)
|
||||
val actual = collectContents(myProject, editor, null, null)
|
||||
|
||||
checkEquals(expected, actual)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// 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
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Comparing
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.plugins.github.api.data.request.GithubGistRequest.FileContent
|
||||
import org.jetbrains.plugins.github.test.GithubTest
|
||||
|
||||
@@ -15,4 +18,13 @@ abstract class GithubCreateGistContentTestBase : GithubTest() {
|
||||
protected fun checkEquals(expected: List<FileContent>, actual: List<FileContent>) {
|
||||
assertTrue("Gist content differs from sample", Comparing.haveEqualElements(expected, actual))
|
||||
}
|
||||
|
||||
protected fun collectContents(
|
||||
project: Project,
|
||||
editor: Editor?,
|
||||
file: VirtualFile?,
|
||||
files: Array<VirtualFile>?,
|
||||
): List<FileContent> {
|
||||
return GithubGistContentsCollector.collectContents(project, editor, file, files)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user