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:
Generated
+1
-1
@@ -1,7 +1,7 @@
|
||||
<component name="libraryTable">
|
||||
<library name="proxy-vole">
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/proxy-vole_20120920.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/proxy-vole_20131209.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
/* The following code was generated by JFlex 1.4.3 on 10/24/14 12:41 PM */
|
||||
/* The following code was generated by JFlex 1.4.3 on 10/24/14 4:28 PM */
|
||||
|
||||
/* It's an automatically generated code. Do not modify it. */
|
||||
package org.intellij.lang.regexp;
|
||||
|
||||
import com.intellij.lexer.FlexLexer;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import java.util.LinkedList;
|
||||
import java.util.EnumSet;
|
||||
import com.intellij.psi.StringEscapesTokenTypes;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
|
||||
@SuppressWarnings("ALL")
|
||||
|
||||
/**
|
||||
* This class is a scanner generated by
|
||||
* <a href="http://www.jflex.de/">JFlex</a> 1.4.3
|
||||
* on 10/24/14 12:41 PM from the specification file
|
||||
* on 10/24/14 4:28 PM from the specification file
|
||||
* <tt>/Users/bas/Projects/IntelliJ IDEA Ultimate/tools/lexer/../../community/RegExpSupport/src/org/intellij/lang/regexp/regexp-lexer.flex</tt>
|
||||
*/
|
||||
class _RegExLexer implements FlexLexer {
|
||||
@@ -404,12 +405,13 @@ class _RegExLexer implements FlexLexer {
|
||||
|
||||
/* user code: */
|
||||
// This adds support for nested states. I'm no JFlex pro, so maybe this is overkill, but it works quite well.
|
||||
private final LinkedList<Integer> states = new LinkedList();
|
||||
private final ArrayList<Integer> states = new ArrayList();
|
||||
|
||||
// This was an idea to use the regex implementation for XML schema regexes (which use a slightly different syntax)
|
||||
// as well, but is currently unfinished as it requires to tweak more places than just the lexer.
|
||||
private boolean xmlSchemaMode;
|
||||
|
||||
private int capturingGroupCount = 0;
|
||||
|
||||
private boolean allowDanglingMetacharacters;
|
||||
private boolean allowNestedCharacterClasses;
|
||||
@@ -433,11 +435,12 @@ class _RegExLexer implements FlexLexer {
|
||||
}
|
||||
|
||||
private void yypushstate(int state) {
|
||||
states.addFirst(yystate());
|
||||
states.add(yystate());
|
||||
yybegin(state);
|
||||
}
|
||||
|
||||
private void yypopstate() {
|
||||
final int state = states.removeFirst();
|
||||
final int state = states.remove(states.size() - 1);
|
||||
yybegin(state);
|
||||
}
|
||||
|
||||
@@ -747,94 +750,85 @@ class _RegExLexer implements FlexLexer {
|
||||
{ yybegin(YYINITIAL); return RegExpTT.GROUP_END;
|
||||
}
|
||||
case 88: break;
|
||||
case 5:
|
||||
{ return RegExpTT.GROUP_BEGIN;
|
||||
}
|
||||
case 89: break;
|
||||
case 37:
|
||||
{ yybegin(YYINITIAL); return RegExpTT.COLON;
|
||||
}
|
||||
case 90: break;
|
||||
case 89: break;
|
||||
case 6:
|
||||
{ return RegExpTT.GROUP_END;
|
||||
}
|
||||
case 91: break;
|
||||
case 90: break;
|
||||
case 25:
|
||||
{ assert false : yytext();
|
||||
}
|
||||
case 92: break;
|
||||
case 91: break;
|
||||
case 18:
|
||||
{ return commentMode ? com.intellij.psi.TokenType.WHITE_SPACE : RegExpTT.CHARACTER;
|
||||
}
|
||||
case 93: break;
|
||||
case 92: break;
|
||||
case 46:
|
||||
{ return commentMode ? RegExpTT.CHARACTER : RegExpTT.REDUNDANT_ESCAPE;
|
||||
}
|
||||
case 94: break;
|
||||
case 93: break;
|
||||
case 23:
|
||||
{ return RegExpTT.NAME;
|
||||
}
|
||||
case 95: break;
|
||||
case 74:
|
||||
{ if (allowOctalNoLeadingZero) return RegExpTT.OCT_CHAR;
|
||||
return yystate() != CLASS2 ? RegExpTT.BACKREF : RegExpTT.ESC_CHARACTER;
|
||||
}
|
||||
case 96: break;
|
||||
case 94: break;
|
||||
case 29:
|
||||
{ yypopstate(); return RegExpTT.CLASS_END;
|
||||
}
|
||||
case 97: break;
|
||||
case 95: break;
|
||||
case 34:
|
||||
{ yybegin(YYINITIAL); return RegExpTT.BAD_CHARACTER;
|
||||
}
|
||||
case 98: break;
|
||||
case 96: break;
|
||||
case 52:
|
||||
{ yypushstate(QUOTED); return RegExpTT.QUOTE_BEGIN;
|
||||
}
|
||||
case 99: break;
|
||||
case 97: break;
|
||||
case 21:
|
||||
{ return RegExpTT.NUMBER;
|
||||
}
|
||||
case 100: break;
|
||||
case 98: break;
|
||||
case 4:
|
||||
{ return RegExpTT.DOT;
|
||||
}
|
||||
case 101: break;
|
||||
case 99: break;
|
||||
case 72:
|
||||
{ return RegExpTT.NEG_LOOKBEHIND;
|
||||
}
|
||||
case 102: break;
|
||||
case 100: break;
|
||||
case 33:
|
||||
{ yypopstate(); if (allowCategoryShorthand) return RegExpTT.CATEGORY_SHORT_HAND; else yypushback(1);
|
||||
}
|
||||
case 103: break;
|
||||
case 101: break;
|
||||
case 44:
|
||||
{ return (allowOctalNoLeadingZero ? RegExpTT.OCT_CHAR : RegExpTT.BAD_OCT_VALUE);
|
||||
}
|
||||
case 104: break;
|
||||
case 102: break;
|
||||
case 41:
|
||||
// lookahead expression with fixed base length
|
||||
zzMarkedPos = zzStartRead + 1;
|
||||
{ yypushstate(CLASS1);
|
||||
return RegExpTT.CLASS_BEGIN;
|
||||
}
|
||||
case 105: break;
|
||||
case 103: break;
|
||||
case 63:
|
||||
{ return RegExpTT.NEG_LOOKAHEAD;
|
||||
}
|
||||
case 106: break;
|
||||
case 104: break;
|
||||
case 10:
|
||||
{ return commentMode ? com.intellij.psi.TokenType.WHITE_SPACE : RegExpTT.CTRL_CHARACTER;
|
||||
}
|
||||
case 107: break;
|
||||
case 105: break;
|
||||
case 61:
|
||||
{ return RegExpTT.NON_CAPT_GROUP;
|
||||
}
|
||||
case 108: break;
|
||||
case 106: break;
|
||||
case 65:
|
||||
{ yybegin(QUOTED_NAMED_GROUP); return RegExpTT.RUBY_QUOTED_NAMED_GROUP;
|
||||
}
|
||||
case 109: break;
|
||||
case 107: break;
|
||||
case 66:
|
||||
// lookahead expression with fixed base length
|
||||
zzMarkedPos = zzStartRead + 1;
|
||||
@@ -846,7 +840,7 @@ class _RegExLexer implements FlexLexer {
|
||||
}
|
||||
return RegExpTT.CLASS_BEGIN;
|
||||
}
|
||||
case 110: break;
|
||||
case 108: break;
|
||||
case 28:
|
||||
{ if (allowNestedCharacterClasses) {
|
||||
yypushstate(CLASS2);
|
||||
@@ -854,72 +848,80 @@ class _RegExLexer implements FlexLexer {
|
||||
}
|
||||
return RegExpTT.CHARACTER;
|
||||
}
|
||||
case 111: break;
|
||||
case 109: break;
|
||||
case 19:
|
||||
{ if (commentMode) { yypushstate(COMMENT); return RegExpTT.COMMENT; } else return RegExpTT.CHARACTER;
|
||||
}
|
||||
case 112: break;
|
||||
case 110: break;
|
||||
case 51:
|
||||
{ yypushstate(PROP); return RegExpTT.PROPERTY;
|
||||
}
|
||||
case 113: break;
|
||||
case 111: break;
|
||||
case 8:
|
||||
{ yypushstate(CLASS2);
|
||||
return RegExpTT.CLASS_BEGIN;
|
||||
}
|
||||
case 114: break;
|
||||
case 112: break;
|
||||
case 71:
|
||||
{ return RegExpTT.POS_LOOKBEHIND;
|
||||
}
|
||||
case 115: break;
|
||||
case 43:
|
||||
{ return yystate() != CLASS2 ? RegExpTT.BACKREF : RegExpTT.ESC_CHARACTER;
|
||||
}
|
||||
case 116: break;
|
||||
case 113: break;
|
||||
case 73:
|
||||
{ return RegExpTT.HEX_CHAR;
|
||||
}
|
||||
case 117: break;
|
||||
case 114: break;
|
||||
case 1:
|
||||
{ handleOptions(); return RegExpTT.OPTIONS_ON;
|
||||
}
|
||||
case 118: break;
|
||||
case 115: break;
|
||||
case 43:
|
||||
{ if (yystate() == CLASS2) return RegExpTT.ESC_CHARACTER;
|
||||
while (yylength() > 2 && Integer.parseInt(yytext().toString().substring(1)) > capturingGroupCount) {
|
||||
yypushback(1);
|
||||
}
|
||||
return RegExpTT.BACKREF;
|
||||
}
|
||||
case 116: break;
|
||||
case 59:
|
||||
{ return allowNestedCharacterClasses ? RegExpTT.ANDAND : RegExpTT.CHARACTER;
|
||||
}
|
||||
case 119: break;
|
||||
case 117: break;
|
||||
case 2:
|
||||
{ yypopstate(); return RegExpTT.COMMENT;
|
||||
}
|
||||
case 120: break;
|
||||
case 118: break;
|
||||
case 17:
|
||||
{ return RegExpTT.MINUS;
|
||||
}
|
||||
case 121: break;
|
||||
case 119: break;
|
||||
case 47:
|
||||
{ return RegExpTT.ESC_CTRL_CHARACTER;
|
||||
}
|
||||
case 122: break;
|
||||
case 120: break;
|
||||
case 32:
|
||||
{ yypopstate(); yypushstate(EMBRACED); return RegExpTT.LBRACE;
|
||||
}
|
||||
case 123: break;
|
||||
case 121: break;
|
||||
case 13:
|
||||
{ return RegExpTT.QUEST;
|
||||
}
|
||||
case 124: break;
|
||||
case 122: break;
|
||||
case 5:
|
||||
{ capturingGroupCount++; return RegExpTT.GROUP_BEGIN;
|
||||
}
|
||||
case 123: break;
|
||||
case 15:
|
||||
{ return RegExpTT.PLUS;
|
||||
}
|
||||
case 125: break;
|
||||
case 124: break;
|
||||
case 67:
|
||||
{ return RegExpTT.OCT_CHAR;
|
||||
}
|
||||
case 126: break;
|
||||
case 125: break;
|
||||
case 39:
|
||||
{ yybegin(YYINITIAL); return RegExpTT.QUOTE;
|
||||
}
|
||||
case 127: break;
|
||||
case 126: break;
|
||||
case 20:
|
||||
{ if (allowDanglingMetacharacters) {
|
||||
yypopstate(); yypushback(1);
|
||||
@@ -927,15 +929,15 @@ class _RegExLexer implements FlexLexer {
|
||||
return RegExpTT.BAD_CHARACTER;
|
||||
}
|
||||
}
|
||||
case 128: break;
|
||||
case 127: break;
|
||||
case 31:
|
||||
{ yypopstate(); yypushback(1);
|
||||
}
|
||||
case 129: break;
|
||||
case 128: break;
|
||||
case 27:
|
||||
{ yybegin(CLASS1); return RegExpTT.CARET;
|
||||
}
|
||||
case 130: break;
|
||||
case 129: break;
|
||||
case 57:
|
||||
// lookahead expression with fixed base length
|
||||
zzMarkedPos = zzStartRead + 1;
|
||||
@@ -945,91 +947,100 @@ class _RegExLexer implements FlexLexer {
|
||||
}
|
||||
return RegExpTT.CHARACTER;
|
||||
}
|
||||
case 131: break;
|
||||
case 130: break;
|
||||
case 58:
|
||||
{ if (!allowNestedCharacterClasses) return RegExpTT.CHARACTER;
|
||||
return RegExpTT.REDUNDANT_ESCAPE;
|
||||
}
|
||||
case 132: break;
|
||||
case 131: break;
|
||||
case 38:
|
||||
{ yybegin(YYINITIAL); return RegExpTT.GT;
|
||||
}
|
||||
case 133: break;
|
||||
case 132: break;
|
||||
case 69:
|
||||
{ yybegin(PY_NAMED_GROUP_REF); return RegExpTT.PYTHON_NAMED_GROUP_REF;
|
||||
}
|
||||
case 134: break;
|
||||
case 133: break;
|
||||
case 16:
|
||||
{ return RegExpTT.UNION;
|
||||
}
|
||||
case 135: break;
|
||||
case 134: break;
|
||||
case 3:
|
||||
{ return RegExpTT.CHARACTER;
|
||||
}
|
||||
case 136: break;
|
||||
case 135: break;
|
||||
case 64:
|
||||
{ yybegin(NAMED_GROUP); return RegExpTT.RUBY_NAMED_GROUP;
|
||||
}
|
||||
case 137: break;
|
||||
case 136: break;
|
||||
case 40:
|
||||
{ yybegin(OPTIONS); return RegExpTT.SET_OPTIONS;
|
||||
}
|
||||
case 138: break;
|
||||
case 137: break;
|
||||
case 26:
|
||||
{ yybegin(CLASS2); return RegExpTT.CHARACTER;
|
||||
}
|
||||
case 139: break;
|
||||
case 138: break;
|
||||
case 68:
|
||||
{ if (xmlSchemaMode) { yypushback(1); return RegExpTT.CHAR_CLASS; } else return RegExpTT.CTRL;
|
||||
}
|
||||
case 140: break;
|
||||
case 139: break;
|
||||
case 30:
|
||||
{ return commentMode ? com.intellij.psi.TokenType.WHITE_SPACE : RegExpTT.ESC_CHARACTER;
|
||||
}
|
||||
case 141: break;
|
||||
case 140: break;
|
||||
case 12:
|
||||
{ return RegExpTT.DOLLAR;
|
||||
}
|
||||
case 142: break;
|
||||
case 141: break;
|
||||
case 50:
|
||||
{ if (xmlSchemaMode) return RegExpTT.CHAR_CLASS; else return StringEscapesTokenTypes.INVALID_CHARACTER_ESCAPE_TOKEN;
|
||||
}
|
||||
case 143: break;
|
||||
case 142: break;
|
||||
case 42:
|
||||
{ return RegExpTT.REDUNDANT_ESCAPE;
|
||||
}
|
||||
case 144: break;
|
||||
case 143: break;
|
||||
case 55:
|
||||
{ return (allowHexDigitClass || allowHorizontalWhitespaceClass ? RegExpTT.CHAR_CLASS : StringEscapesTokenTypes.INVALID_CHARACTER_ESCAPE_TOKEN);
|
||||
}
|
||||
case 145: break;
|
||||
case 144: break;
|
||||
case 48:
|
||||
{ return yystate() != CLASS2 ? RegExpTT.BOUNDARY : RegExpTT.ESC_CHARACTER;
|
||||
}
|
||||
case 146: break;
|
||||
case 145: break;
|
||||
case 22:
|
||||
{ yypopstate(); return RegExpTT.RBRACE;
|
||||
}
|
||||
case 147: break;
|
||||
case 146: break;
|
||||
case 54:
|
||||
{ return StringEscapesTokenTypes.INVALID_UNICODE_ESCAPE_TOKEN;
|
||||
}
|
||||
case 148: break;
|
||||
case 147: break;
|
||||
case 53:
|
||||
{ return RegExpTT.BAD_HEX_VALUE;
|
||||
}
|
||||
case 149: break;
|
||||
case 148: break;
|
||||
case 9:
|
||||
{ return StringEscapesTokenTypes.INVALID_CHARACTER_ESCAPE_TOKEN;
|
||||
}
|
||||
case 150: break;
|
||||
case 149: break;
|
||||
case 11:
|
||||
{ return RegExpTT.CARET;
|
||||
}
|
||||
case 151: break;
|
||||
case 150: break;
|
||||
case 45:
|
||||
{ return RegExpTT.ESC_CHARACTER;
|
||||
}
|
||||
case 151: break;
|
||||
case 74:
|
||||
{ if (allowOctalNoLeadingZero) return RegExpTT.OCT_CHAR;
|
||||
if (yystate() == CLASS2) return RegExpTT.ESC_CHARACTER;
|
||||
while (yylength() > 2 && Integer.parseInt(yytext().toString().substring(1)) > capturingGroupCount) {
|
||||
yypushback(1);
|
||||
}
|
||||
return RegExpTT.BACKREF;
|
||||
}
|
||||
case 152: break;
|
||||
default:
|
||||
if (zzInput == YYEOF && zzStartRead == zzCurrentPos) {
|
||||
|
||||
@@ -79,7 +79,12 @@ public final class RegExpLanguageHosts extends ClassExtension<RegExpLanguageHost
|
||||
return true;
|
||||
}
|
||||
final RegExpLanguageHost host = findRegExpHost(regExpChar);
|
||||
return host != null && host.supportsExtendedHexCharacter(regExpChar);
|
||||
try {
|
||||
return host != null && host.supportsExtendedHexCharacter(regExpChar);
|
||||
} catch (AbstractMethodError e) {
|
||||
// supportsExtendedHexCharacter not present
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean supportsNamedGroupSyntax(@Nullable final RegExpGroup group) {
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
package org.intellij.lang.regexp;
|
||||
|
||||
import com.intellij.lexer.FlexLexer;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import java.util.LinkedList;
|
||||
import java.util.EnumSet;
|
||||
import com.intellij.psi.StringEscapesTokenTypes;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
|
||||
@SuppressWarnings("ALL")
|
||||
%%
|
||||
@@ -20,12 +21,13 @@ import com.intellij.psi.StringEscapesTokenTypes;
|
||||
|
||||
%{
|
||||
// This adds support for nested states. I'm no JFlex pro, so maybe this is overkill, but it works quite well.
|
||||
private final LinkedList<Integer> states = new LinkedList();
|
||||
private final ArrayList<Integer> states = new ArrayList();
|
||||
|
||||
// This was an idea to use the regex implementation for XML schema regexes (which use a slightly different syntax)
|
||||
// as well, but is currently unfinished as it requires to tweak more places than just the lexer.
|
||||
private boolean xmlSchemaMode;
|
||||
|
||||
private int capturingGroupCount = 0;
|
||||
|
||||
private boolean allowDanglingMetacharacters;
|
||||
private boolean allowNestedCharacterClasses;
|
||||
@@ -49,11 +51,12 @@ import com.intellij.psi.StringEscapesTokenTypes;
|
||||
}
|
||||
|
||||
private void yypushstate(int state) {
|
||||
states.addFirst(yystate());
|
||||
states.add(yystate());
|
||||
yybegin(state);
|
||||
}
|
||||
|
||||
private void yypopstate() {
|
||||
final int state = states.removeFirst();
|
||||
final int state = states.remove(states.size() - 1);
|
||||
yybegin(state);
|
||||
}
|
||||
|
||||
@@ -144,15 +147,21 @@ HEX_CHAR=[0-9a-fA-F]
|
||||
subexpressions exist at that point in the regular expression, otherwise the
|
||||
parser will drop digits until the number is smaller or equal to the existing
|
||||
number of groups or it is one digit."
|
||||
|
||||
So, for 100% compatibility, backrefs > 9 should be resolved by the parser, but
|
||||
I'm not sure if it's worth the effort - at least not atm.
|
||||
*/
|
||||
{ESCAPE} [0-7]{3} { if (allowOctalNoLeadingZero) return RegExpTT.OCT_CHAR;
|
||||
return yystate() != CLASS2 ? RegExpTT.BACKREF : RegExpTT.ESC_CHARACTER;
|
||||
if (yystate() == CLASS2) return RegExpTT.ESC_CHARACTER;
|
||||
while (yylength() > 2 && Integer.parseInt(yytext().toString().substring(1)) > capturingGroupCount) {
|
||||
yypushback(1);
|
||||
}
|
||||
return RegExpTT.BACKREF;
|
||||
}
|
||||
|
||||
{ESCAPE} {DIGITS} { return yystate() != CLASS2 ? RegExpTT.BACKREF : RegExpTT.ESC_CHARACTER; }
|
||||
{ESCAPE} {DIGITS} { if (yystate() == CLASS2) return RegExpTT.ESC_CHARACTER;
|
||||
while (yylength() > 2 && Integer.parseInt(yytext().toString().substring(1)) > capturingGroupCount) {
|
||||
yypushback(1);
|
||||
}
|
||||
return RegExpTT.BACKREF;
|
||||
}
|
||||
|
||||
{ESCAPE} "-" { return RegExpTT.ESC_CHARACTER; }
|
||||
{ESCAPE} {META} { return RegExpTT.ESC_CHARACTER; }
|
||||
@@ -256,7 +265,7 @@ HEX_CHAR=[0-9a-fA-F]
|
||||
|
||||
|
||||
<YYINITIAL> {
|
||||
{LPAREN} { return RegExpTT.GROUP_BEGIN; }
|
||||
{LPAREN} { capturingGroupCount++; return RegExpTT.GROUP_BEGIN; }
|
||||
{RPAREN} { return RegExpTT.GROUP_END; }
|
||||
|
||||
"|" { return RegExpTT.UNION; }
|
||||
|
||||
@@ -730,6 +730,14 @@
|
||||
<pattern>([ab]+)=\1</pattern>
|
||||
<expected>OK</expected>
|
||||
</test>
|
||||
<test>
|
||||
<pattern>(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)\105</pattern>
|
||||
<expected>OK</expected>
|
||||
</test>
|
||||
<test>
|
||||
<pattern>(.)\11</pattern>
|
||||
<expected>OK</expected>
|
||||
</test>
|
||||
<test verify="false">
|
||||
<pattern>([ab]+)=\2</pattern>
|
||||
<expected>ERR</expected>
|
||||
|
||||
@@ -262,7 +262,7 @@ libraryLicense(name: "Snappy-Java", libraryName: "Snappy-Java", version: "0.3",
|
||||
libraryLicense(name: "Cucumber-Java", libraryName: "cucumber-java", version: "1.0.14", license: "MIT License", url: "https://github.com/cucumber/cucumber-jvm/", licenseUrl: "http://www.opensource.org/licenses/mit-license.html")
|
||||
libraryLicense(name: "Cucumber-JVM", libraryName: "cucumber-jvm", version: "1.0.14", license: "MIT License", url: "https://github.com/cucumber/cucumber-jvm/", licenseUrl: "http://www.opensource.org/licenses/mit-license.html")
|
||||
libraryLicense(name: "Cucumber-Groovy", libraryName: "cucumber-groovy", version: "1.0.14", license: "MIT License", url: "https://github.com/cucumber/cucumber-jvm/", licenseUrl: "http://www.opensource.org/licenses/mit-license.html")
|
||||
libraryLicense(name: "proxy-vole", libraryName: "proxy-vole", version: "20120920", license: "New BSD License", url: "http://code.google.com/p/proxy-vole/", licenseUrl: "http://opensource.org/licenses/BSD-3-Clause")
|
||||
libraryLicense(name: "proxy-vole", libraryName: "proxy-vole", version: "20131209", license: "New BSD License", url: "http://code.google.com/p/proxy-vole/", licenseUrl: "http://opensource.org/licenses/BSD-3-Clause")
|
||||
libraryLicense(name: "Rhino JavaScript Engine", libraryName: "rhino-js-1_7R4", version: "1.7R4", license: "MPL 1.1", url: "http://www.mozilla.org/rhino/", licenseUrl: "http://www.mozilla.org/MPL/MPL-1.1.html")
|
||||
libraryLicense(name: "asm-4.0-all", libraryName: "asm-4.0-all", version: "4.0", attachedTo: "ByteCodeViewer", license: "BSD", url: "http://asm.objectweb.org/", licenseUrl: "http://asm.objectweb.org/license.html")
|
||||
libraryLicense(name: "jsr305", libraryName: "jsr305", version: "snapshot", license: "BSD", url: "http://code.google.com/p/jsr-305/", licenseUrl: "http://code.google.com/p/jsr-305/source/browse/trunk/ri/LICENSE")
|
||||
|
||||
@@ -691,6 +691,26 @@ private String submapToXml(Map properties, List keys) {
|
||||
return buff.toString()
|
||||
}
|
||||
|
||||
private List<File> getChildren(File file) {
|
||||
if (!file.isDirectory()) return []
|
||||
return file.listFiles().sort { File f -> f.name.toLowerCase() }
|
||||
}
|
||||
|
||||
binding.setVariable("getCustomJDK", { File customJdkDir ->
|
||||
getChildren(customJdkDir).each {
|
||||
if (it.getName().endsWith(".tar.gz")) {
|
||||
def customJdkFileName = it.getAbsolutePath().substring(0, it.getAbsolutePath().length() - 3)
|
||||
if (new File(customJdkFileName).exists()){ ant.delete(file: customJdkFileName) }
|
||||
if (new File("${home}/build/jdk/custom_jdk_${buildNumber}.tar").exists()){
|
||||
ant.delete(file: "${home}/build/jdk/custom_jdk_${buildNumber}.tar")
|
||||
}
|
||||
ant.gunzip(src: it.getAbsolutePath())
|
||||
projectBuilder.info("customJdkFileName: " + customJdkFileName)
|
||||
ant.copy(file: customJdkFileName, tofile: "${home}/build/jdk/custom_jdk_${buildNumber}.tar")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
binding.setVariable("buildWinZip", { String zipPath, List paths ->
|
||||
projectBuilder.stage(".win.zip")
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
import org.jetbrains.jps.api.CmdlineProtoUtil;
|
||||
import org.jetbrains.jps.api.CmdlineRemoteProto;
|
||||
import org.jetbrains.jps.api.RequestFuture;
|
||||
import org.jetbrains.jps.api.TaskFuture;
|
||||
import org.jetbrains.jps.model.java.JavaSourceRootType;
|
||||
|
||||
import javax.swing.*;
|
||||
@@ -138,7 +138,7 @@ public class CompileDriver {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final RequestFuture future = compileInExternalProcess(compileContext, true);
|
||||
final TaskFuture future = compileInExternalProcess(compileContext, true);
|
||||
if (future != null) {
|
||||
while (!future.waitFor(200L, TimeUnit.MILLISECONDS)) {
|
||||
if (indicator.isCanceled()) {
|
||||
@@ -194,7 +194,7 @@ public class CompileDriver {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private RequestFuture compileInExternalProcess(final @NotNull CompileContextImpl compileContext, final boolean onlyCheckUpToDate)
|
||||
private TaskFuture compileInExternalProcess(final @NotNull CompileContextImpl compileContext, final boolean onlyCheckUpToDate)
|
||||
throws Exception {
|
||||
final CompileScope scope = compileContext.getCompileScope();
|
||||
final Collection<String> paths = CompileScopeUtil.fetchFiles(compileContext);
|
||||
@@ -400,7 +400,7 @@ public class CompileDriver {
|
||||
return;
|
||||
}
|
||||
|
||||
final RequestFuture future = compileInExternalProcess(compileContext, false);
|
||||
final TaskFuture future = compileInExternalProcess(compileContext, false);
|
||||
if (future != null) {
|
||||
while (!future.waitFor(200L, TimeUnit.MILLISECONDS)) {
|
||||
if (indicator.isCanceled()) {
|
||||
@@ -646,7 +646,6 @@ public class CompileDriver {
|
||||
final Module[] scopeModules = scope.getAffectedModules();
|
||||
final List<String> modulesWithoutOutputPathSpecified = new ArrayList<String>();
|
||||
final List<String> modulesWithoutJdkAssigned = new ArrayList<String>();
|
||||
final CompilerConfiguration config = CompilerConfiguration.getInstance(myProject);
|
||||
final CompilerManager compilerManager = CompilerManager.getInstance(myProject);
|
||||
for (final Module module : scopeModules) {
|
||||
if (!compilerManager.isValidationEnabled(module)) {
|
||||
|
||||
@@ -58,10 +58,7 @@ import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl;
|
||||
import com.intellij.openapi.roots.*;
|
||||
import com.intellij.openapi.startup.StartupManager;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.ShutDownTracker;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.*;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
@@ -112,8 +109,7 @@ import java.net.InetSocketAddress;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.jetbrains.jps.api.CmdlineRemoteProto.Message.ControllerMessage.ParametersMessage.TargetTypeBuildScope;
|
||||
@@ -152,8 +148,10 @@ public class BuildManager implements ApplicationComponent{
|
||||
private final File mySystemDirectory;
|
||||
private final ProjectManager myProjectManager;
|
||||
|
||||
private final Map<RequestFuture, Project> myAutomakeFutures = Collections.synchronizedMap(new HashMap<RequestFuture, Project>());
|
||||
private final Map<TaskFuture, Project> myAutomakeFutures = Collections.synchronizedMap(new HashMap<TaskFuture, Project>());
|
||||
private final Map<String, RequestFuture> myBuildsInProgress = Collections.synchronizedMap(new HashMap<String, RequestFuture>());
|
||||
private final Map<String, Future<Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler>>> myPreloadedBuilds =
|
||||
Collections.synchronizedMap(new HashMap<String, Future<Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler>>>());
|
||||
private final BuildProcessClasspathManager myClasspathManager = new BuildProcessClasspathManager();
|
||||
private final SequentialTaskExecutor myRequestsProcessor = new SequentialTaskExecutor(PooledThreadExecutor.INSTANCE);
|
||||
private final Map<String, ProjectData> myProjectDataMap = Collections.synchronizedMap(new HashMap<String, ProjectData>());
|
||||
@@ -385,6 +383,8 @@ public class BuildManager implements ApplicationComponent{
|
||||
}
|
||||
|
||||
public void clearState(Project project) {
|
||||
cancelPreloadedBuilds(project);
|
||||
|
||||
final String projectPath = getProjectPath(project);
|
||||
synchronized (myProjectDataMap) {
|
||||
final ProjectData data = myProjectDataMap.get(projectPath);
|
||||
@@ -447,7 +447,7 @@ public class BuildManager implements ApplicationComponent{
|
||||
}
|
||||
final List<TargetTypeBuildScope> scopes = CmdlineProtoUtil.createAllModulesScopes(false);
|
||||
final AutoMakeMessageHandler handler = new AutoMakeMessageHandler(project);
|
||||
final RequestFuture future = scheduleBuild(
|
||||
final TaskFuture future = scheduleBuild(
|
||||
project, false, true, false, scopes, Collections.<String>emptyList(), Collections.<String, String>emptyMap(), handler
|
||||
);
|
||||
if (future != null) {
|
||||
@@ -526,12 +526,12 @@ public class BuildManager implements ApplicationComponent{
|
||||
return false;
|
||||
}
|
||||
|
||||
public Collection<RequestFuture> cancelAutoMakeTasks(Project project) {
|
||||
final Collection<RequestFuture> futures = new SmartList<RequestFuture>();
|
||||
public Collection<TaskFuture> cancelAutoMakeTasks(Project project) {
|
||||
final Collection<TaskFuture> futures = new SmartList<TaskFuture>();
|
||||
synchronized (myAutomakeFutures) {
|
||||
for (Map.Entry<RequestFuture, Project> entry : myAutomakeFutures.entrySet()) {
|
||||
for (Map.Entry<TaskFuture, Project> entry : myAutomakeFutures.entrySet()) {
|
||||
if (entry.getValue().equals(project)) {
|
||||
final RequestFuture future = entry.getKey();
|
||||
final TaskFuture future = entry.getKey();
|
||||
future.cancel(false);
|
||||
futures.add(future);
|
||||
}
|
||||
@@ -540,211 +540,261 @@ public class BuildManager implements ApplicationComponent{
|
||||
return futures;
|
||||
}
|
||||
|
||||
private void cancelPreloadedBuilds(Project project) {
|
||||
final String projectPath = getProjectPath(project);
|
||||
runCommand(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler> pair = takePreloadedProcess(projectPath);
|
||||
if (pair != null) {
|
||||
final RequestFuture<PreloadedProcessMessageHandler> future = pair.first;
|
||||
myMessageDispatcher.cancelSession(future.getRequestID());
|
||||
// waiting for preloaded process from project's task queue guarantees no build is started for this project
|
||||
// until this one gracefully exits and closes all its storages
|
||||
getProjectData(projectPath).taskQueue.submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
future.waitFor();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public RequestFuture scheduleBuild(
|
||||
private Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler> takePreloadedProcess(String projectPath) {
|
||||
Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler> result;
|
||||
final Future<Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler>> preloadProgress = myPreloadedBuilds.remove(projectPath);
|
||||
try {
|
||||
result = preloadProgress != null ? preloadProgress.get() : null;
|
||||
}
|
||||
catch (Throwable e) {
|
||||
LOG.info(e);
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TaskFuture scheduleBuild(
|
||||
final Project project, final boolean isRebuild, final boolean isMake,
|
||||
final boolean onlyCheckUpToDate, final List<TargetTypeBuildScope> scopes,
|
||||
final Collection<String> paths,
|
||||
final Map<String, String> userData, final DefaultMessageHandler messageHandler) {
|
||||
|
||||
final String projectPath = getProjectPath(project);
|
||||
final UUID sessionId = UUID.randomUUID();
|
||||
final boolean isAutomake = messageHandler instanceof AutoMakeMessageHandler;
|
||||
final BuilderMessageHandler handler = new MessageHandlerWrapper(messageHandler) {
|
||||
@Override
|
||||
public void buildStarted(UUID sessionId) {
|
||||
super.buildStarted(sessionId);
|
||||
try {
|
||||
ApplicationManager.getApplication().getMessageBus().syncPublisher(BuildManagerListener.TOPIC).buildStarted(project, sessionId, isAutomake);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sessionTerminated(UUID sessionId) {
|
||||
try {
|
||||
super.sessionTerminated(sessionId);
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
ApplicationManager.getApplication().getMessageBus().syncPublisher(BuildManagerListener.TOPIC).buildFinished(project, sessionId, isAutomake);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
// ensure server is listening
|
||||
if (myListenPort < 0) {
|
||||
try {
|
||||
synchronized (this) {
|
||||
if (myListenPort < 0) {
|
||||
myListenPort = startListening();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), null));
|
||||
handler.sessionTerminated(sessionId);
|
||||
return null;
|
||||
}
|
||||
final BuilderMessageHandler handler = new NotifyingMessageHandler(project, messageHandler, messageHandler instanceof AutoMakeMessageHandler);
|
||||
try {
|
||||
ensureListening();
|
||||
}
|
||||
catch (Exception e) {
|
||||
final UUID sessionId = UUID.randomUUID(); // the actual session did not start, use random UUID
|
||||
handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), null));
|
||||
handler.sessionTerminated(sessionId);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
final RequestFuture<BuilderMessageHandler> future = new RequestFuture<BuilderMessageHandler>(handler, sessionId, new RequestFuture.CancelAction<BuilderMessageHandler>() {
|
||||
@Override
|
||||
public void cancel(RequestFuture<BuilderMessageHandler> future) throws Exception {
|
||||
myMessageDispatcher.cancelSession(future.getRequestID());
|
||||
final DelegateFuture<BuilderMessageHandler> _future = new DelegateFuture<BuilderMessageHandler>();
|
||||
// by using the same queue that processes events we ensure that
|
||||
// the build will be aware of all events that have happened before this request
|
||||
runCommand(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
final Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler> preloaded = takePreloadedProcess(projectPath);
|
||||
final RequestFuture<PreloadedProcessMessageHandler> preloadedFuture = preloaded != null? preloaded.first : null;
|
||||
final boolean usingPreloadedProcess = preloadedFuture != null;
|
||||
|
||||
final UUID sessionId;
|
||||
if (usingPreloadedProcess) {
|
||||
LOG.info("Using preloaded build process to compile " + projectPath);
|
||||
sessionId = preloadedFuture.getRequestID();
|
||||
preloadedFuture.getMessageHandler().setDelegateHandler(handler);
|
||||
}
|
||||
else {
|
||||
sessionId = UUID.randomUUID();
|
||||
}
|
||||
});
|
||||
// by using the same queue that processes events we ensure that
|
||||
// the build will be aware of all events that have happened before this request
|
||||
runCommand(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (future.isCancelled() || project.isDisposed()) {
|
||||
handler.sessionTerminated(sessionId);
|
||||
future.setDone();
|
||||
return;
|
||||
}
|
||||
|
||||
final CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings globals =
|
||||
CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings.newBuilder()
|
||||
.setGlobalOptionsPath(PathManager.getOptionsPath())
|
||||
.build();
|
||||
CmdlineRemoteProto.Message.ControllerMessage.FSEvent currentFSChanges;
|
||||
final SequentialTaskExecutor projectTaskQueue;
|
||||
synchronized (myProjectDataMap) {
|
||||
ProjectData data = myProjectDataMap.get(projectPath);
|
||||
if (data == null) {
|
||||
data = new ProjectData(new SequentialTaskExecutor(PooledThreadExecutor.INSTANCE));
|
||||
myProjectDataMap.put(projectPath, data);
|
||||
}
|
||||
if (isRebuild) {
|
||||
data.dropChanges();
|
||||
}
|
||||
if (IS_UNIT_TEST_MODE) {
|
||||
LOG.info("Scheduling build for " +
|
||||
projectPath +
|
||||
"; CHANGED: " +
|
||||
new HashSet<String>(convertToStringPaths(data.myChanged)) +
|
||||
"; DELETED: " +
|
||||
new HashSet<String>(convertToStringPaths(data.myDeleted)));
|
||||
}
|
||||
currentFSChanges = data.getAndResetRescanFlag() ? null : data.createNextEvent();
|
||||
projectTaskQueue = data.taskQueue;
|
||||
final RequestFuture<? extends BuilderMessageHandler> future = usingPreloadedProcess? preloadedFuture : new RequestFuture<BuilderMessageHandler>(handler, sessionId, new RequestFuture.CancelAction<BuilderMessageHandler>() {
|
||||
@Override
|
||||
public void cancel(RequestFuture<BuilderMessageHandler> future) throws Exception {
|
||||
myMessageDispatcher.cancelSession(future.getRequestID());
|
||||
}
|
||||
});
|
||||
_future.setDelegate(future);
|
||||
|
||||
final CmdlineRemoteProto.Message.ControllerMessage params;
|
||||
if (!usingPreloadedProcess && (future.isCancelled() || project.isDisposed())) {
|
||||
// in case of preloaded process the process was already running, so the handler will be notified upon process termination
|
||||
handler.sessionTerminated(sessionId);
|
||||
((BasicFuture)future).setDone();
|
||||
return;
|
||||
}
|
||||
|
||||
final CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings globals =
|
||||
CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings.newBuilder().setGlobalOptionsPath(PathManager.getOptionsPath()).build();
|
||||
CmdlineRemoteProto.Message.ControllerMessage.FSEvent currentFSChanges;
|
||||
final SequentialTaskExecutor projectTaskQueue;
|
||||
synchronized (myProjectDataMap) {
|
||||
final ProjectData data = getProjectData(projectPath);
|
||||
if (isRebuild) {
|
||||
params = CmdlineProtoUtil.createBuildRequest(projectPath, scopes, Collections.<String>emptyList(), userData, globals, null);
|
||||
data.dropChanges();
|
||||
}
|
||||
else if (onlyCheckUpToDate) {
|
||||
params = CmdlineProtoUtil.createUpToDateCheckRequest(projectPath, scopes, paths, userData, globals, currentFSChanges);
|
||||
}
|
||||
else {
|
||||
params = CmdlineProtoUtil.createBuildRequest(projectPath, scopes, isMake ? Collections.<String>emptyList() : paths,
|
||||
userData, globals, currentFSChanges);
|
||||
if (IS_UNIT_TEST_MODE) {
|
||||
LOG.info("Scheduling build for " +
|
||||
projectPath +
|
||||
"; CHANGED: " +
|
||||
new HashSet<String>(convertToStringPaths(data.myChanged)) +
|
||||
"; DELETED: " +
|
||||
new HashSet<String>(convertToStringPaths(data.myDeleted)));
|
||||
}
|
||||
currentFSChanges = data.getAndResetRescanFlag() ? null : data.createNextEvent();
|
||||
projectTaskQueue = data.taskQueue;
|
||||
}
|
||||
|
||||
myMessageDispatcher.registerBuildMessageHandler(sessionId, new MessageHandlerWrapper(handler) {
|
||||
final CmdlineRemoteProto.Message.ControllerMessage params;
|
||||
if (isRebuild) {
|
||||
params = CmdlineProtoUtil.createBuildRequest(projectPath, scopes, Collections.<String>emptyList(), userData, globals, null);
|
||||
}
|
||||
else if (onlyCheckUpToDate) {
|
||||
params = CmdlineProtoUtil.createUpToDateCheckRequest(projectPath, scopes, paths, userData, globals, currentFSChanges);
|
||||
}
|
||||
else {
|
||||
params = CmdlineProtoUtil.createBuildRequest(projectPath, scopes, isMake ? Collections.<String>emptyList() : paths, userData, globals, currentFSChanges);
|
||||
}
|
||||
if (!usingPreloadedProcess) {
|
||||
myMessageDispatcher.registerBuildMessageHandler(future, params);
|
||||
}
|
||||
|
||||
try {
|
||||
projectTaskQueue.submit(new Runnable() {
|
||||
@Override
|
||||
public void sessionTerminated(UUID sessionId) {
|
||||
public void run() {
|
||||
Throwable execFailure = null;
|
||||
try {
|
||||
super.sessionTerminated(sessionId);
|
||||
}
|
||||
finally {
|
||||
future.setDone();
|
||||
}
|
||||
}
|
||||
}, params);
|
||||
|
||||
try {
|
||||
projectTaskQueue.submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Throwable execFailure = null;
|
||||
try {
|
||||
if (project.isDisposed()) {
|
||||
if (project.isDisposed()) {
|
||||
if (usingPreloadedProcess) {
|
||||
future.cancel(true);
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
myBuildsInProgress.put(projectPath, future);
|
||||
final OSProcessHandler processHandler = launchBuildProcess(project, myListenPort, sessionId);
|
||||
final StringBuilder stdErrOutput = new StringBuilder();
|
||||
}
|
||||
myBuildsInProgress.put(projectPath, future);
|
||||
final OSProcessHandler processHandler;
|
||||
final StringBuilder errorsOnLaunch = new StringBuilder();
|
||||
if (usingPreloadedProcess) {
|
||||
final boolean paramsSent = myMessageDispatcher.sendBuildParameters(future.getRequestID(), params);
|
||||
if (!paramsSent) {
|
||||
myMessageDispatcher.cancelSession(future.getRequestID());
|
||||
}
|
||||
processHandler = preloaded.second;
|
||||
}
|
||||
else {
|
||||
processHandler = launchBuildProcess(project, myListenPort, sessionId, false);
|
||||
processHandler.addProcessListener(new ProcessAdapter() {
|
||||
@Override
|
||||
public void onTextAvailable(ProcessEvent event, Key outputType) {
|
||||
// re-translate builder's output to idea.log
|
||||
final String text = event.getText();
|
||||
if (!StringUtil.isEmptyOrSpaces(text)) {
|
||||
LOG.info("BUILDER_PROCESS [" + outputType.toString() + "]: " + text.trim());
|
||||
if (stdErrOutput.length() < 1024 && ProcessOutputTypes.STDERR.equals(outputType)) {
|
||||
stdErrOutput.append(text);
|
||||
if (ProcessOutputTypes.STDERR.equals(outputType)) {
|
||||
if (errorsOnLaunch.length() < 1024) {
|
||||
final String text = event.getText();
|
||||
if (!StringUtil.isEmptyOrSpaces(text)) {
|
||||
errorsOnLaunch.append(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
processHandler.startNotify();
|
||||
final boolean terminated = processHandler.waitFor();
|
||||
if (terminated) {
|
||||
final int exitValue = processHandler.getProcess().exitValue();
|
||||
if (exitValue != 0) {
|
||||
final StringBuilder msg = new StringBuilder();
|
||||
msg.append("Abnormal build process termination: ");
|
||||
if (stdErrOutput.length() > 0) {
|
||||
msg.append("\n").append(stdErrOutput);
|
||||
}
|
||||
else {
|
||||
msg.append("unknown error");
|
||||
}
|
||||
handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(msg.toString(), null));
|
||||
}
|
||||
|
||||
final boolean terminated = processHandler.waitFor();
|
||||
if (terminated) {
|
||||
final int exitValue = processHandler.getProcess().exitValue();
|
||||
if (exitValue != 0) {
|
||||
final StringBuilder msg = new StringBuilder();
|
||||
msg.append("Abnormal build process termination: ");
|
||||
if (errorsOnLaunch.length() > 0) {
|
||||
msg.append("\n").append(errorsOnLaunch);
|
||||
}
|
||||
}
|
||||
else {
|
||||
handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure("Disconnected from build process", null));
|
||||
else {
|
||||
msg.append("unknown error");
|
||||
}
|
||||
handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(msg.toString(), null));
|
||||
}
|
||||
}
|
||||
catch (Throwable e) {
|
||||
execFailure = e;
|
||||
}
|
||||
finally {
|
||||
myBuildsInProgress.remove(projectPath);
|
||||
if (myMessageDispatcher.getAssociatedChannel(sessionId) == null) {
|
||||
// either the connection has never been established (process not started or execution failed), or no messages were sent from the launched process.
|
||||
// in this case the session cannot be unregistered by the message dispatcher
|
||||
final BuilderMessageHandler unregistered = myMessageDispatcher.unregisterBuildMessageHandler(sessionId);
|
||||
if (unregistered != null) {
|
||||
if (execFailure != null) {
|
||||
unregistered.handleFailure(sessionId, CmdlineProtoUtil.createFailure(execFailure.getMessage(), execFailure));
|
||||
}
|
||||
unregistered.sessionTerminated(sessionId);
|
||||
}
|
||||
}
|
||||
else {
|
||||
handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure("Disconnected from build process", null));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Throwable e) {
|
||||
final BuilderMessageHandler unregistered = myMessageDispatcher.unregisterBuildMessageHandler(sessionId);
|
||||
if (unregistered != null) {
|
||||
unregistered.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), e));
|
||||
unregistered.sessionTerminated(sessionId);
|
||||
catch (Throwable e) {
|
||||
execFailure = e;
|
||||
}
|
||||
finally {
|
||||
myBuildsInProgress.remove(projectPath);
|
||||
if (myMessageDispatcher.getAssociatedChannel(sessionId) == null) {
|
||||
// either the connection has never been established (process not started or execution failed), or no messages were sent from the launched process.
|
||||
// in this case the session cannot be unregistered by the message dispatcher
|
||||
final BuilderMessageHandler unregistered = myMessageDispatcher.unregisterBuildMessageHandler(sessionId);
|
||||
if (unregistered != null) {
|
||||
if (execFailure != null) {
|
||||
unregistered.handleFailure(sessionId, CmdlineProtoUtil.createFailure(execFailure.getMessage(), execFailure));
|
||||
}
|
||||
unregistered.sessionTerminated(sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
if (Registry.is("compiler.process.preload") && !project.isDisposed()) {
|
||||
runCommand(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
final Future<Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler>> preloadResult = launchPreloadedBuildProcess(project, projectTaskQueue);
|
||||
myPreloadedBuilds.put(projectPath, preloadResult);
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.info("Error pre-loading build process for project " + projectPath, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Throwable e) {
|
||||
final BuilderMessageHandler unregistered = myMessageDispatcher.unregisterBuildMessageHandler(sessionId);
|
||||
if (unregistered != null) {
|
||||
unregistered.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), e));
|
||||
unregistered.sessionTerminated(sessionId);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return future;
|
||||
}
|
||||
catch (Throwable e) {
|
||||
handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), e));
|
||||
handler.sessionTerminated(sessionId);
|
||||
}
|
||||
return _future;
|
||||
}
|
||||
|
||||
return null;
|
||||
@NotNull
|
||||
private ProjectData getProjectData(String projectPath) {
|
||||
synchronized (myProjectDataMap) {
|
||||
ProjectData data = myProjectDataMap.get(projectPath);
|
||||
if (data == null) {
|
||||
data = new ProjectData(new SequentialTaskExecutor(PooledThreadExecutor.INSTANCE));
|
||||
myProjectDataMap.put(projectPath, data);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureListening() throws Exception {
|
||||
if (myListenPort < 0) {
|
||||
synchronized (this) {
|
||||
if (myListenPort < 0) {
|
||||
myListenPort = startListening();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -762,7 +812,45 @@ public class BuildManager implements ApplicationComponent{
|
||||
return "com.intellij.compiler.server.BuildManager";
|
||||
}
|
||||
|
||||
private OSProcessHandler launchBuildProcess(Project project, final int port, final UUID sessionId) throws ExecutionException {
|
||||
private Future<Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler>> launchPreloadedBuildProcess(final Project project, SequentialTaskExecutor projectTaskQueue) throws Exception {
|
||||
ensureListening();
|
||||
|
||||
// launching build process from projectTaskQueue ensures that no other build process for this project is currently running
|
||||
return projectTaskQueue.submit(new Callable<Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler>>() {
|
||||
public Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler> call() throws Exception {
|
||||
final RequestFuture<PreloadedProcessMessageHandler> future = new RequestFuture<PreloadedProcessMessageHandler>(new PreloadedProcessMessageHandler(project), UUID.randomUUID(), new RequestFuture.CancelAction<PreloadedProcessMessageHandler>() {
|
||||
@Override
|
||||
public void cancel(RequestFuture<PreloadedProcessMessageHandler> future) throws Exception {
|
||||
myMessageDispatcher.cancelSession(future.getRequestID());
|
||||
}
|
||||
});
|
||||
try {
|
||||
myMessageDispatcher.registerBuildMessageHandler(future, null);
|
||||
final OSProcessHandler processHandler = launchBuildProcess(project, myListenPort, future.getRequestID(), true);
|
||||
processHandler.addProcessListener(new ProcessAdapter() {
|
||||
@Override
|
||||
public void onTextAvailable(ProcessEvent event, Key outputType) {
|
||||
if (ProcessOutputTypes.STDERR.equals(outputType)) {
|
||||
final String text = event.getText();
|
||||
if (!StringUtil.isEmptyOrSpaces(text)) {
|
||||
LOG.info("PRELOADED_BUILD_PROCESS: " + text);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
processHandler.startNotify();
|
||||
return Pair.create(future, processHandler);
|
||||
}
|
||||
catch (ExecutionException e) {
|
||||
myMessageDispatcher.unregisterBuildMessageHandler(future.getRequestID());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private OSProcessHandler launchBuildProcess(Project project, final int port, final UUID sessionId, boolean requestProjectPreload) throws ExecutionException {
|
||||
final String compilerPath;
|
||||
final String vmExecutablePath;
|
||||
JavaSdkVersion sdkVersion = null;
|
||||
@@ -863,6 +951,11 @@ public class BuildManager implements ApplicationComponent{
|
||||
}
|
||||
cmdLine.addParameter("-Djdt.compiler.useSingleThread=true"); // always run eclipse compiler in single-threaded mode
|
||||
|
||||
if (requestProjectPreload) {
|
||||
cmdLine.addParameter("-Dpreload.project.path=" + FileUtil.toCanonicalPath(getProjectPath(project)));
|
||||
cmdLine.addParameter("-Dpreload.config.path=" + FileUtil.toCanonicalPath(PathManager.getOptionsPath()));
|
||||
}
|
||||
|
||||
final String shouldGenerateIndex = System.getProperty(GlobalOptions.GENERATE_CLASSPATH_INDEX_OPTION);
|
||||
if (shouldGenerateIndex != null) {
|
||||
cmdLine.addParameter("-D"+ GlobalOptions.GENERATE_CLASSPATH_INDEX_OPTION +"=" + shouldGenerateIndex);
|
||||
@@ -961,12 +1054,24 @@ public class BuildManager implements ApplicationComponent{
|
||||
|
||||
final Process process = cmdLine.createProcess();
|
||||
|
||||
return new OSProcessHandler(process, null, mySystemCharset) {
|
||||
final OSProcessHandler processHandler = new OSProcessHandler(process, null, mySystemCharset) {
|
||||
@Override
|
||||
protected boolean shouldDestroyProcessRecursively() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
processHandler.addProcessListener(new ProcessAdapter() {
|
||||
@Override
|
||||
public void onTextAvailable(ProcessEvent event, Key outputType) {
|
||||
// re-translate builder's output to idea.log
|
||||
final String text = event.getText();
|
||||
if (!StringUtil.isEmptyOrSpaces(text)) {
|
||||
LOG.info("BUILDER_PROCESS [" + outputType.toString() + "]: " + text.trim());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return processHandler;
|
||||
}
|
||||
|
||||
public File getBuildSystemDirectory() {
|
||||
@@ -1086,6 +1191,50 @@ public class BuildManager implements ApplicationComponent{
|
||||
}
|
||||
}
|
||||
|
||||
private static class NotifyingMessageHandler extends DelegatingMessageHandler {
|
||||
private final Project myProject;
|
||||
private final BuilderMessageHandler myDelegateHandler;
|
||||
private boolean myIsAutomake;
|
||||
|
||||
public NotifyingMessageHandler(@NotNull Project project, @NotNull BuilderMessageHandler delegateHandler, final boolean isAutomake) {
|
||||
myProject = project;
|
||||
myDelegateHandler = delegateHandler;
|
||||
myIsAutomake = isAutomake;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BuilderMessageHandler getDelegateHandler() {
|
||||
return myDelegateHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildStarted(UUID sessionId) {
|
||||
super.buildStarted(sessionId);
|
||||
try {
|
||||
ApplicationManager
|
||||
.getApplication().getMessageBus().syncPublisher(BuildManagerListener.TOPIC).buildStarted(myProject, sessionId, myIsAutomake);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sessionTerminated(UUID sessionId) {
|
||||
try {
|
||||
super.sessionTerminated(sessionId);
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
ApplicationManager.getApplication().getMessageBus().syncPublisher(BuildManagerListener.TOPIC).buildFinished(myProject, sessionId, myIsAutomake);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ProjectWatcher extends ProjectManagerAdapter {
|
||||
private final Map<Project, MessageBusConnection> myConnections = new HashMap<Project, MessageBusConnection>();
|
||||
|
||||
@@ -1169,7 +1318,8 @@ public class BuildManager implements ApplicationComponent{
|
||||
|
||||
@Override
|
||||
public void projectClosing(Project project) {
|
||||
for (RequestFuture future : cancelAutoMakeTasks(project)) {
|
||||
cancelPreloadedBuilds(project);
|
||||
for (TaskFuture future : cancelAutoMakeTasks(project)) {
|
||||
future.waitFor(500, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
@@ -1185,13 +1335,14 @@ public class BuildManager implements ApplicationComponent{
|
||||
}
|
||||
|
||||
private static class ProjectData {
|
||||
@NotNull
|
||||
final SequentialTaskExecutor taskQueue;
|
||||
private final Set<InternedPath> myChanged = new THashSet<InternedPath>();
|
||||
private final Set<InternedPath> myDeleted = new THashSet<InternedPath>();
|
||||
private long myNextEventOrdinal = 0L;
|
||||
private boolean myNeedRescan = true;
|
||||
|
||||
private ProjectData(SequentialTaskExecutor taskQueue) {
|
||||
private ProjectData(@NotNull SequentialTaskExecutor taskQueue) {
|
||||
this.taskQueue = taskQueue;
|
||||
}
|
||||
|
||||
@@ -1328,5 +1479,88 @@ public class BuildManager implements ApplicationComponent{
|
||||
return "/";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final class DelegateFuture<T> implements TaskFuture<T> {
|
||||
@Nullable
|
||||
private TaskFuture<? extends T> myDelegate;
|
||||
private Boolean myRequestedCancelState = null;
|
||||
|
||||
@NotNull
|
||||
public synchronized TaskFuture<? extends T> getDelegate() {
|
||||
TaskFuture<? extends T> delegate = myDelegate;
|
||||
while (delegate == null) {
|
||||
try {
|
||||
wait();
|
||||
}
|
||||
catch (InterruptedException ignored) {
|
||||
}
|
||||
delegate = myDelegate;
|
||||
}
|
||||
return delegate;
|
||||
}
|
||||
|
||||
public synchronized boolean setDelegate(@NotNull TaskFuture<? extends T> delegate) {
|
||||
if (myDelegate == null) {
|
||||
try {
|
||||
myDelegate = delegate;
|
||||
if (myRequestedCancelState != null) {
|
||||
myDelegate.cancel(myRequestedCancelState);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
notifyAll();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public synchronized boolean cancel(boolean mayInterruptIfRunning) {
|
||||
final TaskFuture<? extends T> delegate = myDelegate;
|
||||
if (delegate == null) {
|
||||
myRequestedCancelState = mayInterruptIfRunning;
|
||||
return true;
|
||||
}
|
||||
return delegate.cancel(mayInterruptIfRunning);
|
||||
}
|
||||
|
||||
public void waitFor() {
|
||||
getDelegate().waitFor();
|
||||
}
|
||||
|
||||
public boolean waitFor(long timeout, TimeUnit unit) {
|
||||
return getDelegate().waitFor(timeout, unit);
|
||||
}
|
||||
|
||||
public boolean isCancelled() {
|
||||
final TaskFuture<? extends T> delegate;
|
||||
synchronized (this) {
|
||||
delegate = myDelegate;
|
||||
if (delegate == null) {
|
||||
return myRequestedCancelState != null;
|
||||
}
|
||||
}
|
||||
return delegate.isCancelled();
|
||||
}
|
||||
|
||||
public boolean isDone() {
|
||||
final TaskFuture<? extends T> delegate;
|
||||
synchronized (this) {
|
||||
delegate = myDelegate;
|
||||
if (delegate == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return delegate.isDone();
|
||||
}
|
||||
|
||||
public T get() throws InterruptedException, java.util.concurrent.ExecutionException {
|
||||
return getDelegate().get();
|
||||
}
|
||||
|
||||
public T get(long timeout, TimeUnit unit) throws InterruptedException, java.util.concurrent.ExecutionException, TimeoutException {
|
||||
return getDelegate().get(timeout, unit);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,10 +21,12 @@ import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.util.AttributeKey;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.io.SimpleChannelInboundHandlerAdapter;
|
||||
import org.jetbrains.jps.api.CmdlineProtoUtil;
|
||||
import org.jetbrains.jps.api.CmdlineRemoteProto;
|
||||
import org.jetbrains.jps.api.RequestFuture;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -41,19 +43,34 @@ class BuildMessageDispatcher extends SimpleChannelInboundHandlerAdapter<CmdlineR
|
||||
|
||||
private static final AttributeKey<SessionData> SESSION_DATA = AttributeKey.valueOf("BuildMessageDispatcher.sessionData");
|
||||
|
||||
private final Map<UUID, SessionData> myMessageHandlers = new ConcurrentHashMap<UUID, SessionData>(16, 0.75f, 1);
|
||||
private final Map<UUID, SessionData> mySessionDescriptors = new ConcurrentHashMap<UUID, SessionData>(16, 0.75f, 1);
|
||||
private final Set<UUID> myCanceledSessions = new ConcurrentHashSet<UUID>();
|
||||
|
||||
public void registerBuildMessageHandler(UUID sessionId,
|
||||
BuilderMessageHandler handler,
|
||||
CmdlineRemoteProto.Message.ControllerMessage params) {
|
||||
myMessageHandlers.put(sessionId, new SessionData(sessionId, handler, params));
|
||||
public void registerBuildMessageHandler(@NotNull final RequestFuture<? extends BuilderMessageHandler> future, @Nullable CmdlineRemoteProto.Message.ControllerMessage params) {
|
||||
final BuilderMessageHandler wrappedHandler = new DelegatingMessageHandler() {
|
||||
@Override
|
||||
protected BuilderMessageHandler getDelegateHandler() {
|
||||
return future.getMessageHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sessionTerminated(UUID sessionId) {
|
||||
try {
|
||||
super.sessionTerminated(sessionId);
|
||||
}
|
||||
finally {
|
||||
future.setDone();
|
||||
}
|
||||
}
|
||||
};
|
||||
final UUID sessionId = future.getRequestID();
|
||||
mySessionDescriptors.put(sessionId, new SessionData(sessionId, wrappedHandler, params));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BuilderMessageHandler unregisterBuildMessageHandler(UUID sessionId) {
|
||||
myCanceledSessions.remove(sessionId);
|
||||
final SessionData data = myMessageHandlers.remove(sessionId);
|
||||
final SessionData data = mySessionDescriptors.remove(sessionId);
|
||||
return data != null? data.handler : null;
|
||||
}
|
||||
|
||||
@@ -74,11 +91,36 @@ class BuildMessageDispatcher extends SimpleChannelInboundHandlerAdapter<CmdlineR
|
||||
|
||||
@Nullable
|
||||
public Channel getAssociatedChannel(final UUID sessionId) {
|
||||
final SessionData data = myMessageHandlers.get(sessionId);
|
||||
final SessionData data = mySessionDescriptors.get(sessionId);
|
||||
return data != null? data.channel : null;
|
||||
}
|
||||
|
||||
|
||||
public boolean sendBuildParameters(@NotNull final UUID preloadedSessionId, @NotNull CmdlineRemoteProto.Message.ControllerMessage params) {
|
||||
boolean succeeded = false;
|
||||
final SessionData sessionData = mySessionDescriptors.get(preloadedSessionId);
|
||||
if (sessionData != null) {
|
||||
//noinspection SynchronizationOnLocalVariableOrMethodParameter
|
||||
synchronized (sessionData) {
|
||||
if (sessionData.state == SessionData.State.WAITING_PARAMS) {
|
||||
sessionData.state = SessionData.State.RUNNING;
|
||||
final Channel channel = sessionData.channel;
|
||||
if (channel != null && channel.isActive()) {
|
||||
sessionData.handler.buildStarted(preloadedSessionId);
|
||||
channel.writeAndFlush(CmdlineProtoUtil.toMessage(preloadedSessionId, params));
|
||||
succeeded = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (sessionData.state == SessionData.State.INITIAL) {
|
||||
sessionData.params = params;
|
||||
succeeded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void messageReceived(ChannelHandlerContext context, CmdlineRemoteProto.Message message) throws Exception {
|
||||
SessionData sessionData = context.attr(SESSION_DATA).get();
|
||||
@@ -89,7 +131,7 @@ class BuildMessageDispatcher extends SimpleChannelInboundHandlerAdapter<CmdlineR
|
||||
final CmdlineRemoteProto.Message.UUID id = message.getSessionId();
|
||||
sessionId = new UUID(id.getMostSigBits(), id.getLeastSigBits());
|
||||
|
||||
sessionData = myMessageHandlers.get(sessionId);
|
||||
sessionData = mySessionDescriptors.get(sessionId);
|
||||
if (sessionData != null) {
|
||||
sessionData.channel = context.channel();
|
||||
context.attr(SESSION_DATA).set(sessionData);
|
||||
@@ -119,14 +161,25 @@ class BuildMessageDispatcher extends SimpleChannelInboundHandlerAdapter<CmdlineR
|
||||
final CmdlineRemoteProto.Message.BuilderMessage builderMessage = message.getBuilderMessage();
|
||||
final CmdlineRemoteProto.Message.BuilderMessage.Type msgType = builderMessage.getType();
|
||||
if (msgType == CmdlineRemoteProto.Message.BuilderMessage.Type.PARAM_REQUEST) {
|
||||
final CmdlineRemoteProto.Message.ControllerMessage params = sessionData.params;
|
||||
if (params != null) {
|
||||
handler.buildStarted(sessionId);
|
||||
sessionData.params = null;
|
||||
context.writeAndFlush(CmdlineProtoUtil.toMessage(sessionId, params));
|
||||
}
|
||||
else {
|
||||
cancelSession(sessionId);
|
||||
//noinspection SynchronizationOnLocalVariableOrMethodParameter
|
||||
synchronized (sessionData) {
|
||||
final CmdlineRemoteProto.Message.ControllerMessage params = sessionData.params;
|
||||
if (params != null) {
|
||||
sessionData.state = SessionData.State.RUNNING;
|
||||
handler.buildStarted(sessionId);
|
||||
sessionData.params = null;
|
||||
context.writeAndFlush(CmdlineProtoUtil.toMessage(sessionId, params));
|
||||
}
|
||||
else {
|
||||
if (sessionData.state == SessionData.State.INITIAL) {
|
||||
sessionData.state = SessionData.State.WAITING_PARAMS;
|
||||
}
|
||||
else {
|
||||
// this message is expected to be sent only once.
|
||||
// To be on the safe side, cancel the session
|
||||
cancelSession(sessionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -165,12 +218,19 @@ class BuildMessageDispatcher extends SimpleChannelInboundHandlerAdapter<CmdlineR
|
||||
}
|
||||
|
||||
private static final class SessionData {
|
||||
enum State {
|
||||
INITIAL, WAITING_PARAMS, RUNNING
|
||||
}
|
||||
|
||||
@NotNull
|
||||
final UUID sessionId;
|
||||
@NotNull
|
||||
final BuilderMessageHandler handler;
|
||||
volatile CmdlineRemoteProto.Message.ControllerMessage params;
|
||||
volatile Channel channel;
|
||||
State state = State.INITIAL;
|
||||
|
||||
private SessionData(UUID sessionId, BuilderMessageHandler handler, CmdlineRemoteProto.Message.ControllerMessage params) {
|
||||
private SessionData(@NotNull UUID sessionId, @NotNull BuilderMessageHandler handler, CmdlineRemoteProto.Message.ControllerMessage params) {
|
||||
this.sessionId = sessionId;
|
||||
this.handler = handler;
|
||||
this.params = params;
|
||||
|
||||
@@ -25,6 +25,24 @@ import java.util.UUID;
|
||||
* Date: 4/25/12
|
||||
*/
|
||||
public interface BuilderMessageHandler {
|
||||
BuilderMessageHandler DEAF = new BuilderMessageHandler() {
|
||||
@Override
|
||||
public void buildStarted(UUID sessionId) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleBuildMessage(Channel channel, UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage msg) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleFailure(UUID sessionId, CmdlineRemoteProto.Message.Failure failure) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sessionTerminated(UUID sessionId) {
|
||||
}
|
||||
};
|
||||
|
||||
void buildStarted(UUID sessionId);
|
||||
|
||||
void handleBuildMessage(Channel channel, UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage msg);
|
||||
|
||||
+6
-9
@@ -9,30 +9,27 @@ import java.util.UUID;
|
||||
* @author Eugene Zhuravlev
|
||||
* Date: 7/3/13
|
||||
*/
|
||||
class MessageHandlerWrapper implements BuilderMessageHandler {
|
||||
private final BuilderMessageHandler myHandler;
|
||||
abstract class DelegatingMessageHandler implements BuilderMessageHandler {
|
||||
|
||||
public MessageHandlerWrapper(BuilderMessageHandler handler) {
|
||||
myHandler = handler;
|
||||
}
|
||||
protected abstract BuilderMessageHandler getDelegateHandler();
|
||||
|
||||
@Override
|
||||
public void buildStarted(UUID sessionId) {
|
||||
myHandler.buildStarted(sessionId);
|
||||
getDelegateHandler().buildStarted(sessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleBuildMessage(Channel channel, UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage msg) {
|
||||
myHandler.handleBuildMessage(channel, sessionId, msg);
|
||||
getDelegateHandler().handleBuildMessage(channel, sessionId, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleFailure(UUID sessionId, CmdlineRemoteProto.Message.Failure failure) {
|
||||
myHandler.handleFailure(sessionId, failure);
|
||||
getDelegateHandler().handleFailure(sessionId, failure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sessionTerminated(UUID sessionId) {
|
||||
myHandler.sessionTerminated(sessionId);
|
||||
getDelegateHandler().sessionTerminated(sessionId);
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.compiler.server;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Eugene Zhuravlev
|
||||
* Date: 20-Oct-14
|
||||
*/
|
||||
class PreloadedProcessMessageHandler extends DelegatingMessageHandler {
|
||||
@NotNull
|
||||
private final Project myProject;
|
||||
private volatile BuilderMessageHandler myDelegateHandler;
|
||||
|
||||
public PreloadedProcessMessageHandler(@NotNull Project project) {
|
||||
myProject = project;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Project getProject() {
|
||||
return myProject;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BuilderMessageHandler getDelegateHandler() {
|
||||
final BuilderMessageHandler delegate = myDelegateHandler;
|
||||
return delegate != null? delegate : BuilderMessageHandler.DEAF;
|
||||
}
|
||||
|
||||
public void setDelegateHandler(BuilderMessageHandler delegateHandler) {
|
||||
myDelegateHandler = delegateHandler;
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ public class JavaReferringObjectsValue extends JavaValue {
|
||||
}
|
||||
|
||||
public JavaReferringObjectsValue(@NotNull JavaValue javaValue, boolean isField) {
|
||||
super(null, javaValue.getDescriptor(), javaValue.getEvaluationContext(), null, false);
|
||||
super(null, javaValue.getDescriptor(), javaValue.getEvaluationContext(), javaValue.getNodeManager(), false);
|
||||
myIsField = isField;
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ public class JavaReferringObjectsValue extends JavaValue {
|
||||
return reference;
|
||||
}
|
||||
};
|
||||
children.add(new JavaReferringObjectsValue(null, descriptor, getEvaluationContext(), null, true));
|
||||
children.add(new JavaReferringObjectsValue(null, descriptor, getEvaluationContext(), getNodeManager(), true));
|
||||
i++;
|
||||
}
|
||||
else {
|
||||
@@ -109,7 +109,7 @@ public class JavaReferringObjectsValue extends JavaValue {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
children.add("Referrer " + i++, new JavaReferringObjectsValue(null, descriptor, getEvaluationContext(), null, false));
|
||||
children.add("Referrer " + i++, new JavaReferringObjectsValue(null, descriptor, getEvaluationContext(), getNodeManager(), false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,4 +168,10 @@ public class JavaReferringObjectsValue extends JavaValue {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public XValueModifier getModifier() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV
|
||||
private final EvaluationContextImpl myEvaluationContext;
|
||||
private final NodeManagerImpl myNodeManager;
|
||||
private final boolean myContextSet;
|
||||
private final DebuggerManagerThreadImpl myDebuggerManagerThread;
|
||||
|
||||
protected JavaValue(JavaValue parent,
|
||||
@NotNull ValueDescriptorImpl valueDescriptor,
|
||||
@@ -82,6 +83,7 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV
|
||||
myEvaluationContext = evaluationContext;
|
||||
myNodeManager = nodeManager;
|
||||
myContextSet = contextSet;
|
||||
myDebuggerManagerThread = evaluationContext.getDebugProcess().getManagerThread();
|
||||
}
|
||||
|
||||
static JavaValue create(JavaValue parent,
|
||||
@@ -113,6 +115,10 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV
|
||||
return myEvaluationContext;
|
||||
}
|
||||
|
||||
public NodeManagerImpl getNodeManager() {
|
||||
return myNodeManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void computePresentation(@NotNull final XValueNode node, @NotNull XValuePlace place) {
|
||||
final SuspendContextImpl suspendContext = myEvaluationContext.getSuspendContext();
|
||||
@@ -349,7 +355,11 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV
|
||||
}
|
||||
|
||||
boolean scheduleCommand(SuspendContextCommandImpl command) {
|
||||
return scheduleCommand(myEvaluationContext, null, command);
|
||||
if (myEvaluationContext.getSuspendContext().isResumed()) {
|
||||
return false;
|
||||
}
|
||||
myDebuggerManagerThread.schedule(command);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static boolean scheduleCommand(EvaluationContextImpl evaluationContext,
|
||||
@@ -486,6 +496,7 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV
|
||||
@Nullable
|
||||
@Override
|
||||
public XReferrersProvider getReferrersProvider() {
|
||||
if (myEvaluationContext.getSuspendContext().isResumed()) return null;
|
||||
return new XReferrersProvider() {
|
||||
@Override
|
||||
public XValue getReferringObjectsValue() {
|
||||
@@ -497,16 +508,15 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV
|
||||
@Nullable
|
||||
@Override
|
||||
public XInstanceEvaluator getInstanceEvaluator() {
|
||||
final DebugProcessImpl process = myEvaluationContext.getDebugProcess();
|
||||
return new XInstanceEvaluator() {
|
||||
@Override
|
||||
public void evaluate(@NotNull final XDebuggerEvaluator.XEvaluationCallback callback, @NotNull final XStackFrame frame) {
|
||||
process.getManagerThread().schedule(new DebuggerCommandImpl() {
|
||||
myDebuggerManagerThread.schedule(new DebuggerCommandImpl() {
|
||||
@Override
|
||||
protected void action() throws Exception {
|
||||
ValueDescriptorImpl inspectDescriptor = myValueDescriptor;
|
||||
if (myValueDescriptor instanceof WatchItemDescriptor) {
|
||||
inspectDescriptor = (ValueDescriptorImpl) ((WatchItemDescriptor) myValueDescriptor).getModifier().getInspectItem(getProject());
|
||||
inspectDescriptor = (ValueDescriptorImpl)((WatchItemDescriptor)myValueDescriptor).getModifier().getInspectItem(getProject());
|
||||
}
|
||||
EvaluationContextImpl evaluationContext = ((JavaStackFrame)frame).getFrameDebuggerContext().createEvaluationContext();
|
||||
if (evaluationContext != null) {
|
||||
|
||||
@@ -192,7 +192,7 @@ public class ArrayRenderer extends NodeRendererImpl{
|
||||
|
||||
if(!myForced && END_INDEX < array.length() - 1) {
|
||||
//children.add(nodeManager.createMessageNode(new MessageDescriptor(MORE_ELEMENTS, MessageDescriptor.SPECIAL)));
|
||||
builder.setRemaining(array.length()-END_INDEX);
|
||||
builder.setRemaining(array.length()-1-END_INDEX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2009 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 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.
|
||||
@@ -15,14 +15,5 @@
|
||||
*/
|
||||
package com.intellij.ide.actions;
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.ex.ProjectManagerEx;
|
||||
|
||||
public class TemplateProjectStructureAction extends AnAction implements DumbAware {
|
||||
@Override
|
||||
public void actionPerformed(final AnActionEvent e) {
|
||||
ShowStructureSettingsAction.showDialog(ProjectManagerEx.getInstanceEx().getDefaultProject());
|
||||
}
|
||||
public class TemplateProjectStructureAction extends ShowStructureSettingsAction {
|
||||
}
|
||||
+1
-1
@@ -260,7 +260,7 @@ public class HighlightControlFlowUtil {
|
||||
}
|
||||
else {
|
||||
PsiElement scope = variable instanceof PsiField
|
||||
? variable.getContainingFile()
|
||||
? ((PsiField)variable).getContainingClass()
|
||||
: variable.getParent() != null ? variable.getParent().getParent() : null;
|
||||
if (scope instanceof PsiCodeBlock && scope.getParent() instanceof PsiSwitchStatement) {
|
||||
scope = PsiTreeUtil.getParentOfType(scope, PsiCodeBlock.class);
|
||||
|
||||
+2
@@ -17,6 +17,7 @@ package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.ChangeContextUtil;
|
||||
import com.intellij.codeInsight.FileModificationService;
|
||||
import com.intellij.codeInsight.daemon.GroupNames;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
|
||||
import com.intellij.codeInsight.intention.HighPriorityAction;
|
||||
@@ -208,6 +209,7 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
final PsiElement element = descriptor.getPsiElement();
|
||||
if (element instanceof PsiNewExpression) {
|
||||
if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return;
|
||||
final PsiAnonymousClass anonymousClass = ((PsiNewExpression)element).getAnonymousClass();
|
||||
|
||||
LOG.assertTrue(anonymousClass != null);
|
||||
|
||||
+2
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.FileModificationService;
|
||||
import com.intellij.codeInsight.daemon.GroupNames;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -115,6 +116,7 @@ public class AnonymousCanBeMethodReferenceInspection extends BaseJavaBatchLocalI
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
final PsiElement element = descriptor.getPsiElement();
|
||||
if (element instanceof PsiNewExpression) {
|
||||
if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return;
|
||||
final PsiAnonymousClass anonymousClass = ((PsiNewExpression)element).getAnonymousClass();
|
||||
if (anonymousClass == null) return;
|
||||
final PsiMethod[] methods = anonymousClass.getMethods();
|
||||
|
||||
+4
-1
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.FileModificationService;
|
||||
import com.intellij.codeInsight.daemon.GroupNames;
|
||||
import com.intellij.codeInsight.intention.HighPriorityAction;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
@@ -94,7 +95,9 @@ public class ExplicitTypeCanBeDiamondInspection extends BaseJavaBatchLocalInspec
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
PsiDiamondTypeUtil.replaceExplicitWithDiamond(descriptor.getPsiElement());
|
||||
final PsiElement element = descriptor.getPsiElement();
|
||||
if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return;
|
||||
PsiDiamondTypeUtil.replaceExplicitWithDiamond(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.FileModificationService;
|
||||
import com.intellij.codeInsight.daemon.GroupNames;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -390,6 +391,7 @@ public class LambdaCanBeMethodReferenceInspection extends BaseJavaBatchLocalInsp
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
final PsiElement element = descriptor.getPsiElement();
|
||||
if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return;
|
||||
final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(element, PsiLambdaExpression.class);
|
||||
if (lambdaExpression == null) return;
|
||||
final PsiType functionalInterfaceType = lambdaExpression.getFunctionalInterfaceType();
|
||||
|
||||
+2
@@ -16,6 +16,7 @@
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.FileModificationService;
|
||||
import com.intellij.codeInsight.daemon.GroupNames;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
|
||||
import com.intellij.codeInsight.intention.AddAnnotationPsiFix;
|
||||
@@ -133,6 +134,7 @@ public class PossibleHeapPollutionVarargsInspection extends BaseJavaBatchLocalIn
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
final PsiElement psiElement = descriptor.getPsiElement();
|
||||
if (psiElement instanceof PsiIdentifier) {
|
||||
if (!FileModificationService.getInstance().preparePsiElementForWrite(psiElement)) return;
|
||||
final PsiMethod psiMethod = (PsiMethod)psiElement.getParent();
|
||||
psiMethod.getModifierList().setModifierProperty(PsiModifier.FINAL, true);
|
||||
new AddAnnotationPsiFix("java.lang.SafeVarargs", psiMethod, PsiNameValuePair.EMPTY_ARRAY).applyFix(project, descriptor);
|
||||
|
||||
+2
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.FileModificationService;
|
||||
import com.intellij.codeInsight.daemon.GroupNames;
|
||||
import com.intellij.codeInsight.intention.HighPriorityAction;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
@@ -158,6 +159,7 @@ public class RedundantLambdaCodeBlockInspection extends BaseJavaBatchLocalInspec
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
final PsiElement element = descriptor.getPsiElement();
|
||||
if (element != null) {
|
||||
if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return;
|
||||
final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(element, PsiLambdaExpression.class);
|
||||
if (lambdaExpression != null) {
|
||||
final PsiElement body = lambdaExpression.getBody();
|
||||
|
||||
+1
-1
@@ -470,7 +470,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
|
||||
? InspectionsBundle.message("dataflow.message.return.null.from.notnullable", presentableNullable)
|
||||
: InspectionsBundle.message("dataflow.message.return.nullable.from.notnullable", presentableNullable);
|
||||
final LocalQuickFix[] fixes =
|
||||
PsiTreeUtil.skipParentsOfType(expr, PsiCodeBlock.class, PsiReturnStatement.class) instanceof PsiLambdaExpression
|
||||
PsiTreeUtil.getParentOfType(expr, PsiMethod.class, PsiLambdaExpression.class) instanceof PsiLambdaExpression
|
||||
? LocalQuickFix.EMPTY_ARRAY
|
||||
: new LocalQuickFix[]{ new AnnotateMethodFix(defaultNullable, ArrayUtil.toStringArray(manager.getNotNulls())) {
|
||||
@Override
|
||||
|
||||
@@ -168,6 +168,10 @@ public class JavaLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSett
|
||||
String groupName = ApplicationBundle.message("wrapping.fields.annotation");
|
||||
consumer.showCustomOption(JavaCodeStyleSettings.class, "DO_NOT_WRAP_AFTER_SINGLE_ANNOTATION", "Do not wrap after single annotation", groupName);
|
||||
}
|
||||
else if (settingsType == SettingsType.BLANK_LINES_SETTINGS) {
|
||||
consumer.showAllStandardOptions();
|
||||
consumer.showCustomOption(JavaCodeStyleSettings.class, "BLANK_LINES_AROUND_INITIALIZER", ApplicationBundle.message("editbox.blanklines.around.initializer"), CodeStyleSettingsCustomizable.BLANK_LINES);
|
||||
}
|
||||
else {
|
||||
consumer.showAllStandardOptions();
|
||||
}
|
||||
|
||||
@@ -35,4 +35,6 @@ public class JavaCodeStyleSettings extends CustomCodeStyleSettings {
|
||||
public int ANNOTATION_PARAMETER_WRAP = CommonCodeStyleSettings.DO_NOT_WRAP;
|
||||
public boolean ALIGN_MULTILINE_ANNOTATION_PARAMETERS = false;
|
||||
|
||||
public int BLANK_LINES_AROUND_INITIALIZER = 1;
|
||||
|
||||
}
|
||||
|
||||
+2
-22
@@ -341,31 +341,11 @@ public class JavaArrangementVisitor extends JavaRecursiveElementVisitor {
|
||||
|
||||
@Override
|
||||
public void visitClassInitializer(PsiClassInitializer initializer) {
|
||||
JavaElementArrangementEntry entry = createNewEntry(initializer, initializer.getTextRange(), FIELD, null, true);
|
||||
JavaElementArrangementEntry entry = createNewEntry(initializer, initializer.getTextRange(), INIT_BLOCK, null, true);
|
||||
if (entry == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
PsiElement classLBrace = null;
|
||||
PsiClass clazz = initializer.getContainingClass();
|
||||
if (clazz != null) {
|
||||
classLBrace = clazz.getLBrace();
|
||||
}
|
||||
for (PsiElement e = initializer.getPrevSibling(); e != null; e = e.getPrevSibling()) {
|
||||
JavaElementArrangementEntry prevEntry;
|
||||
if (e == classLBrace) {
|
||||
prevEntry = myEntries.get(clazz);
|
||||
}
|
||||
else {
|
||||
prevEntry = myEntries.get(e);
|
||||
}
|
||||
if (prevEntry != null) {
|
||||
entry.addDependency(prevEntry);
|
||||
}
|
||||
if (!(e instanceof PsiWhiteSpace)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
parseModifiers(initializer.getModifierList(), entry);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.arrangement.engine.ArrangementEngine;
|
||||
import com.intellij.psi.codeStyle.arrangement.group.ArrangementGroupingRule;
|
||||
import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryMatcher;
|
||||
@@ -36,6 +37,7 @@ import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchConditio
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementCompositeMatchCondition;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition;
|
||||
import com.intellij.psi.codeStyle.arrangement.std.*;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.ContainerUtilRt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -62,7 +64,7 @@ public class JavaRearranger implements Rearranger<JavaElementArrangementEntry>,
|
||||
// Type
|
||||
@NotNull private static final Set<ArrangementSettingsToken> SUPPORTED_TYPES =
|
||||
ContainerUtilRt.newLinkedHashSet(
|
||||
FIELD, CONSTRUCTOR, METHOD, CLASS, INTERFACE, ENUM, GETTER, SETTER, OVERRIDDEN
|
||||
FIELD, INIT_BLOCK, CONSTRUCTOR, METHOD, CLASS, INTERFACE, ENUM, GETTER, SETTER, OVERRIDDEN
|
||||
);
|
||||
// Modifier
|
||||
@NotNull private static final Set<ArrangementSettingsToken> SUPPORTED_MODIFIERS =
|
||||
@@ -79,6 +81,9 @@ public class JavaRearranger implements Rearranger<JavaElementArrangementEntry>,
|
||||
@NotNull private static final Collection<Set<ArrangementSettingsToken>> MUTEXES =
|
||||
ContainerUtilRt.newArrayList();
|
||||
|
||||
private static final Set<ArrangementSettingsToken> TYPES_WITH_DISABLED_ORDER = ContainerUtil.newHashSet();
|
||||
private static final Set<ArrangementSettingsToken> TYPES_WITH_DISABLED_NAME_MATCH = ContainerUtil.newHashSet();
|
||||
|
||||
static {
|
||||
Set<ArrangementSettingsToken> visibilityModifiers = ContainerUtilRt.newHashSet(PUBLIC, PROTECTED, PACKAGE_PRIVATE, PRIVATE);
|
||||
MUTEXES.add(visibilityModifiers);
|
||||
@@ -96,6 +101,11 @@ public class JavaRearranger implements Rearranger<JavaElementArrangementEntry>,
|
||||
MODIFIERS_BY_TYPE.put(GETTER, ContainerUtilRt.<ArrangementSettingsToken>newHashSet());
|
||||
MODIFIERS_BY_TYPE.put(SETTER, ContainerUtilRt.<ArrangementSettingsToken>newHashSet());
|
||||
MODIFIERS_BY_TYPE.put(OVERRIDDEN, ContainerUtilRt.<ArrangementSettingsToken>newHashSet());
|
||||
MODIFIERS_BY_TYPE.put(INIT_BLOCK, ContainerUtilRt.newHashSet(STATIC));
|
||||
|
||||
TYPES_WITH_DISABLED_ORDER.add(INIT_BLOCK);
|
||||
|
||||
TYPES_WITH_DISABLED_NAME_MATCH.add(INIT_BLOCK);
|
||||
}
|
||||
|
||||
private static final Map<ArrangementSettingsToken, List<ArrangementSettingsToken>> GROUPING_RULES = ContainerUtilRt.newLinkedHashMap();
|
||||
@@ -129,6 +139,8 @@ public class JavaRearranger implements Rearranger<JavaElementArrangementEntry>,
|
||||
for (ArrangementSettingsToken modifier : visibility) {
|
||||
and(matchRules, FIELD, STATIC, modifier);
|
||||
}
|
||||
and(matchRules, INIT_BLOCK, STATIC);
|
||||
|
||||
for (ArrangementSettingsToken modifier : visibility) {
|
||||
and(matchRules, FIELD, FINAL, modifier);
|
||||
}
|
||||
@@ -136,6 +148,7 @@ public class JavaRearranger implements Rearranger<JavaElementArrangementEntry>,
|
||||
and(matchRules, FIELD, modifier);
|
||||
}
|
||||
and(matchRules, FIELD);
|
||||
and(matchRules, INIT_BLOCK);
|
||||
and(matchRules, CONSTRUCTOR);
|
||||
and(matchRules, METHOD, STATIC);
|
||||
and(matchRules, METHOD);
|
||||
@@ -304,10 +317,14 @@ public class JavaRearranger implements Rearranger<JavaElementArrangementEntry>,
|
||||
}
|
||||
|
||||
CommonCodeStyleSettings commonSettings = settings.getCommonSettings(JavaLanguage.INSTANCE);
|
||||
JavaCodeStyleSettings javaSettings = settings.getCustomSettings(JavaCodeStyleSettings.class);
|
||||
if (FIELD.equals(target.getType())) {
|
||||
if (parent != null && parent.getType() == INTERFACE) {
|
||||
return commonSettings.BLANK_LINES_AROUND_FIELD_IN_INTERFACE;
|
||||
}
|
||||
else if (INIT_BLOCK.equals(previous.getType())) {
|
||||
return javaSettings.BLANK_LINES_AROUND_INITIALIZER;
|
||||
}
|
||||
else {
|
||||
return commonSettings.BLANK_LINES_AROUND_FIELD;
|
||||
}
|
||||
@@ -323,6 +340,9 @@ public class JavaRearranger implements Rearranger<JavaElementArrangementEntry>,
|
||||
else if (CLASS.equals(target.getType())) {
|
||||
return commonSettings.BLANK_LINES_AROUND_CLASS;
|
||||
}
|
||||
else if (INIT_BLOCK.equals(target.getType())) {
|
||||
return javaSettings.BLANK_LINES_AROUND_INITIALIZER;
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
@@ -363,9 +383,10 @@ public class JavaRearranger implements Rearranger<JavaElementArrangementEntry>,
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(@NotNull ArrangementSettingsToken token, @Nullable ArrangementMatchCondition current) {
|
||||
if (SUPPORTED_TYPES.contains(token) || SUPPORTED_ORDERS.contains(token) || StdArrangementTokens.Regexp.NAME.equals(token)) {
|
||||
if (SUPPORTED_TYPES.contains(token)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ArrangementSettingsToken type = null;
|
||||
if (current != null) {
|
||||
type = ArrangementUtil.parseType(current);
|
||||
@@ -373,6 +394,15 @@ public class JavaRearranger implements Rearranger<JavaElementArrangementEntry>,
|
||||
if (type == null) {
|
||||
type = NO_TYPE;
|
||||
}
|
||||
|
||||
if (SUPPORTED_ORDERS.contains(token)) {
|
||||
return !TYPES_WITH_DISABLED_ORDER.contains(type);
|
||||
}
|
||||
|
||||
if (StdArrangementTokens.Regexp.NAME.equals(token)) {
|
||||
return !TYPES_WITH_DISABLED_NAME_MATCH.contains(type);
|
||||
}
|
||||
|
||||
Set<ArrangementSettingsToken> modifiers = MODIFIERS_BY_TYPE.get(type);
|
||||
return modifiers != null && modifiers.contains(token);
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import com.intellij.psi.impl.source.tree.java.ClassElement;
|
||||
import com.intellij.psi.jsp.JspElementType;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -90,6 +91,8 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
|
||||
|
||||
private final JavaWrapManager myWrapManager;
|
||||
private final AlignmentInColumnsHelper myAlignmentInColumnsHelper;
|
||||
private Map<IElementType, Wrap> myPreferredWraps;
|
||||
private AbstractJavaBlock myParentBlock;
|
||||
|
||||
protected AbstractJavaBlock(@NotNull final ASTNode node,
|
||||
final Wrap wrap,
|
||||
@@ -505,12 +508,9 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
|
||||
mySettings.ALIGN_MULTILINE_PARAMETERS_IN_CALLS);
|
||||
}
|
||||
else if (childType == JavaTokenType.LPARENTH && nodeType == JavaElementType.PARAMETER_LIST) {
|
||||
// There is a possible case that particular annotated method definition is too long. We may wrap either after annotation
|
||||
// or after opening lbrace then. Our strategy is to wrap after annotation whenever possible.
|
||||
Wrap wrap = Wrap.createWrap(getWrapType(mySettings.METHOD_PARAMETERS_WRAP), false);
|
||||
child = processParenthesisBlock(result, child,
|
||||
WrappingStrategy.createDoNotWrapCommaStrategy(wrap),
|
||||
mySettings.ALIGN_MULTILINE_PARAMETERS);
|
||||
Wrap wrapToUse = getMethodParametersWrap();
|
||||
WrappingStrategy wrapStrategy = WrappingStrategy.createDoNotWrapCommaStrategy(wrapToUse);
|
||||
child = processParenthesisBlock(result, child, wrapStrategy, mySettings.ALIGN_MULTILINE_PARAMETERS);
|
||||
}
|
||||
else if (childType == JavaTokenType.LPARENTH && nodeType == JavaElementType.RESOURCE_LIST) {
|
||||
Wrap wrap = Wrap.createWrap(getWrapType(mySettings.RESOURCE_LIST_WRAP), false);
|
||||
@@ -572,6 +572,25 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
|
||||
return child;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Wrap getMethodParametersWrap() {
|
||||
Wrap preferredWrap = getModifierListWrap();
|
||||
if (preferredWrap == null) {
|
||||
return Wrap.createWrap(getWrapType(mySettings.METHOD_PARAMETERS_WRAP), false);
|
||||
} else {
|
||||
return Wrap.createChildWrap(preferredWrap, getWrapType(mySettings.METHOD_PARAMETERS_WRAP), false);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Wrap getModifierListWrap() {
|
||||
AbstractJavaBlock parentBlock = getParentBlock();
|
||||
if (parentBlock != null) {
|
||||
return parentBlock.getReservedWrap(JavaElementType.MODIFIER_LIST);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ASTNode processField(@NotNull final List<Block> result,
|
||||
ASTNode child,
|
||||
@NotNull final AlignmentStrategy alignmentStrategy,
|
||||
@@ -1135,7 +1154,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
|
||||
@Nullable
|
||||
@Override
|
||||
public Wrap getReservedWrap(IElementType elementType) {
|
||||
return null;
|
||||
return myPreferredWraps != null ? myPreferredWraps.get(elementType) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1150,7 +1169,11 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
|
||||
* @param reservedWrap reserved wrap instance
|
||||
* @param operationType target operation type to associate with the given wrap instance
|
||||
*/
|
||||
protected void setReservedWrap(final Wrap reservedWrap, final IElementType operationType) {
|
||||
public void setReservedWrap(final Wrap reservedWrap, final IElementType operationType) {
|
||||
if (myPreferredWraps == null) {
|
||||
myPreferredWraps = ContainerUtil.newHashMap();
|
||||
}
|
||||
myPreferredWraps.put(operationType, reservedWrap);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -1262,6 +1285,14 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
|
||||
return null;
|
||||
}
|
||||
|
||||
public AbstractJavaBlock getParentBlock() {
|
||||
return myParentBlock;
|
||||
}
|
||||
|
||||
public void setParentBlock(@NotNull AbstractJavaBlock parentBlock) {
|
||||
myParentBlock = parentBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to answer if special 'variable declaration alignment' strategy should be used for the given node.
|
||||
* I.e. given node is supposed to be parent node of sub-nodes that should be aligned 'by-columns'.
|
||||
|
||||
@@ -61,8 +61,13 @@ public class BlockContainingJavaBlock extends AbstractJavaBlock{
|
||||
|
||||
buildChildren(result, childAlignment, childWrap);
|
||||
|
||||
return result;
|
||||
for (Block block : result) {
|
||||
if (block instanceof AbstractJavaBlock) {
|
||||
((AbstractJavaBlock)block).setParentBlock(this);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void buildChildren(final ArrayList<Block> result, final Alignment childAlignment, final Wrap childWrap) {
|
||||
|
||||
@@ -425,13 +425,8 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor {
|
||||
myResult = Spacing.createSpacing(0, 0, 1, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_CODE);
|
||||
}
|
||||
else if (myRole1 == ChildRole.FIELD) {
|
||||
int lines = Math.max(getLinesAroundField(), getLinesAroundMethod()) + 1;
|
||||
// IJ has been keeping initialization block which starts at the same line as a field for a while.
|
||||
// However, it's not convenient for a situation when particular code is created via PSI - it's easier to not bothering
|
||||
// with whitespace elements when inserting, say, new initialization blocks. That's why we don't enforce new line
|
||||
// only during explicit reformatting ('Reformat' action).
|
||||
//int minLineFeeds = FormatterUtil.isFormatterCalledExplicitly() ? 0 : 1;
|
||||
myResult = Spacing.createSpacing(0, mySettings.SPACE_BEFORE_CLASS_LBRACE ? 1 : 0, 1, true, mySettings.KEEP_BLANK_LINES_BEFORE_RBRACE, lines);
|
||||
int blankLines = myJavaSettings.BLANK_LINES_AROUND_INITIALIZER + 1;
|
||||
myResult = Spacing.createSpacing(0, mySettings.SPACE_BEFORE_CLASS_LBRACE ? 1 : 0, blankLines, true, mySettings.KEEP_BLANK_LINES_BEFORE_RBRACE);
|
||||
}
|
||||
else if (myRole1 == ChildRole.CLASS) {
|
||||
setAroundClassSpacing();
|
||||
@@ -450,7 +445,7 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor {
|
||||
setAroundClassSpacing();
|
||||
}
|
||||
else {
|
||||
final int blankLines = getLinesAroundMethod() + 1;
|
||||
final int blankLines = myJavaSettings.BLANK_LINES_AROUND_INITIALIZER + 1;
|
||||
myResult = Spacing.createSpacing(0, Integer.MAX_VALUE, blankLines, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_DECLARATIONS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ public class SimpleJavaBlock extends AbstractJavaBlock {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setReservedWrap(final Wrap reservedWrap, final IElementType operationType) {
|
||||
public void setReservedWrap(final Wrap reservedWrap, final IElementType operationType) {
|
||||
myReservedWrap.put(operationType, reservedWrap);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@ import com.intellij.formatting.ASTBlock;
|
||||
import com.intellij.formatting.Block;
|
||||
import com.intellij.formatting.Wrap;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
|
||||
import com.intellij.psi.formatter.java.AbstractJavaBlock;
|
||||
import com.intellij.psi.formatter.java.wrap.impl.JavaChildBlockWrapFactory;
|
||||
import com.intellij.psi.formatter.java.wrap.impl.JavaChildWrapArranger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -79,7 +79,7 @@ public class JavaWrapManager {
|
||||
*/
|
||||
@Nullable
|
||||
public Wrap arrangeChildWrap(ASTNode child, ASTNode parent, CommonCodeStyleSettings settings, Wrap suggestedWrap,
|
||||
ReservedWrapsProvider reservedWrapsProvider)
|
||||
AbstractJavaBlock reservedWrapsProvider)
|
||||
{
|
||||
return myChildArranger.arrange(child, parent, settings, suggestedWrap, reservedWrapsProvider);
|
||||
}
|
||||
|
||||
+12
-3
@@ -22,6 +22,7 @@ import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleSettings;
|
||||
import com.intellij.psi.formatter.FormatterUtil;
|
||||
import com.intellij.psi.formatter.java.AbstractJavaBlock;
|
||||
import com.intellij.psi.formatter.java.JavaFormatterUtil;
|
||||
import com.intellij.psi.formatter.java.wrap.JavaWrapManager;
|
||||
import com.intellij.psi.formatter.java.wrap.ReservedWrapsProvider;
|
||||
@@ -70,7 +71,7 @@ public class JavaChildWrapArranger {
|
||||
ASTNode parent,
|
||||
CommonCodeStyleSettings settings,
|
||||
Wrap suggestedWrap,
|
||||
ReservedWrapsProvider reservedWrapsProvider) {
|
||||
AbstractJavaBlock reservedWrapsProvider) {
|
||||
final JavaCodeStyleSettings javaSettings = settings.getRootSettings().getCustomSettings(JavaCodeStyleSettings.class);
|
||||
ASTNode directParent = child.getTreeParent();
|
||||
int role = ((CompositeElement)directParent).getChildRole(child);
|
||||
@@ -188,8 +189,9 @@ public class JavaChildWrapArranger {
|
||||
if (javaSettings.DO_NOT_WRAP_AFTER_SINGLE_ANNOTATION && isFieldModifierListWithSingleAnnotation(parent)) {
|
||||
return Wrap.createWrap(WrapType.NONE, false);
|
||||
}
|
||||
|
||||
return Wrap.createWrap(getWrapType(getAnnotationWrapType(parent.getTreeParent(), child, settings)), true);
|
||||
Wrap wrap = Wrap.createWrap(getWrapType(getAnnotationWrapType(parent.getTreeParent(), child, settings)), true);
|
||||
putPreferredWrapInParentBlock(reservedWrapsProvider, wrap);
|
||||
return wrap;
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -263,6 +265,13 @@ public class JavaChildWrapArranger {
|
||||
return suggestedWrap;
|
||||
}
|
||||
|
||||
private static void putPreferredWrapInParentBlock(@NotNull AbstractJavaBlock block, @NotNull Wrap preferredWrap) {
|
||||
AbstractJavaBlock parentBlock = block.getParentBlock();
|
||||
if (parentBlock != null) {
|
||||
parentBlock.setReservedWrap(preferredWrap, JavaElementType.MODIFIER_LIST);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isFieldModifierListWithSingleAnnotation(@NotNull ASTNode elem) {
|
||||
ASTNode parent = elem.getTreeParent();
|
||||
if (parent != null && parent.getElementType() == JavaElementType.FIELD) {
|
||||
|
||||
+20
-4
@@ -92,12 +92,28 @@ public class ExtractLightMethodObjectHandler {
|
||||
final TextRange range = originalContext.getTextRange();
|
||||
final PsiElement originalAnchor =
|
||||
CodeInsightUtil.findElementInRange(copy, range.getStartOffset(), range.getEndOffset(), originalContext.getClass());
|
||||
//todo before this or super, not found etc
|
||||
final PsiElement anchor = RefactoringUtil.getParentStatement(originalAnchor, false);
|
||||
if (anchor == null) {
|
||||
|
||||
final PsiClass containingClass = PsiTreeUtil.getParentOfType(originalAnchor, PsiClass.class);
|
||||
if (containingClass == null) {
|
||||
return null;
|
||||
}
|
||||
final PsiElement container = anchor.getParent();
|
||||
|
||||
PsiElement anchor = RefactoringUtil.getParentStatement(originalAnchor, false);
|
||||
if (anchor == null) {
|
||||
if (PsiTreeUtil.getParentOfType(originalAnchor, PsiCodeBlock.class) != null) {
|
||||
anchor = originalAnchor;
|
||||
}
|
||||
}
|
||||
|
||||
final PsiElement container;
|
||||
if (anchor == null) {
|
||||
container = ((PsiClassInitializer)containingClass.add(elementFactory.createClassInitializer())).getBody();
|
||||
anchor = container.getLastChild();
|
||||
}
|
||||
else {
|
||||
container = anchor.getParent();
|
||||
}
|
||||
|
||||
final PsiElement firstElementCopy = container.addRangeBefore(elements[0], elements[elements.length - 1], anchor);
|
||||
final PsiElement[] elementsCopy = CodeInsightUtil.findStatementsInRange(copy,
|
||||
firstElementCopy.getTextRange().getStartOffset(),
|
||||
|
||||
@@ -870,16 +870,22 @@ public class RefactoringUtil {
|
||||
JavaCodeStyleManager.getInstance(declaration.getProject()).shortenClassReferences(declaration);
|
||||
if (loopBodyCopy != null) codeBlock.add(loopBodyCopy);
|
||||
} else if (container instanceof PsiLambdaExpression) {
|
||||
final PsiLambdaExpression lambdaExpression = (PsiLambdaExpression)container;
|
||||
final PsiElement lambdaExpressionBody = lambdaExpression.getBody();
|
||||
LOG.assertTrue(lambdaExpressionBody != null);
|
||||
PsiLambdaExpression lambdaExpression = (PsiLambdaExpression)container;
|
||||
final PsiElement invalidBody = lambdaExpression.getBody();
|
||||
if (invalidBody == null) return declaration;
|
||||
|
||||
final PsiLambdaExpression expressionFromText = (PsiLambdaExpression)elementFactory
|
||||
.createExpressionFromText(lambdaExpression.getParameterList().getText() + " -> {}", lambdaExpression);
|
||||
.createExpressionFromText(lambdaExpression.getParameterList().getText() + " -> {}", lambdaExpression.getParent());
|
||||
PsiCodeBlock newBody = (PsiCodeBlock)expressionFromText.getBody();
|
||||
LOG.assertTrue(newBody != null);
|
||||
newBody.add(declaration);
|
||||
|
||||
lambdaExpression =
|
||||
(PsiLambdaExpression)lambdaExpression.replace(elementFactory.createExpressionFromText(
|
||||
lambdaExpression.getParameterList().getText() + " -> " + invalidBody.getText(), lambdaExpression));
|
||||
|
||||
final PsiElement lambdaExpressionBody = lambdaExpression.getBody();
|
||||
LOG.assertTrue(lambdaExpressionBody != null);
|
||||
final PsiStatement lastBodyStatement;
|
||||
if (LambdaUtil.getFunctionalInterfaceReturnType(lambdaExpression) == PsiType.VOID) {
|
||||
lastBodyStatement = elementFactory.createStatementFromText("a;", lambdaExpression);
|
||||
|
||||
@@ -255,41 +255,55 @@ public class GenericsUtil {
|
||||
final PsiSubstitutor substitutor,
|
||||
final PsiElement context,
|
||||
final boolean allowUncheckedConversion) {
|
||||
nextTypeParam:
|
||||
for (PsiTypeParameter typeParameter : typeParams) {
|
||||
PsiType substituted = substitutor.substitute(typeParameter);
|
||||
if (substituted == null) return null;
|
||||
if (context != null) {
|
||||
substituted = PsiUtil.captureToplevelWildcards(substituted, context);
|
||||
PsiType boundError = findTypeParameterBoundError(typeParameter, typeParameter.getExtendsListTypes(),
|
||||
substitutor, context, allowUncheckedConversion);
|
||||
if (boundError != null) {
|
||||
return Pair.create(typeParameter, boundError);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiClassType[] extendsTypes = typeParameter.getExtendsListTypes();
|
||||
for (PsiClassType type : extendsTypes) {
|
||||
PsiType extendsType = substitutor.substitute(type);
|
||||
if (substituted instanceof PsiWildcardType) {
|
||||
if (((PsiWildcardType)substituted).isSuper()) {
|
||||
continue;
|
||||
}
|
||||
final PsiType extendsBound = ((PsiWildcardType)substituted).getExtendsBound();
|
||||
if (acceptExtendsBound(extendsType, extendsBound)) {
|
||||
continue nextTypeParam;
|
||||
}
|
||||
public static PsiType findTypeParameterBoundError(PsiTypeParameter typeParameter,
|
||||
PsiType[] extendsTypes,
|
||||
PsiSubstitutor substitutor,
|
||||
PsiElement context,
|
||||
boolean allowUncheckedConversion) {
|
||||
PsiType substituted = substitutor.substitute(typeParameter);
|
||||
if (substituted == null) return null;
|
||||
if (context != null) {
|
||||
substituted = PsiUtil.captureToplevelWildcards(substituted, context);
|
||||
}
|
||||
|
||||
if (substituted instanceof PsiWildcardType) {
|
||||
if (((PsiWildcardType)substituted).isSuper()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
for (PsiType type : extendsTypes) {
|
||||
PsiType extendsType = substitutor.substitute(type);
|
||||
if (substituted instanceof PsiWildcardType) {
|
||||
final PsiType extendsBound = ((PsiWildcardType)substituted).getExtendsBound();
|
||||
if (acceptExtendsBound(extendsType, extendsBound)) {
|
||||
return null;
|
||||
}
|
||||
else if (substituted instanceof PsiIntersectionType) {
|
||||
for (PsiType extendsBound : ((PsiIntersectionType)substituted).getConjuncts()) {
|
||||
if (acceptExtendsBound(extendsType, extendsBound)) continue nextTypeParam;
|
||||
}
|
||||
}
|
||||
else if (substituted instanceof PsiIntersectionType) {
|
||||
for (PsiType extendsBound : ((PsiIntersectionType)substituted).getConjuncts()) {
|
||||
if (acceptExtendsBound(extendsType, extendsBound)) return null;
|
||||
}
|
||||
else if (substituted instanceof PsiCapturedWildcardType) {
|
||||
final PsiType extendsBound = ((PsiCapturedWildcardType)substituted).getUpperBound();
|
||||
if (acceptExtendsBound(extendsType, extendsBound)) {
|
||||
continue nextTypeParam;
|
||||
}
|
||||
}
|
||||
if (extendsType != null && !TypeConversionUtil.isAssignable(extendsType, substituted, allowUncheckedConversion)) {
|
||||
return Pair.create(typeParameter, extendsType);
|
||||
}
|
||||
else if (substituted instanceof PsiCapturedWildcardType) {
|
||||
final PsiType extendsBound = ((PsiCapturedWildcardType)substituted).getUpperBound();
|
||||
if (acceptExtendsBound(extendsType, extendsBound)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (extendsType != null && !TypeConversionUtil.isAssignable(extendsType, substituted, allowUncheckedConversion)) {
|
||||
return extendsType;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
+22
-7
@@ -254,12 +254,12 @@ public class InferenceSession {
|
||||
}
|
||||
}
|
||||
|
||||
final PsiSubstitutor substitutor = resolveBounds(myInferenceVariables, mySiteSubstitutor);
|
||||
final PsiSubstitutor substitutor = resolveBounds(myInferenceVariables, PsiSubstitutor.EMPTY);
|
||||
if (substitutor != null) {
|
||||
if (myContext != null) {
|
||||
myContext.putUserData(ERASED, myErased);
|
||||
}
|
||||
mySiteSubstitutor = substitutor;
|
||||
mySiteSubstitutor = mySiteSubstitutor.putAll(substitutor);
|
||||
for (InferenceVariable variable : myInferenceVariables) {
|
||||
variable.setInstantiation(substitutor.substitute(variable.getParameter()));
|
||||
}
|
||||
@@ -403,7 +403,7 @@ public class InferenceSession {
|
||||
}
|
||||
|
||||
public PsiSubstitutor retrieveNonPrimitiveEqualsBounds(Collection<InferenceVariable> variables) {
|
||||
PsiSubstitutor substitutor = mySiteSubstitutor;
|
||||
PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
|
||||
for (InferenceVariable variable : variables) {
|
||||
final PsiType equalsBound = getEqualsBound(variable, substitutor);
|
||||
if (!(equalsBound instanceof PsiPrimitiveType)) {
|
||||
@@ -751,6 +751,22 @@ public class InferenceSession {
|
||||
return isProperType(bound) ? bound : substitutor.substitute(bound);
|
||||
}
|
||||
|
||||
private static boolean hasBoundProblems(final List<InferenceVariable> typeParams,
|
||||
final PsiSubstitutor substitutor,
|
||||
final PsiElement context) {
|
||||
for (InferenceVariable typeParameter : typeParams) {
|
||||
if (typeParameter.getCallContext() != context) {
|
||||
continue;
|
||||
}
|
||||
final List<PsiType> extendsTypes = typeParameter.getBounds(InferenceBound.UPPER);
|
||||
final PsiType[] bounds = extendsTypes.toArray(new PsiType[extendsTypes.size()]);
|
||||
if (GenericsUtil.findTypeParameterBoundError(typeParameter, bounds, substitutor, context, true) != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private PsiSubstitutor resolveBounds(final Collection<InferenceVariable> inferenceVariables,
|
||||
PsiSubstitutor substitutor) {
|
||||
final Collection<InferenceVariable> allVars = new ArrayList<InferenceVariable>(inferenceVariables);
|
||||
@@ -759,8 +775,7 @@ public class InferenceSession {
|
||||
if (!myIncorporationPhase.hasCaptureConstraints(vars)) {
|
||||
PsiSubstitutor firstSubstitutor = resolveSubset(vars, substitutor);
|
||||
if (firstSubstitutor != null) {
|
||||
final Set<PsiTypeParameter> parameters = firstSubstitutor.getSubstitutionMap().keySet();
|
||||
if (GenericsUtil.findTypeParameterWithBoundError(parameters.toArray(new PsiTypeParameter[parameters.size()]), firstSubstitutor, myContext, true) != null) {
|
||||
if (hasBoundProblems(vars, firstSubstitutor, myContext)) {
|
||||
firstSubstitutor = null;
|
||||
}
|
||||
}
|
||||
@@ -837,7 +852,7 @@ public class InferenceSession {
|
||||
for (InferenceVariable var : vars) {
|
||||
LOG.assertTrue(var.getInstantiation() == PsiType.NULL);
|
||||
final PsiTypeParameter typeParameter = var.getParameter();
|
||||
if (substitutor.getSubstitutionMap().containsKey(typeParameter) && var.getCallContext() != myContext) {
|
||||
if (substitutor.putAll(mySiteSubstitutor).getSubstitutionMap().containsKey(typeParameter) && var.getCallContext() != myContext) {
|
||||
continue;//todo
|
||||
}
|
||||
|
||||
@@ -849,7 +864,7 @@ public class InferenceSession {
|
||||
type = PsiType.getJavaLangRuntimeException(myManager, GlobalSearchScope.allScope(myManager.getProject()));
|
||||
}
|
||||
else {
|
||||
if (substitutor.getSubstitutionMap().get(typeParameter) != null) continue;
|
||||
if (substitutor.putAll(mySiteSubstitutor).getSubstitutionMap().get(typeParameter) != null) continue;
|
||||
type = myErased ? null : getUpperBound(var, substitutor);
|
||||
}
|
||||
}
|
||||
|
||||
+6
-8
@@ -90,13 +90,9 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm
|
||||
if (method != null && !method.isConstructor()) {
|
||||
returnType = method.getReturnType();
|
||||
if (returnType != null) {
|
||||
List<PsiTypeParameter> params = new ArrayList<PsiTypeParameter>();
|
||||
for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(method)) {
|
||||
params.add(parameter);
|
||||
}
|
||||
typeParams = params.toArray(new PsiTypeParameter[params.size()]);
|
||||
typeParams = method.getTypeParameters();
|
||||
}
|
||||
} else if (myExpression instanceof PsiNewExpression) { //default constructor
|
||||
} else if (myExpression instanceof PsiNewExpression) {
|
||||
final PsiJavaCodeReferenceElement classReference = ((PsiNewExpression)myExpression).getClassOrAnonymousClassReference();
|
||||
if (classReference != null) {
|
||||
final PsiElement psiClass = classReference.resolve();
|
||||
@@ -108,7 +104,9 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm
|
||||
}
|
||||
|
||||
if (typeParams != null) {
|
||||
final InferenceSession callSession = new InferenceSession(typeParams, PsiSubstitutor.EMPTY, myExpression.getManager(), myExpression);
|
||||
PsiSubstitutor siteSubstitutor =
|
||||
resolveResult instanceof MethodCandidateInfo && method != null && !method.isConstructor() ? ((MethodCandidateInfo)resolveResult).getSiteSubstitutor() : PsiSubstitutor.EMPTY;
|
||||
final InferenceSession callSession = new InferenceSession(typeParams, siteSubstitutor, myExpression.getManager(), myExpression);
|
||||
callSession.propagateVariables(session.getInferenceVariables());
|
||||
if (method != null) {
|
||||
final PsiExpression[] args = argumentList.getExpressions();
|
||||
@@ -120,7 +118,7 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm
|
||||
if (!accepted) {
|
||||
return false;
|
||||
}
|
||||
callSession.registerReturnTypeConstraints(returnType, myT);
|
||||
callSession.registerReturnTypeConstraints(siteSubstitutor.substitute(returnType), myT);
|
||||
if (callSession.repeatInferencePhases(true)) {
|
||||
session.registerNestedSession(callSession);
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -91,7 +91,7 @@ public class TypeEqualityConstraint implements ConstraintFormula {
|
||||
final PsiClassType.ClassResolveResult sResult = ((PsiClassType)myS).resolveGenerics();
|
||||
final PsiClass tClass = tResult.getElement();
|
||||
//equal erasure
|
||||
if (tClass != null && tClass.equals(sResult.getElement())) {
|
||||
if (tClass != null && tClass.getManager().areElementsEquivalent(tClass, sResult.getElement())) {
|
||||
final PsiSubstitutor tSubstitutor = tResult.getSubstitutor();
|
||||
final PsiSubstitutor sSubstitutor = sResult.getSubstitutor();
|
||||
for (PsiTypeParameter typeParameter : tClass.getTypeParameters()) {
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
class Test {
|
||||
final Runnable runnable;
|
||||
|
||||
{
|
||||
runnable = () -> System.out.println(<error descr="Variable 'runnable' might not have been initialized">runnable</error>);
|
||||
}
|
||||
|
||||
final Runnable runnable1;
|
||||
{
|
||||
runnable1 = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println(runnable1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
abstract class TestInnerAnonymous {
|
||||
|
||||
|
||||
void foo() {
|
||||
new Object() {
|
||||
final Runnable runnable;
|
||||
|
||||
{
|
||||
runnable = () -> System.out.println(<error descr="Variable 'runnable' might not have been initialized">runnable</error>);
|
||||
}
|
||||
|
||||
final Runnable runnable1;
|
||||
|
||||
{
|
||||
runnable1 = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println(runnable1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private static class MyObject {
|
||||
final Runnable runnable;
|
||||
|
||||
{
|
||||
runnable = () -> System.out.println(<error descr="Variable 'runnable' might not have been initialized">runnable</error>);
|
||||
}
|
||||
|
||||
final Runnable runnable1;
|
||||
|
||||
{
|
||||
runnable1 = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println(runnable1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
|
||||
//expected type parameter
|
||||
class Sample<V> {
|
||||
|
||||
public <K> Map<V, K> test() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> void fun(T t, V v) {
|
||||
}
|
||||
|
||||
<M> M bar() {
|
||||
return null;
|
||||
}
|
||||
|
||||
void run(Sample<Integer> sample) {
|
||||
fun(test(), bar());
|
||||
sample.fun(test(), sample.bar());
|
||||
sample.fun(test(), bar());
|
||||
fun(test(), sample.bar());
|
||||
|
||||
fun(sample.test(), bar());
|
||||
fun(sample.test(), sample.bar());
|
||||
sample.fun(sample.test(), bar());
|
||||
sample.fun(sample.test(), sample.bar());
|
||||
}
|
||||
}
|
||||
|
||||
//expected generic type
|
||||
class Sample1<T> {
|
||||
public <S extends T> List<S> reverse() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void foo(Sample1<Comparable> t)
|
||||
{
|
||||
newTreeSet(t.reverse());
|
||||
newTreeSet(reverse());
|
||||
|
||||
t.newTreeSet(t.reverse());
|
||||
t.newTreeSet(reverse());
|
||||
}
|
||||
|
||||
|
||||
public <E> void newTreeSet(List<E> comparator) {}
|
||||
}
|
||||
|
||||
+2
-2
@@ -67,7 +67,7 @@ class MyTest1 {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
m<error descr="Ambiguous method call: both 'MyTest1.m(I1)' and 'MyTest1.m(I2)' match">(Foo::new)</error>;
|
||||
m<error descr="Ambiguous method call: both 'MyTest1.m(I2)' and 'MyTest1.m(I3)' match">(Foo::new)</error>;
|
||||
}
|
||||
}
|
||||
class MyTest2 {
|
||||
@@ -104,6 +104,6 @@ class MyTest2 {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
m<error descr="Ambiguous method call: both 'MyTest2.m(I1)' and 'MyTest2.m(I2)' match">(Foo::new)</error>;
|
||||
m<error descr="Ambiguous method call: both 'MyTest2.m(I2)' and 'MyTest2.m(I3)' match">(Foo::new)</error>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
class Sample {
|
||||
|
||||
void a() {
|
||||
System.out.println("");
|
||||
<caret>}
|
||||
|
||||
private int foo() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
class Sample {
|
||||
|
||||
void a() {
|
||||
class Local {
|
||||
|
||||
<caret>}
|
||||
}
|
||||
|
||||
private int foo() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
class Sample {
|
||||
static {<caret>}
|
||||
|
||||
int foo() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
class Sample {
|
||||
int <caret>i = 0;
|
||||
|
||||
int foo() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
class Sample {
|
||||
public Sample() {
|
||||
<caret>super();
|
||||
}
|
||||
|
||||
int foo() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
class Foo {
|
||||
interface I<T> {
|
||||
T m(Object op);
|
||||
}
|
||||
|
||||
interface J<T> {
|
||||
int m(T o);
|
||||
}
|
||||
|
||||
|
||||
void f(J r) {}
|
||||
void f(I<String> r) {}
|
||||
|
||||
{
|
||||
f((Object a) -> {
|
||||
int c = 1;
|
||||
return c;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
class Foo {
|
||||
interface I<T> {
|
||||
T m(Object op);
|
||||
}
|
||||
|
||||
interface J<T> {
|
||||
int m(T o);
|
||||
}
|
||||
|
||||
|
||||
void f(J r) {}
|
||||
void f(I<String> r) {}
|
||||
|
||||
{
|
||||
f((Object a) -> <selection>1</selection>);
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
class Foo {
|
||||
interface I<T> {
|
||||
T m(Object op);
|
||||
}
|
||||
|
||||
interface J<T> {
|
||||
void m(T o);
|
||||
}
|
||||
|
||||
|
||||
void f(J r) {}
|
||||
void f(I<String> r) {}
|
||||
|
||||
{
|
||||
f((a) -> {
|
||||
int c = 1;
|
||||
return c;
|
||||
});
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
class Foo {
|
||||
interface I<T> {
|
||||
T m(Object op);
|
||||
}
|
||||
|
||||
interface J<T> {
|
||||
void m(T o);
|
||||
}
|
||||
|
||||
|
||||
void f(J r) {}
|
||||
void f(I<String> r) {}
|
||||
|
||||
{
|
||||
f((a) -> <selection>1</selection>);
|
||||
}
|
||||
}
|
||||
+4
@@ -172,6 +172,10 @@ public class GraphInferenceHighlightingTest extends LightDaemonAnalyzerTestCase
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testSiteSubstitutionInExpressionConstraints() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testIncorporationWithEqualsBoundsSubstitution() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
+4
@@ -39,6 +39,10 @@ public class LightAdvHighlightingJdk8Test extends LightDaemonAnalyzerTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testFinalVariableMightNotHaveBeenInitializedInsideLambda() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
private void doTest() {
|
||||
IdeaTestUtil.setTestVersion(JavaSdkVersion.JDK_1_8, getModule(), getTestRootDisposable());
|
||||
doTest(BASE_PATH + "/" + getTestName(false) + ".java", true, false);
|
||||
|
||||
+4
-2
@@ -83,9 +83,10 @@ class Test {
|
||||
expected: '''\
|
||||
class Test {
|
||||
public int j;
|
||||
{ j = 1; }
|
||||
protected int k;
|
||||
private int i;
|
||||
|
||||
{ j = 1; }
|
||||
}''',
|
||||
rules: [rule(FIELD, PUBLIC), rule(FIELD, PROTECTED), rule(FIELD, PRIVATE)])
|
||||
}
|
||||
@@ -101,10 +102,11 @@ class Test {
|
||||
}''',
|
||||
expected: '''\
|
||||
class Test {
|
||||
{ j = 1; }
|
||||
public int j;
|
||||
protected int k;
|
||||
private int i;
|
||||
|
||||
{ j = 1; }
|
||||
}''',
|
||||
rules: [rule(FIELD, PUBLIC), rule(FIELD, PROTECTED), rule(FIELD, PRIVATE)])
|
||||
}
|
||||
|
||||
+81
@@ -569,4 +569,85 @@ class B extends A {
|
||||
rules: [rule(PUBLIC, METHOD), rule(PRIVATE, METHOD)]
|
||||
)
|
||||
}
|
||||
|
||||
void "test initializer block after fields"() {
|
||||
doTest(
|
||||
initial: '''\
|
||||
public class NewOneClass {
|
||||
|
||||
{
|
||||
a = 1;
|
||||
}
|
||||
|
||||
int a;
|
||||
|
||||
{
|
||||
b = 5;
|
||||
}
|
||||
|
||||
int b;
|
||||
|
||||
}
|
||||
''',
|
||||
expected: '''\
|
||||
public class NewOneClass {
|
||||
|
||||
int a;
|
||||
int b;
|
||||
|
||||
{
|
||||
a = 1;
|
||||
}
|
||||
|
||||
{
|
||||
b = 5;
|
||||
}
|
||||
|
||||
}
|
||||
''',
|
||||
rules: [rule(FIELD), rule(INIT_BLOCK)]
|
||||
)
|
||||
}
|
||||
|
||||
void "test static initializer block"() {
|
||||
doTest(
|
||||
initial: '''\
|
||||
public class NewOneClass {
|
||||
|
||||
static {
|
||||
a = 1;
|
||||
}
|
||||
|
||||
static int a;
|
||||
|
||||
{
|
||||
b = 5;
|
||||
}
|
||||
|
||||
int b;
|
||||
|
||||
}
|
||||
''',
|
||||
expected: '''\
|
||||
public class NewOneClass {
|
||||
|
||||
static int a;
|
||||
|
||||
static {
|
||||
a = 1;
|
||||
}
|
||||
|
||||
int b;
|
||||
|
||||
{
|
||||
b = 5;
|
||||
}
|
||||
|
||||
}
|
||||
''',
|
||||
rules: [rule(STATIC, FIELD), rule(STATIC, INIT_BLOCK), rule(FIELD), rule(INIT_BLOCK)]
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+1
@@ -25,6 +25,7 @@ public class JavaFormatterBlankLinesTest extends AbstractJavaFormatterTest {
|
||||
|
||||
public void testBlankLinesAroundClassInitializationBlock() throws Exception {
|
||||
getSettings().BLANK_LINES_AROUND_METHOD = 3;
|
||||
getJavaSettings().BLANK_LINES_AROUND_INITIALIZER = 3;
|
||||
doTextTest(
|
||||
"class T {\n" +
|
||||
" private final DecimalFormat fmt = new DecimalFormat();\n" +
|
||||
|
||||
@@ -260,7 +260,6 @@ public class JavaFormatterWrapTest extends AbstractJavaFormatterTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Bombed(user = "Roman Shevchenko", year = 2014, month = Calendar.MARCH, day = 14)
|
||||
public void testWrapMethodAnnotationBeforeParams() {
|
||||
// Inspired by IDEA-59536
|
||||
getSettings().RIGHT_MARGIN = 90;
|
||||
|
||||
+50
@@ -214,6 +214,56 @@ public class ExtractMethodObject4DebuggerTest extends LightRefactoringTestCase {
|
||||
" }", false);
|
||||
}
|
||||
|
||||
public void testOnClosingBrace() throws Exception {
|
||||
doTest(" foo()", "int result = new Test().invoke();",
|
||||
|
||||
"public class Test {\n" +
|
||||
" public int invoke() {\n" +
|
||||
" return foo();\n" +
|
||||
" }\n" +
|
||||
" }");
|
||||
}
|
||||
|
||||
public void testOnClosingBraceLocalClass() throws Exception {
|
||||
doTest(" foo()", "int result = new Test().invoke();",
|
||||
|
||||
"public class Test {\n" +
|
||||
" public int invoke() {\n" +
|
||||
" return foo();\n" +
|
||||
" }\n" +
|
||||
" }");
|
||||
}
|
||||
|
||||
public void testOnFieldInitialization() throws Exception {
|
||||
doTest(" foo()", "int result = new Test().invoke();",
|
||||
|
||||
"public class Test {\n" +
|
||||
" public int invoke() {\n" +
|
||||
" return foo();\n" +
|
||||
" }\n" +
|
||||
" }");
|
||||
}
|
||||
|
||||
public void testOnEmptyMethod() throws Exception {
|
||||
doTest(" foo()", "int result = Test.invoke();",
|
||||
|
||||
"public static class Test {\n" +
|
||||
" public static int invoke() {\n" +
|
||||
" return foo();\n" +
|
||||
" }\n" +
|
||||
" }");
|
||||
}
|
||||
|
||||
public void testOnSuperConstructorCall() throws Exception {
|
||||
doTest(" foo()", "int result = new Test().invoke();",
|
||||
|
||||
"public class Test {\n" +
|
||||
" public int invoke() {\n" +
|
||||
" return foo();\n" +
|
||||
" }\n" +
|
||||
" }");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Sdk getProjectJDK() {
|
||||
return IdeaTestUtil.getMockJdk18();
|
||||
|
||||
@@ -437,6 +437,14 @@ public class IntroduceVariableTest extends LightCodeInsightTestCase {
|
||||
doTest(new MockIntroduceVariableHandler("c", false, false, false, "int"));
|
||||
}
|
||||
|
||||
public void testPutInLambdaBody() {
|
||||
doTest(new MockIntroduceVariableHandler("c", false, false, false, "int"));
|
||||
}
|
||||
|
||||
public void testPutInLambdaBodyVoidValueConflict() {
|
||||
doTest(new MockIntroduceVariableHandler("c", false, false, false, "int"));
|
||||
}
|
||||
|
||||
public void testNormalizeDeclarations() {
|
||||
doTest(new MockIntroduceVariableHandler("i3", false, false, false, "int"));
|
||||
}
|
||||
|
||||
@@ -15,14 +15,17 @@
|
||||
*/
|
||||
package org.jetbrains.jps.api;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* @author Eugene Zhuravlev
|
||||
* Date: 5/3/12
|
||||
*/
|
||||
public class BasicFuture<T> implements Future<T> {
|
||||
public class BasicFuture<T> implements TaskFuture<T> {
|
||||
protected final Semaphore mySemaphore = new Semaphore(1);
|
||||
private final AtomicBoolean myDone = new AtomicBoolean(false);
|
||||
private final AtomicBoolean myCanceledState = new AtomicBoolean(false);
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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 org.jetbrains.jps.api;
|
||||
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public interface TaskFuture<T> extends Future<T> {
|
||||
void waitFor();
|
||||
|
||||
boolean waitFor(long timeout, TimeUnit unit);
|
||||
}
|
||||
@@ -2347,6 +2347,13 @@ public class Mappings {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Collection<File> getClassSources(int className) {
|
||||
synchronized (myLock) {
|
||||
return myClassToSourceFile.get(className);
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
synchronized (myLock) {
|
||||
myClassToSubclasses.close();
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.jetbrains.jps.cmdline;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.io.FileSystemUtil;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
@@ -35,7 +36,11 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jps.api.CmdlineProtoUtil;
|
||||
import org.jetbrains.jps.api.CmdlineRemoteProto;
|
||||
import org.jetbrains.jps.api.GlobalOptions;
|
||||
import org.jetbrains.jps.incremental.MessageHandler;
|
||||
import org.jetbrains.jps.incremental.Utils;
|
||||
import org.jetbrains.jps.incremental.fs.BuildFSState;
|
||||
import org.jetbrains.jps.incremental.fs.FSState;
|
||||
import org.jetbrains.jps.incremental.messages.BuildMessage;
|
||||
import org.jetbrains.jps.service.SharedThreadPool;
|
||||
|
||||
import java.io.*;
|
||||
@@ -49,6 +54,9 @@ import java.util.concurrent.TimeUnit;
|
||||
*/
|
||||
@SuppressWarnings("UseOfSystemOutOrSystemErr")
|
||||
public class BuildMain {
|
||||
private static final String PRELOAD_PROJECT_PATH = "preload.project.path";
|
||||
private static final String PRELOAD_CONFIG_PATH = "preload.config.path";
|
||||
|
||||
private static final String LOG_CONFIG_FILE_NAME = "build-log.xml";
|
||||
private static final String LOG_FILE_NAME = "build.log";
|
||||
private static final String DEFAULT_LOGGER_CONFIG = "defaultLogConfig.xml";
|
||||
@@ -65,19 +73,10 @@ public class BuildMain {
|
||||
private static final int SYSTEM_DIR_ARG = SESSION_ID_ARG + 1;
|
||||
|
||||
private static NioEventLoopGroup ourEventLoopGroup;
|
||||
@Nullable
|
||||
private static PreloadedData ourPreloadedData;
|
||||
|
||||
public static void main(String[] args){
|
||||
final long start = System.nanoTime();
|
||||
Runtime.getRuntime().addShutdownHook(new Thread("TimeLogger"){
|
||||
public void run() {
|
||||
final long totalTime = (System.nanoTime() - start) / 1000000L;
|
||||
|
||||
final long millis = totalTime % 1000L ;
|
||||
final long seconds = totalTime / 1000L;
|
||||
|
||||
LOG.info("Total build time " + seconds + " sec " + millis + " ms");
|
||||
}
|
||||
});
|
||||
System.out.println("Build process started. Classpath: " + System.getProperty("java.class.path"));
|
||||
final String host = args[HOST_ARG];
|
||||
final int port = Integer.parseInt(args[PORT_ARG]);
|
||||
@@ -123,6 +122,60 @@ public class BuildMain {
|
||||
final ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)).awaitUninterruptibly();
|
||||
final boolean success = future.isSuccess();
|
||||
if (success) {
|
||||
final String projectPathToPreload = System.getProperty(PRELOAD_PROJECT_PATH, null);
|
||||
final String globalsPathToPreload = System.getProperty(PRELOAD_CONFIG_PATH, null);
|
||||
if (projectPathToPreload != null && globalsPathToPreload != null) {
|
||||
final long preloadStart = System.currentTimeMillis();
|
||||
final PreloadedData data = new PreloadedData();
|
||||
ourPreloadedData = data;
|
||||
try {
|
||||
FileSystemUtil.getAttributes(projectPathToPreload); // this will pre-load all FS optimizations
|
||||
|
||||
final BuildRunner runner = new BuildRunner(new JpsModelLoaderImpl(projectPathToPreload, globalsPathToPreload, null));
|
||||
data.setRunner(runner);
|
||||
|
||||
final File dataStorageRoot = Utils.getDataStorageRoot(projectPathToPreload);
|
||||
final BuildFSState fsState = new BuildFSState(false);
|
||||
final ProjectDescriptor pd = runner.load(new MessageHandler() {
|
||||
@Override
|
||||
public void processMessage(BuildMessage msg) {
|
||||
data.addMessage(msg);
|
||||
}
|
||||
}, dataStorageRoot, fsState);
|
||||
data.setProjectDescriptor(pd);
|
||||
|
||||
try {
|
||||
final File fsStateFile = new File(dataStorageRoot, BuildSession.FS_STATE_FILE);
|
||||
final DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(fsStateFile)));
|
||||
try {
|
||||
final int version = in.readInt();
|
||||
if (version == FSState.VERSION) {
|
||||
final long savedOrdinal = in.readLong();
|
||||
in.readBoolean(); // must skip "has-work-to-do" flag
|
||||
fsState.load(in, pd.getModel(), pd.getBuildRootIndex());
|
||||
data.setFsEventOrdinal(savedOrdinal);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
catch (FileNotFoundException ignored) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.info("Error pre-loading FS state", e);
|
||||
fsState.clearAll();
|
||||
}
|
||||
LOG.info("Pre-loaded project model in " + (System.currentTimeMillis() - preloadStart) + " ms");
|
||||
}
|
||||
catch (Throwable e) {
|
||||
LOG.info("Failed to pre-load project " + projectPathToPreload, e);
|
||||
// just failed to preload the project, the situation will be handled later, when real build starts
|
||||
}
|
||||
}
|
||||
else if (projectPathToPreload != null || globalsPathToPreload != null){
|
||||
LOG.info("Skipping project pre-loading step: both paths to project configuration files and path to global settings must be specified");
|
||||
}
|
||||
future.channel().writeAndFlush(CmdlineProtoUtil.toMessage(sessionId, CmdlineProtoUtil.createParamRequest()));
|
||||
}
|
||||
else {
|
||||
@@ -159,7 +212,7 @@ public class BuildMain {
|
||||
case BUILD_PARAMETERS: {
|
||||
if (mySession == null) {
|
||||
final CmdlineRemoteProto.Message.ControllerMessage.FSEvent delta = controllerMessage.hasFsEvent()? controllerMessage.getFsEvent() : null;
|
||||
final BuildSession session = new BuildSession(mySessionId, channel, controllerMessage.getParamsMessage(), delta);
|
||||
final BuildSession session = new BuildSession(mySessionId, channel, controllerMessage.getParamsMessage(), delta, ourPreloadedData);
|
||||
mySession = session;
|
||||
SharedThreadPool.getInstance().executeOnPooledThread(new Runnable() {
|
||||
@Override
|
||||
|
||||
@@ -58,14 +58,20 @@ public class BuildRunner {
|
||||
public static final boolean PARALLEL_BUILD_ENABLED = Boolean.parseBoolean(System.getProperty(GlobalOptions.COMPILE_PARALLEL_OPTION, "false"));
|
||||
private static final boolean STORE_TEMP_CACHES_IN_MEMORY = PARALLEL_BUILD_ENABLED || Boolean.valueOf(System.getProperty(GlobalOptions.USE_MEMORY_TEMP_CACHE_OPTION, "true"));
|
||||
private final JpsModelLoader myModelLoader;
|
||||
private final List<String> myFilePaths;
|
||||
private final Map<String, String> myBuilderParams;
|
||||
private List<String> myFilePaths = Collections.emptyList();
|
||||
private Map<String, String> myBuilderParams = Collections.emptyMap();
|
||||
private boolean myForceCleanCaches;
|
||||
|
||||
public BuildRunner(JpsModelLoader modelLoader, List<String> filePaths, Map<String, String> builderParams) {
|
||||
public BuildRunner(JpsModelLoader modelLoader) {
|
||||
myModelLoader = modelLoader;
|
||||
myFilePaths = filePaths;
|
||||
myBuilderParams = builderParams;
|
||||
}
|
||||
|
||||
public void setFilePaths(List<String> filePaths) {
|
||||
myFilePaths = filePaths != null? filePaths : Collections.<String>emptyList();
|
||||
}
|
||||
|
||||
public void setBuilderParams(Map<String, String> builderParams) {
|
||||
myBuilderParams = builderParams != null? builderParams : Collections.<String, String>emptyMap();
|
||||
}
|
||||
|
||||
public ProjectDescriptor load(MessageHandler msgHandler, File dataStorageRoot, BuildFSState fsState) throws IOException {
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.concurrency.SequentialTaskExecutor;
|
||||
import com.intellij.util.io.DataOutputStream;
|
||||
import io.netty.channel.Channel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jps.TimingLog;
|
||||
import org.jetbrains.jps.api.*;
|
||||
@@ -57,9 +58,11 @@ import static org.jetbrains.jps.api.CmdlineRemoteProto.Message.ControllerMessage
|
||||
*/
|
||||
final class BuildSession implements Runnable, CanceledStatus {
|
||||
private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.cmdline.BuildSession");
|
||||
private static final String FS_STATE_FILE = "fs_state.dat";
|
||||
public static final String FS_STATE_FILE = "fs_state.dat";
|
||||
private final UUID mySessionId;
|
||||
private final Channel myChannel;
|
||||
@Nullable
|
||||
private final PreloadedData myPreloadedData;
|
||||
private volatile boolean myCanceled = false;
|
||||
private final String myProjectPath;
|
||||
@Nullable
|
||||
@@ -70,6 +73,7 @@ final class BuildSession implements Runnable, CanceledStatus {
|
||||
private volatile ProjectDescriptor myProjectDescriptor;
|
||||
private final Map<Pair<String, String>, ConstantSearchFuture> mySearchTasks = Collections.synchronizedMap(new HashMap<Pair<String, String>, ConstantSearchFuture>());
|
||||
private final ConstantSearch myConstantSearch = new ConstantSearch();
|
||||
@NotNull
|
||||
private final BuildRunner myBuildRunner;
|
||||
private final boolean myForceModelLoading;
|
||||
private final BuildType myBuildType;
|
||||
@@ -78,24 +82,31 @@ final class BuildSession implements Runnable, CanceledStatus {
|
||||
BuildSession(UUID sessionId,
|
||||
Channel channel,
|
||||
CmdlineRemoteProto.Message.ControllerMessage.ParametersMessage params,
|
||||
@Nullable CmdlineRemoteProto.Message.ControllerMessage.FSEvent delta) {
|
||||
@Nullable CmdlineRemoteProto.Message.ControllerMessage.FSEvent delta, @Nullable PreloadedData preloaded) {
|
||||
mySessionId = sessionId;
|
||||
myChannel = channel;
|
||||
|
||||
myPreloadedData = preloaded;
|
||||
|
||||
final CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings globals = params.getGlobalSettings();
|
||||
myProjectPath = FileUtil.toCanonicalPath(params.getProjectId());
|
||||
String globalOptionsPath = FileUtil.toCanonicalPath(globals.getGlobalOptionsPath());
|
||||
myBuildType = convertCompileType(params.getBuildType());
|
||||
myScopes = params.getScopeList();
|
||||
List<String> filePaths = params.getFilePathList();
|
||||
Map<String, String> builderParams = new HashMap<String, String>();
|
||||
final Map<String, String> builderParams = new HashMap<String, String>();
|
||||
for (CmdlineRemoteProto.Message.KeyValuePair pair : params.getBuilderParameterList()) {
|
||||
builderParams.put(pair.getKey(), pair.getValue());
|
||||
}
|
||||
myInitialFSDelta = delta;
|
||||
JpsModelLoaderImpl loader = new JpsModelLoaderImpl(myProjectPath, globalOptionsPath, null);
|
||||
myForceModelLoading = Boolean.parseBoolean(builderParams.get(BuildParametersKeys.FORCE_MODEL_LOADING));
|
||||
myBuildRunner = new BuildRunner(loader, filePaths, builderParams);
|
||||
if (preloaded == null || preloaded.getRunner() == null) {
|
||||
myBuildRunner = new BuildRunner(new JpsModelLoaderImpl(myProjectPath, globalOptionsPath, null));
|
||||
}
|
||||
else {
|
||||
myBuildRunner = preloaded.getRunner();
|
||||
}
|
||||
myBuildRunner.setFilePaths(filePaths);
|
||||
myBuildRunner.setBuilderParams(builderParams);
|
||||
myForceModelLoading = (preloaded != null && preloaded.getProjectDescriptor() != null) || Boolean.parseBoolean(builderParams.get(BuildParametersKeys.FORCE_MODEL_LOADING));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -185,8 +196,9 @@ final class BuildSession implements Runnable, CanceledStatus {
|
||||
// invoked the very first time for this project
|
||||
myBuildRunner.setForceCleanCaches(true);
|
||||
}
|
||||
|
||||
final DataInputStream fsStateStream = createFSDataStream(dataStorageRoot);
|
||||
final ProjectDescriptor preloadedProject = myPreloadedData != null? myPreloadedData.getProjectDescriptor() : null;
|
||||
final DataInputStream fsStateStream =
|
||||
preloadedProject != null || myInitialFSDelta == null /*this will force FS rescan*/? null : createFSDataStream(dataStorageRoot, myInitialFSDelta.getOrdinal());
|
||||
|
||||
if (fsStateStream != null) {
|
||||
// optimization: check whether we can skip the build
|
||||
@@ -198,27 +210,56 @@ final class BuildSession implements Runnable, CanceledStatus {
|
||||
}
|
||||
}
|
||||
|
||||
final BuildFSState fsState = new BuildFSState(false);
|
||||
final BuildFSState fsState = preloadedProject != null? preloadedProject.fsState : new BuildFSState(false);
|
||||
try {
|
||||
final ProjectDescriptor pd = myBuildRunner.load(msgHandler, dataStorageRoot, fsState);
|
||||
TimingLog.LOG.debug("Project descriptor loaded");
|
||||
myProjectDescriptor = pd;
|
||||
if (fsStateStream != null) {
|
||||
try {
|
||||
try {
|
||||
fsState.load(fsStateStream, pd.getModel(), pd.getBuildRootIndex());
|
||||
applyFSEvent(pd, myInitialFSDelta, false);
|
||||
TimingLog.LOG.debug("FS Delta loaded");
|
||||
}
|
||||
finally {
|
||||
fsStateStream.close();
|
||||
final ProjectDescriptor pd;
|
||||
if (preloadedProject != null) {
|
||||
pd = preloadedProject;
|
||||
final List<BuildMessage> preloadMessages = myPreloadedData.getLoadMessages();
|
||||
if (!preloadMessages.isEmpty()) {
|
||||
// replay preload-time messages, so that they are delivered to the IDE
|
||||
for (BuildMessage message : preloadMessages) {
|
||||
msgHandler.processMessage(message);
|
||||
}
|
||||
}
|
||||
catch (Throwable e) {
|
||||
LOG.error(e);
|
||||
fsState.clearAll();
|
||||
if (myInitialFSDelta == null || myPreloadedData.getFsEventOrdinal() + 1L != myInitialFSDelta.getOrdinal()) {
|
||||
// FS rescan was forced
|
||||
fsState.clearAll();
|
||||
}
|
||||
else {
|
||||
// apply events to already loaded state
|
||||
try {
|
||||
applyFSEvent(pd, myInitialFSDelta, false);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
LOG.error(e);
|
||||
fsState.clearAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// standard case
|
||||
pd = myBuildRunner.load(msgHandler, dataStorageRoot, fsState);
|
||||
TimingLog.LOG.debug("Project descriptor loaded");
|
||||
if (fsStateStream != null) {
|
||||
try {
|
||||
try {
|
||||
fsState.load(fsStateStream, pd.getModel(), pd.getBuildRootIndex());
|
||||
applyFSEvent(pd, myInitialFSDelta, false);
|
||||
TimingLog.LOG.debug("FS Delta loaded");
|
||||
}
|
||||
finally {
|
||||
fsStateStream.close();
|
||||
}
|
||||
}
|
||||
catch (Throwable e) {
|
||||
LOG.error(e);
|
||||
fsState.clearAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
myProjectDescriptor = pd;
|
||||
|
||||
myLastEventOrdinal = myInitialFSDelta != null? myInitialFSDelta.getOrdinal() : 0L;
|
||||
|
||||
// free memory
|
||||
@@ -469,11 +510,7 @@ final class BuildSession implements Runnable, CanceledStatus {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private DataInputStream createFSDataStream(File dataStorageRoot) {
|
||||
if (myInitialFSDelta == null) {
|
||||
// this will force FS rescan
|
||||
return null;
|
||||
}
|
||||
private static DataInputStream createFSDataStream(File dataStorageRoot, final long currentEventOrdinal) {
|
||||
try {
|
||||
final File file = new File(dataStorageRoot, FS_STATE_FILE);
|
||||
byte[] bytes;
|
||||
@@ -490,7 +527,7 @@ final class BuildSession implements Runnable, CanceledStatus {
|
||||
return null;
|
||||
}
|
||||
final long savedOrdinal = in.readLong();
|
||||
if (savedOrdinal + 1L != myInitialFSDelta.getOrdinal()) {
|
||||
if (savedOrdinal + 1L != currentEventOrdinal) {
|
||||
return null;
|
||||
}
|
||||
return in;
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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 org.jetbrains.jps.cmdline;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jps.incremental.messages.BuildMessage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Eugene Zhuravlev
|
||||
* Date: 22-Oct-14
|
||||
*/
|
||||
public class PreloadedData {
|
||||
@Nullable
|
||||
private BuildRunner runner;
|
||||
@Nullable
|
||||
private ProjectDescriptor projectDescriptor;
|
||||
|
||||
private final List<BuildMessage> loadMessages = new ArrayList<BuildMessage>();
|
||||
|
||||
private long fsEventOrdinal;
|
||||
|
||||
@Nullable
|
||||
public BuildRunner getRunner() {
|
||||
return runner;
|
||||
}
|
||||
|
||||
public void setRunner(@Nullable BuildRunner runner) {
|
||||
this.runner = runner;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ProjectDescriptor getProjectDescriptor() {
|
||||
return projectDescriptor;
|
||||
}
|
||||
|
||||
public void setProjectDescriptor(@Nullable ProjectDescriptor projectDescriptor) {
|
||||
this.projectDescriptor = projectDescriptor;
|
||||
}
|
||||
|
||||
public long getFsEventOrdinal() {
|
||||
return fsEventOrdinal;
|
||||
}
|
||||
|
||||
public void setFsEventOrdinal(long fsEventOrdinal) {
|
||||
this.fsEventOrdinal = fsEventOrdinal;
|
||||
}
|
||||
|
||||
public List<BuildMessage> getLoadMessages() {
|
||||
return loadMessages;
|
||||
}
|
||||
|
||||
public void addMessage(BuildMessage msg) {
|
||||
loadMessages.add(msg);
|
||||
}
|
||||
}
|
||||
@@ -168,7 +168,16 @@ public class JavacMain {
|
||||
diagnosticConsumer.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, e.getMessage()));
|
||||
}
|
||||
catch (CompilationCanceledException ignored) {
|
||||
diagnosticConsumer.report(new PlainMessageDiagnostic(Diagnostic.Kind.OTHER, "Compilation was canceled"));
|
||||
handleCancelException(diagnosticConsumer);
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
final Throwable cause = e.getCause();
|
||||
if (cause instanceof CompilationCanceledException) {
|
||||
handleCancelException(diagnosticConsumer);
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
fileManager.close();
|
||||
@@ -179,6 +188,10 @@ public class JavacMain {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void handleCancelException(DiagnosticOutputConsumer diagnosticConsumer) {
|
||||
diagnosticConsumer.report(new PlainMessageDiagnostic(Diagnostic.Kind.OTHER, "Compilation was canceled"));
|
||||
}
|
||||
|
||||
private static List<JavaSourceTransformer> getSourceTransformers() {
|
||||
final Class<JavaSourceTransformer> transformerClass = JavaSourceTransformer.class;
|
||||
final ServiceLoader<JavaSourceTransformer> loader = ServiceLoader.load(transformerClass, transformerClass.getClassLoader());
|
||||
|
||||
+22
-1
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.jetbrains.jps.model.serialization;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.JDOMUtil;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.io.FileUtilRt;
|
||||
@@ -30,6 +31,8 @@ import java.io.IOException;
|
||||
* @author nik
|
||||
*/
|
||||
public abstract class JpsLoaderBase {
|
||||
private static final Logger LOG = Logger.getInstance(JpsLoaderBase.class);
|
||||
private static final int MAX_ATTEMPTS = 5;
|
||||
private final JpsMacroExpander myMacroExpander;
|
||||
|
||||
protected JpsLoaderBase(JpsMacroExpander macroExpander) {
|
||||
@@ -66,7 +69,7 @@ public abstract class JpsLoaderBase {
|
||||
|
||||
protected static Element loadRootElement(final File file, final JpsMacroExpander macroExpander) {
|
||||
try {
|
||||
final Element element = JDOMUtil.loadDocument(file).getRootElement();
|
||||
final Element element = tryLoadRootElement(file);
|
||||
macroExpander.substitute(element, SystemInfo.isFileSystemCaseSensitive);
|
||||
return element;
|
||||
}
|
||||
@@ -78,6 +81,24 @@ public abstract class JpsLoaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
private static Element tryLoadRootElement(File file) throws IOException, JDOMException {
|
||||
for (int i = 0; i < MAX_ATTEMPTS - 1; i++) {
|
||||
try {
|
||||
return JDOMUtil.loadDocument(file).getRootElement();
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.info("Loading attempt #" + i + " failed", e);
|
||||
}
|
||||
//most likely configuration file is being written by IDE so we'll wait a little
|
||||
try {
|
||||
//noinspection BusyWait
|
||||
Thread.sleep(300);
|
||||
}
|
||||
catch (InterruptedException ignored) { }
|
||||
}
|
||||
return JDOMUtil.loadDocument(file).getRootElement();
|
||||
}
|
||||
|
||||
protected static boolean isXmlFile(File file) {
|
||||
return file.isFile() && FileUtilRt.extensionEquals(file.getName(), "xml");
|
||||
}
|
||||
|
||||
@@ -193,8 +193,7 @@ public class Standalone {
|
||||
|
||||
public static void runBuild(JpsModelLoader loader, File dataStorageRoot, MessageHandler messageHandler, List<TargetTypeBuildScope> scopes,
|
||||
boolean includeDependenciesToScope) throws Exception {
|
||||
final BuildRunner buildRunner = new BuildRunner(loader, Collections.<String>emptyList(), Collections.<String, String>emptyMap()
|
||||
);
|
||||
final BuildRunner buildRunner = new BuildRunner(loader);
|
||||
ProjectDescriptor descriptor = buildRunner.load(messageHandler, dataStorageRoot, new BuildFSState(true));
|
||||
try {
|
||||
buildRunner.runBuild(descriptor, CanceledStatus.NULL, null, messageHandler, BuildType.BUILD, scopes, includeDependenciesToScope);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.intellij.json.psi.impl;
|
||||
|
||||
import com.intellij.json.JsonLanguage;
|
||||
import com.intellij.json.psi.JsonFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiFileSystemItem;
|
||||
@@ -20,6 +21,8 @@ public class JsonTreeChangePreprocessor implements PsiTreeChangePreprocessor {
|
||||
*/
|
||||
@Override
|
||||
public void treeChanged(@NotNull PsiTreeChangeEventImpl event) {
|
||||
if (!(event.getFile() instanceof JsonFile)) return;
|
||||
|
||||
final PsiElement element = event.getParent();
|
||||
if (element == null || !(element.getManager() instanceof PsiManagerImpl)) {
|
||||
return;
|
||||
|
||||
@@ -66,7 +66,9 @@ public class JsonStructureViewTest extends JsonTestCase {
|
||||
" -object\n" +
|
||||
" nestedObject\n" +
|
||||
" -array\n" +
|
||||
" +array\n" +
|
||||
" -array\n" +
|
||||
" -object\n" +
|
||||
" deepNestedObject\n" +
|
||||
" -object\n" +
|
||||
" siblingObject\n");
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -52,7 +52,7 @@ netty-all-4.1.0.Beta3.jar
|
||||
oromatcher.jar
|
||||
picocontainer.jar
|
||||
protobuf-2.5.0.jar
|
||||
proxy-vole_20120920.jar
|
||||
proxy-vole_20131209.jar
|
||||
pty4j-0.3.jar
|
||||
purejavacomm.jar
|
||||
resolver.jar
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.ide;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class WelcomeWizardUtil {
|
||||
private static volatile String ourDefaultLAF;
|
||||
private static volatile String ourWizardLAF;
|
||||
private static volatile String ourWizardMacKeymap;
|
||||
private static volatile String ourWizardEditorScheme;
|
||||
private static volatile Boolean ourAutoScrollToSource;
|
||||
private static volatile Set<String> ourFeaturedPluginsToInstall = new HashSet<String>();
|
||||
|
||||
public static void setDefaultLAF(String laf) {
|
||||
ourDefaultLAF = laf;
|
||||
}
|
||||
|
||||
public static String getDefaultLAF() {
|
||||
return ourDefaultLAF;
|
||||
}
|
||||
|
||||
public static void setWizardLAF(String laf) {
|
||||
ourWizardLAF = laf;
|
||||
}
|
||||
|
||||
public static String getWizardLAF() {
|
||||
return ourWizardLAF;
|
||||
}
|
||||
|
||||
public static void setWizardKeymap(@Nullable String keymap) {
|
||||
ourWizardMacKeymap = keymap;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getWizardMacKeymap() {
|
||||
return ourWizardMacKeymap;
|
||||
}
|
||||
|
||||
public static void setWizardEditorScheme(@Nullable String wizardEditorScheme) {
|
||||
ourWizardEditorScheme = wizardEditorScheme;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getWizardEditorScheme() {
|
||||
return ourWizardEditorScheme;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Boolean getAutoScrollToSource() {
|
||||
return ourAutoScrollToSource;
|
||||
}
|
||||
|
||||
public static void setAutoScrollToSource(@Nullable Boolean autoScrollToSource) {
|
||||
ourAutoScrollToSource = autoScrollToSource;
|
||||
}
|
||||
|
||||
public static Set<String> getFeaturedPluginsToInstall() {
|
||||
return Collections.unmodifiableSet(ourFeaturedPluginsToInstall);
|
||||
}
|
||||
|
||||
public static void setFeaturedPluginsToInstall(Set<String> pluginsToInstall) {
|
||||
ourFeaturedPluginsToInstall.clear();
|
||||
ourFeaturedPluginsToInstall.addAll(pluginsToInstall);
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class FCTSBackedLighterAST extends LighterAST {
|
||||
public class FCTSBackedLighterAST extends LighterAST {
|
||||
private final FlyweightCapableTreeStructure<LighterASTNode> myTreeStructure;
|
||||
|
||||
public FCTSBackedLighterAST(final CharTable charTable, final FlyweightCapableTreeStructure<LighterASTNode> treeStructure) {
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.intellij.lang;
|
||||
|
||||
import com.intellij.util.CharTable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Top-level node in AST, corresponds to PsiFile.
|
||||
@@ -28,4 +29,7 @@ public interface FileASTNode extends ASTNode {
|
||||
CharTable getCharTable();
|
||||
|
||||
boolean isParsed();
|
||||
|
||||
@Nullable
|
||||
LighterAST getLighterAST();
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package com.intellij.lang;
|
||||
|
||||
import com.intellij.psi.tree.IFileElementType;
|
||||
import com.intellij.psi.tree.ILightStubFileElementType;
|
||||
import com.intellij.util.CharTable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -46,19 +44,4 @@ public abstract class LighterAST {
|
||||
|
||||
@NotNull
|
||||
public abstract List<LighterASTNode> getChildren(@NotNull final LighterASTNode parent);
|
||||
|
||||
public static @Nullable LighterAST getLighterASTFromFileAST(@NotNull FileASTNode node, @NotNull Language language) {
|
||||
final IFileElementType contentType = LanguageParserDefinitions.INSTANCE.forLanguage(language).getFileNodeType();
|
||||
assert contentType instanceof ILightStubFileElementType;
|
||||
|
||||
final LighterAST tree;
|
||||
if (!node.isParsed()) {
|
||||
final ILightStubFileElementType<?> type = (ILightStubFileElementType)contentType;
|
||||
tree = new FCTSBackedLighterAST(node.getCharTable(), type.parseContentsLight(node));
|
||||
}
|
||||
else {
|
||||
tree = new TreeBackedLighterAST(node);
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ public class LightStubBuilder implements StubBuilder {
|
||||
LOG.error("File is not of ILightStubFileElementType: " + contentType + ", " + file);
|
||||
return null;
|
||||
}
|
||||
final LighterAST tree = LighterAST.getLighterASTFromFileAST(file.getNode(), language);
|
||||
final LighterAST tree = file.getNode().getLighterAST();
|
||||
if (tree == null) return null;
|
||||
|
||||
final StubElement rootStub = createStubForFile(file, tree);
|
||||
|
||||
@@ -30,7 +30,10 @@ import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.io.StreamUtil;
|
||||
import com.intellij.openapi.util.io.ZipFileCache;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.*;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.PlatformUtilsCore;
|
||||
import com.intellij.util.ReflectionUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.intellij.util.execution.ParametersListUtil;
|
||||
@@ -166,7 +169,7 @@ public class PluginManagerCore {
|
||||
return app != null && app.isUnitTestMode();
|
||||
}
|
||||
|
||||
public static void savePluginsList(Collection<String> ids, boolean append, File plugins) throws IOException {
|
||||
public static void savePluginsList(@NotNull Collection<String> ids, boolean append, @NotNull File plugins) throws IOException {
|
||||
if (!plugins.isFile()) {
|
||||
FileUtil.ensureCanCreateFile(plugins);
|
||||
}
|
||||
@@ -700,7 +703,13 @@ public class PluginManagerCore {
|
||||
List<IdeaPluginDescriptorImpl> result,
|
||||
@Nullable StartupProgress progress,
|
||||
int pluginsCount) {
|
||||
final File pluginsHome = new File(pluginsPath);
|
||||
loadDescriptors(new File(pluginsPath), result, progress, pluginsCount);
|
||||
}
|
||||
|
||||
public static void loadDescriptors(@NotNull File pluginsHome,
|
||||
List<IdeaPluginDescriptorImpl> result,
|
||||
@Nullable StartupProgress progress,
|
||||
int pluginsCount) {
|
||||
final File[] files = pluginsHome.listFiles();
|
||||
if (files != null) {
|
||||
int i = result.size();
|
||||
|
||||
@@ -89,6 +89,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
|
||||
|
||||
private final ASTNode myOriginalTree;
|
||||
private final MyTreeStructure myParentLightTree;
|
||||
private final int myOffset;
|
||||
|
||||
private static TokenSet ourAnyLanguageWhitespaceTokens = TokenSet.EMPTY;
|
||||
|
||||
@@ -132,7 +133,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
|
||||
@Nullable ASTNode originalTree,
|
||||
@Nullable MyTreeStructure parentLightTree) {
|
||||
this(project, containingFile, parserDefinition.getWhitespaceTokens(), parserDefinition.getCommentTokens(), lexer, charTable, text,
|
||||
originalTree, parentLightTree);
|
||||
originalTree, parentLightTree, 0);
|
||||
}
|
||||
|
||||
public PsiBuilderImpl(Project project,
|
||||
@@ -144,6 +145,19 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
|
||||
@NotNull final CharSequence text,
|
||||
@Nullable ASTNode originalTree,
|
||||
@Nullable MyTreeStructure parentLightTree) {
|
||||
this(project, containingFile, whiteSpaces, comments, lexer, charTable, text, originalTree, parentLightTree, 0);
|
||||
}
|
||||
|
||||
private PsiBuilderImpl(Project project,
|
||||
PsiFile containingFile,
|
||||
@NotNull TokenSet whiteSpaces,
|
||||
@NotNull TokenSet comments,
|
||||
@NotNull Lexer lexer,
|
||||
CharTable charTable,
|
||||
@NotNull final CharSequence text,
|
||||
@Nullable ASTNode originalTree,
|
||||
@Nullable MyTreeStructure parentLightTree,
|
||||
int offset) {
|
||||
myProject = project;
|
||||
myFile = containingFile;
|
||||
|
||||
@@ -156,6 +170,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
|
||||
myCharTable = charTable;
|
||||
myOriginalTree = originalTree;
|
||||
myParentLightTree = parentLightTree;
|
||||
myOffset = offset;
|
||||
|
||||
cacheLexemes();
|
||||
}
|
||||
@@ -174,8 +189,8 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
|
||||
@NotNull final Lexer lexer,
|
||||
@NotNull final LighterLazyParseableNode chameleon,
|
||||
@NotNull final CharSequence text) {
|
||||
this(project, chameleon.getContainingFile(), parserDefinition, lexer, chameleon.getCharTable(), text,
|
||||
null, ((LazyParseableToken)chameleon).myParent);
|
||||
this(project, chameleon.getContainingFile(), parserDefinition.getWhitespaceTokens(), parserDefinition.getCommentTokens(), lexer,
|
||||
chameleon.getCharTable(), text, null, ((LazyParseableToken)chameleon).myParent, chameleon.getStartOffset());
|
||||
}
|
||||
|
||||
private void cacheLexemes() {
|
||||
@@ -335,12 +350,12 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
|
||||
|
||||
@Override
|
||||
public int getStartOffset() {
|
||||
return myBuilder.myLexStarts[myLexemeIndex];
|
||||
return myBuilder.myLexStarts[myLexemeIndex] + myBuilder.myOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEndOffset() {
|
||||
return myBuilder.myLexStarts[myDoneMarker.myLexemeIndex];
|
||||
return myBuilder.myLexStarts[myDoneMarker.myLexemeIndex] + myBuilder.myOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -487,12 +502,12 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
|
||||
|
||||
@Override
|
||||
public int getEndOffset() {
|
||||
return myTokenEnd;
|
||||
return myTokenEnd + myBuilder.myOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStartOffset() {
|
||||
return myTokenStart;
|
||||
return myTokenStart + myBuilder.myOffset;
|
||||
}
|
||||
|
||||
public CharSequence getText() {
|
||||
@@ -636,12 +651,12 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
|
||||
|
||||
@Override
|
||||
public int getEndOffset() {
|
||||
return myBuilder.myLexStarts[myLexemeIndex];
|
||||
return myBuilder.myLexStarts[myLexemeIndex] + myBuilder.myOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStartOffset() {
|
||||
return myBuilder.myLexStarts[myLexemeIndex];
|
||||
return myBuilder.myLexStarts[myLexemeIndex] + myBuilder.myOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1474,6 +1489,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
|
||||
private final LimitedPool<Token> myPool;
|
||||
private final LimitedPool<LazyParseableToken> myLazyPool;
|
||||
private final StartMarker myRoot;
|
||||
private final int myOffset;
|
||||
|
||||
public MyTreeStructure(@NotNull StartMarker root, @Nullable final MyTreeStructure parentTree) {
|
||||
if (parentTree == null) {
|
||||
@@ -1499,10 +1515,12 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
|
||||
return new LazyParseableToken();
|
||||
}
|
||||
});
|
||||
myOffset = 0;
|
||||
}
|
||||
else {
|
||||
myPool = parentTree.myPool;
|
||||
myLazyPool = parentTree.myLazyPool;
|
||||
myOffset = parentTree.getRoot().getStartOffset();
|
||||
}
|
||||
myRoot = root;
|
||||
}
|
||||
|
||||
@@ -16,16 +16,19 @@
|
||||
|
||||
package com.intellij.psi.impl.source.tree;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.lang.FileASTNode;
|
||||
import com.intellij.lang.*;
|
||||
import com.intellij.openapi.util.Getter;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.PsiManagerEx;
|
||||
import com.intellij.psi.impl.source.CharTableImpl;
|
||||
import com.intellij.psi.impl.source.PsiFileImpl;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.IFileElementType;
|
||||
import com.intellij.psi.tree.ILightStubFileElementType;
|
||||
import com.intellij.util.CharTable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class FileElement extends LazyParseableElement implements FileASTNode, Getter<FileElement> {
|
||||
private volatile CharTable myCharTable = new CharTableImpl();
|
||||
@@ -47,6 +50,35 @@ public class FileElement extends LazyParseableElement implements FileASTNode, Ge
|
||||
return myCharTable;
|
||||
}
|
||||
|
||||
private static final Key<LighterAST> ourTreeKey = Key.create("lighter.key");
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public LighterAST getLighterAST() {
|
||||
final IFileElementType contentType = (IFileElementType)getElementType();
|
||||
assert contentType instanceof ILightStubFileElementType;
|
||||
|
||||
LighterAST tree;
|
||||
if (!isParsed()) {
|
||||
tree = getUserData(ourTreeKey);
|
||||
if (tree == null) {
|
||||
final ILightStubFileElementType<?> type = (ILightStubFileElementType)contentType;
|
||||
tree = new FCTSBackedLighterAST(getCharTable(), type.parseContentsLight(this));
|
||||
putUserData(ourTreeKey, tree);
|
||||
}
|
||||
}
|
||||
else {
|
||||
tree = new TreeBackedLighterAST(this);
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetCachesOnParsedStateUpdate() {
|
||||
putUserData(ourTreeKey, null);
|
||||
super.resetCachesOnParsedStateUpdate();
|
||||
}
|
||||
|
||||
public FileElement(IElementType type, CharSequence text) {
|
||||
super(type, text);
|
||||
}
|
||||
|
||||
@@ -181,8 +181,8 @@ public class LazyParseableElement extends CompositeElement {
|
||||
if (rawFirstChild() != null) {
|
||||
LOG.error("Reentrant parsing?");
|
||||
}
|
||||
|
||||
myText = null;
|
||||
|
||||
resetCachesOnParsedStateUpdate();
|
||||
|
||||
if (parsedNode == null) return;
|
||||
super.rawAddChildrenWithoutNotifications((TreeElement)parsedNode);
|
||||
@@ -199,6 +199,10 @@ public class LazyParseableElement extends CompositeElement {
|
||||
}
|
||||
}
|
||||
|
||||
protected void resetCachesOnParsedStateUpdate() {
|
||||
myText = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rawAddChildrenWithoutNotifications(@NotNull TreeElement first) {
|
||||
if (myText() != null) {
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.dupLocator;
|
||||
|
||||
import com.intellij.lang.LighterAST;
|
||||
import com.intellij.lang.LighterASTNode;
|
||||
import com.intellij.psi.PsiFile;
|
||||
|
||||
/**
|
||||
* Created by Maxim.Mossienko on 10/1/2014.
|
||||
*/
|
||||
public interface LightDuplicateProfile {
|
||||
void process(PsiFile file, Callback callback);
|
||||
|
||||
interface Callback {
|
||||
void process(LighterAST ast, LighterASTNode node, int hash);
|
||||
}
|
||||
}
|
||||
+29
-15
@@ -18,15 +18,18 @@ package com.intellij.dupLocator.index;
|
||||
import com.intellij.dupLocator.DuplicatesProfile;
|
||||
import com.intellij.dupLocator.DuplocateVisitor;
|
||||
import com.intellij.dupLocator.DuplocatorState;
|
||||
import com.intellij.dupLocator.LightDuplicateProfile;
|
||||
import com.intellij.dupLocator.treeHash.FragmentsCollector;
|
||||
import com.intellij.dupLocator.util.PsiFragment;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.lang.LighterAST;
|
||||
import com.intellij.lang.LighterASTNode;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.fileTypes.LanguageFileType;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.util.SystemProperties;
|
||||
import com.intellij.util.indexing.*;
|
||||
import com.intellij.util.io.DataExternalizer;
|
||||
@@ -52,6 +55,7 @@ import java.util.Map;
|
||||
public class DuplicatesIndex extends FileBasedIndexExtension<Integer, TIntArrayList> implements PsiDependentIndex {
|
||||
static boolean ourEnabled = SystemProperties.getBooleanProperty("idea.enable.duplicates.online.calculation",
|
||||
isEnabledByDefault());
|
||||
static boolean ourEnabledLightProfiles = true;
|
||||
|
||||
private static boolean isEnabledByDefault() {
|
||||
Application application = ApplicationManager.getApplication();
|
||||
@@ -59,7 +63,7 @@ public class DuplicatesIndex extends FileBasedIndexExtension<Integer, TIntArrayL
|
||||
}
|
||||
|
||||
@NonNls public static final ID<Integer, TIntArrayList> NAME = ID.create("DuplicatesIndex");
|
||||
private static final int myBaseVersion = 10;
|
||||
private static final int myBaseVersion = 12;
|
||||
|
||||
private final FileBasedIndex.InputFilter myInputFilter = new FileBasedIndex.InputFilter() {
|
||||
@Override
|
||||
@@ -107,21 +111,31 @@ public class DuplicatesIndex extends FileBasedIndexExtension<Integer, TIntArrayL
|
||||
@Override
|
||||
@NotNull
|
||||
public Map<Integer, TIntArrayList> map(@NotNull final FileContent inputData) {
|
||||
return ApplicationManager.getApplication().runReadAction(new Computable<Map<Integer, TIntArrayList>>() {
|
||||
@Override
|
||||
public Map<Integer, TIntArrayList> compute() {
|
||||
FileType type = inputData.getFileType();
|
||||
FileType type = inputData.getFileType();
|
||||
|
||||
DuplicatesProfile profile = findDuplicatesProfile(type);
|
||||
if (profile == null) return Collections.emptyMap();
|
||||
DuplicatesProfile profile = findDuplicatesProfile(type);
|
||||
if (profile == null) return Collections.emptyMap();
|
||||
|
||||
MyFragmentsCollector collector = new MyFragmentsCollector(profile, ((LanguageFileType)type).getLanguage());
|
||||
DuplocateVisitor visitor = profile.createVisitor(collector, true);
|
||||
visitor.visitNode(((FileContentImpl)inputData).getPsiFileAccountingForUnsavedDocument());
|
||||
PsiFile psiFile = ((FileContentImpl)inputData).getPsiFileAccountingForUnsavedDocument();
|
||||
|
||||
return collector.getMap();
|
||||
}
|
||||
});
|
||||
if (profile instanceof LightDuplicateProfile && ourEnabledLightProfiles) {
|
||||
final THashMap<Integer, TIntArrayList> result = new THashMap<Integer, TIntArrayList>();
|
||||
((LightDuplicateProfile)profile).process(psiFile, new LightDuplicateProfile.Callback() {
|
||||
@Override
|
||||
public void process(LighterAST ast, LighterASTNode node, int hash) {
|
||||
TIntArrayList list = result.get(hash);
|
||||
if (list == null) { result.put(hash, list = new TIntArrayList(1)); }
|
||||
list.add(node.getStartOffset());
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
MyFragmentsCollector collector = new MyFragmentsCollector(profile, ((LanguageFileType)type).getLanguage());
|
||||
DuplocateVisitor visitor = profile.createVisitor(collector, true);
|
||||
|
||||
visitor.visitNode(psiFile);
|
||||
|
||||
return collector.getMap();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -135,7 +149,7 @@ public class DuplicatesIndex extends FileBasedIndexExtension<Integer, TIntArrayL
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return myBaseVersion + (ourEnabled ? 0xFF : 0);
|
||||
return myBaseVersion + (ourEnabled ? 0xFF : 0) + (ourEnabledLightProfiles ? 0x7F : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+176
-67
@@ -3,10 +3,16 @@ package com.intellij.dupLocator.index;
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.dupLocator.DuplicatesProfile;
|
||||
import com.intellij.dupLocator.DuplocatorState;
|
||||
import com.intellij.dupLocator.LightDuplicateProfile;
|
||||
import com.intellij.dupLocator.treeHash.FragmentsCollector;
|
||||
import com.intellij.dupLocator.util.PsiFragment;
|
||||
import com.intellij.lang.LighterAST;
|
||||
import com.intellij.lang.LighterASTNode;
|
||||
import com.intellij.lang.TreeBackedLighterAST;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileWithId;
|
||||
@@ -35,94 +41,126 @@ public class DuplicatesInspectionBase extends LocalInspectionTool {
|
||||
final DuplicatesProfile profile = DuplicatesIndex.findDuplicatesProfile(psiFile.getFileType());
|
||||
if (profile == null) return ProblemDescriptor.EMPTY_ARRAY;
|
||||
|
||||
final DuplocatorState state = profile.getDuplocatorState(psiFile.getLanguage());
|
||||
final SmartList<ProblemDescriptor> descriptors = new SmartList<ProblemDescriptor>();
|
||||
final Ref<DuplicatedCodeProcessor> myProcessorRef = new Ref<DuplicatedCodeProcessor>();
|
||||
|
||||
final TreeMap<Integer, TextRange> reportedRanges = new TreeMap<Integer, TextRange>();
|
||||
final TIntObjectHashMap<VirtualFile> reportedFiles = new TIntObjectHashMap<VirtualFile>();
|
||||
final TIntObjectHashMap<PsiElement> reportedPsi = new TIntObjectHashMap<PsiElement>();
|
||||
final TIntIntHashMap reportedOffsetInOtherFiles = new TIntIntHashMap();
|
||||
final TIntIntHashMap fragmentSize = new TIntIntHashMap();
|
||||
if (profile instanceof LightDuplicateProfile && DuplicatesIndex.ourEnabledLightProfiles) {
|
||||
|
||||
profile.createVisitor(new FragmentsCollector() {
|
||||
@Override
|
||||
public void add(int hash, final int cost, @Nullable final PsiFragment frag) {
|
||||
if (!DuplicatesIndex.isIndexedFragment(frag, cost, profile, state)) {
|
||||
return;
|
||||
((LightDuplicateProfile)profile).process(psiFile, new LightDuplicateProfile.Callback() {
|
||||
DuplicatedCodeProcessor<LighterASTNode> myProcessor;
|
||||
@Override
|
||||
public void process(final LighterAST ast, final LighterASTNode node, int hash) {
|
||||
class LightDuplicatedCodeProcessor extends DuplicatedCodeProcessor<LighterASTNode> {
|
||||
|
||||
LightDuplicatedCodeProcessor(VirtualFile file, Project project) {
|
||||
super(file, project);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TextRange getRangeInElement(LighterASTNode node) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PsiElement getPsi(LighterASTNode node) {
|
||||
return ((TreeBackedLighterAST)ast).unwrap(node).getPsi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getStartOffset(LighterASTNode node) {
|
||||
return node.getStartOffset();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getEndOffset(LighterASTNode node) {
|
||||
return node.getEndOffset();
|
||||
}
|
||||
}
|
||||
if (myProcessor == null) {
|
||||
myProcessor = new LightDuplicatedCodeProcessor(virtualFile, psiFile.getProject());
|
||||
myProcessorRef.set(myProcessor);
|
||||
}
|
||||
myProcessor.process(hash, node);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
final DuplocatorState state = profile.getDuplocatorState(psiFile.getLanguage());
|
||||
profile.createVisitor(new FragmentsCollector() {
|
||||
DuplicatedCodeProcessor<PsiFragment> myProcessor;
|
||||
@Override
|
||||
public void add(int hash, final int cost, @Nullable final PsiFragment frag) {
|
||||
if (!DuplicatesIndex.isIndexedFragment(frag, cost, profile, state)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ProgressManager.checkCanceled();
|
||||
FileBasedIndex.getInstance().processValues(DuplicatesIndex.NAME, hash, null, new FileBasedIndex.ValueProcessor<TIntArrayList>() {
|
||||
final ProjectFileIndex myProjectFileIndex = ProjectFileIndex.SERVICE.getInstance(psiFile.getProject());
|
||||
@Override
|
||||
public boolean process(final VirtualFile file, final TIntArrayList list) {
|
||||
for(int i = 0, len = list.size(); i < len; ++i) {
|
||||
ProgressManager.checkCanceled();
|
||||
class OldDuplicatedCodeProcessor extends DuplicatedCodeProcessor<PsiFragment> {
|
||||
|
||||
int value = list.getQuick(i);
|
||||
OldDuplicatedCodeProcessor(VirtualFile file, Project project) {
|
||||
super(file, project);
|
||||
}
|
||||
|
||||
if (myProjectFileIndex.isInSource(virtualFile) && !myProjectFileIndex.isInSource(file)) return true;
|
||||
if (!myProjectFileIndex.isInSource(virtualFile) && myProjectFileIndex.isInSource(file)) return true;
|
||||
final int startOffset = frag.getStartOffset();
|
||||
final int endOffset = frag.getEndOffset();
|
||||
if (file.equals(virtualFile) && value >= startOffset && value < endOffset) continue;
|
||||
|
||||
PsiElement[] elements = frag.getElements();
|
||||
PsiElement target = elements[0];
|
||||
@Override
|
||||
protected TextRange getRangeInElement(PsiFragment node) {
|
||||
PsiElement[] elements = node.getElements();
|
||||
TextRange rangeInElement = null;
|
||||
if (elements.length > 1) {
|
||||
PsiElement firstElement = elements[0];
|
||||
target = firstElement.getParent();
|
||||
|
||||
PsiElement lastElement = elements[elements.length - 1];
|
||||
rangeInElement = new TextRange(
|
||||
elements[0].getStartOffsetInParent(),
|
||||
lastElement.getStartOffsetInParent() + lastElement.getTextLength()
|
||||
);
|
||||
}
|
||||
return rangeInElement;
|
||||
}
|
||||
|
||||
Integer fragmentStartOffsetInteger = startOffset;
|
||||
SortedMap<Integer,TextRange> map = reportedRanges.subMap(fragmentStartOffsetInteger, endOffset);
|
||||
int newFragmentSize = !map.isEmpty() ? 0:1;
|
||||
@Override
|
||||
protected PsiElement getPsi(PsiFragment node) {
|
||||
PsiElement[] elements = node.getElements();
|
||||
|
||||
Iterator<Integer> iterator = map.keySet().iterator();
|
||||
while(iterator.hasNext()) {
|
||||
Integer next = iterator.next();
|
||||
iterator.remove();
|
||||
reportedFiles.remove(next);
|
||||
reportedOffsetInOtherFiles.remove(next);
|
||||
reportedPsi.remove(next);
|
||||
newFragmentSize += fragmentSize.remove(next);
|
||||
}
|
||||
return elements.length > 1 ? elements[0].getParent() : elements[0];
|
||||
}
|
||||
|
||||
reportedRanges.put(fragmentStartOffsetInteger, rangeInElement);
|
||||
reportedFiles.put(fragmentStartOffsetInteger, file);
|
||||
reportedOffsetInOtherFiles.put(fragmentStartOffsetInteger, value);
|
||||
reportedPsi.put(fragmentStartOffsetInteger, target);
|
||||
fragmentSize.put(fragmentStartOffsetInteger, newFragmentSize);
|
||||
@Override
|
||||
protected int getStartOffset(PsiFragment node) {
|
||||
return node.getStartOffset();
|
||||
}
|
||||
|
||||
return false;
|
||||
@Override
|
||||
protected int getEndOffset(PsiFragment node) {
|
||||
return node.getEndOffset();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}, GlobalSearchScope.projectScope(psiFile.getProject()));
|
||||
if (myProcessor == null) {
|
||||
myProcessor = new OldDuplicatedCodeProcessor(virtualFile, psiFile.getProject());
|
||||
myProcessorRef.set(myProcessor);
|
||||
}
|
||||
myProcessor.process(hash, frag);
|
||||
}
|
||||
}, true).visitNode(psiFile);
|
||||
}
|
||||
|
||||
DuplicatedCodeProcessor<?> processor = myProcessorRef.get();
|
||||
|
||||
final SmartList<ProblemDescriptor> descriptors = new SmartList<ProblemDescriptor>();
|
||||
|
||||
if (processor != null) {
|
||||
|
||||
for(Map.Entry<Integer, TextRange> entry:processor.reportedRanges.entrySet()) {
|
||||
final Integer offset = entry.getKey();
|
||||
// todo 3 statements constant
|
||||
if (processor.fragmentSize.get(offset) < 3) continue;
|
||||
final VirtualFile file = processor.reportedFiles.get(offset);
|
||||
String message = "Found duplicated code in " + file.getPath();
|
||||
|
||||
PsiElement targetElement = processor.reportedPsi.get(offset);
|
||||
TextRange rangeInElement = entry.getValue();
|
||||
final int offsetInOtherFile = processor.reportedOffsetInOtherFiles.get(offset);
|
||||
|
||||
LocalQuickFix fix = createNavigateToDupeFix(file, offsetInOtherFile);
|
||||
ProblemDescriptor descriptor = manager
|
||||
.createProblemDescriptor(targetElement, rangeInElement, message, ProblemHighlightType.WEAK_WARNING, isOnTheFly, fix);
|
||||
descriptors.add(descriptor);
|
||||
}
|
||||
}, true).visitNode(psiFile);
|
||||
|
||||
for(Map.Entry<Integer, TextRange> entry:reportedRanges.entrySet()) {
|
||||
final Integer offset = entry.getKey();
|
||||
// todo 3 statements constant
|
||||
if (fragmentSize.get(offset) < 3) continue;
|
||||
final VirtualFile file = reportedFiles.get(offset);
|
||||
String message = "Found duplicated code in " + file.getPath();
|
||||
|
||||
PsiElement targetElement = reportedPsi.get(offset);
|
||||
TextRange rangeInElement = entry.getValue();
|
||||
final int offsetInOtherFile = reportedOffsetInOtherFiles.get(offset);
|
||||
|
||||
LocalQuickFix fix = createNavigateToDupeFix(file, offsetInOtherFile);
|
||||
ProblemDescriptor descriptor = manager
|
||||
.createProblemDescriptor(targetElement, rangeInElement, message, ProblemHighlightType.WEAK_WARNING, isOnTheFly, fix);
|
||||
descriptors.add(descriptor);
|
||||
}
|
||||
|
||||
return descriptors.isEmpty() ? null : descriptors.toArray(new ProblemDescriptor[descriptors.size()]);
|
||||
@@ -131,4 +169,75 @@ public class DuplicatesInspectionBase extends LocalInspectionTool {
|
||||
protected LocalQuickFix createNavigateToDupeFix(@NotNull VirtualFile file, int offsetInOtherFile) {
|
||||
return null;
|
||||
}
|
||||
|
||||
static abstract class DuplicatedCodeProcessor<T> implements FileBasedIndex.ValueProcessor<TIntArrayList> {
|
||||
final TreeMap<Integer, TextRange> reportedRanges = new TreeMap<Integer, TextRange>();
|
||||
final TIntObjectHashMap<VirtualFile> reportedFiles = new TIntObjectHashMap<VirtualFile>();
|
||||
final TIntObjectHashMap<PsiElement> reportedPsi = new TIntObjectHashMap<PsiElement>();
|
||||
final TIntIntHashMap reportedOffsetInOtherFiles = new TIntIntHashMap();
|
||||
final TIntIntHashMap fragmentSize = new TIntIntHashMap();
|
||||
final VirtualFile virtualFile;
|
||||
final Project project;
|
||||
final ProjectFileIndex myProjectFileIndex;
|
||||
T myNode;
|
||||
|
||||
DuplicatedCodeProcessor(VirtualFile file, Project project) {
|
||||
virtualFile = file;
|
||||
this.project = project;
|
||||
myProjectFileIndex = ProjectFileIndex.SERVICE.getInstance(project);
|
||||
}
|
||||
|
||||
void process(int hash, T node) {
|
||||
ProgressManager.checkCanceled();
|
||||
myNode = node;
|
||||
FileBasedIndex.getInstance().processValues(DuplicatesIndex.NAME, hash, null, this, GlobalSearchScope.projectScope(project));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(VirtualFile file, TIntArrayList list) {
|
||||
for(int i = 0, len = list.size(); i < len; ++i) {
|
||||
ProgressManager.checkCanceled();
|
||||
|
||||
int value = list.getQuick(i);
|
||||
|
||||
if (myProjectFileIndex.isInSource(virtualFile) && !myProjectFileIndex.isInSource(file)) return true;
|
||||
if (!myProjectFileIndex.isInSource(virtualFile) && myProjectFileIndex.isInSource(file)) return true;
|
||||
final int startOffset = getStartOffset(myNode);
|
||||
final int endOffset = getEndOffset(myNode);
|
||||
if (file.equals(virtualFile) && value >= startOffset && value < endOffset) continue;
|
||||
|
||||
PsiElement target = getPsi(myNode);
|
||||
TextRange rangeInElement = getRangeInElement(myNode);
|
||||
|
||||
Integer fragmentStartOffsetInteger = startOffset;
|
||||
SortedMap<Integer,TextRange> map = reportedRanges.subMap(fragmentStartOffsetInteger, endOffset);
|
||||
int newFragmentSize = !map.isEmpty() ? 0:1;
|
||||
|
||||
Iterator<Integer> iterator = map.keySet().iterator();
|
||||
while(iterator.hasNext()) {
|
||||
Integer next = iterator.next();
|
||||
iterator.remove();
|
||||
reportedFiles.remove(next);
|
||||
reportedOffsetInOtherFiles.remove(next);
|
||||
reportedPsi.remove(next);
|
||||
newFragmentSize += fragmentSize.remove(next);
|
||||
}
|
||||
|
||||
reportedRanges.put(fragmentStartOffsetInteger, rangeInElement);
|
||||
reportedFiles.put(fragmentStartOffsetInteger, file);
|
||||
reportedOffsetInOtherFiles.put(fragmentStartOffsetInteger, value);
|
||||
reportedPsi.put(fragmentStartOffsetInteger, target);
|
||||
fragmentSize.put(fragmentStartOffsetInteger, newFragmentSize);
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected abstract TextRange getRangeInElement(T node);
|
||||
protected abstract PsiElement getPsi(T node);
|
||||
|
||||
protected abstract int getStartOffset(T node);
|
||||
protected abstract int getEndOffset(T node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.dvcs.push;
|
||||
|
||||
import com.intellij.openapi.components.PersistentStateComponent;
|
||||
import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.openapi.components.StoragePathMacros;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.xmlb.annotations.AbstractCollection;
|
||||
import com.intellij.util.xmlb.annotations.Tag;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@State(name = "Push.Settings", storages = {@Storage(file = StoragePathMacros.WORKSPACE_FILE)})
|
||||
public class PushSettings implements PersistentStateComponent<PushSettings.State> {
|
||||
|
||||
private State myState = new State();
|
||||
|
||||
public static class State {
|
||||
@Tag("excluded-roots")
|
||||
@AbstractCollection(surroundWithTag = false, elementTag = "path")
|
||||
public Set<String> EXCLUDED_ROOTS = ContainerUtil.newHashSet();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public State getState() {
|
||||
return myState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadState(State state) {
|
||||
myState = state;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Set<String> getExcludedRepoRoots() {
|
||||
return myState.EXCLUDED_ROOTS;
|
||||
}
|
||||
|
||||
public void saveExcludedRepoRoots(@NotNull Set<String> roots) {
|
||||
myState.EXCLUDED_ROOTS = roots;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,5 +8,8 @@
|
||||
<!--<keyboard-shortcut first-keystroke="control shift K" keymap="$default"/>-->
|
||||
<!--</action>-->
|
||||
</actions>
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<projectService serviceImplementation="com.intellij.dvcs.push.PushSettings"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ package com.intellij.dvcs.push;
|
||||
import com.intellij.dvcs.DvcsUtil;
|
||||
import com.intellij.dvcs.push.ui.*;
|
||||
import com.intellij.dvcs.repo.Repository;
|
||||
import com.intellij.dvcs.repo.RepositoryManager;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.Task;
|
||||
@@ -53,6 +53,8 @@ public class PushController implements Disposable {
|
||||
@NotNull private final List<PushSupport<?, ?, ?>> myPushSupports;
|
||||
@NotNull private final PushLog myPushLog;
|
||||
@NotNull private final VcsPushDialog myDialog;
|
||||
@NotNull private final PushSettings myExcludedSettings;
|
||||
@NotNull private final Set<String> myExcludedRepositoryRoots;
|
||||
private boolean mySingleRepoProject;
|
||||
private static final int DEFAULT_CHILDREN_PRESENTATION_NUMBER = 20;
|
||||
private final ExecutorService myExecutorService = Executors.newSingleThreadExecutor();
|
||||
@@ -64,6 +66,8 @@ public class PushController implements Disposable {
|
||||
@NotNull VcsPushDialog dialog,
|
||||
@NotNull List<? extends Repository> preselectedRepositories) {
|
||||
myProject = project;
|
||||
myExcludedSettings = ServiceManager.getService(project, PushSettings.class);
|
||||
myExcludedRepositoryRoots = ContainerUtil.newHashSet(myExcludedSettings.getExcludedRepoRoots());
|
||||
myPreselectedRepositories = preselectedRepositories;
|
||||
myPushSupports = getAffectedSupports(myProject);
|
||||
mySingleRepoProject = isSingleRepoProject(myPushSupports);
|
||||
@@ -125,7 +129,8 @@ public class PushController implements Disposable {
|
||||
Map<RepositoryNode, MyRepoModel> others = ContainerUtil.newLinkedHashMap();
|
||||
for (Map.Entry<RepositoryNode, MyRepoModel> entry : myView2Model.entrySet()) {
|
||||
MyRepoModel model = entry.getValue();
|
||||
if (myPreselectedRepositories.contains(model.getRepository())) {
|
||||
Repository repository = model.getRepository();
|
||||
if (preselectByUser(repository)) {
|
||||
priorityLoading.put(entry.getKey(), model);
|
||||
}
|
||||
else if (model.getSupport().shouldRequestIncomingChangesForNotCheckedRepositories()) {
|
||||
@@ -188,6 +193,7 @@ public class PushController implements Disposable {
|
||||
@Override
|
||||
public void onTargetChanged(T newTarget) {
|
||||
repoNode.setChecked(true);
|
||||
myExcludedRepositoryRoots.remove(model.getRepository().getRoot().getPath());
|
||||
model.setTarget(newTarget);
|
||||
model.clearErrors();
|
||||
loadCommits(model, repoNode, false);
|
||||
@@ -196,9 +202,14 @@ public class PushController implements Disposable {
|
||||
@Override
|
||||
public void onSelectionChanged(boolean isSelected) {
|
||||
myDialog.enableOkActions(isPushAllowed());
|
||||
if (isSelected && !model.hasCommitInfo() && !model.getSupport().shouldRequestIncomingChangesForNotCheckedRepositories()) {
|
||||
//download incoming if was not loaded before and marked as selected
|
||||
loadCommits(model, repoNode, false);
|
||||
if (isSelected) {
|
||||
boolean forceLoad = myExcludedRepositoryRoots.remove(model.getRepository().getRoot().getPath());
|
||||
if (!model.hasCommitInfo() && (forceLoad || !model.getSupport().shouldRequestIncomingChangesForNotCheckedRepositories())) {
|
||||
loadCommits(model, repoNode, false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
myExcludedRepositoryRoots.add(model.getRepository().getRoot().getPath());
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -235,9 +246,54 @@ public class PushController implements Disposable {
|
||||
|
||||
public boolean isPushAllowed() {
|
||||
JTree tree = myPushLog.getTree();
|
||||
return !tree.isEditing() && !getSelectedRepoNode().isEmpty() && allNodesAreLoaded(myView2Model.keySet());
|
||||
return !tree.isEditing() &&
|
||||
ContainerUtil.exists(myPushSupports, new Condition<PushSupport<?, ?, ?>>() {
|
||||
@Override
|
||||
public boolean value(PushSupport<?, ?, ?> support) {
|
||||
return isPushAllowed(support);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isPushAllowed(@NotNull PushSupport<?, ?, ?> pushSupport) {
|
||||
Collection<RepositoryNode> nodes = getNodesForSupport(pushSupport);
|
||||
if (pushSupport.getRepositoryManager().isSyncEnabled()) {
|
||||
return hasSomethingToPush(nodes) || (hasCheckedNode(nodes) && allNodesAreLoaded(nodes));
|
||||
}
|
||||
return hasSomethingToPush(nodes) || (hasCheckedNode(nodes));
|
||||
}
|
||||
|
||||
private boolean hasSomethingToPush(Collection<RepositoryNode> nodes) {
|
||||
return ContainerUtil.exists(nodes, new Condition<RepositoryNode>() {
|
||||
@Override
|
||||
public boolean value(@NotNull RepositoryNode node) {
|
||||
PushTarget target = myView2Model.get(node).getTarget();
|
||||
//if node is selected target should not be null
|
||||
return (node.isChecked() || node.isLoading()) && target != null && target.hasSomethingToPush();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean hasCheckedNode(@NotNull Collection<RepositoryNode> nodes) {
|
||||
return ContainerUtil.exists(nodes, new Condition<RepositoryNode>() {
|
||||
@Override
|
||||
public boolean value(@NotNull RepositoryNode node) {
|
||||
return node.isChecked();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Collection<RepositoryNode> getNodesForSupport(final PushSupport<?, ?, ?> support) {
|
||||
return ContainerUtil.mapNotNull(myView2Model.entrySet(), new Function<Map.Entry<RepositoryNode, MyRepoModel>, RepositoryNode>() {
|
||||
@Override
|
||||
public RepositoryNode fun(Map.Entry<RepositoryNode, MyRepoModel> entry) {
|
||||
return support.equals(entry.getValue().getSupport()) ? entry.getKey() : null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private static boolean allNodesAreLoaded(@NotNull Collection<RepositoryNode> nodes) {
|
||||
return !ContainerUtil.exists(nodes, new Condition<RepositoryNode>() {
|
||||
@Override
|
||||
@@ -294,8 +350,8 @@ public class PushController implements Disposable {
|
||||
}
|
||||
else {
|
||||
List<? extends VcsFullCommitDetails> commits = outgoing.getCommits();
|
||||
shouldBeSelected = shouldSelectInitially(target, repository, model.getSupport().getRepositoryManager(), !commits.isEmpty());
|
||||
model.setLoadedCommits(commits);
|
||||
shouldBeSelected = shouldSelect(model);
|
||||
myPushLog.setChildren(node,
|
||||
getPresentationForCommits(PushController.this.myProject, model.getLoadedCommits(),
|
||||
model.getNumberOfShownCommits()));
|
||||
@@ -315,19 +371,24 @@ public class PushController implements Disposable {
|
||||
node.startLoading(myPushLog.getTree(), myExecutorService.submit(task, result));
|
||||
}
|
||||
|
||||
private boolean shouldSelectInitially(@NotNull PushTarget target, @NotNull Repository repository,
|
||||
@NotNull RepositoryManager repositoryManager, boolean hasCommits) {
|
||||
boolean shouldBeSelected;
|
||||
if (mySingleRepoProject) {
|
||||
shouldBeSelected = true;
|
||||
}
|
||||
else if (repositoryManager.isSyncEnabled()) {
|
||||
shouldBeSelected = hasCommits || target.hasSomethingToPush();
|
||||
}
|
||||
else {
|
||||
shouldBeSelected = (hasCommits || target.hasSomethingToPush()) && myPreselectedRepositories.contains(repository);
|
||||
}
|
||||
return shouldBeSelected;
|
||||
private boolean shouldSelect(@NotNull MyRepoModel model) {
|
||||
if (mySingleRepoProject) return true;
|
||||
Repository repository = model.getRepository();
|
||||
return hasCommitsToPush(model) && (preselectByUser(repository) || notExcludedByUser(repository));
|
||||
}
|
||||
|
||||
private boolean notExcludedByUser(@NotNull Repository repository) {
|
||||
return !myExcludedRepositoryRoots.contains(repository.getRoot().getPath());
|
||||
}
|
||||
|
||||
private boolean preselectByUser(@NotNull Repository repository) {
|
||||
return myPreselectedRepositories.contains(repository);
|
||||
}
|
||||
|
||||
private static boolean hasCommitsToPush(@NotNull MyRepoModel model) {
|
||||
PushTarget target = model.getTarget();
|
||||
assert target != null;
|
||||
return (!model.getLoadedCommits().isEmpty() || target.hasSomethingToPush());
|
||||
}
|
||||
|
||||
public PushLog getPushPanelLog() {
|
||||
@@ -338,6 +399,7 @@ public class PushController implements Disposable {
|
||||
Task.Backgroundable task = new Task.Backgroundable(myProject, "Pushing...", false) {
|
||||
@Override
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
myExcludedSettings.saveExcludedRepoRoots(myExcludedRepositoryRoots);
|
||||
for (PushSupport support : myPushSupports) {
|
||||
doPush(support, force);
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ class LoadingIcon extends JBImageIcon {
|
||||
}
|
||||
|
||||
public boolean imageUpdate(Image img, int flags, int x, int y, int w, int h) {
|
||||
if (myNode instanceof RepositoryNode && !((RepositoryNode)myNode).isLoading()) return false;
|
||||
if ((flags & (FRAMEBITS | ALLBITS)) != 0) {
|
||||
TreeNode[] pathToRoot = myModel.getPathToRoot(myNode);
|
||||
if (pathToRoot != null) {
|
||||
|
||||
@@ -155,6 +155,7 @@ public class RepositoryNode extends CheckedTreeNode implements EditableTreeNode,
|
||||
|
||||
public void stopLoading() {
|
||||
myLoading.set(false);
|
||||
myLoadingIcon.setImageObserver(null);
|
||||
}
|
||||
|
||||
public boolean isLoading() {
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.ui.OptionAction;
|
||||
import com.intellij.openapi.ui.ValidationInfo;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -81,6 +82,18 @@ public class VcsPushDialog extends DialogWrapper {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected ValidationInfo doValidate() {
|
||||
enableOkActions(myController.isPushAllowed());
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean postponeValidation() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected Action[] createActions() {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.intellij.ide.ui;
|
||||
|
||||
import com.intellij.ide.IdeBundle;
|
||||
import com.intellij.ide.WelcomeWizardUtil;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
@@ -128,6 +129,9 @@ public class UISettings extends SimpleModificationTracker implements PersistentS
|
||||
public UISettings() {
|
||||
tweakPlatformDefaults();
|
||||
setSystemFontFaceAndSize();
|
||||
|
||||
Boolean scrollToSource = WelcomeWizardUtil.getAutoScrollToSource();
|
||||
if (scrollToSource != null) DEFAULT_AUTOSCROLL_TO_SOURCE = scrollToSource;
|
||||
}
|
||||
|
||||
private void tweakPlatformDefaults() {
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
+1
@@ -164,6 +164,7 @@ public class StdArrangementTokens {
|
||||
@NotNull public static final ArrangementSettingsToken PROPERTY = invertible("PROPERTY", StdArrangementTokenType.ENTRY_TYPE);
|
||||
@NotNull public static final ArrangementSettingsToken EVENT_HANDLER = invertible("EVENT_HANDLER", StdArrangementTokenType.ENTRY_TYPE);
|
||||
@NotNull public static final ArrangementSettingsToken STATIC_INIT = invertible("STATIC_INIT", StdArrangementTokenType.ENTRY_TYPE);
|
||||
@NotNull public static final ArrangementSettingsToken INIT_BLOCK = invertible("INITIALIZER BLOCK", StdArrangementTokenType.ENTRY_TYPE);
|
||||
@NotNull public static final ArrangementSettingsToken NAMESPACE = invertible("NAMESPACE", StdArrangementTokenType.ENTRY_TYPE);
|
||||
@NotNull public static final ArrangementSettingsToken TRAIT = invertible("TRAIT", StdArrangementTokenType.ENTRY_TYPE);
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ public class Descriptor {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.ex.Descriptor");
|
||||
private final ScopeToolState myState;
|
||||
private final InspectionProfileImpl myInspectionProfile;
|
||||
private final String myScopeName;
|
||||
|
||||
public Descriptor(@NotNull ScopeToolState state, @NotNull InspectionProfileImpl inspectionProfile, @NotNull Project project) {
|
||||
myState = state;
|
||||
@@ -54,6 +55,7 @@ public class Descriptor {
|
||||
final String[] groupPath = tool.getGroupPath();
|
||||
myGroup = groupPath.length == 0 ? new String[]{InspectionProfileEntry.GENERAL_GROUP_NAME} : groupPath;
|
||||
myKey = HighlightDisplayKey.find(tool.getShortName());
|
||||
myScopeName = state.getScopeName();
|
||||
myScope = state.getScope(project);
|
||||
myLevel = inspectionProfile.getErrorLevel(myKey, myScope, project);
|
||||
myEnabled = inspectionProfile.isToolEnabled(myKey, myScope, project);
|
||||
@@ -137,6 +139,11 @@ public class Descriptor {
|
||||
return myGroup;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getScopeName() {
|
||||
return myScopeName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public NamedScope getScope() {
|
||||
return myScope;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user