From 17a014e70ef7ff8769ee783cf3c75a0402087c78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yann=20C=C3=A9bron?= Date: Thu, 31 Oct 2013 19:44:20 +0100 Subject: [PATCH 01/20] IDEA-94376 Icons: web.xml --- platform/icons/src/javaee/web_xml.png | Bin 703 -> 571 bytes platform/icons/src/javaee/web_xml@2x.png | Bin 0 -> 1138 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 platform/icons/src/javaee/web_xml@2x.png diff --git a/platform/icons/src/javaee/web_xml.png b/platform/icons/src/javaee/web_xml.png index 760533d662db895bc4a02ec5deff26822ac7d21a..ac51ed31cff823848e5da53b7d00209b22698c39 100644 GIT binary patch delta 557 zcmV+|0@D4z1-k^08Gi-<001BJ|6u?C02y>eSaefwW^{L9a%BKPWN%_+AW3auXJt}l zVPtu6$z?nM00G@eL_t(|+G70w|33o=%$++I!oUq0fVdIHNls1%(@YG+gD4=bM;Bzl zqL2}b7|3;wK)e^qhr5Ik-3uZ7n=lMm^U)8*doVTpAsbO#5Pt$9{HA`v;(%pf4RJA1 z!5Gekx!~g(|Nji60Y;DuumGAuv!i(ykX53=ynIdNROXB_-_=h=sUucQQ+#DHQdSff!_nwcTe zl#L-}(KiO5CV#MIAYPhs)z4X0@k4z3J8{OZ4}bp`<7Ygo-wV!Zz7hh_O8$;AvlDzR7t5o8F+??78SXPGGyGxy{2fYHDbv{vzWl=i{LgOu=d1kaul(t?{ka4E%wqlKr2Xcn{q)uT&0zk|0RGSb{?G*e z+K&F^pZ}}||9`Iz|FReVxFi3&E&s<)|H@eZ(H8&HD*w}Y|I{`A)*k8Uv+Jn^?ad|h z*;Dq|kN4Jy_|$&**Ma!gi23}v`pf|O(RTXlzgS>oSy@t9TUJ?IS}9eND@=7mlj2xj zV9dOe+uxr5)=mG|SpV5x|JjTG+HU{fGXLL&|KOqj;(w0+;->%Or~l+E|Kykd<)#1T zK>z5q|LKJP>YD%VX#eoU|M7AE_kI8SjsO0u|No#Mha_JB000?uQchCR_xSw&4uV5t0002DNkla;)wtP@gA@){(~OG+UE{O%5}9vYI`RYApI0akG} zWd+^*EUk*bLXZHna8yTojf!G*rdfCdNPyEKxwEazPQK14L$e7aAX3oUTpwd9R}|py z=LHfF(uiReEog1QO{ilB4U5BxEwKjw-cW&1TZ$d5TUi-S=e zll|Dl+WN7@GPAA`Yrk@n+~j`cUe8VK`mr>-=~8{+Np4QgdEe)K&U?;#iD{ZxZ2kTH zO9v{qy%g`3`lGwM`-U+Gs&Bm%@7SHZ76i;nNUQ~co=QkCHXM=)GPmp?MS-I4UlLfU zCe}&;%RZxc!HyDZRRb12U!-2|f31LRyJ-zXg%fstEvc25_=LIKj~JWtB}Bh0OQ5ex z$=UKlmfwV8>`8NtRVRxT_}^=wVgg@}5bQqUq~8lmPYpYYV*@{v>KEbC;Ep1J9a{~G zg`Mi!W)yR=hyVs2+hSIqz)Hbyl>}3b;I~48sa9~iB;NAu2TqRS)5ka3BQSqTiM{$d zOEE-7CP19Or2IxnaqK%@xVg+oyx+5zaCniFG*mTdmy0 zWqy4=bnc7$*!a+@YTza$tl)<}oQ_8Fa+4&jv7&;5Px;d6Iw z|Hc=Mp=p(;pbUxQA529zOS$n~slW~#88@&#prd2mAa*u?Pp2vr3^sO+k^^VNT_cIj zdp3k?-&@r^P=dtS3sm+qV+}n`Ev<+&y6}|mBqGgDxLt0T8cX}KjJ(W1U^VnSIE%zr z-}YW(A2~)H#49E+Poitv^`AqBr`n(P2Yo~c2ngwy5UxvOV7LX1z8r#_6Lnq(GLjpd zUx(lAz;kU^$lI4PpIEM{(F+oZjIWjBJ>X~KcqMWIwKWnR-gK1)Pa!Soa61(+L`6JV zgF=BoaJrEmpXgk13uqtjTh_;4E)}_Wo~h-v@%{CT6U@wCw6$9MZGv~J-~$A@P(X5; zpw?|*-{ptU>KViE)H)c#ZJu&9@X|6uB?-W@@K`7q5IyUm$qq2AOz#YJ&N(sQAs!t1 zVLOui7F^4RVGt3->8Pk1nx$~h#CH~6Wf>wXiiWKJc06|nJBwB6559hGIxkR+uTAgdBg&w2sHg|x`x519tn3($1~bRMW$2FJ5+REt1+ zqf=~-1oc>IMnX0#7fF0P+K!n+a literal 0 HcmV?d00001 From 6df23a1974795923188fd37437e132baf3f96dd2 Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Thu, 31 Oct 2013 20:32:27 +0100 Subject: [PATCH 02/20] hotswap: allow class reload task cancelling as long as no classes are actually reloaded --- .../debugger/impl/HotSwapManager.java | 11 ++++--- .../debugger/impl/ReloadClassesWorker.java | 32 +++++++++++++------ .../intellij/debugger/ui/HotSwapUIImpl.java | 3 +- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/HotSwapManager.java b/java/debugger/impl/src/com/intellij/debugger/impl/HotSwapManager.java index bf0569b4755b..7347b430cacd 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/HotSwapManager.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/HotSwapManager.java @@ -155,13 +155,14 @@ public class HotSwapManager extends AbstractProjectComponent { } private void reloadClasses(DebuggerSession session, Map classesToReload, HotSwapProgress progress) { - if (progress.isCancelled()) { - session.setModifiedClassesScanRequired(true); - return; - } final long newSwapTime = System.currentTimeMillis(); new ReloadClassesWorker(session, progress).reloadClasses(classesToReload); - setTimeStamp(session, newSwapTime); + if (progress.isCancelled()) { + session.setModifiedClassesScanRequired(true); + } + else { + setTimeStamp(session, newSwapTime); + } } public static Map> findModifiedClasses(List sessions, Map> generatedPaths) { diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/ReloadClassesWorker.java b/java/debugger/impl/src/com/intellij/debugger/impl/ReloadClassesWorker.java index 9a43bfbcc881..2898e3f10dd9 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/ReloadClassesWorker.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/ReloadClassesWorker.java @@ -114,32 +114,44 @@ class ReloadClassesWorker { try { RedefineProcessor redefineProcessor = new RedefineProcessor(virtualMachineProxy); - int processedClassesCount = 0; - for (final String qualifiedName : modifiedClasses.keySet()) { - processedClassesCount++; + int processedEntriesCount = 0; + for (final Map.Entry entry : modifiedClasses.entrySet()) { + if (redefineProcessor.getProcessedClassesCount() == 0 && myProgress.isCancelled()) { + // once at least one class has been actually reloaded, do not interrupt the whole process + break; + } + processedEntriesCount++; + final String qualifiedName = entry.getKey(); if (qualifiedName != null) { myProgress.setText(qualifiedName); - myProgress.setFraction(processedClassesCount / (double)modifiedClasses.size()); + myProgress.setFraction(processedEntriesCount / (double)modifiedClasses.size()); } - final HotSwapFile fileDescr = modifiedClasses.get(qualifiedName); try { - redefineProcessor.processClass(qualifiedName, fileDescr.file); + redefineProcessor.processClass(qualifiedName, entry.getValue().file); } catch (IOException e) { reportProblem(qualifiedName, e); } } + + if (redefineProcessor.getProcessedClassesCount() == 0 && myProgress.isCancelled()) { + // once at least one class has been actually reloaded, do not interrupt the whole process + return; + } + redefineProcessor.processPending(); myProgress.setFraction(1); final int partiallyRedefinedClassesCount = redefineProcessor.getPartiallyRedefinedClassesCount(); if (partiallyRedefinedClassesCount == 0) { - myProgress.addMessage(myDebuggerSession, MessageCategory.INFORMATION, - DebuggerBundle.message("status.classes.reloaded", redefineProcessor.getProcessedClassesCount())); + myProgress.addMessage( + myDebuggerSession, MessageCategory.INFORMATION, DebuggerBundle.message("status.classes.reloaded", redefineProcessor.getProcessedClassesCount()) + ); } else { - final String message = DebuggerBundle.message("status.classes.not.all.versions.reloaded", partiallyRedefinedClassesCount, - redefineProcessor.getProcessedClassesCount()); + final String message = DebuggerBundle.message( + "status.classes.not.all.versions.reloaded", partiallyRedefinedClassesCount, redefineProcessor.getProcessedClassesCount() + ); myProgress.addMessage(myDebuggerSession, MessageCategory.WARNING, message); } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUIImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUIImpl.java index 9c61c8c73fe5..19f94cbe8658 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUIImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUIImpl.java @@ -269,8 +269,7 @@ public class HotSwapUIImpl extends HotSwapUI implements ProjectComponent { return result.get(); } - private static void reloadModifiedClasses(final Map> modifiedClasses, - final HotSwapProgressImpl progress) { + private static void reloadModifiedClasses(final Map> modifiedClasses, final HotSwapProgressImpl progress) { ProgressManager.getInstance().runProcess(new Runnable() { public void run() { HotSwapManager.reloadModifiedClasses(modifiedClasses, progress); From eafc40eb89069a06bb09db9806514849144f71f8 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 31 Oct 2013 15:59:33 +0100 Subject: [PATCH 03/20] expand ~ in file chooser path (PY-3138) --- .../src/com/intellij/openapi/fileChooser/ex/LocalFsFinder.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/fileChooser/ex/LocalFsFinder.java b/platform/platform-impl/src/com/intellij/openapi/fileChooser/ex/LocalFsFinder.java index ec12a86d2667..c02c77b83469 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileChooser/ex/LocalFsFinder.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileChooser/ex/LocalFsFinder.java @@ -64,7 +64,8 @@ public class LocalFsFinder implements FileLookup.Finder, FileLookup { return null; } - public String normalize(@NotNull final String path) { + public String normalize(@NotNull String path) { + path = FileUtil.expandUserHome(path); final File file = new File(path); if (file.isAbsolute()) return file.getAbsolutePath(); From a867f5eab8c07174a8b038bab4b9fca03e5828cf Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 31 Oct 2013 16:27:28 +0100 Subject: [PATCH 04/20] check for disposed project in invalid SDK notification (PY-7570) --- .../src/com/jetbrains/python/sdk/PythonSdkType.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkType.java b/python/src/com/jetbrains/python/sdk/PythonSdkType.java index 9b65dca3c70b..0e64b29b374d 100644 --- a/python/src/com/jetbrains/python/sdk/PythonSdkType.java +++ b/python/src/com/jetbrains/python/sdk/PythonSdkType.java @@ -25,7 +25,6 @@ import com.intellij.notification.Notification; import com.intellij.notification.NotificationListener; import com.intellij.notification.NotificationType; import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; @@ -556,16 +555,22 @@ public class PythonSdkType extends SdkType { @NotNull public static Notification createInvalidSdkNotification(@Nullable final Project project) { + String message = "Cannot run the project interpreter."; + if (project != null && !project.isDisposed()) { + message += " Configure..."; + } return new Notification("xxx", "Invalid Project Interpreter", - "Cannot run the project interpreter. Configure...", + message, NotificationType.ERROR, new NotificationListener() { @Override public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { - final ShowSettingsUtil settings = ShowSettingsUtil.getInstance(); - settings.showSettingsDialog(project, "Project Interpreter"); + if (project != null && !project.isDisposed()) { + final ShowSettingsUtil settings = ShowSettingsUtil.getInstance(); + settings.showSettingsDialog(project, "Project Interpreter"); + } notification.expire(); } }); From 997bc544830fdfe65128f6a6d5b8711befab4c14 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 31 Oct 2013 16:35:04 +0100 Subject: [PATCH 05/20] allow "create class" fix for names which start with multiple uppercase characters but aren't entirely uppercase (PY-7423) --- .../python/inspections/PyUnresolvedReferencesInspection.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java index cd8241015855..514bc9d563ce 100644 --- a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java @@ -835,7 +835,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection { } private void addCreateClassFix(String refText, PsiElement element, List actions) { - if (refText.length() > 2 && Character.isUpperCase(refText.charAt(0)) && !Character.isUpperCase(refText.charAt(1)) && + if (refText.length() > 2 && Character.isUpperCase(refText.charAt(0)) && !refText.toUpperCase().equals(refText) && PsiTreeUtil.getParentOfType(element, PyImportStatementBase.class) == null) { PsiElement anchor = element; if (element instanceof PyQualifiedExpression) { From d4fc857429e3125d27cf98b5a36dbba934aa0072 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 31 Oct 2013 17:20:44 +0100 Subject: [PATCH 06/20] PyStringLiteralLexer doesn't handle escape sequences in raw strings (PY-10322) --- .../jetbrains/python/lexer/PyStringLiteralLexer.java | 5 +++-- .../testData/selectWord/escapeSequenceRaw/after1.py | 1 + .../testData/selectWord/escapeSequenceRaw/before.py | 1 + .../com/jetbrains/python/PySelectWordTest.java | 4 ++++ .../jetbrains/python/PyStringLiteralLexerTest.java | 5 +++++ .../jetbrains/python/fixtures/PyLexerTestCase.java | 11 +++++++++-- 6 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 python/testData/selectWord/escapeSequenceRaw/after1.py create mode 100644 python/testData/selectWord/escapeSequenceRaw/before.py diff --git a/python/src/com/jetbrains/python/lexer/PyStringLiteralLexer.java b/python/src/com/jetbrains/python/lexer/PyStringLiteralLexer.java index 6cfeac5af1cb..10c8c9be41cb 100644 --- a/python/src/com/jetbrains/python/lexer/PyStringLiteralLexer.java +++ b/python/src/com/jetbrains/python/lexer/PyStringLiteralLexer.java @@ -221,6 +221,7 @@ public class PyStringLiteralLexer extends LexerBase { if (myBuffer.charAt(i) == '\\') { LOG.assertTrue(myState == AFTER_FIRST_QUOTE); i++; + if (myIsRaw) return i; if (i == myBufferEnd) { myState = AFTER_LAST_QUOTE; return i; @@ -282,7 +283,7 @@ public class PyStringLiteralLexer extends LexerBase { final int quote_limit = myIsTriple ? 3 : 1; int qcnt = 0; // count consequent quotes while (i < myBufferEnd) { // scan to next \something - if (myBuffer.charAt(i) == '\\') { + if (myBuffer.charAt(i) == '\\' && !myIsRaw) { return i; } if (myState == BEFORE_FIRST_QUOTE && myBuffer.charAt(i) == myQuoteChar) { @@ -292,7 +293,7 @@ public class PyStringLiteralLexer extends LexerBase { qcnt = 0; // for last quote detection in the same pass } } - else if (myState == AFTER_FIRST_QUOTE && myBuffer.charAt(i) == myQuoteChar) { // done? + else if (myState == AFTER_FIRST_QUOTE && myBuffer.charAt(i) == myQuoteChar && (!myIsRaw || myBuffer.charAt(i-1) != '\\')) { // done? qcnt += 1; if (qcnt == quote_limit) { myState = AFTER_LAST_QUOTE; diff --git a/python/testData/selectWord/escapeSequenceRaw/after1.py b/python/testData/selectWord/escapeSequenceRaw/after1.py new file mode 100644 index 000000000000..cc34c2e72f4e --- /dev/null +++ b/python/testData/selectWord/escapeSequenceRaw/after1.py @@ -0,0 +1 @@ +s = r'Hello\nWorld!' \ No newline at end of file diff --git a/python/testData/selectWord/escapeSequenceRaw/before.py b/python/testData/selectWord/escapeSequenceRaw/before.py new file mode 100644 index 000000000000..7de388ad5996 --- /dev/null +++ b/python/testData/selectWord/escapeSequenceRaw/before.py @@ -0,0 +1 @@ +s = r'Hello\nWorld!' \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PySelectWordTest.java b/python/testSrc/com/jetbrains/python/PySelectWordTest.java index 9321d576a925..0d043332488b 100644 --- a/python/testSrc/com/jetbrains/python/PySelectWordTest.java +++ b/python/testSrc/com/jetbrains/python/PySelectWordTest.java @@ -46,6 +46,10 @@ public class PySelectWordTest extends PyTestCase { doTest(); } + public void testEscapeSequenceRaw() { // PY-10322 + doTest(); + } + private void doTest() { CodeInsightTestUtil.doWordSelectionTestOnDirectory(myFixture, "selectWord/" + getTestName(true), "py"); } diff --git a/python/testSrc/com/jetbrains/python/PyStringLiteralLexerTest.java b/python/testSrc/com/jetbrains/python/PyStringLiteralLexerTest.java index 037eef11427e..100f84988709 100644 --- a/python/testSrc/com/jetbrains/python/PyStringLiteralLexerTest.java +++ b/python/testSrc/com/jetbrains/python/PyStringLiteralLexerTest.java @@ -26,4 +26,9 @@ public class PyStringLiteralLexerTest extends PyLexerTestCase { PyLexerTestCase.doLexerTest("u\"\\N{LATIN SMALL LETTER B}\"", new PyStringLiteralLexer(PyTokenTypes.SINGLE_QUOTED_UNICODE), "Py:SINGLE_QUOTED_UNICODE", "VALID_STRING_ESCAPE_TOKEN", "Py:SINGLE_QUOTED_UNICODE"); } + + public void testRawBackslashN() { + PyLexerTestCase.doLexerTest("r'[\\w\\']'", new PyStringLiteralLexer(PyTokenTypes.SINGLE_QUOTED_STRING), true, + "r'[\\w\\']'"); + } } diff --git a/python/testSrc/com/jetbrains/python/fixtures/PyLexerTestCase.java b/python/testSrc/com/jetbrains/python/fixtures/PyLexerTestCase.java index 4ce5e3516364..aa6fc8af0ab4 100644 --- a/python/testSrc/com/jetbrains/python/fixtures/PyLexerTestCase.java +++ b/python/testSrc/com/jetbrains/python/fixtures/PyLexerTestCase.java @@ -35,6 +35,13 @@ public abstract class PyLexerTestCase extends PlatformLiteFixture { } public static void doLexerTest(String text, Lexer lexer, String... expectedTokens) { + doLexerTest(text, lexer, false, expectedTokens); + } + + public static void doLexerTest(String text, + Lexer lexer, + boolean checkTokenText, + String... expectedTokens) { lexer.start(text); int idx = 0; int tokenPos = 0; @@ -44,13 +51,13 @@ public abstract class PyLexerTestCase extends PlatformLiteFixture { lexer.advance(); while (lexer.getTokenType() != null) { remainingTokens.append(","); - remainingTokens.append(" \"").append(lexer.getTokenType().toString()).append("\""); + remainingTokens.append(" \"").append(checkTokenText ? lexer.getTokenText() : lexer.getTokenType().toString()).append("\""); lexer.advance(); } fail("Too many tokens. Following tokens: " + remainingTokens.toString()); } assertEquals("Token offset mismatch at position " + idx, tokenPos, lexer.getTokenStart()); - String tokenName = lexer.getTokenType().toString(); + String tokenName = checkTokenText ? lexer.getTokenText() : lexer.getTokenType().toString(); assertEquals("Token mismatch at position " + idx, expectedTokens[idx], tokenName); idx++; tokenPos = lexer.getTokenEnd(); From 2067b87220922d5c5c23191bba1db12064e3560f Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 31 Oct 2013 17:27:25 +0100 Subject: [PATCH 07/20] help for Python language injection settings (PY-10753) --- .../python/intelliLang/PyLanguageInjectionSupport.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyLanguageInjectionSupport.java b/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyLanguageInjectionSupport.java index 8065e1f686f7..5a91db5ce994 100644 --- a/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyLanguageInjectionSupport.java +++ b/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyLanguageInjectionSupport.java @@ -27,6 +27,7 @@ import org.intellij.plugins.intelliLang.inject.config.BaseInjection; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.List; @@ -79,4 +80,10 @@ public class PyLanguageInjectionSupport extends AbstractLanguageInjectionSupport // with multiple injection text ranges (PY-10691) return true; } + + @Nullable + @Override + public String getHelpId() { + return "reference.settings.language.injection.generic.python"; + } } From 09b25c16d195806725b62e0d9d7a167bddc4cd91 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 31 Oct 2013 17:40:05 +0100 Subject: [PATCH 08/20] from __future__ import is moved after module docstring (PY-10080) --- .../quickfix/MoveFromFutureImportQuickFix.java | 15 ++++++++++----- .../inspections/MoveFromFutureImportDocString.py | 10 ++++++++++ .../MoveFromFutureImportDocString_after.py | 10 ++++++++++ .../com/jetbrains/python/PyQuickFixTest.java | 9 ++++++--- 4 files changed, 36 insertions(+), 8 deletions(-) create mode 100644 python/testData/inspections/MoveFromFutureImportDocString.py create mode 100644 python/testData/inspections/MoveFromFutureImportDocString_after.py diff --git a/python/src/com/jetbrains/python/inspections/quickfix/MoveFromFutureImportQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/MoveFromFutureImportQuickFix.java index 74b54e245fc3..7b3b235ffd73 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/MoveFromFutureImportQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/MoveFromFutureImportQuickFix.java @@ -21,14 +21,13 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.jetbrains.python.PyBundle; +import com.jetbrains.python.documentation.DocStringUtil; import com.jetbrains.python.psi.PyFile; +import com.jetbrains.python.psi.PyStringLiteralExpression; import org.jetbrains.annotations.NotNull; /** - * Created by IntelliJ IDEA. - * Author: Alexey.Ivanov - * Date: 24.03.2010 - * Time: 20:15:23 + * @author Alexey.Ivanov */ public class MoveFromFutureImportQuickFix implements LocalQuickFix { @NotNull @@ -46,7 +45,13 @@ public class MoveFromFutureImportQuickFix implements LocalQuickFix { PsiFile psiFile = problemElement.getContainingFile(); if (psiFile instanceof PyFile) { PyFile file = (PyFile)psiFile; - file.addBefore(problemElement, file.getStatements().get(0)); + PyStringLiteralExpression docString = DocStringUtil.findDocStringExpression(file); + if (docString != null) { + file.addAfter(problemElement, docString.getParent() /* PyExpressionStatement */); + } + else { + file.addBefore(problemElement, file.getStatements().get(0)); + } problemElement.delete(); } } diff --git a/python/testData/inspections/MoveFromFutureImportDocString.py b/python/testData/inspections/MoveFromFutureImportDocString.py new file mode 100644 index 000000000000..8d7212411954 --- /dev/null +++ b/python/testData/inspections/MoveFromFutureImportDocString.py @@ -0,0 +1,10 @@ +"""This is a docstring.""" + +from __future__ import print_function +#comment +from __future__ import absolute_import + +class A: + pass + +from __future__ import with_statement diff --git a/python/testData/inspections/MoveFromFutureImportDocString_after.py b/python/testData/inspections/MoveFromFutureImportDocString_after.py new file mode 100644 index 000000000000..86ac79b6053e --- /dev/null +++ b/python/testData/inspections/MoveFromFutureImportDocString_after.py @@ -0,0 +1,10 @@ +"""This is a docstring.""" +from __future__ import with_statement + +from __future__ import print_function +#comment +from __future__ import absolute_import + +class A: + pass + diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index a4ef43bbf8d4..9f5dcccf4b8c 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -27,9 +27,7 @@ import com.jetbrains.python.psi.LanguageLevel; import org.jetbrains.annotations.NonNls; /** - * Test actions that various inspections add. - * User: dcheryasov - * Date: Nov 29, 2008 12:47:08 AM + * @author dcheryasov */ @TestDataPath("$CONTENT_ROOT/../testData/inspections/") public class PyQuickFixTest extends PyTestCase { @@ -161,6 +159,11 @@ public class PyQuickFixTest extends PyTestCase { PyBundle.message("QFIX.move.from.future.import"), true, true); } + public void testFromFutureImportQuickFixDocString() { // PY-10080 + doInspectionTest("MoveFromFutureImportDocString.py", PyFromFutureImportInspection.class, + PyBundle.message("QFIX.move.from.future.import"), true, true); + } + public void testComparisonWithNoneQuickFix() { doInspectionTest("ComparisonWithNone.py", PyComparisonWithNoneInspection.class, PyBundle.message("QFIX.replace.equality"), true, true); From 421644134010d413a623bd3f30c59f0cc1ba76dc Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 31 Oct 2013 17:49:34 +0100 Subject: [PATCH 09/20] import sorter skips from __future__ imports (PY-10022) --- .../codeInsight/imports/PyImportOptimizer.java | 18 +++++++++++++++--- python/testData/optimizeImports/order.after.py | 1 + python/testData/optimizeImports/order.py | 1 + 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/imports/PyImportOptimizer.java b/python/src/com/jetbrains/python/codeInsight/imports/PyImportOptimizer.java index fee8c8518ce7..6d5e3c850de2 100644 --- a/python/src/com/jetbrains/python/codeInsight/imports/PyImportOptimizer.java +++ b/python/src/com/jetbrains/python/codeInsight/imports/PyImportOptimizer.java @@ -85,6 +85,9 @@ public class PyImportOptimizer implements ImportOptimizer { } LanguageLevel langLevel = LanguageLevel.forElement(myFile); for (PyImportStatementBase importStatement : myImportBlock) { + if (importStatement instanceof PyFromImportStatement && ((PyFromImportStatement)importStatement).isFromFuture()) { + continue; + } if (importStatement instanceof PyImportStatement && importStatement.getImportElements().length > 1) { for (PyImportElement importElement : importStatement.getImportElements()) { myMissorted = true; @@ -96,7 +99,7 @@ public class PyImportOptimizer implements ImportOptimizer { else { PsiElement toImport; if (importStatement instanceof PyFromImportStatement) { - toImport = ((PyFromImportStatement) importStatement).resolveImportSource(); + toImport = ((PyFromImportStatement)importStatement).resolveImportSource(); } else { toImport = importStatement.getImportElements()[0].resolve(); @@ -140,12 +143,21 @@ public class PyImportOptimizer implements ImportOptimizer { addImports(myThirdPartyImports); addImports(myProjectImports); PsiElement lastElement = myImportBlock.get(myImportBlock.size()-1); - myFile.deleteChildRange(myImportBlock.get(0), lastElement); + myFile.deleteChildRange(findFirstNonFutureImport(), lastElement); for (PyImportStatementBase anImport : myBuiltinImports) { anImport.putCopyableUserData(PyBlock.IMPORT_GROUP_BEGIN, null); } } + private PyImportStatementBase findFirstNonFutureImport() { + for (PyImportStatementBase importStatement: myImportBlock) { + if (!(importStatement instanceof PyFromImportStatement && ((PyFromImportStatement)importStatement).isFromFuture())) { + return importStatement; + } + } + return myImportBlock.get(0); + } + private static void markGroupBegin(List imports) { if (imports.size() > 0) { imports.get(0).putCopyableUserData(PyBlock.IMPORT_GROUP_BEGIN, true); @@ -154,7 +166,7 @@ public class PyImportOptimizer implements ImportOptimizer { private void addImports(final List imports) { for (PyImportStatementBase newImport: imports) { - myFile.addBefore(newImport, myImportBlock.get(0)); + myFile.addBefore(newImport, findFirstNonFutureImport()); } } } diff --git a/python/testData/optimizeImports/order.after.py b/python/testData/optimizeImports/order.after.py index e89fc2619475..e3719e04dcc0 100644 --- a/python/testData/optimizeImports/order.after.py +++ b/python/testData/optimizeImports/order.after.py @@ -1,3 +1,4 @@ +from __future__ import with_statement import sys import datetime diff --git a/python/testData/optimizeImports/order.py b/python/testData/optimizeImports/order.py index 4232fbd83ed6..ad3147aef522 100644 --- a/python/testData/optimizeImports/order.py +++ b/python/testData/optimizeImports/order.py @@ -1,3 +1,4 @@ +from __future__ import with_statement import foo import sys from bar import * From 9c4cb80d869fe9cd0d20941fb3d6774471c61969 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 31 Oct 2013 17:56:44 +0100 Subject: [PATCH 10/20] snakeCase() macro for live templates (PY-9989) --- .../macro/CapitalizeAndUnderscoreMacro.java | 12 ++++++-- .../template/macro/SnakeCaseMacro.java | 30 +++++++++++++++++++ .../src/META-INF/LangExtensions.xml | 1 + 3 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/codeInsight/template/macro/SnakeCaseMacro.java diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/macro/CapitalizeAndUnderscoreMacro.java b/platform/lang-impl/src/com/intellij/codeInsight/template/macro/CapitalizeAndUnderscoreMacro.java index 8ae675bf464e..f7e134f41f0a 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/macro/CapitalizeAndUnderscoreMacro.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/macro/CapitalizeAndUnderscoreMacro.java @@ -32,23 +32,31 @@ public class CapitalizeAndUnderscoreMacro extends MacroBase { super("capitalizeAndUnderscore", CodeInsightBundle.message("macro.capitalizeAndUnderscore.string")); } + protected CapitalizeAndUnderscoreMacro(String name, String description) { + super(name, description); + } + @Override protected Result calculateResult(@NotNull Expression[] params, ExpressionContext context, boolean quick) { String text = getTextResult(params, context, true); if (text != null && text.length() > 0) { final String[] words = NameUtil.nameToWords(text); boolean insertUnderscore = false; - final StringBuffer buf = new StringBuffer(); + final StringBuilder buf = new StringBuilder(); for (String word : words) { if (insertUnderscore) { buf.append("_"); } else { insertUnderscore = true; } - buf.append(StringUtil.toUpperCase(word)); + buf.append(convertCase(word)); } return new TextResult(buf.toString()); } return null; } + + protected String convertCase(String word) { + return StringUtil.toUpperCase(word); + } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/macro/SnakeCaseMacro.java b/platform/lang-impl/src/com/intellij/codeInsight/template/macro/SnakeCaseMacro.java new file mode 100644 index 000000000000..9e5d8ac25106 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/macro/SnakeCaseMacro.java @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * 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.intellij.codeInsight.template.macro; + +/** + * @author yole + */ +public class SnakeCaseMacro extends CapitalizeAndUnderscoreMacro { + public SnakeCaseMacro() { + super("snakeCase", "snakeCase(String)"); + } + + @Override + protected String convertCase(String word) { + return word.toLowerCase(); + } +} diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml index 737fc16383aa..99595f0058b8 100644 --- a/platform/platform-resources/src/META-INF/LangExtensions.xml +++ b/platform/platform-resources/src/META-INF/LangExtensions.xml @@ -432,6 +432,7 @@ + From 39bdf7e68f929fa359d1ca3df500d4c84b04bf18 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 31 Oct 2013 18:42:11 +0100 Subject: [PATCH 11/20] external tool macro for directory containing Python interpreter (PY-6533) --- .../application/options/PathMacrosImpl.java | 3 +- python/src/META-INF/python-core.xml | 3 +- .../python/sdk/InterpreterDirectoryMacro.java | 73 +++++++++++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 python/src/com/jetbrains/python/sdk/InterpreterDirectoryMacro.java diff --git a/platform/projectModel-impl/src/com/intellij/application/options/PathMacrosImpl.java b/platform/projectModel-impl/src/com/intellij/application/options/PathMacrosImpl.java index 715834f27595..493b21d85306 100644 --- a/platform/projectModel-impl/src/com/intellij/application/options/PathMacrosImpl.java +++ b/platform/projectModel-impl/src/com/intellij/application/options/PathMacrosImpl.java @@ -129,7 +129,8 @@ public class PathMacrosImpl extends PathMacros implements ApplicationComponent, "SelectionStartLine", "SelectionEndLine", "SelectionStartColumn", - "SelectionEndColumn" + "SelectionEndColumn", + "PyInterpreterDirectory" ); public PathMacrosImpl() { diff --git a/python/src/META-INF/python-core.xml b/python/src/META-INF/python-core.xml index a426b2433c60..35255369d2ed 100644 --- a/python/src/META-INF/python-core.xml +++ b/python/src/META-INF/python-core.xml @@ -473,6 +473,8 @@ + + @@ -504,7 +506,6 @@ - diff --git a/python/src/com/jetbrains/python/sdk/InterpreterDirectoryMacro.java b/python/src/com/jetbrains/python/sdk/InterpreterDirectoryMacro.java new file mode 100644 index 000000000000..a601aeb8dc6a --- /dev/null +++ b/python/src/com/jetbrains/python/sdk/InterpreterDirectoryMacro.java @@ -0,0 +1,73 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * 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.jetbrains.python.sdk; + +import com.intellij.ide.macro.Macro; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.PathUtil; +import org.jetbrains.annotations.Nullable; + +/** + * @author yole + */ +public class InterpreterDirectoryMacro extends Macro { + @Override + public String getName() { + return "PyInterpreterDirectory"; + } + + @Override + public String getDescription() { + return "The directory containing the Python interpreter selected for the project"; + } + + @Nullable + @Override + public String expand(DataContext dataContext) throws ExecutionCancelledException { + Module module = LangDataKeys.MODULE.getData(dataContext); + if (module == null) { + Project project = CommonDataKeys.PROJECT.getData(dataContext); + if (project == null) { + return null; + } + Module[] modules = ModuleManager.getInstance(project).getModules(); + if (modules.length == 0) { + return null; + } + module = modules[0]; + } + Sdk sdk = PythonSdkType.findPythonSdk(module); + if (sdk != null) { + VirtualFile homeDir = sdk.getHomeDirectory(); + if (homeDir == null) { + return null; + } + String path = PathUtil.getLocalPath(homeDir.getParent()); + if (path != null) { + return FileUtil.toSystemDependentName(path); + } + } + return null; + } +} From c61eb4b007d058e776c2836ee9eef3deb3bb4a03 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 31 Oct 2013 19:33:50 +0100 Subject: [PATCH 12/20] default color for builtin names under darcula (PY-9529) --- colorSchemes/src/colorSchemes/Darcula.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/colorSchemes/src/colorSchemes/Darcula.xml b/colorSchemes/src/colorSchemes/Darcula.xml index 8356b0f6137a..bd7b3ac8efd0 100644 --- a/colorSchemes/src/colorSchemes/Darcula.xml +++ b/colorSchemes/src/colorSchemes/Darcula.xml @@ -1113,6 +1113,11 @@ +