Skip to content

Commit

Permalink
Added Scan Files Before Checkin
Browse files Browse the repository at this point in the history
  • Loading branch information
leinardi committed Aug 31, 2018
1 parent b351480 commit 693dcc4
Show file tree
Hide file tree
Showing 16 changed files with 298 additions and 38 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#### [0.7.0]
- New: Added Scan Files Before Checkin
#### [0.5.0]
- New: Added real-time scanning!
#### [0.2.0]
- New: UX based on Checkstyle-IDEA plugin.
#### [0.2.0]
- New: Plugin id and name changed (please remove manually the old plugin).
#### [0.1.0]
- Initial release.
48 changes: 35 additions & 13 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import org.commonmark.node.Node
import org.commonmark.parser.Parser
import org.commonmark.renderer.html.HtmlRenderer

/*
* Copyright 2018 Roberto Leinardi.
*
Expand All @@ -20,24 +24,26 @@ buildscript {
addRepos(repositories)
}
dependencies {
classpath deps.errorprone_plugin
classpath 'com.atlassian.commonmark:commonmark:0.11.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

plugins {
id 'org.jetbrains.intellij' version '0.3.7'
id 'net.ltgt.errorprone' version '0.0.14'
id 'idea'
id 'java'
id 'checkstyle'
}

apply plugin: 'idea'
apply plugin: 'org.jetbrains.intellij'
apply plugin: 'java'
apply plugin: 'net.ltgt.errorprone'
apply plugin: 'checkstyle'

def hasPyCharm = project.hasProperty('pycharmPath')
def hasPythonPlugin = project.hasProperty('pythonPlugin')
def props = new Properties()
rootProject.file('src/main/resources/com/leinardi/pycharm/pylint/PylintBundle.properties').withInputStream {
props.load(it)
}

checkstyle {
ignoreFailures = false // Whether this task will ignore failures and continue running the build.
Expand All @@ -49,30 +55,39 @@ checkstyle {

gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
options.compilerArgs << '-Xlint:unchecked' << '-Xlint:deprecation'
}
}

group 'com.leinardi.plugins'
version '0.1.0'
group 'com.leinardi.pycharm'
version version

sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8

intellij {
version ideaVersion
pluginName 'Pylint'
pluginName props.getProperty('plugin.name')
downloadSources Boolean.valueOf(downloadIdeaSources)
updateSinceUntilBuild = false
updateSinceUntilBuild = true
if (hasPyCharm) {
alternativeIdePath pycharmPath
} else if (hasPythonPlugin) {
plugins += [pythonPlugin]
} else {
throw new StopActionException("Define either pycharmPath or pythonPlugin in your gradle.properties")
throw new StopActionException('Define either pycharmPath or pythonPlugin in your gradle.properties')
}
}

patchPluginXml {
version project.property('version')
sinceBuild project.property('sinceBuild')
untilBuild project.property('untilBuild')
pluginDescription props.getProperty('plugin.Pylint-PyCharm.description')
changeNotes getChangelogHtml()
}


repositories {
addRepos(repositories)
if (hasPyCharm) {
Expand All @@ -88,3 +103,10 @@ dependencies {
}
compile 'com.squareup.moshi:moshi:1.6.0'
}

def getChangelogHtml() {
Parser parser = Parser.builder().build()
Node document = parser.parseReader(rootProject.file('CHANGELOG.md').newReader())
HtmlRenderer renderer = HtmlRenderer.builder().build()
renderer.render(document)
}
4 changes: 3 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
version=0.1.0
version=0.7.0
ideaVersion=2016.1
sinceBuild=145.258
untilBuild=
downloadIdeaSources=true
#######################################################################################################################
# Uncomment one of the following settings: either pycharmPath or pythonPlugin
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/com/leinardi/pycharm/pylint/PylintConfigService.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@
public class PylintConfigService implements PersistentStateComponent<PylintConfigService> {
public PylintConfigService() {
pathToPylint = PylintBundle.message("config.pylint.path.default");
scanBeforeCheckin = true;
}

private String pathToPylint;
private boolean scanBeforeCheckin;

public String getPathToPylint() {
return pathToPylint;
Expand All @@ -41,6 +43,14 @@ public void setPathToPylint(String pathToPylint) {
this.pathToPylint = pathToPylint;
}

public boolean isScanBeforeCheckin() {
return scanBeforeCheckin;
}

public void setScanBeforeCheckin(boolean scanBeforeCheckin) {
this.scanBeforeCheckin = scanBeforeCheckin;
}

@Nullable
@Override
public PylintConfigService getState() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/*
* Copyright 2018 Roberto Leinardi.
*
* 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.leinardi.pycharm.pylint.handlers;

import com.intellij.CommonBundle;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vcs.CheckinProjectPanel;
import com.intellij.openapi.vcs.changes.CommitExecutor;
import com.intellij.openapi.vcs.checkin.CheckinHandler;
import com.intellij.openapi.vcs.ui.RefreshableOnComponent;
import com.intellij.psi.PsiFile;
import com.intellij.util.PairConsumer;
import com.intellij.util.ui.UIUtil;
import com.leinardi.pycharm.pylint.PylintConfigService;
import com.leinardi.pycharm.pylint.PylintPlugin;
import com.leinardi.pycharm.pylint.checker.Problem;
import com.leinardi.pycharm.pylint.toolwindow.PylintToolWindowPanel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.intellij.openapi.vcs.checkin.CheckinHandler.ReturnResult.CANCEL;
import static com.intellij.openapi.vcs.checkin.CheckinHandler.ReturnResult.CLOSE_WINDOW;
import static com.intellij.openapi.vcs.checkin.CheckinHandler.ReturnResult.COMMIT;
import static com.leinardi.pycharm.pylint.PylintBundle.message;
import static java.util.stream.Collectors.toList;

public class ScanFilesBeforeCheckinHandler extends CheckinHandler {
private static final Logger LOG = Logger.getInstance(ScanFilesBeforeCheckinHandler.class);

private final CheckinProjectPanel checkinPanel;
private final PylintConfigService pylintConfigService;

public ScanFilesBeforeCheckinHandler(@NotNull final CheckinProjectPanel myCheckinPanel) {
this.checkinPanel = myCheckinPanel;
pylintConfigService = PylintConfigService.getInstance(checkinPanel.getProject());
}

@Nullable
@Override
public RefreshableOnComponent getBeforeCheckinConfigurationPanel() {
final JCheckBox checkBox = new JCheckBox(message("handler.before.checkin.checkbox"));

return new RefreshableOnComponent() {
@Override
public JComponent getComponent() {
final JPanel panel = new JPanel(new BorderLayout());
panel.add(checkBox);
return panel;
}
@Override
public void refresh() {
}
@Override
public void saveState() {
pylintConfigService.setScanBeforeCheckin(checkBox.isSelected());
}
@Override
public void restoreState() {
checkBox.setSelected(pylintConfigService.isScanBeforeCheckin());
}
};
}

@Override
public ReturnResult beforeCheckin(@Nullable final CommitExecutor executor,
final PairConsumer<Object, Object> additionalDataConsumer) {
final Project project = checkinPanel.getProject();
if (project == null) {
LOG.warn("Could not get project for check-in panel, skipping");
return COMMIT;
}

final PylintPlugin plugin = project.getComponent(PylintPlugin.class);
if (plugin == null) {
LOG.warn("Could not get Pylint Plug-in, skipping");
return COMMIT;
}

if (pylintConfigService.isScanBeforeCheckin()) {
try {
final Map<PsiFile, List<Problem>> scanResults = new HashMap<>();
new Task.Modal(project, message("handler.before.checkin.scan.text"), false) {
@Override
public void run(@NotNull final ProgressIndicator progressIndicator) {
progressIndicator.setText(message("handler.before.checkin.scan.in-progress"));
progressIndicator.setIndeterminate(true);
scanResults.putAll(plugin.scanFiles(new ArrayList<>(checkinPanel.getVirtualFiles())));
}
}.queue();

return processScanResults(scanResults, executor, plugin);

} catch (ProcessCanceledException e) {
return CANCEL;
}

} else {
return COMMIT;
}
}

private ReturnResult processScanResults(final Map<PsiFile, List<Problem>> results,
final CommitExecutor executor,
final PylintPlugin plugin) {
final int errorCount = errorCountOf(results);
if (errorCount == 0) {
return COMMIT;
}

final int answer = promptUser(plugin, errorCount, executor);
if (answer == Messages.OK) {
showResultsInToolWindow(results, plugin);
return CLOSE_WINDOW;

} else if (answer == Messages.CANCEL || answer < 0) {
return CANCEL;
}

return COMMIT;
}

private int errorCountOf(final Map<PsiFile, List<Problem>> results) {
return results.entrySet().stream()
.filter(this::hasProblemsThatAreNotIgnored)
.collect(toList())
.size();
}

private boolean hasProblemsThatAreNotIgnored(final Map.Entry<PsiFile, List<Problem>> entry) {
return entry.getValue().size() > 0;
}

private int promptUser(final PylintPlugin plugin,
final int errorCount,
final CommitExecutor executor) {
String commitButtonText;
if (executor != null) {
commitButtonText = executor.getActionText();
} else {
commitButtonText = checkinPanel.getCommitActionName();
}

if (commitButtonText.endsWith("...")) {
commitButtonText = commitButtonText.substring(0, commitButtonText.length() - 3);
}

final String[] buttons = new String[]{
message("handler.before.checkin.error.review"),
commitButtonText,
CommonBundle.getCancelButtonText()};

return Messages.showDialog(plugin.getProject(), message("handler.before.checkin.error.text", errorCount),
message("handler.before.checkin.error.title"),
buttons, 0, UIUtil.getWarningIcon());
}

private void showResultsInToolWindow(final Map<PsiFile, List<Problem>> results,
final PylintPlugin plugin) {
final PylintToolWindowPanel toolWindowPanel = PylintToolWindowPanel.panelFor(plugin.getProject());
if (toolWindowPanel != null) {
toolWindowPanel.displayResults(results);
toolWindowPanel.showToolWindow();
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2018 Roberto Leinardi.
*
* 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.leinardi.pycharm.pylint.handlers;

import com.intellij.openapi.vcs.CheckinProjectPanel;
import com.intellij.openapi.vcs.changes.CommitContext;
import com.intellij.openapi.vcs.checkin.CheckinHandler;
import com.intellij.openapi.vcs.checkin.CheckinHandlerFactory;
import org.jetbrains.annotations.NotNull;

public class ScanFilesBeforeCheckinHandlerFactory extends CheckinHandlerFactory {

@NotNull
@Override
public CheckinHandler createHandler(@NotNull final CheckinProjectPanel checkinProjectPanel,
@NotNull final CommitContext commitContext) {
return new ScanFilesBeforeCheckinHandler(checkinProjectPanel);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public ResultTreeNode(final String fileName, final int[] problemCounts) {
this.text = PylintBundle.message("plugin.results.scan-file-result",
fileName,
ResultTreeModel.concatProblems(problemCounts));
icon = Icons.icon("/fileTypes/any_type.png");
icon = Icons.icon("/com/leinardi/pycharm/pylint/images/pythonFile.png");
}

/**
Expand Down
Loading

0 comments on commit 693dcc4

Please sign in to comment.