Skip to content

Commit 18107b7

Browse files
authored
Merge pull request #349 from SeeSharpSoft/fb_github_reporter
GitHub reporter
2 parents 78a5db4 + 3c9751d commit 18107b7

File tree

12 files changed

+268
-52
lines changed

12 files changed

+268
-52
lines changed

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Compatible with _IntelliJ IDEA PhpStorm WebStorm PyCharm RubyMine AppCode
1212
This plugin introduces CSV (_Comma-Separated Values_) as a language to Jetbrains IDE with a syntax definition, structured language elements and associated file types (.csv/.tsv/.psv).
1313
This enables default editor features like syntax validation, highlighting and inspections for CSV-alike files.
1414

15-
![CSV Plugin Example](./docs/example.png)
15+
![CSV Table Editor Example](./docs/example.png)
1616

1717
## Features
1818

@@ -346,7 +346,7 @@ The plugin provides six types of intentions:
346346

347347
Install it from the Jetbrains plugin repository within your IDE (**recommended**):
348348

349-
- _File > Settings > Plugins > Browse repositories... > Search 'CSV Plugin' > Category 'Editor'_
349+
- _File > Settings > Plugins > Browse repositories... > Search 'CSV' > Category 'Editor'_
350350

351351
You can also download the JAR package from the [Jetbrains plugin repository](https://plugins.jetbrains.com/plugin/10037-csv-plugin) or from [GitHub Releases](https://github.com/SeeSharpSoft/intellij-csv-validator/releases) and add it manually to your plugins:
352352

@@ -356,7 +356,7 @@ You can also download the JAR package from the [Jetbrains plugin repository](htt
356356

357357
#### CSV Table Editor doesn't seem to work
358358

359-
Please ensure in the Settings that the corresponding files types (csv, tab/tsv, psv) are properly mapped (_File > Settings > Editor > File Types_).
359+
Please ensure in the settings that the corresponding files types (csv, tab/tsv, psv) are properly mapped (_File > Settings > Editor > File Types_).
360360

361361
#### CSV Table Editor causes the IDE to stop working properly
362362

@@ -368,9 +368,11 @@ In some cases the error log doesn't seem to point this out in a noticeable manne
368368

369369
Please read the [official instructions](https://intellij-support.jetbrains.com/hc/en-us/articles/206544879-Selecting-the-JDK-version-the-IDE-will-run-under) on how to switch to a newer JRE, or [manually install](https://github.com/SeeSharpSoft/intellij-csv-validator#installation) a [prior CSV plugin version](https://github.com/SeeSharpSoft/intellij-csv-validator/releases/tag/2.10.0).
370370

371-
#### Features of the CSV Plugin are not available
371+
#### Features of the CSV Table Editor are not available
372372

373-
If another plugin for CSV files like [Rainbow CSV](https://plugins.jetbrains.com/plugin/12896-rainbow-csv) is enabled, this plugin might not be taken into account for parsing and presenting CSV file content.
373+
If another plugin for CSV files like [Rainbow CSV](https://plugins.jetbrains.com/plugin/12896-rainbow-csv) is enabled, this plugin might not be taken into account for parsing and presenting CSV file content.
374+
375+
Therefore, please ensure in the settings the proper mapping of corresponding files types (_File > Settings > Editor > File Types_).
374376

375377
## Contribution
376378

build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ intellij {
7474
pluginName = 'CSV'
7575
updateSinceUntilBuild = false
7676
downloadSources = Boolean.parseBoolean(System.getenv().getOrDefault('IDEA_SOURCES', "true"))
77+
78+
plugins = [
79+
System.getenv().getOrDefault('GIT_PLUGIN_VERSION', 'Git4Idea'),
80+
System.getenv().getOrDefault('GITHUB_PLUGIN_VERSION', 'org.jetbrains.plugins.github')
81+
]
7782
}
7883
patchPluginXml {
7984
changeNotes = """<pre style="font-family: sans-serif">

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
# https://www.jetbrains.com/intellij-repository/releases
33
# https://www.jetbrains.com/intellij-repository/snapshots
44

5-
name='CSV Plugin'
5+
name='CSV Table Editor'
66
java.system.class.loader=com.intellij.util.lang.PathClassLoader
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
package net.seesharpsoft.intellij.plugins.csv;
2+
3+
import com.intellij.ide.DataManager;
4+
import com.intellij.openapi.actionSystem.CommonDataKeys;
5+
import com.intellij.openapi.actionSystem.DataContext;
6+
import com.intellij.openapi.application.ApplicationInfo;
7+
import com.intellij.openapi.application.ApplicationManager;
8+
import com.intellij.openapi.diagnostic.ErrorReportSubmitter;
9+
import com.intellij.openapi.diagnostic.IdeaLoggingEvent;
10+
import com.intellij.openapi.diagnostic.SubmittedReportInfo;
11+
import com.intellij.openapi.progress.*;
12+
import com.intellij.openapi.project.Project;
13+
import com.intellij.util.Consumer;
14+
import org.jetbrains.annotations.NotNull;
15+
import org.jetbrains.annotations.Nullable;
16+
import org.jetbrains.plugins.github.api.*;
17+
import org.jetbrains.plugins.github.api.data.GithubIssueState;
18+
import org.jetbrains.plugins.github.api.data.GithubResponsePage;
19+
import org.jetbrains.plugins.github.api.data.GithubSearchedIssue;
20+
import org.jetbrains.plugins.github.api.data.request.GithubRequestPagination;
21+
import org.jetbrains.plugins.github.authentication.GithubAuthenticationManager;
22+
import org.jetbrains.plugins.github.authentication.accounts.GithubAccount;
23+
24+
import java.awt.*;
25+
import java.io.IOException;
26+
import java.util.Collections;
27+
28+
public class CsvGithubIssueSubmitter extends ErrorReportSubmitter {
29+
30+
public static final String GIT_USER = "SeeSharpSoft";
31+
public static final String GIT_REPO = "intellij-csv-validator";
32+
public static final GHRepositoryPath GITHUB_FULL_PATH = new GHRepositoryPath(GIT_USER, GIT_REPO);
33+
34+
private static class CsvGithubSubmitException extends RuntimeException {
35+
CsvGithubSubmitException(Throwable exception) {
36+
super(exception);
37+
}
38+
}
39+
40+
@NotNull
41+
@Override
42+
public String getReportActionText() {
43+
return "Report to 'CSV Table Editor' (Github)";
44+
}
45+
46+
@Override
47+
public boolean submit(IdeaLoggingEvent @NotNull [] events, @Nullable String additionalInfo, @NotNull Component parentComponent, @NotNull Consumer<? super SubmittedReportInfo> consumer) {
48+
final DataContext dataContext = DataManager.getInstance().getDataContext(parentComponent);
49+
final Project project = CommonDataKeys.PROJECT.getData(dataContext);
50+
51+
for (IdeaLoggingEvent event : events) {
52+
if (!submit(event, additionalInfo, project, consumer)) {
53+
return false;
54+
}
55+
}
56+
return true;
57+
}
58+
59+
protected boolean submit(IdeaLoggingEvent event, String additionalInfo, Project project, Consumer<? super SubmittedReportInfo> consumer) {
60+
GithubAuthenticationManager githubAuthManager = GithubAuthenticationManager.getInstance();
61+
if (!githubAuthManager.ensureHasAccounts(project)) {
62+
return false;
63+
}
64+
GithubAccount githubAccount = githubAuthManager.getSingleOrDefaultAccount(project);
65+
assert githubAccount != null;
66+
// the cast shouldn't be needed due to inheritance, but Java complains for some reason in 2022.1
67+
GithubApiRequestExecutor githubExecutor = GithubApiRequestExecutor.class.cast(GithubApiRequestExecutorManager.getInstance().getExecutor(githubAccount, project));
68+
69+
Task submitTask = new Task.Backgroundable(project, getReportActionText()) {
70+
@Override
71+
public void run(@NotNull ProgressIndicator indicator) {
72+
submitToGithub(event, additionalInfo, githubExecutor, consumer, indicator);
73+
}
74+
};
75+
76+
if (ApplicationManager.getApplication().isUnitTestMode()) {
77+
submitTask.run(new EmptyProgressIndicator());
78+
} else {
79+
ProgressManager.getInstance().run(submitTask);
80+
}
81+
82+
return true;
83+
}
84+
85+
private void submitToGithub(IdeaLoggingEvent event,
86+
String additionalInfo,
87+
GithubApiRequestExecutor githubExecutor,
88+
Consumer<? super SubmittedReportInfo> consumer,
89+
ProgressIndicator progressIndicator) {
90+
try {
91+
SubmittedReportInfo.SubmissionStatus status;
92+
93+
String issueTitle = getIssueTitle(event);
94+
String issueDetails = getIssueDetails(event, additionalInfo);
95+
String foundIssueId = searchExistingIssues(githubExecutor, issueTitle, progressIndicator);
96+
97+
if (foundIssueId == null) {
98+
githubExecutor.execute(progressIndicator, createNewIssue(issueTitle, issueDetails));
99+
status = SubmittedReportInfo.SubmissionStatus.NEW_ISSUE;
100+
} else {
101+
githubExecutor.execute(progressIndicator, updateExistingIssue(foundIssueId, issueDetails));
102+
status = SubmittedReportInfo.SubmissionStatus.DUPLICATE;
103+
}
104+
consumer.consume(new SubmittedReportInfo(status));
105+
} catch (IOException exc) {
106+
throw new CsvGithubSubmitException(exc);
107+
}
108+
}
109+
110+
protected GithubApiRequest updateExistingIssue(String issueId, String content) throws IOException {
111+
return GithubApiRequests.Repos.Issues.Comments.create(
112+
GithubServerPath.DEFAULT_SERVER,
113+
GIT_USER,
114+
GIT_REPO,
115+
issueId,
116+
content
117+
);
118+
}
119+
120+
protected GithubApiRequest createNewIssue(String title, String content) throws IOException {
121+
return GithubApiRequests.Repos.Issues.create(
122+
GithubServerPath.DEFAULT_SERVER,
123+
GIT_USER,
124+
GIT_REPO,
125+
title,
126+
content,
127+
null,
128+
Collections.emptyList(),
129+
Collections.emptyList());
130+
}
131+
132+
protected String searchExistingIssues(GithubApiRequestExecutor githubExecutor, String needle, ProgressIndicator progressIndicator) throws IOException {
133+
GithubApiRequest<GithubResponsePage<GithubSearchedIssue>> existingIssueRequest =
134+
GithubApiRequests.Search.Issues.get(
135+
GithubServerPath.DEFAULT_SERVER,
136+
GITHUB_FULL_PATH,
137+
GithubIssueState.open.name(),
138+
null,
139+
needle,
140+
new GithubRequestPagination(1, 1)
141+
);
142+
143+
String issueId = null;
144+
GithubResponsePage<GithubSearchedIssue> foundIssuesPage = githubExecutor.execute(progressIndicator, existingIssueRequest);
145+
if (foundIssuesPage != null && !foundIssuesPage.getItems().isEmpty()) {
146+
GithubSearchedIssue foundIssue = foundIssuesPage.getItems().get(0);
147+
if (foundIssue.getTitle().equals(needle)) {
148+
issueId = Long.toString(foundIssue.getNumber());
149+
}
150+
}
151+
152+
return issueId;
153+
}
154+
155+
protected String getIssueTitle(IdeaLoggingEvent event) {
156+
String throwableText = event.getThrowableText();
157+
int index = Math.min(throwableText.indexOf("\r"), throwableText.indexOf("\n"));
158+
return "[Automated Report] " + event.getThrowableText().substring(0, index);
159+
}
160+
161+
protected String getIssueDetails(IdeaLoggingEvent event, String additionalInfo) {
162+
return "Message\n---\n" +
163+
(additionalInfo != null && !additionalInfo.isEmpty() ? additionalInfo : "<no message>") +
164+
"\n\n" +
165+
"Stacktrace\n---\n" +
166+
event.getThrowableText() +
167+
"\n\n" +
168+
"Plugin\n---\n" +
169+
getPluginDescriptor().getPluginClassLoader() +
170+
"\n\n" +
171+
"IDE\n---\n" +
172+
ApplicationInfo.getInstance().getVersionName() +
173+
" (" +
174+
ApplicationInfo.getInstance().getBuild() +
175+
")";
176+
}
177+
178+
@Override
179+
public String getPrivacyNoticeText() {
180+
return "An automated issue report contains the provided message, " +
181+
"the stacktrace, " +
182+
"the plugin class loader info (" +
183+
getPluginDescriptor().getPluginClassLoader() +
184+
"), the used IDE version name (" +
185+
ApplicationInfo.getInstance().getVersionName() +
186+
") and build number (" +
187+
ApplicationInfo.getInstance().getBuild() +
188+
"). " +
189+
"By sending the report I agree to the information be used for resolving this and further related issues. " +
190+
"All provided information will be publicly available at " +
191+
String.format("https://%s/%s/%s/issues", GithubServerPath.DEFAULT_SERVER, GIT_USER, GIT_REPO);
192+
}
193+
}

src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvPlugin.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ private static void openLink(Project project, String link) {
4242
}
4343

4444
public static void doAsyncProjectMaintenance(@NotNull Project project) {
45-
ProgressManager.getInstance().run(new Task.Backgroundable(project, "CSV plugin validation") {
45+
ProgressManager.getInstance().run(new Task.Backgroundable(project, "CSV Table Editor validation") {
4646
public void run(@NotNull ProgressIndicator progressIndicator) {
4747
// initialize progress indication
4848
progressIndicator.setIndeterminate(false);
@@ -72,11 +72,11 @@ public void runActivity(@NotNull Project project) {
7272

7373
NotificationGroup notificationGroup = NotificationGroupManager.getInstance().getNotificationGroup("net.seesharpsoft.intellij.plugins.csv");
7474
Notification notification = notificationGroup.createNotification(
75-
"CSV Plugin " + getVersion() + " - Change Notes",
75+
"CSV Table Editor " + getVersion() + " - Change Notes",
7676
getChangeNotes() +
7777
"<p>You can always <b>customize plugin settings</b> to your likings (shortcuts below)!</p>" +
7878
"<br>" +
79-
"<p>Visit the <b>CSV Plugin homepage</b> to read more about the available features & settings, " +
79+
"<p>Visit the <b>CSV Table Editor homepage</b> to read more about the available features & settings, " +
8080
"submit issues & feature request, " +
8181
"or show your support by rating this plugin. <b>Thanks!</b></p>"
8282
,
@@ -92,7 +92,7 @@ public void runActivity(@NotNull Project project) {
9292
notification.addAction(NotificationAction.create("Formatting", (anActionEvent, notification1) -> {
9393
openLink(project, "#preferences.sourceCode.CSV/TSV/PSV");
9494
}));
95-
notification.addAction(NotificationAction.create("Open CSV Plugin homepage", (anActionEvent, notification1) -> {
95+
notification.addAction(NotificationAction.create("Open CSV Table Editor homepage", (anActionEvent, notification1) -> {
9696
openLink(project, "https://github.com/SeeSharpSoft/intellij-csv-validator");
9797
}));
9898

src/main/java/net/seesharpsoft/intellij/plugins/csv/editor/table/swing/CsvTableEditorSwing.form

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@
104104
<properties>
105105
<horizontalTextPosition value="10"/>
106106
<iconTextGap value="2"/>
107-
<text value="CSV plugin"/>
107+
<text value="CSV Table Editor"/>
108108
</properties>
109109
</component>
110110
</children>

src/main/java/net/seesharpsoft/intellij/plugins/csv/psi/CsvPsiTreeUpdater.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -223,12 +223,13 @@ private List<Pair<TextRange, String>> collectRangesToDelete(List<Integer> indice
223223
}
224224

225225
public void addRow(@NotNull PsiElement anchor, boolean before) {
226-
while (anchor != null && !(anchor instanceof CsvRecord)) {
227-
anchor = anchor.getParent();
226+
PsiElement record = anchor;
227+
while (record != null && !(record instanceof CsvRecord)) {
228+
record = record.getParent();
228229
}
229-
assert anchor instanceof CsvRecord;
230-
doAction(new AddSiblingPsiAction(anchor, createRecord(), before));
231-
doAddLineBreak(anchor, before);
230+
assert record instanceof CsvRecord;
231+
doAction(new AddSiblingPsiAction(record, createRecord(), before));
232+
doAddLineBreak(record, before);
232233
}
233234

234235
public void deleteRow(@NotNull PsiElement row) {
@@ -277,9 +278,9 @@ public void replaceComment(@NotNull PsiElement toReplace, @Nullable String textA
277278
doAction(new ReplacePsiAction(toReplace, createComment(text)));
278279
}
279280

280-
public void replaceField(@NotNull PsiElement toReplace, @Nullable String text, boolean enquoteCommentIndicator) {
281+
public void replaceField(@NotNull PsiElement toReplace, @Nullable String textArg, boolean enquoteCommentIndicator) {
281282
assert toReplace instanceof CsvField;
282-
if (text == null) text = "";
283+
String text = textArg == null ? "" : textArg;
283284
// do not replace if not necessary
284285
if (toReplace.getText().equals(text)) return;
285286

src/main/resources/META-INF/plugin.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@
5151
<!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html for description -->
5252
<idea-version since-build="221" />
5353

54-
<!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.html
55-
on how to target different products -->
54+
<!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.html on how to target different products -->
5655
<depends>com.intellij.modules.lang</depends>
56+
<depends optional="true" config-file="withGithub.xml">org.jetbrains.plugins.github</depends>
5757

5858
<extensions defaultExtensionNs="com.intellij">
5959
<fileType name="CSV" language="csv" implementationClass="net.seesharpsoft.intellij.plugins.csv.CsvFileType" extensions="csv" fieldName="INSTANCE"/>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<idea-plugin>
2+
<extensions defaultExtensionNs="com.intellij">
3+
<errorHandler implementation="net.seesharpsoft.intellij.plugins.csv.CsvGithubIssueSubmitter" />
4+
</extensions>
5+
</idea-plugin>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package net.seesharpsoft.intellij.plugins.csv.actions;
2+
3+
import com.intellij.openapi.actionSystem.AnAction;
4+
import com.intellij.openapi.actionSystem.Presentation;
5+
import com.intellij.openapi.actionSystem.ex.ActionUtil;
6+
import com.intellij.testFramework.TestActionEvent;
7+
import com.intellij.testFramework.fixtures.BasePlatformTestCase;
8+
import com.intellij.testFramework.fixtures.CodeInsightTestFixture;
9+
import net.seesharpsoft.intellij.plugins.csv.components.CsvFileAttributes;
10+
import org.jetbrains.annotations.NotNull;
11+
12+
public class CsvActionTestBase extends BasePlatformTestCase {
13+
14+
@Override
15+
protected String getTestDataPath() {
16+
return "./src/test/resources/actions";
17+
}
18+
19+
@Override
20+
protected void tearDown() throws Exception {
21+
CsvFileAttributes.getInstance(this.getProject()).reset();
22+
super.tearDown();
23+
}
24+
25+
@NotNull
26+
public Presentation testActionGroup(@NotNull AnAction action, CodeInsightTestFixture myFixture) {
27+
// TODO this function is used instead of myFixture.testAction, due to an error with respect to missing component in datacontext
28+
// return myFixture.testAction(action);
29+
TestActionEvent e = new TestActionEvent(action);
30+
if (ActionUtil.lastUpdateAndCheckDumb(action, e, true)) {
31+
action.update(e);
32+
}
33+
return e.getPresentation();
34+
}
35+
36+
}

0 commit comments

Comments
 (0)