Skip to content

Commit

Permalink
Added real-time scanning
Browse files Browse the repository at this point in the history
  • Loading branch information
leinardi committed Aug 31, 2018
1 parent b861835 commit 38d5faa
Show file tree
Hide file tree
Showing 31 changed files with 2,071 additions and 366 deletions.
1 change: 0 additions & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
#Wed Aug 22 22:33:37 CEST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
Expand Down
59 changes: 59 additions & 0 deletions src/main/java/com/leinardi/pycharm/pylint/PylintConfigService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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;

import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.project.Project;
import com.intellij.util.xmlb.XmlSerializerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@State(name = "PylintConfigService", storages = {@Storage("pylint.xml")})
public class PylintConfigService implements PersistentStateComponent<PylintConfigService> {
public PylintConfigService() {
pathToPylint = PylintBundle.message("config.pylint.path.default");
}

private String pathToPylint;

public String getPathToPylint() {
return pathToPylint;
}

public void setPathToPylint(String pathToPylint) {
this.pathToPylint = pathToPylint;
}

@Nullable
@Override
public PylintConfigService getState() {
return this;
}

@Override
public void loadState(@NotNull PylintConfigService config) {
XmlSerializerUtil.copyBean(config, this);
}

@Nullable
public static PylintConfigService getInstance(Project project) {
return ServiceManager.getService(project, PylintConfigService.class);
}
}
127 changes: 127 additions & 0 deletions src/main/java/com/leinardi/pycharm/pylint/PylintConfigurable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* 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;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.project.Project;
import com.leinardi.pycharm.pylint.ui.PylintConfigPanel;
import org.jetbrains.annotations.NotNull;

import javax.swing.JComponent;

/**
* The "configurable component" required by PyCharm to provide a Swing form for inclusion into the 'Settings'
* dialog. Registered in {@code plugin.xml} as a {@code projectConfigurable} extension.
*/
public class PylintConfigurable implements Configurable {
private static final Logger LOG = Logger.getInstance(PylintConfigurable.class);

private final Project project;

private final PylintConfigPanel configPanel;
private final PylintConfigService pylintConfigService;
// private final PylintProjectService pylintProjectService;
// private final PluginConfigurationManager pluginConfigurationManager;

public PylintConfigurable(@NotNull final Project project/*,
@NotNull final PylintProjectService pylintProjectService,
@NotNull final PluginConfigurationManager pluginConfigurationManager*/) {
this(project, new PylintConfigPanel(project/*, pylintProjectService*/)/*,
pylintProjectService, pluginConfigurationManager*/);
}

PylintConfigurable(@NotNull final Project project,
@NotNull final PylintConfigPanel configPanel/*,
@NotNull final PylintProjectService pylintProjectService,
@NotNull final PluginConfigurationManager pluginConfigurationManager*/) {
this.project = project;
this.configPanel = configPanel;
pylintConfigService = PylintConfigService.getInstance(project);
// this.pylintProjectService = pylintProjectService;
// this.pluginConfigurationManager = pluginConfigurationManager;
}

@Override
public String getDisplayName() {
return PylintBundle.message("plugin.configuration-name");
}

@Override
public String getHelpTopic() {
return null;
}

@Override
public JComponent createComponent() {
reset();
return configPanel.getPanel();
}

@Override
public boolean isModified() {
// final PluginConfiguration oldConfig = pluginConfigurationManager.getCurrent();
// final PluginConfiguration newConfig = PluginConfigurationBuilder
// .from(configPanel.getPluginConfiguration())
// .withScanBeforeCheckin(oldConfig.isScanBeforeCheckin())
// .build();
//
boolean result = !configPanel.getPathToPylint().equals(pylintConfigService.getPathToPylint());
if (LOG.isDebugEnabled()) {
LOG.debug("Has config changed? " + result);
}
return result;
}

@Override
public void apply() {
// final PluginConfiguration newConfig = PluginConfigurationBuilder.from(configPanel
// .getPluginConfiguration())
// .withScanBeforeCheckin(pluginConfigurationManager.getCurrent().isScanBeforeCheckin())
// .build();
// pluginConfigurationManager.setCurrent(newConfig, true);
//
// activateCurrentPylintVersion(newConfig.getPylintVersion(), newConfig.getThirdPartyClasspath());
// if (!newConfig.isCopyLibs()) {
// new TempDirProvider().deleteCopiedLibrariesDir(project);
// }
pylintConfigService.setPathToPylint(configPanel.getPathToPylint());
}

// private void activateCurrentPylintVersion(final String pylintVersion,
// final List<String> thirdPartyClasspath) {
// // Invalidate cache *before* activating the new Pylint version
// getCheckerFactoryCache().invalidate();
//
// pylintProjectService.activatePylintVersion(pylintVersion, thirdPartyClasspath);
// }
//
// private CheckerFactoryCache getCheckerFactoryCache() {
// return ServiceManager.getService(project, CheckerFactoryCache.class);
// }
//
// public void reset() {
// final PluginConfiguration pluginConfig = pluginConfigurationManager.getCurrent();
// configPanel.showPluginConfiguration(pluginConfig);
//
// activateCurrentPylintVersion(pluginConfig.getPylintVersion(), pluginConfig.getThirdPartyClasspath());
// }
@Override
public void disposeUIResources() {
// do nothing
}
}
176 changes: 176 additions & 0 deletions src/main/java/com/leinardi/pycharm/pylint/PylintInspection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/*
* 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;

import com.intellij.codeInspection.InspectionManager;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFile;
import com.leinardi.pycharm.pylint.checker.Problem;
import com.leinardi.pycharm.pylint.checker.ScanFiles;
import com.leinardi.pycharm.pylint.checker.ScannableFile;
import com.leinardi.pycharm.pylint.exception.PylintPluginParseException;
import com.leinardi.pycharm.pylint.ui.PylintInspectionPanel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.JComponent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import static com.leinardi.pycharm.pylint.PylintBundle.message;
import static com.leinardi.pycharm.pylint.util.Async.asyncResultOf;
import static com.leinardi.pycharm.pylint.util.Notifications.showException;
import static com.leinardi.pycharm.pylint.util.Notifications.showWarning;
import static java.util.Collections.singletonList;
import static java.util.Optional.ofNullable;

public class PylintInspection extends LocalInspectionTool {

private static final Logger LOG = Logger.getInstance(PylintInspection.class);
private static final List<Problem> NO_PROBLEMS_FOUND = Collections.emptyList();

private final PylintInspectionPanel configPanel = new PylintInspectionPanel();

private PylintPlugin plugin(final Project project) {
final PylintPlugin pylintPlugin = project.getComponent(PylintPlugin.class);
if (pylintPlugin == null) {
throw new IllegalStateException("Couldn't get pylint plugin");
}
return pylintPlugin;
}

@Nullable
@Override
public JComponent createOptionsPanel() {
return configPanel;
}

@Override
public ProblemDescriptor[] checkFile(@NotNull final PsiFile psiFile,
@NotNull final InspectionManager manager,
final boolean isOnTheFly) {
// final Module module = moduleOf(psiFile);
return asProblemDescriptors(asyncResultOf(() -> inspectFile(psiFile, /*module, */manager), NO_PROBLEMS_FOUND),
manager);
}

@Nullable
private Module moduleOf(@NotNull final PsiFile psiFile) {
return ModuleUtil.findModuleForPsiElement(psiFile);
}

@Nullable
public List<Problem> inspectFile(@NotNull final PsiFile psiFile,
// @Nullable final Module module,
@NotNull final InspectionManager manager) {
LOG.debug("Inspection has been invoked.");

final PylintPlugin plugin = plugin(manager.getProject());

// ConfigurationLocation configurationLocation = null;
final List<ScannableFile> scannableFiles = new ArrayList<>();
try {
// configurationLocation = plugin.getConfigurationLocation(module, null);
// if (configurationLocation == null || configurationLocation.isBlacklisted()) {
// return NO_PROBLEMS_FOUND;
// }

scannableFiles.addAll(ScannableFile.createAndValidate(singletonList(psiFile), plugin/*, module*/));
ScanFiles scanFiles = new ScanFiles(plugin, Collections.singletonList(psiFile.getVirtualFile()));
Map<PsiFile, List<Problem>> map = scanFiles.call();
if (map.isEmpty()) {
return NO_PROBLEMS_FOUND;
}
return map.get(psiFile);

} catch (ProcessCanceledException | AssertionError e) {
LOG.debug("Process cancelled when scanning: " + psiFile.getName());
return NO_PROBLEMS_FOUND;

} catch (PylintPluginParseException e) {
LOG.debug("Parse exception caught when scanning: " + psiFile.getName(), e);
return NO_PROBLEMS_FOUND;

} catch (Throwable e) {
handlePluginException(e, psiFile, /*plugin, configurationLocation,*/ manager.getProject());
return NO_PROBLEMS_FOUND;

} finally {
scannableFiles.forEach(ScannableFile::deleteIfRequired);
}
}

// private List<Problem> dropIgnoredProblems(final List<Problem> problems) {
// return problems.stream()
// .filter(problem -> problem.severityLevel() != SeverityLevel.Ignore)
// .collect(toList());
// }

private void handlePluginException(final Throwable e,
final @NotNull PsiFile psiFile,
// final PylintPlugin plugin,
// final ConfigurationLocation
// configurationLocation,
final @NotNull Project project) {
// if (e.getCause() != null && e.getCause() instanceof FileNotFoundException) {
// disableActiveConfiguration(plugin, project);
// } else
if (e.getCause() != null && e.getCause() instanceof IOException) {
showWarning(project, message("pylint.file-io-failed"));
// blacklist(configurationLocation);

} else {
LOG.warn("Pylint threw an exception when scanning: " + psiFile.getName(), e);
showException(project, e);
// blacklist(configurationLocation);
}
}

// private void disableActiveConfiguration(final PylintPlugin plugin, final Project project) {
// plugin.configurationManager().disableActiveConfiguration();
// showWarning(project, message("pylint.configuration-disabled.file-not-found"));
// }

// private void blacklist(final ConfigurationLocation configurationLocation) {
// if (configurationLocation != null) {
// configurationLocation.blacklist();
// }
// }

@NotNull
private ProblemDescriptor[] asProblemDescriptors(final List<Problem> results, final InspectionManager manager) {
return ofNullable(results)
.map(problems -> problems.stream()
.map(problem -> problem.toProblemDescriptor(manager))
.toArray(ProblemDescriptor[]::new))
.orElseGet(() -> ProblemDescriptor.EMPTY_ARRAY);
}

// private CheckerFactory checkerFactory(final Project project) {
// return ServiceManager.getService(project, CheckerFactory.class);
// }

}
Loading

0 comments on commit 38d5faa

Please sign in to comment.