mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -120,8 +120,10 @@ target('default': "Build artifacts") {
|
||||
LayoutInfo layoutInfo = layoutFull(args, paths.distAll, usedJars)
|
||||
generateLicensesTable("$paths.artifacts/third-party-libraries.txt", layoutInfo.usedModules);
|
||||
|
||||
egg = buildDebuggerEgg(paths.sandbox, "$home/python", buildName)
|
||||
def egg = buildDebuggerEgg(paths.sandbox, "$home/python", buildName)
|
||||
ant.copy(file: egg, todir: paths.distAll)
|
||||
def eggPy3k = buildDebuggerEggPy3K(paths.sandbox, "$home/python", buildName)
|
||||
ant.copy(file: eggPy3k, todir: paths.distAll)
|
||||
|
||||
scramble()
|
||||
|
||||
|
||||
@@ -1,16 +1,55 @@
|
||||
import groovy.io.FileType
|
||||
import org.apache.tools.ant.util.StringUtils
|
||||
|
||||
private List<String> listTopLevelModules(String root) {
|
||||
def list = []
|
||||
|
||||
def dir = new File(root)
|
||||
dir.eachFile (FileType.FILES) { file ->
|
||||
if (file.name.endsWith(".py")) {
|
||||
list << file.name - ".py"
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
binding.setVariable("buildDebuggerEgg", {String sandbox, String pythonHome, String buildName ->
|
||||
def eggDir = "$sandbox/pycharm-debug"
|
||||
def egg = "$sandbox/pycharm-debug.egg"
|
||||
return buildEgg(sandbox, pythonHome, buildName, "pycharm-debug", {
|
||||
ant.fileset(dir: "$pythonHome/helpers/pydev") {
|
||||
include(name: "**/*.py")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
binding.setVariable("buildDebuggerEggPy3K", {String sandbox, String pythonHome, String buildName ->
|
||||
return buildEgg(sandbox, pythonHome, buildName, "pycharm-debug-py3k") {
|
||||
ant.fileset(dir: "$pythonHome/helpers/pydev") {
|
||||
include(name: "**/*.py")
|
||||
exclude(name:"**/_pydev_BaseHTTPServer.py")
|
||||
exclude(name:"**/_pydev_SimpleXMLRPCServer.py")
|
||||
exclude(name:"**/_pydev_SocketServer.py")
|
||||
exclude(name:"**/_pydev_inspect.py")
|
||||
exclude(name:"**/_pydev_threading.py")
|
||||
exclude(name:"**/_pydev_xmlrpclib.py")
|
||||
exclude(name:"**/jyimportsTipper.py")
|
||||
exclude(name:"**/pydevconsole_code_for_ironpython.py")
|
||||
exclude(name:"**/pydevd_exec.py")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
private GString buildEgg(String sandbox, String pythonHome, String buildName, String eggName, Closure custom) {
|
||||
def eggDir = "$sandbox/$eggName"
|
||||
def egg = "$sandbox/${eggName}.egg"
|
||||
|
||||
ant.delete(dir: eggDir)
|
||||
ant.mkdir(dir: eggDir)
|
||||
ant.mkdir(dir:"$eggDir/pydev")
|
||||
ant.copy(todir: "$eggDir/pydev") {
|
||||
fileset(dir: "$pythonHome/helpers/pydev") {
|
||||
include(name: "**/*.py")
|
||||
}
|
||||
|
||||
ant.copy(todir: "$eggDir") {
|
||||
custom()
|
||||
}
|
||||
ant.mkdir(dir:"$eggDir/EGG-INFO")
|
||||
|
||||
ant.mkdir(dir: "$eggDir/EGG-INFO")
|
||||
|
||||
ant.copy(todir: "$eggDir/EGG-INFO") {
|
||||
fileset(dir: "$pythonHome/resources/debugger-egg/EGG-INFO")
|
||||
@@ -20,11 +59,14 @@ binding.setVariable("buildDebuggerEgg", {String sandbox, String pythonHome, Stri
|
||||
replacefilter(token: "@@BUILD_NUMBER@@", value: buildName)
|
||||
}
|
||||
|
||||
ant.replace(file: "$eggDir/pydev/pydevd_comm.py") {
|
||||
ant.replace(file: "$eggDir/pydevd_comm.py") {
|
||||
replacefilter(token: "@@BUILD_NUMBER@@", value: buildName)
|
||||
}
|
||||
|
||||
ant.echo(file: "$eggDir/EGG-INFO/top_level.txt", message: listTopLevelModules("$eggDir").join(StringUtils.LINE_SEP))
|
||||
|
||||
ant.zip(destfile: egg) {
|
||||
fileset(dir: eggDir)
|
||||
}
|
||||
return egg
|
||||
})
|
||||
}
|
||||
|
||||
@@ -366,6 +366,7 @@ target(name: "zip", description: "Generate zip plugin file") {
|
||||
ant.copy(file: "${pluginHelp}/pytonpluginhelp_mac.jar", tofile: "${plugindir}/help/pythonpluginhelp_mac.jar")
|
||||
|
||||
ant.move(file: "${debugEgg}", todir: "${plugindir}")
|
||||
ant.move(file: "${debugEggPy3K}", todir: "${plugindir}")
|
||||
|
||||
ant.zip(basedir: "${zipdir}", destfile: "${output}/${zipname}")
|
||||
}
|
||||
@@ -375,6 +376,8 @@ target(name: "build") {
|
||||
depends("compile")
|
||||
egg = buildDebuggerEgg(outDir, pluginHome, "PythonPlugin-${buildNumber}")
|
||||
setProperty("debugEgg", "$egg")
|
||||
egg = buildDebuggerEggPy3K(outDir, pluginHome, "PythonPlugin-${buildNumber}")
|
||||
setProperty("debugEggPy3K", "$egg")
|
||||
}
|
||||
|
||||
target(name: "dist", description: "main target") {
|
||||
|
||||
@@ -328,9 +328,9 @@ class BaseHTTPRequestHandler(SocketServer.StreamRequestHandler):
|
||||
method = getattr(self, mname)
|
||||
method()
|
||||
self.wfile.flush() #actually send the response if not already done.
|
||||
except socket.timeout, e:
|
||||
except socket.timeout:
|
||||
#a read or a write timed out. Discard this connection
|
||||
self.log_error("Request timed out: %r", e)
|
||||
self.log_error("Request timed out: %r", sys.exc_info()[1])
|
||||
self.close_connection = 1
|
||||
return
|
||||
|
||||
@@ -596,7 +596,7 @@ def test(HandlerClass = BaseHTTPRequestHandler,
|
||||
httpd = ServerClass(server_address, HandlerClass)
|
||||
|
||||
sa = httpd.socket.getsockname()
|
||||
print "Serving HTTP on", sa[0], "port", sa[1], "..."
|
||||
print ("Serving HTTP on", sa[0], "port", sa[1], "...")
|
||||
httpd.serve_forever()
|
||||
|
||||
|
||||
|
||||
@@ -735,7 +735,7 @@ def getframeinfo(frame, context=1):
|
||||
|
||||
def getlineno(frame):
|
||||
"""Get the line number from a frame object, allowing for optimization."""
|
||||
# Written by Marc-André Lemburg; revised by Jim Hugunin and Fredrik Lundh.
|
||||
# Written by Marc-Andr Lemburg; revised by Jim Hugunin and Fredrik Lundh.
|
||||
lineno = frame.f_lineno
|
||||
code = frame.f_code
|
||||
if hasattr(code, 'co_lnotab'):
|
||||
|
||||
@@ -119,5 +119,8 @@ class PyDevFrontEnd(PrefilterFrontEnd):
|
||||
self.shell.user_ns = locals
|
||||
|
||||
def is_automagic(self):
|
||||
return True if self.ipython0.rc.automagic else False
|
||||
if self.ipython0.rc.automagic:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
@@ -4,9 +4,6 @@ import com.google.common.collect.Lists;
|
||||
import com.intellij.extapi.psi.ASTDelegatePsiElement;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
@@ -15,7 +12,6 @@ import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.PyTokenTypes;
|
||||
import com.jetbrains.python.PythonStringUtil;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -29,8 +25,6 @@ import java.util.List;
|
||||
* @author max
|
||||
*/
|
||||
public class PyPsiUtils {
|
||||
public static final Key<Pair<PsiElement, TextRange>> SELECTION_BREAKS_AST_NODE =
|
||||
new Key<Pair<PsiElement, TextRange>>("python.selection.breaks.ast.node");
|
||||
private static final Logger LOG = Logger.getInstance(PyPsiUtils.class.getName());
|
||||
|
||||
private PyPsiUtils() {
|
||||
@@ -68,52 +62,6 @@ public class PyPsiUtils {
|
||||
return node;
|
||||
}
|
||||
|
||||
public static PsiElement replaceExpression(@NotNull final PsiElement oldExpression,
|
||||
@NotNull final PsiElement newExpression) {
|
||||
final Pair<PsiElement, TextRange> data = oldExpression.getUserData(SELECTION_BREAKS_AST_NODE);
|
||||
if (data != null) {
|
||||
final PsiElement element = data.first;
|
||||
final TextRange textRange = data.second;
|
||||
final String parentText = element.getText();
|
||||
final String prefix = parentText.substring(0, textRange.getStartOffset());
|
||||
final String suffix = parentText.substring(textRange.getEndOffset(), element.getTextLength());
|
||||
final PyElementGenerator generator = PyElementGenerator.getInstance(oldExpression.getProject());
|
||||
final LanguageLevel languageLevel = LanguageLevel.forElement(oldExpression);
|
||||
if (element instanceof PyStringLiteralExpression) {
|
||||
final Pair<String, String> quotes = PythonStringUtil.getQuotes(parentText);
|
||||
final PsiElement parent = element.getParent();
|
||||
final boolean parensNeeded = parent instanceof PyExpression && !(parent instanceof PyParenthesizedExpression);
|
||||
if (quotes != null) {
|
||||
final String leftQuote = quotes.getFirst();
|
||||
final String rightQuote = quotes.getSecond();
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
if (parensNeeded) {
|
||||
builder.append("(");
|
||||
}
|
||||
if (!leftQuote.endsWith(prefix)) {
|
||||
builder.append(prefix + rightQuote + " + ");
|
||||
}
|
||||
final int pos = builder.toString().length();
|
||||
builder.append(newExpression.getText());
|
||||
if (!rightQuote.startsWith(suffix)) {
|
||||
builder.append(" + " + leftQuote + suffix);
|
||||
}
|
||||
if (parensNeeded) {
|
||||
builder.append(")");
|
||||
}
|
||||
final PsiElement expression = generator.createExpressionFromText(languageLevel, builder.toString());
|
||||
final PsiElement newElement = element.replace(expression);
|
||||
return newElement.findElementAt(pos);
|
||||
}
|
||||
}
|
||||
final PsiElement expression = generator.createFromText(languageLevel, element.getClass(), prefix + newExpression.getText() + suffix);
|
||||
return element.replace(expression);
|
||||
}
|
||||
else {
|
||||
return oldExpression.replace(newExpression);
|
||||
}
|
||||
}
|
||||
|
||||
public static void addBeforeInParent(@NotNull final PsiElement anchor, @NotNull final PsiElement... newElements) {
|
||||
final ASTNode anchorNode = anchor.getNode();
|
||||
LOG.assertTrue(anchorNode != null);
|
||||
|
||||
@@ -4,7 +4,6 @@ import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.process.ProcessHandler;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.xdebugger.frame.XValueChildrenList;
|
||||
@@ -32,7 +31,7 @@ public class MultiProcessDebugger implements ProcessDebugger {
|
||||
private final int myTimeoutInMillis;
|
||||
|
||||
private RemoteDebugger myMainDebugger;
|
||||
private List<RemoteDebugger> myOtherDebuggers = Lists.newArrayList();
|
||||
private final List<RemoteDebugger> myOtherDebuggers = Lists.newArrayList();
|
||||
private ServerSocket myDebugServerSocket;
|
||||
private DebuggerProcessAcceptor myDebugProcessAcceptor;
|
||||
private DebuggerProcessListener myOtherDebuggerCloseListener;
|
||||
@@ -114,7 +113,7 @@ public class MultiProcessDebugger implements ProcessDebugger {
|
||||
@Override
|
||||
public void disconnect() {
|
||||
myMainDebugger.disconnect();
|
||||
for (ProcessDebugger d : myOtherDebuggers) {
|
||||
for (ProcessDebugger d : Lists.newArrayList(myOtherDebuggers)) {
|
||||
d.disconnect();
|
||||
}
|
||||
if (myDebugProcessAcceptor != null) {
|
||||
@@ -230,26 +229,33 @@ public class MultiProcessDebugger implements ProcessDebugger {
|
||||
}
|
||||
|
||||
private void cleanOtherDebuggers() {
|
||||
synchronized (myOtherDebuggers) {
|
||||
removeDisconnected(Lists.newArrayList(myOtherDebuggers));
|
||||
}
|
||||
}
|
||||
|
||||
private void removeDisconnected(ArrayList<RemoteDebugger> debuggers) {
|
||||
boolean allConnected = true;
|
||||
for (RemoteDebugger d : myOtherDebuggers) {
|
||||
for (RemoteDebugger d : debuggers) {
|
||||
if (!d.isConnected()) {
|
||||
allConnected = false;
|
||||
}
|
||||
}
|
||||
if (!allConnected) {
|
||||
List<RemoteDebugger> newList = Lists.newArrayList();
|
||||
for (RemoteDebugger d : myOtherDebuggers) {
|
||||
for (RemoteDebugger d : debuggers) {
|
||||
if (d.isConnected()) {
|
||||
newList.add(d);
|
||||
}
|
||||
}
|
||||
|
||||
myOtherDebuggers = newList;
|
||||
myOtherDebuggers.clear();
|
||||
myOtherDebuggers.addAll(newList);
|
||||
}
|
||||
}
|
||||
|
||||
private void collectAndRegisterOtherDebuggersThreads(List<PyThreadInfo> threads) {
|
||||
for (RemoteDebugger d : myOtherDebuggers) {
|
||||
for (RemoteDebugger d : Lists.newArrayList(myOtherDebuggers)) {
|
||||
threads.addAll(d.getThreads());
|
||||
for (PyThreadInfo t : d.getThreads()) {
|
||||
myThreadRegistry.register(t.getId(), d);
|
||||
@@ -261,7 +267,7 @@ public class MultiProcessDebugger implements ProcessDebugger {
|
||||
@Override
|
||||
public void execute(@NotNull AbstractCommand command) {
|
||||
myMainDebugger.execute(command);
|
||||
for (ProcessDebugger d : myOtherDebuggers) {
|
||||
for (ProcessDebugger d : Lists.newArrayList(myOtherDebuggers)) {
|
||||
d.execute(command);
|
||||
}
|
||||
}
|
||||
@@ -282,7 +288,7 @@ public class MultiProcessDebugger implements ProcessDebugger {
|
||||
@Override
|
||||
public void close() {
|
||||
myMainDebugger.close();
|
||||
for (ProcessDebugger d : myOtherDebuggers) {
|
||||
for (ProcessDebugger d : Lists.newArrayList(myOtherDebuggers)) {
|
||||
d.close();
|
||||
}
|
||||
}
|
||||
@@ -329,7 +335,7 @@ public class MultiProcessDebugger implements ProcessDebugger {
|
||||
@Override
|
||||
public void removeBreakpoint(String typeId, String file, int line) {
|
||||
myMainDebugger.removeBreakpoint(typeId, file, line);
|
||||
for (ProcessDebugger d : myOtherDebuggers) {
|
||||
for (ProcessDebugger d : Lists.newArrayList(myOtherDebuggers)) {
|
||||
d.removeBreakpoint(typeId, file, line);
|
||||
}
|
||||
}
|
||||
@@ -428,7 +434,9 @@ public class MultiProcessDebugger implements ProcessDebugger {
|
||||
}
|
||||
|
||||
private void addDebugger(RemoteDebugger debugger) {
|
||||
myOtherDebuggers.add(debugger);
|
||||
synchronized (myOtherDebuggers) {
|
||||
myOtherDebuggers.add(debugger);
|
||||
}
|
||||
}
|
||||
|
||||
public void addCloseListener(RemoteDebuggerCloseListener listener) {
|
||||
@@ -444,7 +452,7 @@ public class MultiProcessDebugger implements ProcessDebugger {
|
||||
public void addExceptionBreakpoint(ExceptionBreakpointCommandFactory factory) {
|
||||
myMainDebugger.execute(factory.createAddCommand(myMainDebugger));
|
||||
|
||||
for (RemoteDebugger d : myOtherDebuggers) {
|
||||
for (RemoteDebugger d : Lists.newArrayList(myOtherDebuggers)) {
|
||||
d.execute(factory.createAddCommand(d));
|
||||
}
|
||||
}
|
||||
@@ -453,7 +461,7 @@ public class MultiProcessDebugger implements ProcessDebugger {
|
||||
public void removeExceptionBreakpoint(ExceptionBreakpointCommandFactory factory) {
|
||||
myMainDebugger.execute(factory.createRemoveCommand(myMainDebugger));
|
||||
|
||||
for (RemoteDebugger d : myOtherDebuggers) {
|
||||
for (RemoteDebugger d : Lists.newArrayList(myOtherDebuggers)) {
|
||||
d.execute(factory.createRemoveCommand(d));
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ public class DocutilsCommandLineState extends RestCommandLineState {
|
||||
VirtualFile virtualFile = findOutput();
|
||||
if (virtualFile != null) {
|
||||
if (myConfiguration.openInBrowser()) {
|
||||
BrowserUtil.launchBrowser(virtualFile.getUrl());
|
||||
BrowserUtil.browse(virtualFile);
|
||||
}
|
||||
else {
|
||||
FileEditorManager.getInstance(myConfiguration.getProject()).openFile(virtualFile, true);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
<orderEntry type="module" module-name="relaxng" scope="RUNTIME" />
|
||||
<orderEntry type="library" name="Velocity" level="project" />
|
||||
<orderEntry type="module" module-name="python-helpers" scope="RUNTIME" />
|
||||
<orderEntry type="module" module-name="chronon-embedder" />
|
||||
</component>
|
||||
</module>
|
||||
|
||||
|
||||
+1
-1
@@ -56,8 +56,8 @@ public class PyClassNameCompletionContributor extends CompletionContributor {
|
||||
Collection<VirtualFile> files = FileTypeIndex.getFiles(PythonFileType.INSTANCE, PyProjectScopeBuilder.excludeSdkTestsScope(targetFile));
|
||||
for (VirtualFile file : files) {
|
||||
PsiFile pyFile = targetFile.getManager().findFile(file);
|
||||
if (pyFile == null) continue;
|
||||
PsiFileSystemItem importable = (PsiFileSystemItem) PyUtil.turnInitIntoDir(pyFile);
|
||||
if (importable == null) continue;
|
||||
if (PythonReferenceImporter.isImportableModule(targetFile, importable)) {
|
||||
LookupElementBuilder element = PyModuleType.buildFileLookupElement(importable, null);
|
||||
if (element != null) {
|
||||
|
||||
@@ -34,16 +34,18 @@ public class AddImportHelper {
|
||||
BUILTIN, THIRD_PARTY, PROJECT
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiElement getFileInsertPosition(final PsiFile file) {
|
||||
return getInsertPosition(file, null, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement getInsertPosition(final PsiFile file, @Nullable String nameToImport, @Nullable ImportPriority priority) {
|
||||
PsiElement feeler = file.getFirstChild();
|
||||
LOG.assertTrue(feeler != null);
|
||||
if (feeler == null) return null;
|
||||
// skip initial comments and whitespace and try to get just below the last import stmt
|
||||
boolean skipped_over_imports = false;
|
||||
boolean skipped_over_doc = false;
|
||||
boolean skippedOverImports = false;
|
||||
boolean skippedOverDoc = false;
|
||||
PsiElement seeker = feeler;
|
||||
do {
|
||||
if (feeler instanceof PyImportStatementBase) {
|
||||
@@ -52,20 +54,20 @@ public class AddImportHelper {
|
||||
}
|
||||
seeker = feeler;
|
||||
feeler = feeler.getNextSibling();
|
||||
skipped_over_imports = true;
|
||||
skippedOverImports = true;
|
||||
}
|
||||
else if (PyUtil.instanceOf(feeler, PsiWhiteSpace.class, PsiComment.class)) {
|
||||
seeker = feeler;
|
||||
feeler = feeler.getNextSibling();
|
||||
}
|
||||
// maybe we arrived at the doc comment stmt; skip over it, too
|
||||
else if (!skipped_over_imports && !skipped_over_doc && file instanceof PyFile) {
|
||||
else if (!skippedOverImports && !skippedOverDoc && file instanceof PyFile) {
|
||||
PsiElement doc_elt =
|
||||
PythonDocStringFinder.find((PyElement)file); // this gives the literal; its parent is the expr seeker may have encountered
|
||||
if (doc_elt != null && doc_elt.getParent() == feeler) {
|
||||
feeler = feeler.getNextSibling();
|
||||
seeker = feeler; // skip over doc even if there's nothing below it
|
||||
skipped_over_doc = true;
|
||||
skippedOverDoc = true;
|
||||
}
|
||||
else {
|
||||
break; // not a doc comment, stop on it
|
||||
@@ -108,7 +110,9 @@ public class AddImportHelper {
|
||||
else {
|
||||
containingFile = source != null ? source.getContainingFile() : null;
|
||||
}
|
||||
ImportPriority relativeToPriority = source == null ? ImportPriority.BUILTIN : getImportPriority(file, containingFile);
|
||||
ImportPriority relativeToPriority = source == null || containingFile == null
|
||||
? ImportPriority.BUILTIN
|
||||
: getImportPriority(file, containingFile);
|
||||
final int rc = priority.compareTo(relativeToPriority);
|
||||
if (rc < 0) {
|
||||
return true;
|
||||
|
||||
@@ -277,7 +277,7 @@ public class PythonReferenceImporter implements ReferenceImporter {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean isImportableModule(PsiFile targetFile, PsiFileSystemItem file) {
|
||||
public static boolean isImportableModule(PsiFile targetFile, @NotNull PsiFileSystemItem file) {
|
||||
PsiDirectory parent = (PsiDirectory)file.getParent();
|
||||
return parent != null && file != targetFile &&
|
||||
(parent.findFile(PyNames.INIT_DOT_PY) != null ||
|
||||
|
||||
@@ -354,7 +354,7 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
|
||||
private void passToAllThreads(final ResumeOrStepCommand.Mode mode) {
|
||||
dropFrameCaches();
|
||||
if (isConnected()) {
|
||||
for (PyThreadInfo suspendedThread : mySuspendedThreads) {
|
||||
for (PyThreadInfo suspendedThread : Lists.newArrayList(mySuspendedThreads)) {
|
||||
myDebugger.resumeOrStep(suspendedThread.getId(), mode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,7 +330,11 @@ public class PyDocstringGenerator {
|
||||
if (myFunction != null) {
|
||||
myFunction = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(myFunction);
|
||||
}
|
||||
myDocStringOwner = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(myDocStringOwner);
|
||||
PyDocStringOwner owner = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(myDocStringOwner);
|
||||
if (owner != null) {
|
||||
myDocStringOwner = owner;
|
||||
}
|
||||
|
||||
myDocStringExpression = myDocStringOwner.getDocStringExpression();
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -65,7 +65,10 @@ public class PyModuleFindUsagesHandler extends FindUsagesHandler {
|
||||
if (target instanceof PyFile && PyNames.INIT_DOT_PY.equals(((PyFile)target).getName())) {
|
||||
List<PsiReference> result = new ArrayList<PsiReference>();
|
||||
result.addAll(super.findReferencesToHighlight(target, searchScope));
|
||||
result.addAll(ReferencesSearch.search(PyUtil.turnInitIntoDir(target), searchScope, false).findAll());
|
||||
PsiElement targetDir = PyUtil.turnInitIntoDir(target);
|
||||
if (targetDir != null) {
|
||||
result.addAll(ReferencesSearch.search(targetDir, searchScope, false).findAll());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return super.findReferencesToHighlight(target, searchScope);
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
package com.jetbrains.python.inspections;
|
||||
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyStringLiteralExpressionImpl;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
@@ -33,10 +43,10 @@ public class PyStringFormatParser {
|
||||
}
|
||||
|
||||
public static class SubstitutionChunk extends FormatStringChunk {
|
||||
private String myMappingKey;
|
||||
private String myConversionFlags;
|
||||
private String myWidth;
|
||||
private String myPrecision;
|
||||
@Nullable private String myMappingKey;
|
||||
@Nullable private String myConversionFlags;
|
||||
@Nullable private String myWidth;
|
||||
@Nullable private String myPrecision;
|
||||
private char myLengthModifier;
|
||||
private char myConversionType;
|
||||
private boolean myUnclosedMapping;
|
||||
@@ -45,7 +55,7 @@ public class PyStringFormatParser {
|
||||
super(startIndex, startIndex);
|
||||
}
|
||||
|
||||
public void setEndIndex(int endIndex) {
|
||||
private void setEndIndex(int endIndex) {
|
||||
myEndIndex = endIndex;
|
||||
}
|
||||
|
||||
@@ -53,39 +63,43 @@ public class PyStringFormatParser {
|
||||
return myConversionType;
|
||||
}
|
||||
|
||||
public void setConversionType(char conversionType) {
|
||||
private void setConversionType(char conversionType) {
|
||||
myConversionType = conversionType;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getMappingKey() {
|
||||
return myMappingKey;
|
||||
}
|
||||
|
||||
public void setMappingKey(String mappingKey) {
|
||||
private void setMappingKey(@Nullable String mappingKey) {
|
||||
myMappingKey = mappingKey;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getConversionFlags() {
|
||||
return myConversionFlags;
|
||||
}
|
||||
|
||||
public void setConversionFlags(String conversionFlags) {
|
||||
private void setConversionFlags(@Nullable String conversionFlags) {
|
||||
myConversionFlags = conversionFlags;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getWidth() {
|
||||
return myWidth;
|
||||
}
|
||||
|
||||
public void setWidth(String width) {
|
||||
private void setWidth(@Nullable String width) {
|
||||
myWidth = width;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getPrecision() {
|
||||
return myPrecision;
|
||||
}
|
||||
|
||||
public void setPrecision(String precision) {
|
||||
private void setPrecision(@Nullable String precision) {
|
||||
myPrecision = precision;
|
||||
}
|
||||
|
||||
@@ -93,7 +107,7 @@ public class PyStringFormatParser {
|
||||
return myLengthModifier;
|
||||
}
|
||||
|
||||
public void setLengthModifier(char lengthModifier) {
|
||||
private void setLengthModifier(char lengthModifier) {
|
||||
myLengthModifier = lengthModifier;
|
||||
}
|
||||
|
||||
@@ -101,13 +115,13 @@ public class PyStringFormatParser {
|
||||
return myUnclosedMapping;
|
||||
}
|
||||
|
||||
public void setUnclosedMapping(boolean unclosedMapping) {
|
||||
private void setUnclosedMapping(boolean unclosedMapping) {
|
||||
myUnclosedMapping = unclosedMapping;
|
||||
}
|
||||
}
|
||||
|
||||
private final String myLiteral;
|
||||
private final List<FormatStringChunk> myResult = new ArrayList<FormatStringChunk>();
|
||||
@NotNull private final String myLiteral;
|
||||
@NotNull private final List<FormatStringChunk> myResult = new ArrayList<FormatStringChunk>();
|
||||
private int myPos;
|
||||
|
||||
private static final String CONVERSION_FLAGS = "#0- +";
|
||||
@@ -115,10 +129,11 @@ public class PyStringFormatParser {
|
||||
private static final String LENGTH_MODIFIERS = "hlL";
|
||||
private static final String VALID_CONVERSION_TYPES = "diouxXeEfFgGcrs";
|
||||
|
||||
public PyStringFormatParser(String literal) {
|
||||
public PyStringFormatParser(@NotNull String literal) {
|
||||
myLiteral = literal;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<FormatStringChunk> parse() {
|
||||
myPos = 0;
|
||||
while(myPos < myLiteral.length()) {
|
||||
@@ -173,7 +188,7 @@ public class PyStringFormatParser {
|
||||
chunk.setEndIndex(myPos);
|
||||
}
|
||||
|
||||
private boolean isAtSet(final String characterSet) {
|
||||
private boolean isAtSet(@NotNull final String characterSet) {
|
||||
return myPos < myLiteral.length() && characterSet.indexOf(myLiteral.charAt(myPos)) >= 0;
|
||||
}
|
||||
|
||||
@@ -181,6 +196,7 @@ public class PyStringFormatParser {
|
||||
return myPos < myLiteral.length() && myLiteral.charAt(myPos) == c;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String parseWidth() {
|
||||
if (isAt('*')) {
|
||||
myPos++;
|
||||
@@ -189,7 +205,8 @@ public class PyStringFormatParser {
|
||||
return parseWhileCharacterInSet(DIGITS);
|
||||
}
|
||||
|
||||
private String parseWhileCharacterInSet(final String characterSet) {
|
||||
@NotNull
|
||||
private String parseWhileCharacterInSet(@NotNull final String characterSet) {
|
||||
int flagStart = myPos;
|
||||
while(isAtSet(characterSet)) {
|
||||
myPos++;
|
||||
@@ -197,6 +214,7 @@ public class PyStringFormatParser {
|
||||
return myLiteral.substring(flagStart, myPos);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<SubstitutionChunk> parseSubstitutions() {
|
||||
List<SubstitutionChunk> result = new ArrayList<SubstitutionChunk>();
|
||||
for (FormatStringChunk chunk : parse()) {
|
||||
@@ -206,4 +224,86 @@ public class PyStringFormatParser {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<SubstitutionChunk> getPositionalSubstitutions(@NotNull List<SubstitutionChunk> substitutions) {
|
||||
final ArrayList<SubstitutionChunk> result = new ArrayList<SubstitutionChunk>();
|
||||
for (SubstitutionChunk s : substitutions) {
|
||||
if (s.getMappingKey() == null) {
|
||||
result.add(s);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Map<String, SubstitutionChunk> getKeywordSubstitutions(@NotNull List<SubstitutionChunk> substitutions) {
|
||||
final Map<String, SubstitutionChunk> result = new HashMap<String, SubstitutionChunk>();
|
||||
for (SubstitutionChunk s : substitutions) {
|
||||
final String key = s.getMappingKey();
|
||||
if (key != null) {
|
||||
result.put(key, s);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public static List<TextRange> substitutionsToRanges(@NotNull List<SubstitutionChunk> substitutions) {
|
||||
final List<TextRange> ranges = new ArrayList<TextRange>();
|
||||
for (SubstitutionChunk substitution : substitutions) {
|
||||
ranges.add(TextRange.create(substitution.getStartIndex(), substitution.getEndIndex()));
|
||||
}
|
||||
return ranges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the RHS operand of %-based string literal format expression.
|
||||
*/
|
||||
@Nullable
|
||||
public static PyExpression getFormatValueExpression(@NotNull PyStringLiteralExpression element) {
|
||||
final PsiElement parent = element.getParent();
|
||||
if (parent instanceof PyBinaryExpression) {
|
||||
final PyBinaryExpression binaryExpr = (PyBinaryExpression)parent;
|
||||
if (binaryExpr.isOperator("%")) {
|
||||
PyExpression expr = binaryExpr.getRightExpression();
|
||||
while (expr instanceof PyParenthesizedExpression) {
|
||||
expr = ((PyParenthesizedExpression)expr).getContainedExpression();
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the argument list of the str.format() literal format expression.
|
||||
*/
|
||||
@Nullable
|
||||
public static PyArgumentList getNewStyleFormatValueExpression(@NotNull PyStringLiteralExpression element) {
|
||||
final PsiElement parent = element.getParent();
|
||||
if (parent instanceof PyQualifiedExpression) {
|
||||
final PyQualifiedExpression qualifiedExpr = (PyQualifiedExpression)parent;
|
||||
final String name = qualifiedExpr.getReferencedName();
|
||||
if ("format".equals(name)) {
|
||||
final PsiElement parent2 = qualifiedExpr.getParent();
|
||||
if (parent2 instanceof PyCallExpression) {
|
||||
final PyCallExpression callExpr = (PyCallExpression)parent2;
|
||||
return callExpr.getArgumentList();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<TextRange> getEscapeRanges(@NotNull String s) {
|
||||
final List<TextRange> ranges = new ArrayList<TextRange>();
|
||||
Matcher matcher = PyStringLiteralExpressionImpl.PATTERN_ESCAPE.matcher(s);
|
||||
while (matcher.find()) {
|
||||
ranges.add(TextRange.create(matcher.start(), matcher.end()));
|
||||
}
|
||||
return ranges;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,7 +419,7 @@ public class ManagePackagesDialog extends DialogWrapper {
|
||||
else {
|
||||
URL url = e.getURL();
|
||||
if (url != null) {
|
||||
BrowserUtil.launchBrowser(url.toString());
|
||||
BrowserUtil.browse(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class PyStringLiteralExpressionImpl extends PyElementImpl implements PyStringLiteralExpression, RegExpLanguageHost {
|
||||
private static final Pattern PATTERN_ESCAPE = Pattern
|
||||
public static final Pattern PATTERN_ESCAPE = Pattern
|
||||
.compile("\\\\(\n|\\\\|'|\"|a|b|f|n|r|t|v|([0-7]{1,3})|x([0-9a-fA-F]{1,2})" + "|N(\\{.*?\\})|u([0-9a-fA-F]){4}|U([0-9a-fA-F]{8}))");
|
||||
private static final Map<String, String> escapeMap = initializeEscapeMap();
|
||||
private String stringValue;
|
||||
|
||||
@@ -129,11 +129,12 @@ public class QualifiedNameFinder {
|
||||
*/
|
||||
private static class PathChoosingVisitor implements RootVisitor {
|
||||
|
||||
@Nullable
|
||||
private final VirtualFile myVFile;
|
||||
private List<String> myResult;
|
||||
private boolean myIsModuleSource;
|
||||
|
||||
private PathChoosingVisitor(VirtualFile file) {
|
||||
private PathChoosingVisitor(@NotNull VirtualFile file) {
|
||||
if (!file.isDirectory() && file.getName().equals(PyNames.INIT_DOT_PY)) {
|
||||
myVFile = file.getParent();
|
||||
}
|
||||
@@ -143,20 +144,22 @@ public class QualifiedNameFinder {
|
||||
}
|
||||
|
||||
public boolean visitRoot(VirtualFile root, Module module, Sdk sdk, boolean isModuleSource) {
|
||||
final String relativePath = VfsUtilCore.getRelativePath(myVFile, root, '/');
|
||||
if (relativePath != null) {
|
||||
List<String> result = StringUtil.split(relativePath, "/");
|
||||
if (myResult == null || result.size() < myResult.size() || (isModuleSource && !myIsModuleSource)) {
|
||||
if (result.size() > 0) {
|
||||
result.set(result.size() - 1, FileUtil.getNameWithoutExtension(result.get(result.size() - 1)));
|
||||
}
|
||||
for (String component : result) {
|
||||
if (!PyNames.isIdentifier(component)) {
|
||||
return true;
|
||||
if (myVFile != null) {
|
||||
final String relativePath = VfsUtilCore.getRelativePath(myVFile, root, '/');
|
||||
if (relativePath != null) {
|
||||
List<String> result = StringUtil.split(relativePath, "/");
|
||||
if (myResult == null || result.size() < myResult.size() || (isModuleSource && !myIsModuleSource)) {
|
||||
if (result.size() > 0) {
|
||||
result.set(result.size() - 1, FileUtil.getNameWithoutExtension(result.get(result.size() - 1)));
|
||||
}
|
||||
for (String component : result) {
|
||||
if (!PyNames.isIdentifier(component)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
myResult = result;
|
||||
myIsModuleSource = isModuleSource;
|
||||
}
|
||||
myResult = result;
|
||||
myIsModuleSource = isModuleSource;
|
||||
}
|
||||
}
|
||||
return myResult == null || myResult.size() > 0;
|
||||
|
||||
@@ -14,7 +14,6 @@ import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import com.jetbrains.python.findUsages.PyFindUsagesHandlerFactory;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -43,7 +42,7 @@ public class PyRefactoringUtil {
|
||||
return;
|
||||
}
|
||||
if (element instanceof PyStringLiteralExpression) {
|
||||
final Pair<PsiElement, TextRange> selection = pattern.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE);
|
||||
final Pair<PsiElement, TextRange> selection = pattern.getUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE);
|
||||
if (selection != null) {
|
||||
final String substring = selection.getSecond().substring(pattern.getText());
|
||||
final PyStringLiteralExpression expr = (PyStringLiteralExpression)element;
|
||||
@@ -51,7 +50,7 @@ public class PyRefactoringUtil {
|
||||
if (text != null && expr.getStringNodes().size() == 1) {
|
||||
final int start = text.indexOf(substring);
|
||||
if (start >= 0) {
|
||||
element.putUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE, Pair.create(element, TextRange.from(start, substring.length())));
|
||||
element.putUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE, Pair.create(element, TextRange.from(start, substring.length())));
|
||||
occurrences.add(element);
|
||||
return;
|
||||
}
|
||||
@@ -113,7 +112,7 @@ public class PyRefactoringUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
expression.putUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE, Pair.create(parent, textRange));
|
||||
expression.putUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE, Pair.create(parent, textRange));
|
||||
return expression;
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -1,9 +1,27 @@
|
||||
package com.jetbrains.python.refactoring;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.util.Function;
|
||||
import com.jetbrains.python.PyElementTypes;
|
||||
import com.jetbrains.python.PythonStringUtil;
|
||||
import com.jetbrains.python.inspections.PyStringFormatParser;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
import com.jetbrains.python.psi.types.PyTypeChecker;
|
||||
import com.jetbrains.python.psi.types.PyTypeParser;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.jetbrains.python.PyTokenTypes.*;
|
||||
|
||||
@@ -11,6 +29,8 @@ import static com.jetbrains.python.PyTokenTypes.*;
|
||||
* @author Dennis.Ushakov
|
||||
*/
|
||||
public class PyReplaceExpressionUtil implements PyElementTypes {
|
||||
public static final Key<Pair<PsiElement, TextRange>> SELECTION_BREAKS_AST_NODE =
|
||||
new Key<Pair<PsiElement, TextRange>>("python.selection.breaks.ast.node");
|
||||
|
||||
private PyReplaceExpressionUtil() {}
|
||||
|
||||
@@ -37,6 +57,210 @@ public class PyReplaceExpressionUtil implements PyElementTypes {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static PsiElement replaceExpression(@NotNull final PsiElement oldExpression,
|
||||
@NotNull final PsiElement newExpression) {
|
||||
final Pair<PsiElement, TextRange> data = oldExpression.getUserData(SELECTION_BREAKS_AST_NODE);
|
||||
if (data != null) {
|
||||
final PsiElement element = data.first;
|
||||
final TextRange textRange = data.second;
|
||||
final String parentText = element.getText();
|
||||
final String prefix = parentText.substring(0, textRange.getStartOffset());
|
||||
final String suffix = parentText.substring(textRange.getEndOffset(), element.getTextLength());
|
||||
final PyElementGenerator generator = PyElementGenerator.getInstance(oldExpression.getProject());
|
||||
final LanguageLevel languageLevel = LanguageLevel.forElement(oldExpression);
|
||||
if (element instanceof PyStringLiteralExpression) {
|
||||
return replaceSubstringInStringLiteral((PyStringLiteralExpression)element, newExpression, textRange);
|
||||
}
|
||||
final PsiElement expression = generator.createFromText(languageLevel, element.getClass(), prefix + newExpression.getText() + suffix);
|
||||
return element.replace(expression);
|
||||
}
|
||||
else {
|
||||
return oldExpression.replace(newExpression);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement replaceSubstringInStringLiteral(@NotNull PyStringLiteralExpression oldExpression,
|
||||
@NotNull PsiElement newExpression,
|
||||
@NotNull TextRange textRange) {
|
||||
final String fullText = oldExpression.getText();
|
||||
final Pair<String, String> detectedQuotes = PythonStringUtil.getQuotes(fullText);
|
||||
final Pair<String, String> quotes = detectedQuotes != null ? detectedQuotes : Pair.create("'", "'");
|
||||
final String prefix = fullText.substring(0, textRange.getStartOffset());
|
||||
final String suffix = fullText.substring(textRange.getEndOffset(), oldExpression.getTextLength());
|
||||
final PyExpression valueExpression = PyStringFormatParser.getFormatValueExpression(oldExpression);
|
||||
final String newText = newExpression.getText();
|
||||
|
||||
final PyElementGenerator generator = PyElementGenerator.getInstance(oldExpression.getProject());
|
||||
final LanguageLevel languageLevel = LanguageLevel.forElement(oldExpression);
|
||||
final List<PyStringFormatParser.SubstitutionChunk> substitutions = new PyStringFormatParser(fullText).parseSubstitutions();
|
||||
|
||||
if (valueExpression != null && !containsStringFormatting(fullText, textRange)) {
|
||||
if (valueExpression instanceof PyTupleExpression) {
|
||||
// 'foo%s' % (x,) -> '%s%s' % (s, x)
|
||||
final String newLiteralText = prefix + "%s" + suffix;
|
||||
final PyStringLiteralExpression newLiteralExpression = generator.createStringLiteralAlreadyEscaped(newLiteralText);
|
||||
oldExpression.replace(newLiteralExpression);
|
||||
|
||||
final PyTupleExpression tuple = (PyTupleExpression)valueExpression;
|
||||
final PyExpression[] members = tuple.getElements();
|
||||
final List<PyStringFormatParser.SubstitutionChunk> positional = PyStringFormatParser.getPositionalSubstitutions(substitutions);
|
||||
final int i = getPositionInRanges(PyStringFormatParser.substitutionsToRanges(positional), textRange);
|
||||
final int n = members.length;
|
||||
if (n > 0 && i <= n) {
|
||||
final boolean last = i == n;
|
||||
final ASTNode trailingComma = PyPsiUtils.getNextComma(members[n - 1].getNode());
|
||||
if (trailingComma != null) {
|
||||
tuple.getNode().removeChild(trailingComma);
|
||||
}
|
||||
final PyExpression before = last ? null : members[i];
|
||||
PyUtil.addListNode(tuple, newExpression, before != null ? before.getNode() : null, i == 0 || !last, last, !last);
|
||||
return newExpression;
|
||||
}
|
||||
}
|
||||
else if (valueExpression instanceof PyDictLiteralExpression) {
|
||||
// 'foo%(x)s' % {'x': x} -> '%(s)s%(x)s' % {'x': x, 's': s}
|
||||
// TODO: Support the dict() function
|
||||
final String newLiteralText = prefix + "%(" + newText + ")s" + suffix;
|
||||
final PyStringLiteralExpression newLiteralExpression = generator.createStringLiteralAlreadyEscaped(newLiteralText);
|
||||
oldExpression.replace(newLiteralExpression);
|
||||
|
||||
final PyDictLiteralExpression dict = (PyDictLiteralExpression)valueExpression;
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append("{");
|
||||
final PyKeyValueExpression[] elements = dict.getElements();
|
||||
builder.append(StringUtil.join(elements, new Function<PyKeyValueExpression, String>() {
|
||||
@Override
|
||||
public String fun(PyKeyValueExpression expression) {
|
||||
return expression.getText();
|
||||
}
|
||||
}, ","));
|
||||
if (elements.length > 0) {
|
||||
builder.append(",");
|
||||
}
|
||||
builder.append(quotes.getSecond());
|
||||
builder.append(newText);
|
||||
builder.append(quotes.getSecond());
|
||||
builder.append(":");
|
||||
final int pos = builder.toString().length();
|
||||
builder.append(newText);
|
||||
builder.append("}");
|
||||
final PyExpression newDictLiteral = generator.createExpressionFromText(languageLevel, builder.toString());
|
||||
final PsiElement newElement = valueExpression.replace(newDictLiteral);
|
||||
return newElement.findElementAt(pos);
|
||||
}
|
||||
else {
|
||||
final TypeEvalContext context = TypeEvalContext.slow();
|
||||
final PyType valueType = valueExpression.getType(context);
|
||||
final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(oldExpression);
|
||||
final PyType tupleType = builtinCache.getTupleType();
|
||||
final PyType mappingType = PyTypeParser.getTypeByName(null, "collections.Mapping");
|
||||
if (!PyTypeChecker.match(tupleType, valueType, context) ||
|
||||
(mappingType != null && !PyTypeChecker.match(mappingType, valueType, context))) {
|
||||
// 'foo%s' % value if value is not tuple or mapping -> '%s%s' % (s, value)
|
||||
final String newLiteralText = prefix + "%s" + suffix;
|
||||
final PyStringLiteralExpression newLiteralExpression = generator.createStringLiteralAlreadyEscaped(newLiteralText);
|
||||
oldExpression.replace(newLiteralExpression);
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append("(");
|
||||
final List<PyStringFormatParser.SubstitutionChunk> positional = PyStringFormatParser.getPositionalSubstitutions(substitutions);
|
||||
final int i = getPositionInRanges(PyStringFormatParser.substitutionsToRanges(positional), textRange);
|
||||
final int pos;
|
||||
if (i == 0) {
|
||||
pos = builder.toString().length();
|
||||
builder.append(newText);
|
||||
builder.append(",");
|
||||
builder.append(valueExpression.getText());
|
||||
}
|
||||
else {
|
||||
builder.append(valueExpression.getText());
|
||||
builder.append(",");
|
||||
pos = builder.toString().length();
|
||||
builder.append(newText);
|
||||
}
|
||||
builder.append(")");
|
||||
final PsiElement newElement = valueExpression.replace(generator.createExpressionFromText(languageLevel, builder.toString()));
|
||||
return newElement.findElementAt(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isConcatFormatting(oldExpression) || substitutions.size() > 0) {
|
||||
// 'foobar' + 'baz' -> s + 'bar' + 'baz'
|
||||
// 'foobar%s' -> s + 'bar%s'
|
||||
// 'f%soobar' % x -> (s + 'bar') % x
|
||||
final String leftQuote = quotes.getFirst();
|
||||
final String rightQuote = quotes.getSecond();
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
if (valueExpression != null) {
|
||||
builder.append("(");
|
||||
}
|
||||
if (!leftQuote.endsWith(prefix)) {
|
||||
builder.append(prefix + rightQuote + " + ");
|
||||
}
|
||||
final int pos = builder.toString().length();
|
||||
builder.append(newText);
|
||||
if (!rightQuote.startsWith(suffix)) {
|
||||
builder.append(" + " + leftQuote + suffix);
|
||||
}
|
||||
if (valueExpression != null) {
|
||||
builder.append(")");
|
||||
}
|
||||
final PsiElement expression = generator.createExpressionFromText(languageLevel, builder.toString());
|
||||
final PsiElement newElement = oldExpression.replace(expression);
|
||||
return newElement.findElementAt(pos);
|
||||
}
|
||||
else {
|
||||
// 'foobar' -> '%sbar' % s
|
||||
// TODO: Handle extracting substring from a string with new-style formatting
|
||||
final PsiElement parent = oldExpression.getParent();
|
||||
final boolean parensNeeded = parent instanceof PyExpression && !(parent instanceof PyParenthesizedExpression);
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
if (parensNeeded) {
|
||||
builder.append("(");
|
||||
}
|
||||
builder.append(prefix);
|
||||
builder.append("%s");
|
||||
builder.append(suffix);
|
||||
builder.append(" % ");
|
||||
final int pos = builder.toString().length();
|
||||
builder.append(newText);
|
||||
if (parensNeeded) {
|
||||
builder.append(")");
|
||||
}
|
||||
final PyExpression expression = generator.createExpressionFromText(languageLevel, builder.toString());
|
||||
final PsiElement newElement = oldExpression.replace(expression);
|
||||
return newElement.findElementAt(pos);
|
||||
}
|
||||
}
|
||||
|
||||
private static int getPositionInRanges(@NotNull List<TextRange> ranges, @NotNull TextRange range) {
|
||||
final int end = range.getEndOffset();
|
||||
final int size = ranges.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
final TextRange r = ranges.get(i);
|
||||
if (end < r.getStartOffset()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
private static boolean containsStringFormatting(@NotNull String s, @NotNull TextRange range) {
|
||||
final List<TextRange> ranges = PyStringFormatParser.substitutionsToRanges(new PyStringFormatParser(s).parseSubstitutions());
|
||||
for (TextRange r : ranges) {
|
||||
if (range.contains(r)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isConcatFormatting(PyStringLiteralExpression element) {
|
||||
final PsiElement parent = element.getParent();
|
||||
return parent instanceof PyBinaryExpression && ((PyBinaryExpression)parent).isOperator("+");
|
||||
}
|
||||
|
||||
private static boolean isNotAssociative(@NotNull final PyBinaryExpression binaryExpression) {
|
||||
final IElementType opType = getOperationType(binaryExpression);
|
||||
return COMPARISON_OPERATIONS.contains(opType) || binaryExpression instanceof PySliceExpression ||
|
||||
@@ -68,7 +292,7 @@ public class PyReplaceExpressionUtil implements PyElementTypes {
|
||||
return -priority;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Nullable
|
||||
private static IElementType getOperationType(@NotNull final PyElement expr) {
|
||||
if (expr instanceof PyBinaryExpression) return ((PyBinaryExpression)expr).getOperator();
|
||||
return ((PyPrefixExpression)expr).getOperator();
|
||||
|
||||
+7
@@ -9,6 +9,7 @@ import com.intellij.refactoring.changeSignature.ChangeInfo;
|
||||
import com.intellij.refactoring.changeSignature.ChangeSignatureUsageProcessor;
|
||||
import com.intellij.refactoring.changeSignature.ParameterInfo;
|
||||
import com.intellij.refactoring.rename.RenameUtil;
|
||||
import com.intellij.refactoring.rename.ResolveSnapshotProvider;
|
||||
import com.intellij.usageView.UsageInfo;
|
||||
import com.intellij.util.Query;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
@@ -314,4 +315,10 @@ public class PyChangeSignatureUsageProcessor implements ChangeSignatureUsageProc
|
||||
public boolean setupDefaultValues(ChangeInfo changeInfo, Ref<UsageInfo[]> refUsages, Project project) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerConflictResolvers(List<ResolveSnapshotProvider.ResolveSnapshot> snapshots,
|
||||
@NotNull ResolveSnapshotProvider resolveSnapshotProvider,
|
||||
UsageInfo[] usages, ChangeInfo changeInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyFunctionBuilder;
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils;
|
||||
import com.jetbrains.python.refactoring.PyReplaceExpressionUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -308,7 +309,7 @@ public class PyExtractMethodUtil {
|
||||
|
||||
// replace statements with call
|
||||
if (callElement != null) {
|
||||
callElement = PyPsiUtils.replaceExpression(expression, callElement);
|
||||
callElement = PyReplaceExpressionUtil.replaceExpression(expression, callElement);
|
||||
}
|
||||
|
||||
// Set editor
|
||||
@@ -397,7 +398,7 @@ public class PyExtractMethodUtil {
|
||||
}
|
||||
|
||||
private static PyFunction insertGeneratedMethod(PsiElement anchor, final PyFunction generatedMethod) {
|
||||
final Pair<PsiElement, TextRange> data = anchor.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE);
|
||||
final Pair<PsiElement, TextRange> data = anchor.getUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE);
|
||||
if (data != null) {
|
||||
anchor = data.first;
|
||||
}
|
||||
|
||||
@@ -31,14 +31,15 @@ import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.PythonStringUtil;
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
|
||||
import com.jetbrains.python.inspections.PyStringFormatParser;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
import com.jetbrains.python.psi.types.PyNoneType;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import com.jetbrains.python.refactoring.NameSuggesterUtil;
|
||||
import com.jetbrains.python.refactoring.PyRefactoringUtil;
|
||||
import com.jetbrains.python.refactoring.PyReplaceExpressionUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -115,12 +116,12 @@ abstract public class IntroduceHandler implements RefactoringActionHandler {
|
||||
protected PsiElement replaceExpression(PsiElement expression, PyExpression newExpression, IntroduceOperation operation) {
|
||||
PyExpressionStatement statement = PsiTreeUtil.getParentOfType(expression, PyExpressionStatement.class);
|
||||
if (statement != null) {
|
||||
if (statement.getExpression() == expression && expression.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE) == null) {
|
||||
if (statement.getExpression() == expression && expression.getUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE) == null) {
|
||||
statement.delete();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return PyPsiUtils.replaceExpression(expression, newExpression);
|
||||
return PyReplaceExpressionUtil.replaceExpression(expression, newExpression);
|
||||
}
|
||||
|
||||
private final IntroduceValidator myValidator;
|
||||
@@ -172,7 +173,7 @@ abstract public class IntroduceHandler implements RefactoringActionHandler {
|
||||
}
|
||||
};
|
||||
String text = expression.getText();
|
||||
final Pair<PsiElement, TextRange> selection = expression.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE);
|
||||
final Pair<PsiElement, TextRange> selection = expression.getUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE);
|
||||
if (selection != null) {
|
||||
text = selection.getSecond().substring(text);
|
||||
}
|
||||
@@ -271,10 +272,8 @@ abstract public class IntroduceHandler implements RefactoringActionHandler {
|
||||
}
|
||||
|
||||
if (singleElementSelection && element1 instanceof PyStringLiteralExpression) {
|
||||
// TODO: Protect against escapes
|
||||
// TODO: Protect against substrings with format characters
|
||||
// TODO: Handle extracting substring from a string with formatting
|
||||
final PyStringLiteralExpression literal = (PyStringLiteralExpression)element1;
|
||||
// Currently introduce for substrings of a multi-part string literals is not supported
|
||||
if (literal.getStringNodes().size() > 1) {
|
||||
showCannotPerformError(project, editor);
|
||||
return;
|
||||
@@ -286,7 +285,13 @@ abstract public class IntroduceHandler implements RefactoringActionHandler {
|
||||
final TextRange innerRange = literal.getStringValueTextRange();
|
||||
final TextRange intersection = selectionRange.shiftRight(-offset).intersection(innerRange);
|
||||
final TextRange finalRange = intersection != null ? intersection : selectionRange;
|
||||
element1.putUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE, Pair.create(element1, finalRange));
|
||||
final String text = literal.getText();
|
||||
// TODO: Protect against substrings with new-style format characters
|
||||
if (breaksStringFormatting(text, finalRange) || breaksStringEscaping(text, finalRange)) {
|
||||
showCannotPerformError(project, editor);
|
||||
return;
|
||||
}
|
||||
element1.putUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE, Pair.create(element1, finalRange));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,6 +302,26 @@ abstract public class IntroduceHandler implements RefactoringActionHandler {
|
||||
performActionOnElement(operation);
|
||||
}
|
||||
|
||||
private boolean breaksStringFormatting(@NotNull String s, @NotNull TextRange range) {
|
||||
return breaksRanges(PyStringFormatParser.substitutionsToRanges(new PyStringFormatParser(s).parseSubstitutions()), range);
|
||||
}
|
||||
|
||||
private boolean breaksStringEscaping(@NotNull String s, @NotNull TextRange range) {
|
||||
return breaksRanges(PyStringFormatParser.getEscapeRanges(s), range);
|
||||
}
|
||||
|
||||
private boolean breaksRanges(@NotNull List<TextRange> ranges, @NotNull TextRange range) {
|
||||
for (TextRange r : ranges) {
|
||||
if (range.contains(r)) {
|
||||
continue;
|
||||
}
|
||||
if (range.intersectsStrict(r)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void showCannotPerformError(Project project, Editor editor) {
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, PyBundle.message("refactoring.introduce.selection.error"), myDialogTitle,
|
||||
"refactoring.extractMethod");
|
||||
@@ -473,7 +498,7 @@ abstract public class IntroduceHandler implements RefactoringActionHandler {
|
||||
|
||||
@Override
|
||||
public void visitPyStringLiteralExpression(PyStringLiteralExpression node) {
|
||||
final Pair<PsiElement, TextRange> data = node.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE);
|
||||
final Pair<PsiElement, TextRange> data = node.getUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE);
|
||||
if (data != null) {
|
||||
final PsiElement parent = data.getFirst();
|
||||
final String text = parent.getText();
|
||||
@@ -566,7 +591,7 @@ abstract public class IntroduceHandler implements RefactoringActionHandler {
|
||||
@Nullable
|
||||
public PsiElement addDeclaration(IntroduceOperation operation, PsiElement declaration) {
|
||||
final PsiElement expression = operation.getInitializer();
|
||||
final Pair<PsiElement, TextRange> data = expression.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE);
|
||||
final Pair<PsiElement, TextRange> data = expression.getUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE);
|
||||
if (data == null) {
|
||||
return addDeclaration(expression, declaration, operation);
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyExpression;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils;
|
||||
import com.jetbrains.python.refactoring.PyRefactoringUtil;
|
||||
import com.jetbrains.python.refactoring.PyReplaceExpressionUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -40,8 +40,8 @@ public abstract class IntroduceValidator {
|
||||
public abstract String check(String name, PsiElement psiElement);
|
||||
|
||||
public static boolean isDefinedInScope(String name, PsiElement psiElement) {
|
||||
if (psiElement.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE) != null) {
|
||||
final Pair<PsiElement,TextRange> data = psiElement.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE);
|
||||
if (psiElement.getUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE) != null) {
|
||||
final Pair<PsiElement,TextRange> data = psiElement.getUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE);
|
||||
psiElement = data.first;
|
||||
}
|
||||
PsiElement context = PsiTreeUtil.getParentOfType(psiElement, PyFunction.class);
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
|
||||
import com.jetbrains.python.codeInsight.imports.AddImportHelper;
|
||||
import com.jetbrains.python.psi.PyExpression;
|
||||
import com.jetbrains.python.psi.PyFile;
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils;
|
||||
import com.jetbrains.python.refactoring.PyReplaceExpressionUtil;
|
||||
import com.jetbrains.python.refactoring.introduce.IntroduceHandler;
|
||||
import com.jetbrains.python.refactoring.introduce.IntroduceOperation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -29,7 +29,7 @@ public class PyIntroduceConstantHandler extends IntroduceHandler {
|
||||
if (PsiTreeUtil.getParentOfType(expression, ScopeOwner.class) instanceof PyFile) {
|
||||
return super.replaceExpression(expression, newExpression, operation);
|
||||
}
|
||||
return PyPsiUtils.replaceExpression(expression, newExpression);
|
||||
return PyReplaceExpressionUtil.replaceExpression(expression, newExpression);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@ import com.jetbrains.python.inspections.quickfix.AddFieldQuickFix;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyFunctionBuilder;
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils;
|
||||
import com.jetbrains.python.refactoring.PyReplaceExpressionUtil;
|
||||
import com.jetbrains.python.refactoring.introduce.IntroduceHandler;
|
||||
import com.jetbrains.python.refactoring.introduce.IntroduceOperation;
|
||||
import com.jetbrains.python.refactoring.introduce.variable.PyIntroduceVariableHandler;
|
||||
@@ -78,7 +78,7 @@ public class PyIntroduceFieldHandler extends IntroduceHandler {
|
||||
@Override
|
||||
protected PsiElement replaceExpression(PsiElement expression, PyExpression newExpression, IntroduceOperation operation) {
|
||||
if (operation.getInitPlace() != InitPlace.SAME_METHOD) {
|
||||
return PyPsiUtils.replaceExpression(expression, newExpression);
|
||||
return PyReplaceExpressionUtil.replaceExpression(expression, newExpression);
|
||||
}
|
||||
return super.replaceExpression(expression, newExpression, operation);
|
||||
}
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils;
|
||||
import com.jetbrains.python.refactoring.PyReplaceExpressionUtil;
|
||||
import com.jetbrains.python.refactoring.introduce.IntroduceHandler;
|
||||
import com.jetbrains.python.refactoring.introduce.IntroduceOperation;
|
||||
import com.jetbrains.python.refactoring.introduce.variable.VariableValidator;
|
||||
@@ -56,7 +56,7 @@ public class PyIntroduceParameterHandler extends IntroduceHandler {
|
||||
@Nullable
|
||||
@Override
|
||||
protected PsiElement replaceExpression(PsiElement expression, PyExpression newExpression, IntroduceOperation operation) {
|
||||
return PyPsiUtils.replaceExpression(expression, newExpression);
|
||||
return PyReplaceExpressionUtil.replaceExpression(expression, newExpression);
|
||||
}
|
||||
|
||||
protected boolean isValidIntroduceContext(PsiElement element) {
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
a = b'bar'
|
||||
b'foo' + a + b'baz'
|
||||
b'foo' + a + b'baz' + suffix
|
||||
@@ -1 +1 @@
|
||||
b'foo<selection>bar</selection>baz'
|
||||
b'foo<selection>bar</selection>baz' + suffix
|
||||
@@ -1,2 +1,2 @@
|
||||
a = "hello"
|
||||
print(a + " world")
|
||||
print(a + " world" + "!")
|
||||
|
||||
@@ -1 +1 @@
|
||||
print(<selection>"hello</selection> world")
|
||||
print(<selection>"hello</selection> world" + "!")
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
a = "hello"
|
||||
print(a + " world")
|
||||
print(a + " world" + "!")
|
||||
@@ -1 +1 @@
|
||||
print("<selection>hello</selection> world")
|
||||
print("<selection>hello</selection> world" + "!")
|
||||
@@ -1,2 +1,2 @@
|
||||
a = "lo wor"
|
||||
print("hel" + a + "ld")
|
||||
print(prefix + "hel" + a + "ld")
|
||||
@@ -1 +1 @@
|
||||
print("hel<selection>lo wor</selection>ld")
|
||||
print(prefix + "hel<selection>lo wor</selection>ld")
|
||||
@@ -1,2 +1,2 @@
|
||||
a = "world"
|
||||
print("hello " + a)
|
||||
print("hello " + a + suffix)
|
||||
@@ -1 +1 @@
|
||||
print("hello <selection>world</selection>")
|
||||
print("hello <selection>world</selection>" + suffix)
|
||||
@@ -0,0 +1,2 @@
|
||||
a = "hello"
|
||||
print("%s world" % a)
|
||||
@@ -0,0 +1 @@
|
||||
print("<selection>hello</selection> world")
|
||||
@@ -0,0 +1,2 @@
|
||||
a = "World"
|
||||
print("%s: %s %s" % ("Error", "Hello", a))
|
||||
@@ -0,0 +1 @@
|
||||
print("%s: %s <selection>World</selection>" % ("Error", "Hello"))
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
a = "World"
|
||||
print("%s: %s %s" % ("Error", "Hello", a))
|
||||
@@ -0,0 +1 @@
|
||||
print("%s: %s <selection>World</selection>" % ("Error", "Hello",))
|
||||
@@ -0,0 +1,2 @@
|
||||
a = "Hello"
|
||||
print("%s %s" % (a, "World"))
|
||||
@@ -0,0 +1 @@
|
||||
print("<selection>Hello</selection> %s" % ("World",))
|
||||
@@ -0,0 +1 @@
|
||||
print(u"Hel<selection>lo \u00d6sterreich\\!\</selection>n")
|
||||
@@ -0,0 +1 @@
|
||||
print("Hel<selection>lo %</selection>s!" % "World")
|
||||
@@ -0,0 +1,2 @@
|
||||
a = u"lo \u00d6sterreich\\!\n"
|
||||
print(u"Hel%s\n" % a)
|
||||
@@ -0,0 +1 @@
|
||||
print(u"Hel<selection>lo \u00d6sterreich\\!\n</selection>\n")
|
||||
@@ -0,0 +1,2 @@
|
||||
a = "lo %s"
|
||||
print(("Hel" + a + "!") % "World")
|
||||
@@ -0,0 +1 @@
|
||||
print("Hel<selection>lo %s</selection>!" % "World")
|
||||
@@ -0,0 +1,2 @@
|
||||
a = "Hello"
|
||||
print("%(a)s %(name)s" % {"name": "World", "a": a})
|
||||
@@ -0,0 +1 @@
|
||||
print("<selection>Hello</selection> %(name)s" % {"name": "World"})
|
||||
@@ -0,0 +1,2 @@
|
||||
a = "Hello"
|
||||
print("%s %s" % (a, "World"))
|
||||
@@ -0,0 +1 @@
|
||||
print("<selection>Hello</selection> %s" % "World")
|
||||
@@ -1,2 +1,2 @@
|
||||
a = 'foo'
|
||||
print((a + 'bar').upper())
|
||||
print(('%sbar' % a).upper())
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
a = "two"
|
||||
"one " + a + " three"
|
||||
"one %s three" % a
|
||||
@@ -1,2 +1,2 @@
|
||||
a = 'foo'
|
||||
x = a + 'bar'
|
||||
x = '%sbar' % a
|
||||
@@ -0,0 +1,2 @@
|
||||
a = "Hello"
|
||||
print("%s: %s %s" % ("Error", a, "World"))
|
||||
@@ -0,0 +1 @@
|
||||
print("%s: <selection>Hello</selection> %s" % ("Error", "World"))
|
||||
@@ -3,4 +3,4 @@ print(""""One two
|
||||
|
||||
* """ + a + """
|
||||
* Four
|
||||
* Five""")
|
||||
* Five""" + suffix)
|
||||
@@ -2,4 +2,4 @@ print(""""One two
|
||||
|
||||
* <selection>Three</selection>
|
||||
* Four
|
||||
* Five""")
|
||||
* Five""" + suffix)
|
||||
@@ -42,7 +42,7 @@ public class PySuppressInspectionsTest extends PyTestCase {
|
||||
myFixture.configureByFile("inspections/suppress/suppressForStatement.py");
|
||||
myFixture.enableInspections(PyUnresolvedReferencesInspection.class);
|
||||
final List<IntentionAction> intentions = myFixture.filterAvailableIntentions("Suppress for statement");
|
||||
assertEquals(2, intentions.size());
|
||||
assertEquals(3, intentions.size()); // Rename reference, Ignore unresolved reference, Mark all unresolved attributes
|
||||
final IntentionAction suppressAction = intentions.get(0);
|
||||
myFixture.launchAction(suppressAction);
|
||||
myFixture.checkResultByFile("inspections/suppress/suppressForStatement.after.py");
|
||||
|
||||
@@ -88,6 +88,11 @@ public class PyIntroduceVariableTest extends PyIntroduceTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-3654
|
||||
public void testSimpleSubstring() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-3654
|
||||
public void testLeftSubstring() {
|
||||
doTest();
|
||||
@@ -128,11 +133,61 @@ public class PyIntroduceVariableTest extends PyIntroduceTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-6354
|
||||
// PY-3654
|
||||
public void testBytesSubstring() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-3654
|
||||
public void testSubstringContainsFormatChars() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-3654
|
||||
public void testSubstringBreaksFormatChars() {
|
||||
doTestCannotPerform();
|
||||
}
|
||||
|
||||
// PY-3654
|
||||
public void testSubstringContainsEscapes() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-3654
|
||||
public void testSubstringBreaksEscapes() {
|
||||
doTestCannotPerform();
|
||||
}
|
||||
|
||||
// PY-3654
|
||||
public void testSubstringBeforeFormatTuple() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-3654
|
||||
public void testSubstringInsideFormatTuple() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-3654
|
||||
public void testSubstringAfterFormatTuple() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-3654
|
||||
public void testSubstringAfterFormatTupleWithComma() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-3654
|
||||
public void testSubstringFromFormatDict() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-3654
|
||||
public void testSubstringFromFormatSingleValue() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
private void doTestCannotPerform() {
|
||||
boolean thrownExpectedException = false;
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user