Skip to content

Commit

Permalink
Refactor: separate core/ui for import sorting.
Browse files Browse the repository at this point in the history
  • Loading branch information
fabioz committed May 9, 2024
1 parent 78b9494 commit f39d157
Show file tree
Hide file tree
Showing 14 changed files with 550 additions and 492 deletions.
1 change: 1 addition & 0 deletions plugins/org.python.pydev.ast/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Export-Package: org.python.pydev.ast,
org.python.pydev.ast.refactoring,
org.python.pydev.ast.runners,
org.python.pydev.ast.simpleassist,
org.python.pydev.ast.sort_imports,
org.python.pydev.core.pep8,
org.python.pydev.core.templates,
org.python.pydev.plugin,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package org.python.pydev.editor.actions.organize_imports;
package org.python.pydev.ast.sort_imports;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
Expand All @@ -15,25 +16,22 @@
import java.util.Set;
import java.util.TreeMap;

import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
import org.python.pydev.ast.formatter.PyFormatter;
import org.python.pydev.core.IPyFormatStdProvider;
import org.python.pydev.core.docutils.ImportHandle;
import org.python.pydev.core.docutils.ImportHandle.ImportHandleInfo;
import org.python.pydev.core.docutils.PyImportsHandling;
import org.python.pydev.core.docutils.PySelection;
import org.python.pydev.core.formatter.FormatStd;
import org.python.pydev.core.imports.ImportPreferences;
import org.python.pydev.core.log.Log;
import org.python.pydev.editor.actions.PyFormatAction;
import org.python.pydev.plugin.PyDevUiPrefs;
import org.python.pydev.shared_core.SharedCorePlugin;
import org.python.pydev.shared_core.io.FileUtils;
import org.python.pydev.shared_core.string.FastStringBuffer;
import org.python.pydev.shared_core.string.StringUtils;
import org.python.pydev.shared_core.structure.Tuple;
import org.python.pydev.shared_core.structure.Tuple3;
import org.python.pydev.ui.importsconf.ImportsPreferencesPage;

public class ImportArranger {

Expand Down Expand Up @@ -181,32 +179,13 @@ public int compare(Tuple<String, String> o1, Tuple<String, String> o2) {
}
}

/**
* @return the maximum number of columns that may be available in a line.
*/
private static int getMaxCols(boolean multilineImports) {
final int maxCols;
if (multilineImports) {
if (SharedCorePlugin.inTestMode()) {
maxCols = 80;
} else {
IPreferenceStore chainedPrefStore = PyDevUiPrefs.getChainedPrefStore();
maxCols = chainedPrefStore
.getInt(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_PRINT_MARGIN_COLUMN);
}
} else {
maxCols = Integer.MAX_VALUE;
}
return maxCols;
}

/**
* @return true if the imports should be split with parenthesis (instead of escaping)
*/
private static boolean getBreakImportsWithParenthesis(IPyFormatStdProvider edit) {
String breakIportMode = ImportsPreferencesPage.getBreakIportMode(edit);
String breakIportMode = ImportPreferences.getBreakImportMode(edit);
boolean breakWithParenthesis = true;
if (!breakIportMode.equals(ImportsPreferencesPage.BREAK_IMPORTS_MODE_PARENTHESIS)) {
if (!breakIportMode.equals(ImportPreferences.BREAK_IMPORTS_MODE_PARENTHESIS)) {
breakWithParenthesis = false;
}
return breakWithParenthesis;
Expand All @@ -225,21 +204,20 @@ private static boolean getBreakImportsWithParenthesis(IPyFormatStdProvider edit)
protected final IPyFormatStdProvider edit;

public ImportArranger(IDocument doc, boolean removeUnusedImports, String endLineDelim, String indentStr,
boolean automatic, IPyFormatStdProvider edit) {
boolean automatic, IPyFormatStdProvider edit, int maxCols) {
this.doc = doc;
this.endLineDelim = endLineDelim;
this.indentStr = indentStr;
this.removeUnusedImports = removeUnusedImports;
this.automatic = automatic;
this.edit = edit;
multilineImports = ImportsPreferencesPage.getMultilineImports(edit);
sortNamesGrouped = ImportsPreferencesPage.getSortNamesGrouped(edit);
multilineImports = ImportPreferences.getMultilineImports(edit);
sortNamesGrouped = ImportPreferences.getSortNamesGrouped(edit);
breakWithParenthesis = getBreakImportsWithParenthesis(edit);
maxCols = getMaxCols(multilineImports);
}

public void perform() {
perform(ImportsPreferencesPage.getGroupImports(edit), edit);
perform(ImportPreferences.getGroupImports(edit), edit);
}

protected void perform(boolean groupFromImports, IPyFormatStdProvider edit) {
Expand Down Expand Up @@ -301,13 +279,19 @@ private void perform(boolean groupFromImports, boolean executeOnlyIfChanged, IPy
}

try {
PyFormatAction std = new PyFormatAction();
boolean throwSyntaxError = false;
ISelectionProvider selectionProvider = null;
int[] regionsToFormat = null;
FormatStd formatStd = (FormatStd) edit.getFormatStd();
File editorFile = edit.getEditorFile();
String filepath = null;
if (editorFile != null) {
filepath = FileUtils.getFileAbsolutePath(editorFile);
}

IDocument psDoc = new Document(finalStr);
PySelection ps = new PySelection(psDoc);
std.applyFormatAction(edit, ps, regionsToFormat, throwSyntaxError, selectionProvider);

boolean isOpenedFile = false;
boolean throwSyntaxError = false;
PyFormatter.formatAll(filepath, psDoc, edit, isOpenedFile, formatStd, throwSyntaxError, true);

finalStr = psDoc.get();
if (addNewLinesToImports) {
// Leave 2 empty new lines separating imports from code
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package org.python.pydev.editor.actions.organize_imports;
package org.python.pydev.ast.sort_imports;

import java.io.File;
import java.util.Collections;
Expand All @@ -13,7 +13,6 @@
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
Expand All @@ -27,13 +26,12 @@
import org.python.pydev.core.MisconfigurationException;
import org.python.pydev.core.PythonNatureWithoutProjectException;
import org.python.pydev.core.docutils.ImportHandle;
import org.python.pydev.core.imports.ImportPreferences;
import org.python.pydev.core.log.Log;
import org.python.pydev.plugin.nature.PythonNature;
import org.python.pydev.shared_core.io.FileUtils;
import org.python.pydev.shared_core.string.FastStringBuffer;
import org.python.pydev.shared_core.string.StringUtils;
import org.python.pydev.shared_core.structure.Tuple3;
import org.python.pydev.ui.importsconf.ImportsPreferencesPage;

public class Pep8ImportArranger extends ImportArranger {

Expand Down Expand Up @@ -103,8 +101,8 @@ static class PathImportClassifier extends ImportClassifier {

private Map<Object, Integer> mapToClassification = new HashMap<Object, Integer>();

PathImportClassifier(IProject project) throws MisconfigurationException, PythonNatureWithoutProjectException {
PythonNature nature = PythonNature.getPythonNature(project);
PathImportClassifier(IPythonNature nature)
throws MisconfigurationException, PythonNatureWithoutProjectException {
if (nature != null) {
try {
String externalProjectSourcePath = nature.getPythonPathNature().getProjectExternalSourcePath(true);
Expand Down Expand Up @@ -178,16 +176,16 @@ private int classifyInternal(String module) {

final ImportClassifier classifier;

public Pep8ImportArranger(IDocument doc, boolean removeUnusedImports, String endLineDelim, IProject prj,
String indentStr, boolean automatic, IPyFormatStdProvider edit) {
super(doc, removeUnusedImports, endLineDelim, indentStr, automatic, edit);
classifier = getClassifier(prj);
public Pep8ImportArranger(IDocument doc, boolean removeUnusedImports, String endLineDelim, IPythonNature nature,
String indentStr, boolean automatic, IPyFormatStdProvider edit, int maxCols) {
super(doc, removeUnusedImports, endLineDelim, indentStr, automatic, edit, maxCols);
classifier = getClassifier(nature);
}

private ImportClassifier getClassifier(IProject p) {
if (p != null) {
private ImportClassifier getClassifier(IPythonNature nature) {
if (nature != null) {
try {
return new PathImportClassifier(p);
return new PathImportClassifier(nature);
} catch (MisconfigurationException e) {
} catch (PythonNatureWithoutProjectException e) {
}
Expand Down Expand Up @@ -218,7 +216,7 @@ public int compare(Tuple3<Integer, String, ImportHandle> o1, Tuple3<Integer, Str
return class1 - class2;
}

if (ImportsPreferencesPage.getSortFromImportsFirst(edit)) {
if (ImportPreferences.getSortFromImportsFirst(edit)) {
int type1 = getImportType(o1.o3);
int type2 = getImportType(o2.o3);
if (type1 != type2) {
Expand Down Expand Up @@ -278,7 +276,7 @@ protected int insertImportsHere(int lineOfFirstOldImport) {
}

/**
*
*
* This enum encapsulates the logic of the {@link ImportArranger#skipOverDocComment} method.
* The order is significant, the matches method is called in order on
* each value, until the value for the line in consideration is found.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package org.python.pydev.ast.sort_imports;

import java.io.File;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.python.pydev.core.IInterpreterInfo;
import org.python.pydev.core.IPyFormatStdProvider;
import org.python.pydev.core.IPythonNature;
import org.python.pydev.core.ISystemModulesManager;
import org.python.pydev.core.MisconfigurationException;
import org.python.pydev.core.ModulesKey;
import org.python.pydev.core.PythonNatureWithoutProjectException;
import org.python.pydev.core.docutils.PySelection;
import org.python.pydev.core.imports.ImportPreferences;
import org.python.pydev.core.log.Log;
import org.python.pydev.core.pep8.ISortRunner;
import org.python.pydev.shared_core.io.FileUtils;
import org.python.pydev.shared_core.io.PyUnsupportedEncodingException;
import org.python.pydev.shared_core.string.StringUtils;
import org.python.pydev.shared_core.utils.DocUtils;

public class SortImports {

public void sortImports(IPythonNature pythonNature, IAdaptable projectAdaptable, File targetFile,
String fileContents, final IDocument doc, boolean removeUnusedImports, String endLineDelim,
String indentStr, boolean automatic, IPyFormatStdProvider edit, int maxCols)
throws MisconfigurationException, PythonNatureWithoutProjectException {

Set<String> knownThirdParty = new HashSet<String>();
// isort itself already has a reasonable stdLib, so, don't do our own.

String importEngine = ImportPreferences.getImportEngine(projectAdaptable);
if (!ImportPreferences.IMPORT_ENGINE_ISORT.equals(importEngine)) {
if (pythonNature != null) {
IInterpreterInfo projectInterpreter = pythonNature.getProjectInterpreter();
ISystemModulesManager modulesManager = projectInterpreter.getModulesManager();
ModulesKey[] onlyDirectModules = modulesManager.getOnlyDirectModules();

Set<String> stdLib = new HashSet<>();

for (ModulesKey modulesKey : onlyDirectModules) {
if (modulesKey.file == null) {
int i = modulesKey.name.indexOf('.');
String name;
if (i < 0) {
name = modulesKey.name;
} else {
name = modulesKey.name.substring(0, i);
}
// Add all names to std lib
stdLib.add(name);
}
}
for (ModulesKey modulesKey : onlyDirectModules) {
int i = modulesKey.name.indexOf('.');
String name;
if (i < 0) {
name = modulesKey.name;
} else {
name = modulesKey.name.substring(0, i);
}

// Consider all in site-packages to be third party.
if (modulesKey.file != null && modulesKey.file.toString().contains("site-packages")) {
stdLib.remove(name);
knownThirdParty.add(name);
}
}
}
}

switch (importEngine) {
case ImportPreferences.IMPORT_ENGINE_ISORT:
if (fileContents.length() > 0) {
String encoding = null;
try {
encoding = FileUtils.getPythonFileEncoding(doc, null);
} catch (PyUnsupportedEncodingException e) {
Log.log(e);
}
if (encoding == null) {
encoding = "utf-8";
}

Optional<String> executableLocation = ImportPreferences
.getISortExecutable(projectAdaptable);
String[] args = ImportPreferences.getISortArguments(projectAdaptable);

String isortResult = ISortRunner.formatWithISort(targetFile, pythonNature,
fileContents, encoding, targetFile.getParentFile(), args, knownThirdParty,
executableLocation);

if (isortResult != null) {
String delimiter = PySelection.getDelimiter(doc);
if (!delimiter.equals(System.lineSeparator())) {
// Argh, isort seems to not keep line delimiters, so, hack around it.
isortResult = StringUtils.replaceAll(isortResult, System.lineSeparator(), delimiter);
}
try {
DocUtils.updateDocRangeWithContents(doc, fileContents, isortResult.toString());
} catch (Exception e) {
Log.log(
StringUtils.format(
"Error trying to apply isort result. Curr doc:\n>>>%s\n<<<.\nNew doc:\\n>>>%s\\n<<<.",
fileContents, isortResult.toString()),
e);
}
}
}
break;

case ImportPreferences.IMPORT_ENGINE_REGULAR_SORT:
performArrangeImports(doc, removeUnusedImports, endLineDelim, indentStr, automatic, edit, maxCols);
break;

default: //case ImportsPreferencesPage.IMPORT_ENGINE_PEP_8:
pep8PerformArrangeImports(doc, removeUnusedImports, endLineDelim, pythonNature, indentStr, automatic,
edit, maxCols);
break;
}
}

public static void performPep8ArrangeImports(Document doc, String endLineDelim, String indentStr,
boolean automatic, IPyFormatStdProvider edit, IPythonNature nature, int maxCols) {
pep8PerformArrangeImports(doc, false, endLineDelim, nature, indentStr, automatic, edit, maxCols);
}

/**
* Used by legacy tests.
* @param doc
* @param endLineDelim
* @param indentStr
*/
public static void performArrangeImports(Document doc, String endLineDelim, String indentStr,
IPyFormatStdProvider edit, int maxCols) {
performArrangeImports(doc, false, endLineDelim, indentStr, false, edit, maxCols);
}

/**
* Pep8 compliant version. Actually does the action in the document.
*
* @param doc
* @param removeUnusedImports
* @param endLineDelim
*/
public static void pep8PerformArrangeImports(IDocument doc, boolean removeUnusedImports, String endLineDelim,
IPythonNature nature, String indentStr, boolean automatic, IPyFormatStdProvider edit, int maxCols) {
new Pep8ImportArranger(doc, removeUnusedImports, endLineDelim, nature, indentStr, automatic, edit, maxCols)
.perform();
}

/**
* Actually does the action in the document. Public for testing.
*
* @param doc
* @param removeUnusedImports
* @param endLineDelim
*/
public static void performArrangeImports(IDocument doc, boolean removeUnusedImports, String endLineDelim,
String indentStr, boolean automatic, IPyFormatStdProvider edit, int maxCols) {
new ImportArranger(doc, removeUnusedImports, endLineDelim, indentStr, automatic, edit, maxCols).perform();
}
}
1 change: 1 addition & 0 deletions plugins/org.python.pydev.core/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Export-Package: org.python.copiedfromeclipsesrc,
org.python.pydev.core.docutils,
org.python.pydev.core.editor,
org.python.pydev.core.formatter,
org.python.pydev.core.imports,
org.python.pydev.core.interactive_console,
org.python.pydev.core.interpreters,
org.python.pydev.core.log,
Expand Down
Loading

0 comments on commit f39d157

Please sign in to comment.