Merge remote-tracking branch 'origin/master'

This commit is contained in:
Roman Shevchenko
2016-12-05 13:40:29 +01:00
37 changed files with 851 additions and 348 deletions
@@ -191,19 +191,19 @@ class _RegExLexer implements FlexLexer {
"\1\17\1\20\1\21\1\22\1\2\1\23\1\24\1\25"+
"\1\26\1\27\1\30\1\31\1\30\1\32\1\33\1\34"+
"\1\10\1\35\1\2\1\36\1\37\1\23\1\40\1\41"+
"\1\42\1\43\1\44\1\45\1\1\1\46\1\47\2\50"+
"\1\51\1\52\1\0\1\53\1\54\1\55\1\56\1\57"+
"\1\60\1\10\1\61\1\62\1\63\1\64\1\10\1\64"+
"\1\65\2\66\1\67\1\70\1\71\1\72\1\73\1\74"+
"\1\75\1\10\1\76\1\77\1\100\1\0\1\101\1\102"+
"\1\103\1\104\1\0\1\105\1\106\1\107\1\110\1\111"+
"\1\0\1\112\1\0\1\113\1\0\1\114\1\115\1\116"+
"\2\72\1\117\1\120\1\121\1\122\1\123\1\124\1\125"+
"\1\126\1\0\1\113\1\0\1\127\2\72\1\117\1\130"+
"\1\113\1\72\1\117\1\72\1\131\4\72";
"\1\42\1\43\1\44\1\45\1\1\1\26\1\46\1\47"+
"\2\50\1\51\1\52\1\0\1\53\1\54\1\55\1\56"+
"\1\57\1\60\1\10\1\61\1\62\1\63\1\64\1\10"+
"\1\64\1\65\2\66\1\67\1\70\1\71\1\72\1\73"+
"\1\74\1\75\1\10\1\76\1\77\1\100\1\0\1\101"+
"\1\102\1\103\1\104\1\0\1\105\1\106\1\107\1\110"+
"\1\111\1\0\1\112\1\0\1\113\1\0\1\114\1\115"+
"\1\116\2\72\1\117\1\120\1\121\1\122\1\123\1\124"+
"\1\125\1\126\1\0\1\113\1\0\1\127\2\72\1\117"+
"\1\130\1\113\1\72\1\117\1\72\1\131\4\72";
private static int [] zzUnpackAction() {
int [] result = new int[138];
int [] result = new int[139];
int offset = 0;
offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result);
return result;
@@ -235,20 +235,20 @@ class _RegExLexer implements FlexLexer {
"\0\u03e0\0\u04d8\0\u03e0\0\u0516\0\u03e0\0\u0554\0\u03e0\0\u03e0"+
"\0\u03e0\0\u0592\0\u03e0\0\u05d0\0\u03e0\0\u060e\0\u03e0\0\u064c"+
"\0\u03e0\0\u03e0\0\u045c\0\u03e0\0\u03e0\0\u03e0\0\u068a\0\u06c8"+
"\0\u03e0\0\u03e0\0\u03e0\0\u03e0\0\u03e0\0\u0706\0\u0744\0\u03e0"+
"\0\u0782\0\u07c0\0\u03e0\0\u07fe\0\u083c\0\u03e0\0\u03e0\0\u03e0"+
"\0\u03e0\0\u03e0\0\u03e0\0\u087a\0\u08b8\0\u03e0\0\u03e0\0\u08f6"+
"\0\u03e0\0\u03e0\0\u03e0\0\u03e0\0\u0934\0\u0972\0\u03e0\0\u03e0"+
"\0\u09b0\0\u03e0\0\u03e0\0\u03e0\0\u09ee\0\u03e0\0\u03e0\0\u03e0"+
"\0\u03e0\0\u0a2c\0\u0a6a\0\u03e0\0\u03e0\0\u03e0\0\u03e0\0\u0aa8"+
"\0\u03e0\0\u0ae6\0\u0b24\0\u0b62\0\u03e0\0\u03e0\0\u03e0\0\u0ba0"+
"\0\u0bde\0\u0c1c\0\u03e0\0\u03e0\0\u03e0\0\u03e0\0\u03e0\0\u03e0"+
"\0\u03e0\0\u0c5a\0\u0c98\0\u0cd6\0\u03e0\0\u0d14\0\u03e0\0\u0d52"+
"\0\u03e0\0\u03e0\0\u0d90\0\u0dce\0\u0e0c\0\u03e0\0\u0e4a\0\u0e88"+
"\0\u0ec6\0\u0f04";
"\0\u03e0\0\u03e0\0\u0706\0\u03e0\0\u03e0\0\u03e0\0\u0744\0\u0782"+
"\0\u03e0\0\u07c0\0\u07fe\0\u03e0\0\u083c\0\u087a\0\u03e0\0\u03e0"+
"\0\u03e0\0\u03e0\0\u03e0\0\u03e0\0\u08b8\0\u08f6\0\u03e0\0\u03e0"+
"\0\u0934\0\u03e0\0\u03e0\0\u03e0\0\u03e0\0\u0972\0\u09b0\0\u03e0"+
"\0\u03e0\0\u09ee\0\u03e0\0\u03e0\0\u03e0\0\u0a2c\0\u03e0\0\u03e0"+
"\0\u03e0\0\u03e0\0\u0a6a\0\u0aa8\0\u03e0\0\u03e0\0\u03e0\0\u03e0"+
"\0\u0ae6\0\u03e0\0\u0b24\0\u0b62\0\u0ba0\0\u03e0\0\u03e0\0\u03e0"+
"\0\u0bde\0\u0c1c\0\u0c5a\0\u03e0\0\u03e0\0\u03e0\0\u03e0\0\u03e0"+
"\0\u03e0\0\u03e0\0\u0c98\0\u0cd6\0\u0d14\0\u03e0\0\u0d52\0\u03e0"+
"\0\u0d90\0\u03e0\0\u03e0\0\u0dce\0\u0e0c\0\u0e4a\0\u03e0\0\u0e88"+
"\0\u0ec6\0\u0f04\0\u0f42";
private static int [] zzUnpackRowMap() {
int [] result = new int[138];
int [] result = new int[139];
int offset = 0;
offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result);
return result;
@@ -287,66 +287,67 @@ class _RegExLexer implements FlexLexer {
"\1\64\7\43\1\64\4\43\1\64\20\43\1\62\1\43"+
"\1\63\1\43\1\27\63\43\5\65\1\66\5\65\1\67"+
"\1\65\1\70\10\65\26\67\1\65\3\67\3\65\1\67"+
"\4\65\1\71\5\65\65\13\1\72\10\13\13\65\1\46"+
"\12\65\26\46\1\65\3\46\3\65\1\46\6\65\1\73"+
"\16\65\1\46\12\65\26\46\1\65\3\46\1\65\1\74"+
"\1\65\1\46\17\65\1\66\5\65\1\46\12\65\26\46"+
"\1\65\3\46\3\65\1\46\13\65\2\44\2\65\1\66"+
"\5\65\1\46\3\65\1\44\6\65\26\46\1\44\3\46"+
"\3\65\1\46\12\65\13\75\1\46\4\75\1\32\5\75"+
"\26\46\1\75\3\46\3\75\1\46\4\75\1\76\5\75"+
"\120\0\1\77\64\0\1\100\1\101\5\0\1\102\55\0"+
"\1\103\1\104\1\105\4\106\1\103\1\107\1\103\1\107"+
"\1\110\1\103\1\111\1\112\1\103\1\107\5\106\2\113"+
"\1\114\1\115\3\116\3\117\1\120\2\121\2\122\3\123"+
"\1\110\1\124\1\125\1\126\1\104\1\127\1\130\1\131"+
"\2\103\1\112\1\110\1\103\1\112\1\103\1\112\6\103"+
"\47\0\1\132\27\0\2\44\14\0\1\44\34\0\1\44"+
"\4\65\1\71\5\65\65\13\1\72\10\13\13\65\1\73"+
"\12\65\26\73\1\65\3\73\3\65\1\73\6\65\1\74"+
"\16\65\1\73\12\65\26\73\1\65\3\73\1\65\1\75"+
"\1\65\1\73\17\65\1\66\5\65\1\73\12\65\26\73"+
"\1\65\3\73\3\65\1\73\13\65\2\44\2\65\1\66"+
"\5\65\1\73\3\65\1\44\6\65\26\73\1\44\3\73"+
"\3\65\1\73\12\65\13\76\1\46\4\76\1\32\5\76"+
"\26\46\1\76\3\46\3\76\1\46\4\76\1\77\5\76"+
"\120\0\1\100\64\0\1\101\1\102\5\0\1\103\55\0"+
"\1\104\1\105\1\106\4\107\1\104\1\110\1\104\1\110"+
"\1\111\1\104\1\112\1\113\1\104\1\110\5\107\2\114"+
"\1\115\1\116\3\117\3\120\1\121\2\122\2\123\3\124"+
"\1\111\1\125\1\126\1\127\1\105\1\130\1\131\1\132"+
"\2\104\1\113\1\111\1\104\1\113\1\104\1\113\6\104"+
"\47\0\1\133\27\0\2\44\14\0\1\44\34\0\1\44"+
"\22\0\2\46\1\0\2\46\5\0\5\46\6\0\32\46"+
"\3\0\1\46\23\0\1\133\75\0\1\134\1\135\5\0"+
"\1\102\47\0\1\136\5\0\1\103\1\104\1\105\4\106"+
"\1\103\3\107\1\110\1\103\1\111\1\112\1\103\1\107"+
"\5\106\2\113\1\114\1\115\3\116\3\117\1\120\2\121"+
"\2\122\3\123\1\110\1\124\1\125\1\126\1\104\1\127"+
"\1\130\1\131\2\103\1\112\1\110\1\103\1\112\1\103"+
"\1\112\6\103\71\0\1\137\17\0\1\67\12\0\26\67"+
"\3\0\1\46\23\0\1\134\75\0\1\135\1\136\5\0"+
"\1\103\47\0\1\137\5\0\1\104\1\105\1\106\4\107"+
"\1\104\3\110\1\111\1\104\1\112\1\113\1\104\1\110"+
"\5\107\2\114\1\115\1\116\3\117\3\120\1\121\2\122"+
"\2\123\3\124\1\111\1\125\1\126\1\127\1\105\1\130"+
"\1\131\1\132\2\104\1\113\1\111\1\104\1\113\1\104"+
"\1\113\6\104\71\0\1\140\17\0\1\67\12\0\26\67"+
"\1\0\3\67\3\0\1\67\25\0\1\70\12\0\26\70"+
"\1\0\3\70\3\0\1\70\23\0\1\140\70\0\1\141"+
"\37\0\1\142\13\0\1\143\1\144\6\0\1\145\1\0"+
"\1\145\1\146\1\147\1\150\11\0\1\100\75\0\1\151"+
"\1\152\64\0\2\104\51\0\1\104\23\0\1\153\51\0"+
"\1\153\27\0\1\154\147\0\1\155\1\156\14\0\76\157"+
"\1\0\2\160\3\0\1\161\20\0\2\160\1\0\1\160"+
"\4\0\3\160\5\0\2\160\3\0\1\160\22\0\2\162"+
"\24\0\2\162\1\0\1\162\4\0\3\162\5\0\2\162"+
"\3\0\1\162\101\0\1\163\1\164\25\0\1\165\144\0"+
"\1\166\12\0\1\167\75\0\1\170\1\171\1\0\5\172"+
"\1\0\70\172\11\0\1\151\66\0\1\173\51\0\1\173"+
"\52\0\1\174\45\0\2\175\24\0\2\175\1\0\1\175"+
"\4\0\3\175\5\0\2\175\3\0\1\175\22\0\2\176"+
"\4\0\1\177\17\0\2\176\1\0\1\176\4\0\3\176"+
"\5\0\2\176\3\0\1\176\22\0\2\200\24\0\2\200"+
"\1\0\1\200\4\0\3\200\5\0\2\200\3\0\1\200"+
"\21\0\5\172\1\201\70\172\2\0\1\202\51\0\1\202"+
"\30\0\1\116\67\0\2\203\4\0\1\175\17\0\2\203"+
"\1\0\1\203\4\0\3\203\5\0\2\203\3\0\1\203"+
"\22\0\2\204\24\0\2\204\1\0\1\204\4\0\3\204"+
"\5\0\2\204\3\0\1\204\22\0\2\205\4\0\1\175"+
"\17\0\2\205\1\0\1\205\4\0\3\205\5\0\2\205"+
"\3\0\1\205\22\0\2\206\24\0\2\206\1\0\1\206"+
"\4\0\3\206\5\0\2\206\3\0\1\206\22\0\2\207"+
"\4\0\1\175\17\0\2\207\1\0\1\207\4\0\3\207"+
"\5\0\2\207\3\0\1\207\22\0\2\210\4\0\1\175"+
"\17\0\2\210\1\0\1\210\4\0\3\210\5\0\2\210"+
"\3\0\1\210\22\0\2\211\4\0\1\175\17\0\2\211"+
"\1\0\1\211\4\0\3\211\5\0\2\211\3\0\1\211"+
"\22\0\2\212\4\0\1\175\17\0\2\212\1\0\1\212"+
"\4\0\3\212\5\0\2\212\3\0\1\212\22\0\2\212"+
"\4\0\1\177\17\0\2\212\1\0\1\212\4\0\3\212"+
"\5\0\2\212\3\0\1\212\21\0";
"\1\0\3\70\3\0\1\70\13\0\2\73\10\0\5\73"+
"\6\0\32\73\3\0\1\73\23\0\1\141\70\0\1\142"+
"\37\0\1\143\13\0\1\144\1\145\6\0\1\146\1\0"+
"\1\146\1\147\1\150\1\151\11\0\1\101\75\0\1\152"+
"\1\153\64\0\2\105\51\0\1\105\23\0\1\154\51\0"+
"\1\154\27\0\1\155\147\0\1\156\1\157\14\0\76\160"+
"\1\0\2\161\3\0\1\162\20\0\2\161\1\0\1\161"+
"\4\0\3\161\5\0\2\161\3\0\1\161\22\0\2\163"+
"\24\0\2\163\1\0\1\163\4\0\3\163\5\0\2\163"+
"\3\0\1\163\101\0\1\164\1\165\25\0\1\166\144\0"+
"\1\167\12\0\1\170\75\0\1\171\1\172\1\0\5\173"+
"\1\0\70\173\11\0\1\152\66\0\1\174\51\0\1\174"+
"\52\0\1\175\45\0\2\176\24\0\2\176\1\0\1\176"+
"\4\0\3\176\5\0\2\176\3\0\1\176\22\0\2\177"+
"\4\0\1\200\17\0\2\177\1\0\1\177\4\0\3\177"+
"\5\0\2\177\3\0\1\177\22\0\2\201\24\0\2\201"+
"\1\0\1\201\4\0\3\201\5\0\2\201\3\0\1\201"+
"\21\0\5\173\1\202\70\173\2\0\1\203\51\0\1\203"+
"\30\0\1\117\67\0\2\204\4\0\1\176\17\0\2\204"+
"\1\0\1\204\4\0\3\204\5\0\2\204\3\0\1\204"+
"\22\0\2\205\24\0\2\205\1\0\1\205\4\0\3\205"+
"\5\0\2\205\3\0\1\205\22\0\2\206\4\0\1\176"+
"\17\0\2\206\1\0\1\206\4\0\3\206\5\0\2\206"+
"\3\0\1\206\22\0\2\207\24\0\2\207\1\0\1\207"+
"\4\0\3\207\5\0\2\207\3\0\1\207\22\0\2\210"+
"\4\0\1\176\17\0\2\210\1\0\1\210\4\0\3\210"+
"\5\0\2\210\3\0\1\210\22\0\2\211\4\0\1\176"+
"\17\0\2\211\1\0\1\211\4\0\3\211\5\0\2\211"+
"\3\0\1\211\22\0\2\212\4\0\1\176\17\0\2\212"+
"\1\0\1\212\4\0\3\212\5\0\2\212\3\0\1\212"+
"\22\0\2\213\4\0\1\176\17\0\2\213\1\0\1\213"+
"\4\0\3\213\5\0\2\213\3\0\1\213\22\0\2\213"+
"\4\0\1\200\17\0\2\213\1\0\1\213\4\0\3\213"+
"\5\0\2\213\3\0\1\213\21\0";
private static int [] zzUnpackTrans() {
int [] result = new int[3906];
int [] result = new int[3968];
int offset = 0;
offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result);
return result;
@@ -387,15 +388,15 @@ class _RegExLexer implements FlexLexer {
"\12\0\1\1\5\0\2\11\1\1\2\11\2\1\12\11"+
"\1\1\1\11\1\1\1\11\1\1\3\11\1\1\1\11"+
"\1\1\1\11\1\1\1\11\1\1\2\11\1\1\3\11"+
"\2\1\5\11\2\1\1\11\1\0\1\1\1\11\2\1"+
"\6\11\2\1\2\11\1\1\4\11\2\1\2\11\1\1"+
"\3\11\1\0\4\11\1\0\1\1\4\11\1\0\1\11"+
"\1\0\1\1\1\0\3\11\3\1\7\11\1\0\1\1"+
"\1\0\1\11\1\1\1\11\1\1\2\11\3\1\1\11"+
"\4\1";
"\2\1\2\11\1\1\3\11\2\1\1\11\1\0\1\1"+
"\1\11\2\1\6\11\2\1\2\11\1\1\4\11\2\1"+
"\2\11\1\1\3\11\1\0\4\11\1\0\1\1\4\11"+
"\1\0\1\11\1\0\1\1\1\0\3\11\3\1\7\11"+
"\1\0\1\1\1\0\1\11\1\1\1\11\1\1\2\11"+
"\3\1\1\11\4\1";
private static int [] zzUnpackAttribute() {
int [] result = new int[138];
int [] result = new int[139];
int offset = 0;
offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result);
return result;
@@ -217,7 +217,8 @@ public class RegExpParser implements PsiParser {
// DEFLIST
if (parseClassIntersection(builder)) {
while (RegExpTT.CHARACTERS2.contains(builder.getTokenType()) ||
while (RegExpTT.CHARACTERS.contains(builder.getTokenType()) ||
builder.getTokenType() == RegExpTT.CHAR_CLASS ||
builder.getTokenType() == RegExpTT.CLASS_BEGIN ||
builder.getTokenType() == RegExpTT.PROPERTY ||
builder.getTokenType() == RegExpTT.BRACKET_EXPRESSION_BEGIN) {
@@ -257,9 +258,14 @@ public class RegExpParser implements PsiParser {
else if (token == RegExpTT.BRACKET_EXPRESSION_BEGIN) {
parseBracketExpression(builder);
}
else if (RegExpTT.CHARACTERS2.contains(token)) {
else if (RegExpTT.CHARACTERS.contains(token)) {
parseSimpleClassdef(builder);
}
else if (token == RegExpTT.CHAR_CLASS) {
final PsiBuilder.Marker m = builder.mark();
builder.advanceLexer();
m.done(RegExpElementTypes.SIMPLE_CLASS);
}
else if (token == RegExpTT.PROPERTY) {
parseProperty(builder);
}
@@ -291,7 +297,7 @@ public class RegExpParser implements PsiParser {
}
private void parseSimpleClassdef(PsiBuilder builder) {
assert RegExpTT.CHARACTERS2.contains(builder.getTokenType());
assert RegExpTT.CHARACTERS.contains(builder.getTokenType());
final PsiBuilder.Marker marker = builder.mark();
makeChar(builder);
@@ -301,7 +307,7 @@ public class RegExpParser implements PsiParser {
builder.advanceLexer();
final IElementType t = builder.getTokenType();
if (RegExpTT.CHARACTERS2.contains(t)) {
if (RegExpTT.CHARACTERS.contains(t) || t == RegExpTT.CHAR_CLASS) {
m.drop();
makeChar(builder);
marker.done(RegExpElementTypes.CHAR_RANGE);
@@ -449,7 +455,7 @@ public class RegExpParser implements PsiParser {
marker.drop();
parseNamedCharacter(builder);
}
else if (RegExpTT.SIMPLE_CLASSES.contains(type)) {
else if (type == RegExpTT.DOT || type == RegExpTT.CHAR_CLASS) {
builder.advanceLexer();
marker.done(RegExpElementTypes.SIMPLE_CLASS);
}
@@ -135,22 +135,17 @@ public interface RegExpTT {
IElementType RUBY_QUOTED_NAMED_GROUP_CALL = new RegExpElementType("RUBY_QUOTED_NAMED_GROUP_CALL");
TokenSet CHARACTERS = TokenSet.create(CHARACTER,
ESC_CTRL_CHARACTER,
ESC_CHARACTER,
CTRL_CHARACTER,
CTRL,
UNICODE_CHAR,
HEX_CHAR, BAD_HEX_VALUE,
OCT_CHAR, BAD_OCT_VALUE,
REDUNDANT_ESCAPE,
MINUS,
StringEscapesTokenTypes.INVALID_UNICODE_ESCAPE_TOKEN,
StringEscapesTokenTypes.INVALID_CHARACTER_ESCAPE_TOKEN);
TokenSet SIMPLE_CLASSES = TokenSet.create(DOT, CHAR_CLASS);
// caret is just a character in classes after the first position: [a^] matches "a" or "^"
TokenSet CHARACTERS2 = TokenSet.orSet(CHARACTERS, SIMPLE_CLASSES);
ESC_CTRL_CHARACTER,
ESC_CHARACTER,
CTRL_CHARACTER,
CTRL,
UNICODE_CHAR,
HEX_CHAR, BAD_HEX_VALUE,
OCT_CHAR, BAD_OCT_VALUE,
REDUNDANT_ESCAPE,
MINUS,
StringEscapesTokenTypes.INVALID_UNICODE_ESCAPE_TOKEN,
StringEscapesTokenTypes.INVALID_CHARACTER_ESCAPE_TOKEN);
TokenSet QUANTIFIERS = TokenSet.create(QUEST, PLUS, STAR, LBRACE);
@@ -100,6 +100,7 @@ RBRACKET="]"
ESCAPE="\\"
NAME=[:letter:]([:letter:]|_|-|" "|"("|")"|[:digit:])*
GROUP_NAME=[:letter:]([:letter:]|_|-|" "|[:digit:])*
ANY=[^]
META1 = {ESCAPE} | {LBRACKET} | "^"
@@ -340,25 +341,25 @@ HEX_CHAR=[0-9a-fA-F]
}
<NAMED_GROUP> {
{NAME} { return RegExpTT.NAME; }
{GROUP_NAME} { return RegExpTT.NAME; }
">" { yybegin(YYINITIAL); return RegExpTT.GT; }
{ANY} { yybegin(YYINITIAL); return RegExpTT.BAD_CHARACTER; }
}
<QUOTED_NAMED_GROUP> {
{NAME} { return RegExpTT.NAME; }
{GROUP_NAME} { return RegExpTT.NAME; }
"'" { yybegin(YYINITIAL); return RegExpTT.QUOTE; }
{ANY} { yybegin(YYINITIAL); return RegExpTT.BAD_CHARACTER; }
}
<PY_NAMED_GROUP_REF> {
{NAME} { return RegExpTT.NAME; }
{GROUP_NAME} { return RegExpTT.NAME; }
")" { yybegin(YYINITIAL); return RegExpTT.GROUP_END; }
{ANY} { yybegin(YYINITIAL); return RegExpTT.BAD_CHARACTER; }
}
<PY_COND_REF> {
{NAME} { return RegExpTT.NAME; }
{GROUP_NAME} { return RegExpTT.NAME; }
[:digit:]+ { return RegExpTT.NUMBER; }
")" { yybegin(YYINITIAL); return RegExpTT.GROUP_END; }
{ANY} { yybegin(YYINITIAL); return RegExpTT.BAD_CHARACTER; }
@@ -84,9 +84,7 @@ public final class RegExpAnnotator extends RegExpElementVisitor implements Annot
public void visitRegExpCharRange(RegExpCharRange range) {
final RegExpCharRange.Endpoint from = range.getFrom();
final RegExpCharRange.Endpoint to = range.getTo();
final boolean a = from instanceof RegExpChar;
final boolean b = to instanceof RegExpChar;
if (a && b) {
if (from instanceof RegExpChar && to instanceof RegExpChar) {
final Character t = ((RegExpChar)to).getValue();
final Character f = ((RegExpChar)from).getValue();
if (t != null && f != null) {
@@ -99,8 +97,8 @@ public final class RegExpAnnotator extends RegExpElementVisitor implements Annot
}
}
}
else if (a != b) {
myHolder.createErrorAnnotation(range, "Character class (e.g. '\\\\w') may not be used inside character range");
else if (to instanceof RegExpSimpleClass) {
myHolder.createErrorAnnotation(to, "Character class not allowed inside character range");
}
else if (from.getText().equals(to.getText())) {
myHolder.createWarningAnnotation(range, "Redundant character range");
@@ -151,12 +149,20 @@ public final class RegExpAnnotator extends RegExpElementVisitor implements Annot
}
}
private void checkForDuplicates(RegExpClassElement element, Set<Character> seen) {
private void checkForDuplicates(RegExpClassElement element, Set<Object> seen) {
if (element instanceof RegExpChar) {
final RegExpChar regExpChar = (RegExpChar)element;
final Character value = regExpChar.getValue();
if (value != null && !seen.add(value)) {
myHolder.createWarningAnnotation(regExpChar, "Duplicate character '" + regExpChar.getText() + "' in character class");
myHolder.createWarningAnnotation(regExpChar, "Duplicate character '" + regExpChar.getText() + "' inside character class");
}
}
else if (element instanceof RegExpSimpleClass) {
final RegExpSimpleClass regExpSimpleClass = (RegExpSimpleClass)element;
final RegExpSimpleClass.Kind kind = regExpSimpleClass.getKind();
if (!seen.add(kind)) {
myHolder.createWarningAnnotation(regExpSimpleClass, "Duplicate predefined character class '" + regExpSimpleClass.getText() +
"' inside character class");
}
}
else if (element instanceof RegExpClass) {
@@ -184,6 +184,10 @@ public class RegExpParsingTest extends ParsingTestCase {
public void testGroups33() throws IOException { doCodeTest("(?<asdf>[a-c])\\1"); }
public void testGroups34() throws IOException { doCodeTest("(?<asdf>[a-c])\\k<asdf>"); }
public void testGroups35() throws IOException { doCodeTest("\\k<adsf>"); }
public void testGroups36() throws IOException { doCodeTest("(?P<name>{"); }
public void testGroups37() throws IOException { doCodeTest("(?P=name)"); }
public void testGroups38() throws IOException { doCodeTest("\\g'name'"); }
public void testGroups39() throws IOException { doCodeTest("(?(name)yes-pattern|no-pattern)"); }
public void testEscapes1() throws IOException { doCodeTest("\\q"); }
public void testEscapes2() throws IOException { doCodeTest("\\#"); }
+7 -3
View File
@@ -37,7 +37,11 @@
</quantifiers>
<charclasses>
<test verify="false">
<pattern><![CDATA[[<warning descr="Redundant character range">\w-\w</warning>]]]></pattern>
<pattern><![CDATA[[\w-<warning descr="Duplicate predefined character class '\w' inside character class">\w</warning>]]]></pattern>
<expected>OK</expected>
</test>
<test>
<pattern>[\w-a]</pattern>
<expected>OK</expected>
</test>
<test host="com.intellij.psi.impl.JavaRegExpHost">
@@ -106,7 +110,7 @@
<expected>OK</expected>
</test>
<test>
<pattern><![CDATA[[\Qabc?*+.)<warning descr="Duplicate character ')' in character class">)</warning>]<warning descr="Duplicate character ']' in character class">]</warning>[<warning descr="Duplicate character ']' in character class">]</warning>\E]]]></pattern>
<pattern><![CDATA[[\Qabc?*+.)<warning descr="Duplicate character ')' inside character class">)</warning>]<warning descr="Duplicate character ']' inside character class">]</warning>[<warning descr="Duplicate character ']' inside character class">]</warning>\E]]]></pattern>
<expected>OK</expected>
</test>
<test host="com.intellij.psi.impl.JavaRegExpHost">
@@ -133,7 +137,7 @@
<expected>OK</expected>
</test>
<test>
<pattern><![CDATA[[:xdig<warning descr="Duplicate character 'i' in character class">i</warning>t<warning descr="Duplicate character ':' in character class">:</warning>]+]]></pattern>
<pattern><![CDATA[[:xdig<warning descr="Duplicate character 'i' inside character class">i</warning>t<warning descr="Duplicate character ':' inside character class">:</warning>]+]]></pattern>
<expected>OK</expected>
</test>
</namedchars>
+3 -2
View File
@@ -3,10 +3,11 @@ REGEXP_FILE
RegExpBranchImpl: <[\w-\w]>
RegExpClassImpl: <[\w-\w]>
PsiElement(CLASS_BEGIN)('[')
RegExpCharRangeImpl: <\w-\w>
RegExpUnionImpl: <\w-\w>
RegExpSimpleClassImpl: <\w>
PsiElement(CHAR_CLASS)('\w')
PsiElement(MINUS)('-')
RegExpCharImpl: <->
PsiElement(MINUS)('-')
RegExpSimpleClassImpl: <\w>
PsiElement(CHAR_CLASS)('\w')
PsiElement(CLASS_END)(']')
+14
View File
@@ -0,0 +1,14 @@
REGEXP_FILE
RegExpPatternImpl: <(?P<name>{>
RegExpBranchImpl: <(?P<name>{>
RegExpClosureImpl: <(?P<name>{>
RegExpGroupImpl: <(?P<name>>
PsiElement(PYTHON_NAMED_GROUP)('(?P<')
PsiElement(NAME)('name')
PsiElement(GT)('>')
PsiErrorElement:Dangling metacharacter
<empty list>
RegExpQuantifierImpl: <{>
PsiElement(LBRACE)('{')
PsiErrorElement:Number expected
<empty list>
+7
View File
@@ -0,0 +1,7 @@
REGEXP_FILE
RegExpPatternImpl: <(?P=name)>
RegExpBranchImpl: <(?P=name)>
RegExpNamedGroupRefImpl: <(?P=name)>
PsiElement(PYTHON_NAMED_GROUP_REF)('(?P=')
PsiElement(NAME)('name')
PsiElement(GROUP_END)(')')
+7
View File
@@ -0,0 +1,7 @@
REGEXP_FILE
RegExpPatternImpl: <\g'name'>
RegExpBranchImpl: <\g'name'>
RegExpNamedGroupRefImpl: <\g'name'>
PsiElement(RUBY_QUOTED_NAMED_GROUP_CALL)('\g'')
PsiElement(NAME)('name')
PsiElement(QUOTE)(''')
+53
View File
@@ -0,0 +1,53 @@
REGEXP_FILE
RegExpPatternImpl: <(?(name)yes-pattern|no-pattern)>
RegExpBranchImpl: <(?(name)yes-pattern|no-pattern)>
RegExpPyCondRefImpl: <(?(name)yes-pattern|no-pattern)>
PsiElement(PYTHON_COND_REF)('(?(')
PsiElement(NAME)('name')
PsiElement(GROUP_END)(')')
RegExpBranchImpl: <yes-pattern>
RegExpCharImpl: <y>
PsiElement(CHARACTER)('y')
RegExpCharImpl: <e>
PsiElement(CHARACTER)('e')
RegExpCharImpl: <s>
PsiElement(CHARACTER)('s')
RegExpCharImpl: <->
PsiElement(MINUS)('-')
RegExpCharImpl: <p>
PsiElement(CHARACTER)('p')
RegExpCharImpl: <a>
PsiElement(CHARACTER)('a')
RegExpCharImpl: <t>
PsiElement(CHARACTER)('t')
RegExpCharImpl: <t>
PsiElement(CHARACTER)('t')
RegExpCharImpl: <e>
PsiElement(CHARACTER)('e')
RegExpCharImpl: <r>
PsiElement(CHARACTER)('r')
RegExpCharImpl: <n>
PsiElement(CHARACTER)('n')
PsiElement(UNION)('|')
RegExpBranchImpl: <no-pattern>
RegExpCharImpl: <n>
PsiElement(CHARACTER)('n')
RegExpCharImpl: <o>
PsiElement(CHARACTER)('o')
RegExpCharImpl: <->
PsiElement(MINUS)('-')
RegExpCharImpl: <p>
PsiElement(CHARACTER)('p')
RegExpCharImpl: <a>
PsiElement(CHARACTER)('a')
RegExpCharImpl: <t>
PsiElement(CHARACTER)('t')
RegExpCharImpl: <t>
PsiElement(CHARACTER)('t')
RegExpCharImpl: <e>
PsiElement(CHARACTER)('e')
RegExpCharImpl: <r>
PsiElement(CHARACTER)('r')
RegExpCharImpl: <n>
PsiElement(CHARACTER)('n')
PsiElement(GROUP_END)(')')
@@ -26,7 +26,7 @@ import java.util.*;
public class BackwardReferenceIndexUtil {
static void registerFile(String filePath,
Set<? extends JavacRef> refs,
Collection<? extends JavacRef> refs,
List<JavacDef> defs,
final BackwardReferenceIndexWriter writer) {
final int fileId = writer.enumeratePath(filePath);
@@ -19,16 +19,20 @@ import org.jetbrains.jps.javac.ast.api.JavacDef;
import org.jetbrains.jps.javac.ast.api.JavacFileReferencesRegistrar;
import org.jetbrains.jps.javac.ast.api.JavacRef;
import java.util.Collection;
import java.util.List;
import java.util.Set;
public class BackwardReferenceRegistrar implements JavacFileReferencesRegistrar {
private BackwardReferenceIndexWriter myWriter;
private volatile BackwardReferenceIndexWriter myWriter;
@Override
public boolean initialize() {
public void initialize() {
myWriter = BackwardReferenceIndexWriter.getInstance();
return myWriter != null;
}
@Override
public boolean isEnabled() {
return BackwardReferenceIndexWriter.isEnabled();
}
@Override
@@ -37,7 +41,7 @@ public class BackwardReferenceRegistrar implements JavacFileReferencesRegistrar
}
@Override
public void registerFile(String filePath, Set<? extends JavacRef> refs, List<JavacDef> defs) {
public void registerFile(String filePath, Collection<JavacRef> refs, List<JavacDef> defs) {
BackwardReferenceIndexUtil.registerFile(filePath, refs, defs, myWriter);
}
}
@@ -1042,6 +1042,10 @@ public class JavaBuilder extends ModuleLevelBuilder {
}
}
for (JavaCompilerToolExtension extension : JavaCompilerToolExtension.getExtensions()) {
vmOptions.addAll(extension.getExternalBuildProcessOptions(compilingTool));
}
compilingTool.processCompilerOptions(context, options);
JAVAC_OPTIONS.set(context, options);
@@ -22,6 +22,8 @@ import org.jetbrains.jps.service.JpsServiceManager;
import javax.tools.*;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* @author Eugene Zhuravlev
@@ -51,6 +53,10 @@ public abstract class JavaCompilerToolExtension {
public void processData(String dataName, byte[] content) {
}
public List<String> getExternalBuildProcessOptions(@NotNull JavaCompilingTool compilingTool) {
return Collections.emptyList();
}
public static Iterable<JavaCompilerToolExtension> getExtensions() {
return JpsServiceManager.getInstance().getExtensions(JavaCompilerToolExtension.class);
}
@@ -15,6 +15,7 @@
*/
package org.jetbrains.jps.javac.ast;
import com.intellij.util.Consumer;
import com.intellij.util.ReflectionUtil;
import com.sun.source.util.JavacTask;
import com.sun.source.util.TaskEvent;
@@ -27,7 +28,7 @@ import gnu.trove.THashMap;
import gnu.trove.THashSet;
import gnu.trove.TObjectHashingStrategy;
import org.jetbrains.jps.javac.ast.api.JavacDef;
import org.jetbrains.jps.javac.ast.api.JavacFileReferencesRegistrar;
import org.jetbrains.jps.javac.ast.api.JavacFileData;
import org.jetbrains.jps.javac.ast.api.JavacRef;
import javax.lang.model.element.ElementKind;
@@ -35,14 +36,11 @@ import javax.lang.model.element.TypeElement;
import javax.tools.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
final class JavacReferenceCollectorListener implements TaskListener {
private final JavacFileReferencesRegistrar[] myFullASTListeners;
private final JavacFileReferencesRegistrar[] myOnlyImportsListeners;
private final boolean myDivideImportRefs;
private final Consumer<JavacFileData> myDataConsumer;
private final JavacTreeRefScanner myAstScanner;
private Name myAsterisk;
@@ -50,11 +48,11 @@ final class JavacReferenceCollectorListener implements TaskListener {
private final Map<String, IncompletelyProcessedFile> myIncompletelyProcessedFiles = new THashMap<String, IncompletelyProcessedFile>(10);
static void installOn(JavaCompiler.CompilationTask task,
JavacFileReferencesRegistrar[] fullASTListenerArray,
JavacFileReferencesRegistrar[] onlyImportsListenerArray) {
boolean divideImportRefs,
Consumer<JavacFileData> dataConsumer) {
JavacTask javacTask = (JavacTask)task;
Method addTaskMethod = ReflectionUtil.getMethod(JavacTask.class, "addTaskListener", TaskListener.class); // jdk >= 8
final JavacReferenceCollectorListener taskListener = new JavacReferenceCollectorListener(fullASTListenerArray, onlyImportsListenerArray);
final JavacReferenceCollectorListener taskListener = new JavacReferenceCollectorListener(divideImportRefs, dataConsumer);
if (addTaskMethod != null) {
try {
addTaskMethod.invoke(task, taskListener);
@@ -71,9 +69,9 @@ final class JavacReferenceCollectorListener implements TaskListener {
}
}
private JavacReferenceCollectorListener(JavacFileReferencesRegistrar[] fullASTListenerArray, JavacFileReferencesRegistrar[] importsListenerArray) {
myFullASTListeners = fullASTListenerArray;
myOnlyImportsListeners = importsListenerArray;
private JavacReferenceCollectorListener(boolean divideImportRefs, Consumer<JavacFileData> dataConsumer) {
myDivideImportRefs = divideImportRefs;
myDataConsumer = dataConsumer;
myAstScanner = JavacTreeRefScanner.createASTScanner();
}
@@ -93,37 +91,32 @@ final class JavacReferenceCollectorListener implements TaskListener {
final int size = declarations.size();
boolean isFileDataComplete = true;
boolean submitImportsOnlyData = true;
boolean collectImportsData = true;
JCTree declarationToProcess = null;
final Set<JavacRef.JavacSymbolRefBase> collectedReferences;
final List<JavacDef> collectedDefinitions;
final TypeElement analyzedElement = e.getTypeElement();
IncompletelyProcessedFile incompletelyProcessedFile;
switch (size) {
case 0:
collectedReferences = IncompletelyProcessedFile.createReferenceHolder();
collectedDefinitions = IncompletelyProcessedFile.createDefinitionHolder();
incompletelyProcessedFile = new IncompletelyProcessedFile(0, fileName);
break;
case 1:
collectedReferences = IncompletelyProcessedFile.createReferenceHolder();
collectedDefinitions = IncompletelyProcessedFile.createDefinitionHolder();
incompletelyProcessedFile = new IncompletelyProcessedFile(0, fileName);
declarationToProcess = declarations.get(0);
break;
default:
IncompletelyProcessedFile incompletelyProcessedFile = myIncompletelyProcessedFiles.get(fileName);
incompletelyProcessedFile = myIncompletelyProcessedFiles.get(fileName);
if (incompletelyProcessedFile == null) {
myIncompletelyProcessedFiles.put(fileName, incompletelyProcessedFile = new IncompletelyProcessedFile(size));
myIncompletelyProcessedFiles.put(fileName, incompletelyProcessedFile = new IncompletelyProcessedFile(size, fileName));
} else {
submitImportsOnlyData = false;
collectImportsData = false;
}
if (--incompletelyProcessedFile.remainDeclarations == 0) {
if (incompletelyProcessedFile.decrementRemainDeclarationsAndGet() == 0) {
myIncompletelyProcessedFiles.remove(fileName);
} else {
isFileDataComplete = false;
}
collectedReferences = incompletelyProcessedFile.collectedReferences;
collectedDefinitions = incompletelyProcessedFile.collectedDefinitions;
for (JCTree declaration : declarations) {
if (declaration.type != null && declaration.type.tsym == analyzedElement) {
@@ -135,22 +128,22 @@ final class JavacReferenceCollectorListener implements TaskListener {
if (declarationToProcess == null) throw new IllegalStateException("Can't find tree for " + analyzedElement.getQualifiedName());
}
if (submitImportsOnlyData) {
scanImports(unit, collectedReferences);
for (JavacFileReferencesRegistrar r : myOnlyImportsListeners) {
r.registerFile(fileName, collectedReferences, collectedDefinitions);
if (collectImportsData) {
scanImports(unit, incompletelyProcessedFile.myFileData.getRefs());
if (myDivideImportRefs) {
scanImports(unit, incompletelyProcessedFile.myFileData.getImportRefs());
}
}
if (myFullASTListeners.length == 0) return;
final IncompletelyProcessedFile finalIncompletelyProcessedFile = incompletelyProcessedFile;
JavacTreeScannerSink sink = new JavacTreeScannerSink() {
@Override
public void sinkReference(JavacRef.JavacSymbolRefBase ref) {
collectedReferences.add(ref);
finalIncompletelyProcessedFile.myFileData.getRefs().add(ref);
}
@Override
public void sinkDeclaration(JavacDef def) {
collectedDefinitions.add(def);
finalIncompletelyProcessedFile.myFileData.getDefs().add(def);
}
};
myAstScanner.scan(declarationToProcess, sink);
@@ -160,9 +153,7 @@ final class JavacReferenceCollectorListener implements TaskListener {
myAstScanner.scan(annotation, sink);
}
for (JavacFileReferencesRegistrar r : myFullASTListeners) {
r.registerFile(e.getSourceFile().getName(), collectedReferences, collectedDefinitions);
}
myDataConsumer.consume(incompletelyProcessedFile.myFileData);
}
}
}
@@ -178,7 +169,7 @@ final class JavacReferenceCollectorListener implements TaskListener {
return myAsterisk;
}
private void scanImports(JCTree.JCCompilationUnit compilationUnit, Set<JavacRef.JavacSymbolRefBase> symbols) {
private void scanImports(JCTree.JCCompilationUnit compilationUnit, Collection<JavacRef> symbols) {
for (JCTree.JCImport anImport : compilationUnit.getImports()) {
final JCTree.JCFieldAccess id = (JCTree.JCFieldAccess)anImport.getQualifiedIdentifier();
final Symbol sym = id.sym;
@@ -205,7 +196,7 @@ final class JavacReferenceCollectorListener implements TaskListener {
}
}
private static void collectClassImports(Symbol baseImport, Set<JavacRef.JavacSymbolRefBase> collector) {
private static void collectClassImports(Symbol baseImport, Collection<JavacRef> collector) {
for (Symbol symbol = baseImport;
symbol != null && symbol.getKind() != ElementKind.PACKAGE;
symbol = symbol.owner) {
@@ -213,31 +204,38 @@ final class JavacReferenceCollectorListener implements TaskListener {
}
}
private static class IncompletelyProcessedFile {
private final Set<JavacRef.JavacSymbolRefBase> collectedReferences = createReferenceHolder();
private final List<JavacDef> collectedDefinitions = createDefinitionHolder();
private int remainDeclarations;
private class IncompletelyProcessedFile {
private final JavacFileData myFileData;
private int myRemainDeclarations;
private IncompletelyProcessedFile(int remainDeclarations) {
this.remainDeclarations = remainDeclarations;
private IncompletelyProcessedFile(int remainDeclarations, String filePath) {
myRemainDeclarations = remainDeclarations;
myFileData = new JavacFileData(filePath,
createReferenceHolder(),
myDivideImportRefs ? createReferenceHolder() : Collections.<JavacRef>emptyList(),
createDefinitionHolder());
}
private static Set<JavacRef.JavacSymbolRefBase> createReferenceHolder() {
return new THashSet<JavacRef.JavacSymbolRefBase>(new TObjectHashingStrategy<JavacRef.JavacSymbolRefBase>() {
@Override
public int computeHashCode(JavacRef.JavacSymbolRefBase ref) {
return ref.getOriginalElement().hashCode();
}
@Override
public boolean equals(JavacRef.JavacSymbolRefBase r1, JavacRef.JavacSymbolRefBase r2) {
return r1.getOriginalElement() == r2.getOriginalElement();
}
});
}
private static List<JavacDef> createDefinitionHolder() {
return new ArrayList<JavacDef>();
private int decrementRemainDeclarationsAndGet() {
return --myRemainDeclarations;
}
}
private static Set<JavacRef> createReferenceHolder() {
return new THashSet<JavacRef>(new TObjectHashingStrategy<JavacRef>() {
@Override
public int computeHashCode(JavacRef ref) {
return ((JavacRef.JavacSymbolRefBase) ref).getOriginalElement().hashCode();
}
@Override
public boolean equals(JavacRef r1, JavacRef r2) {
return ((JavacRef.JavacSymbolRefBase) r1).getOriginalElement() == ((JavacRef.JavacSymbolRefBase) r2).getOriginalElement();
}
});
}
private static List<JavacDef> createDefinitionHolder() {
return new ArrayList<JavacDef>();
}
}
@@ -15,34 +15,19 @@
*/
package org.jetbrains.jps.javac.ast;
import com.intellij.util.SmartList;
import org.jetbrains.jps.javac.ast.api.JavacFileReferencesRegistrar;
import org.jetbrains.jps.service.JpsServiceManager;
import com.intellij.util.Consumer;
import org.jetbrains.jps.javac.ast.api.JavacFileData;
import javax.tools.*;
import java.util.List;
/**
* Code here should not depend on any javac private API located in tools.jar if no JavacFileReferencesRegistrar-s will be run.
* A workaround to allow run standalone jps with improperly configured classloader without NoClassDefFoundError (e.g: IDEA-162877)
*/
public class JavacReferencesCollector {
public static void installOn(JavaCompiler.CompilationTask task) {
List<JavacFileReferencesRegistrar> fullASTListeners = new SmartList<JavacFileReferencesRegistrar>();
List<JavacFileReferencesRegistrar> onlyImportsListeners = new SmartList<JavacFileReferencesRegistrar>();
for (JavacFileReferencesRegistrar listener : JpsServiceManager.getInstance().getExtensions(JavacFileReferencesRegistrar.class)) {
if (!listener.initialize()) {
continue;
}
(listener.onlyImports() ? onlyImportsListeners : fullASTListeners).add(listener);
}
final JavacFileReferencesRegistrar[] fullASTListenerArray = fullASTListeners.toArray(new JavacFileReferencesRegistrar[fullASTListeners.size()]);
final JavacFileReferencesRegistrar[] onlyImportsListenerArray = onlyImportsListeners.toArray(new JavacFileReferencesRegistrar[onlyImportsListeners.size()]);
if (fullASTListenerArray.length == 0 && onlyImportsListenerArray.length == 0) {
return;
}
JavacReferenceCollectorListener.installOn(task, fullASTListenerArray, onlyImportsListenerArray);
public static void installOn(JavaCompiler.CompilationTask task,
boolean divideImportRefs,
Consumer<JavacFileData> fileDataConsumer) {
JavacReferenceCollectorListener.installOn(task, divideImportRefs, fileDataConsumer);
}
}
@@ -15,14 +15,23 @@
*/
package org.jetbrains.jps.javac.ast;
import com.intellij.openapi.util.AtomicClearableLazyValue;
import com.intellij.util.Consumer;
import com.intellij.util.SystemProperties;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.TestOnly;
import org.jetbrains.jps.builders.java.JavaCompilingTool;
import org.jetbrains.jps.javac.DiagnosticOutputConsumer;
import org.jetbrains.jps.javac.JavaCompilerToolExtension;
import org.jetbrains.jps.javac.ast.api.JavacFileData;
import org.jetbrains.jps.javac.ast.api.JavacFileReferencesRegistrar;
import org.jetbrains.jps.model.java.compiler.JavaCompilers;
import org.jetbrains.jps.service.JpsServiceManager;
import javax.tools.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @author Eugene Zhuravlev
@@ -31,6 +40,24 @@ import java.util.Collection;
public class RefCollectorCompilerToolExtension extends JavaCompilerToolExtension{
public static final String ID = "ASTReferenceCollector";
private static final String ENABLED_PARAM = "jps.ref.collector.enabled";
private static final String DIVIDE_IMPORTS_PARAM = "jps.divide.imports";
private final AtomicClearableLazyValue<List<JavacFileReferencesRegistrar>> myRegistrars = new AtomicClearableLazyValue<List<JavacFileReferencesRegistrar>>() {
@NotNull
@Override
protected List<JavacFileReferencesRegistrar> compute() {
List<JavacFileReferencesRegistrar> result = new ArrayList<JavacFileReferencesRegistrar>();
for (JavacFileReferencesRegistrar registrar : JpsServiceManager.getInstance().getExtensions(JavacFileReferencesRegistrar.class)) {
if (registrar.isEnabled()) {
registrar.initialize();
result.add(registrar);
}
}
return result;
}
};
@NotNull
@Override
public String getId() {
@@ -41,15 +68,94 @@ public class RefCollectorCompilerToolExtension extends JavaCompilerToolExtension
public void beforeCompileTaskExecution(@NotNull JavaCompilingTool compilingTool,
@NotNull JavaCompiler.CompilationTask task,
@NotNull Collection<String> options,
@NotNull DiagnosticOutputConsumer diagnosticConsumer) {
// todo: transfer collected data via DiagnosticOutputConsumer.customOutputData()
if (JavaCompilers.JAVAC_ID.equals(compilingTool.getId())) {
JavacReferencesCollector.installOn(task);
@NotNull final DiagnosticOutputConsumer diagnosticConsumer) {
if (isJavac(compilingTool)) {
final String isEnabledProperty = System.getProperty(ENABLED_PARAM);
Consumer<JavacFileData> fileDataConsumer;
boolean divideImportsRefs;
if (isEnabledProperty == null) {
//in-process
final RefCollectorOptions refCollectorOptions = new RefCollectorOptions();
fileDataConsumer = refCollectorOptions.enabled ? new Consumer<JavacFileData>() {
@Override
public void consume(JavacFileData data) {
submitFileData(data);
}
} : null;
divideImportsRefs = refCollectorOptions.divideImportRefs;
} else if (isEnabledProperty.equals("true")) {
//external
fileDataConsumer = new Consumer<JavacFileData>() {
@Override
public void consume(JavacFileData data) {
diagnosticConsumer.customOutputData(ID, "javac-refs", data.asBytes());
}
};
divideImportsRefs = SystemProperties.getBooleanProperty(DIVIDE_IMPORTS_PARAM, false);
} else {
return;
}
if (fileDataConsumer != null) {
JavacReferencesCollector.installOn(task, divideImportsRefs, fileDataConsumer);
}
}
}
@Override
public List<String> getExternalBuildProcessOptions(@NotNull JavaCompilingTool compilingTool) {
List<String> options = new ArrayList<String>(2);
if (isJavac(compilingTool)) {
final RefCollectorOptions refCollectorOptions = new RefCollectorOptions();
options.add("-D" + ENABLED_PARAM + "=" + refCollectorOptions.enabled);
if (refCollectorOptions.enabled && refCollectorOptions.divideImportRefs) {
options.add("-D" + DIVIDE_IMPORTS_PARAM + "=true");
}
} else {
options.add("-D" + ENABLED_PARAM + "=false");
}
return options;
}
@Override
public void processData(String dataName, byte[] content) {
// todo!
submitFileData(JavacFileData.fromBytes(content));
}
private void submitFileData(@NotNull JavacFileData data) {
for (JavacFileReferencesRegistrar registrar : myRegistrars.getValue()) {
registrar.registerFile(data.getFilePath(), registrar.onlyImports() ? data.getImportRefs() : data.getRefs(), data.getDefs());
}
}
private static boolean isJavac(@NotNull JavaCompilingTool compilingTool) {
return JavaCompilers.JAVAC_ID.equals(compilingTool.getId());
}
private static class RefCollectorOptions {
private final boolean enabled;
private final boolean divideImportRefs;
private RefCollectorOptions() {
boolean enabled = false;
boolean divideImportRefs = false;
for (JavacFileReferencesRegistrar listener : JpsServiceManager.getInstance().getExtensions(JavacFileReferencesRegistrar.class)) {
if (listener.isEnabled()) {
enabled = true;
if (listener.onlyImports()) {
divideImportRefs = true;
}
}
}
this.enabled = enabled;
this.divideImportRefs = divideImportRefs;
}
}
@TestOnly
public void clearRegistrars() {
myRegistrars.drop();
}
}
@@ -0,0 +1,247 @@
/*
* Copyright 2000-2016 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.javac.ast.api;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream;
import com.intellij.util.ThrowableConsumer;
import com.intellij.util.io.*;
import com.intellij.util.io.DataOutputStream;
import org.jetbrains.annotations.NotNull;
import java.io.*;
import java.util.Collection;
import java.util.List;
public class JavacFileData {
private final String myFilePath;
private final Collection<JavacRef> myRefs;
private final Collection<JavacRef> myImportRefs;
private final List<JavacDef> myDefs;
public JavacFileData(@NotNull String path,
@NotNull Collection<JavacRef> refs,
@NotNull Collection<JavacRef> importRefs,
@NotNull List<JavacDef> defs) {
myFilePath = path;
myRefs = refs;
myImportRefs = importRefs;
myDefs = defs;
}
@NotNull
public String getFilePath() {
return myFilePath;
}
@NotNull
public Collection<JavacRef> getRefs() {
return myRefs;
}
@NotNull
public Collection<JavacRef> getImportRefs() {
return myImportRefs;
}
@NotNull
public List<JavacDef> getDefs() {
return myDefs;
}
@NotNull
public byte[] asBytes() {
final BufferExposingByteArrayOutputStream os = new BufferExposingByteArrayOutputStream();
DataOutputStream stream = new DataOutputStream(os);
try {
EXTERNALIZER.save(stream, this);
}
catch (IOException e) {
throw new RuntimeException(e);
}
return os.toByteArray();
}
@NotNull
public static JavacFileData fromBytes(byte[] bytes) {
final UnsyncByteArrayInputStream is = new UnsyncByteArrayInputStream(bytes);
try {
return EXTERNALIZER.read(new DataInputStream(is));
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
private static final DataExternalizer<JavacFileData> EXTERNALIZER = new DataExternalizer<JavacFileData>() {
DataExternalizer<JavacRef> myRefSeqExternalizer = createRefExternalizer();
DataExternalizer<JavacDef> myDefSeqExternalizer = createDefExternalizer();
@Override
public void save(@NotNull DataOutput out, JavacFileData data) throws IOException {
EnumeratorStringDescriptor.INSTANCE.save(out, data.getFilePath());
saveRefs(out, data.getRefs());
saveRefs(out, data.getImportRefs());
saveDefs(out, data.getDefs());
}
@Override
public JavacFileData read(@NotNull DataInput in) throws IOException {
return new JavacFileData(EnumeratorStringDescriptor.INSTANCE.read(in),
readRefs(in),
readRefs(in),
readDefs(in));
}
private void saveRefs(final DataOutput out, Collection<JavacRef> refs) throws IOException {
DataInputOutputUtil.writeSeq(out, refs, new ThrowableConsumer<JavacRef, IOException>() {
@Override
public void consume(JavacRef ref) throws IOException {
myRefSeqExternalizer.save(out, ref);
}
});
}
private Collection<JavacRef> readRefs(final DataInput in) throws IOException {
return DataInputOutputUtil.readSeq(in, new ThrowableComputable<JavacRef, IOException>() {
@Override
public JavacRef compute() throws IOException {
return myRefSeqExternalizer.read(in);
}
});
}
private void saveDefs(final DataOutput out, List<JavacDef> defs) throws IOException {
DataInputOutputUtil.writeSeq(out, defs, new ThrowableConsumer<JavacDef, IOException>() {
@Override
public void consume(JavacDef def) throws IOException {
myDefSeqExternalizer.save(out, def);
}
});
}
private List<JavacDef> readDefs(final DataInput in) throws IOException {
return DataInputOutputUtil.readSeq(in, new ThrowableComputable<JavacDef, IOException>() {
@Override
public JavacDef compute() throws IOException {
return myDefSeqExternalizer.read(in);
}
});
}
};
private static DataExternalizer<JavacDef> createDefExternalizer() {
return new DataExternalizer<JavacDef>() {
private static final byte CLASS_MARKER = 0;
private static final byte FUN_EXPR_MARKER = 1;
DataExternalizer<JavacRef> refExternalizer = createRefExternalizer();
@Override
public void save(@NotNull DataOutput out, JavacDef def) throws IOException {
if (def instanceof JavacDef.JavacClassDef) {
out.writeByte(CLASS_MARKER);
final JavacRef[] superClasses = ((JavacDef.JavacClassDef)def).getSuperClasses();
out.writeInt(superClasses.length);
for (JavacRef aClass : superClasses) {
refExternalizer.save(out, aClass);
}
}
else if (def instanceof JavacDef.JavacFunExprDef) {
out.writeByte(FUN_EXPR_MARKER);
} else {
throw new IllegalStateException("unknown type: " + def.getClass());
}
refExternalizer.save(out, def.getDefinedElement());
}
@Override
public JavacDef read(@NotNull DataInput in) throws IOException {
final byte marker = in.readByte();
switch (marker) {
case CLASS_MARKER:
final int supersSize = in.readInt();
JavacRef[] superClasses = new JavacRef[supersSize];
for (int i = 0; i < supersSize; i++) {
superClasses[i] = refExternalizer.read(in);
}
return new JavacDef.JavacClassDef(refExternalizer.read(in), superClasses);
case FUN_EXPR_MARKER:
return new JavacDef.JavacFunExprDef(refExternalizer.read(in));
default: throw new IllegalStateException("unknown marker " + marker);
}
}
};
}
private static DataExternalizer<JavacRef> createRefExternalizer() {
return new DataExternalizer<JavacRef>() {
private static final byte CLASS_MARKER = 0;
private static final byte METHOD_MARKER = 1;
private static final byte FIELD_MARKER = 2;
@Override
public void save(@NotNull DataOutput out, JavacRef ref) throws IOException {
if (ref instanceof JavacRef.JavacClass) {
out.writeByte(CLASS_MARKER);
out.writeBoolean(((JavacRef.JavacClass)ref).isAnonymous());
}
else if (ref instanceof JavacRef.JavacField) {
out.writeByte(FIELD_MARKER);
writeBytes(out, ref.getOwnerName());
}
else if (ref instanceof JavacRef.JavacMethod) {
out.writeByte(METHOD_MARKER);
writeBytes(out, ref.getOwnerName());
out.write(((JavacRef.JavacMethod)ref).getParamCount());
} else {
throw new IllegalStateException("unknown type: " + ref.getClass());
}
out.writeLong(ref.getFlags());
writeBytes(out, ref.getName());
}
@Override
public JavacRef read(@NotNull DataInput in) throws IOException {
final byte marker = in.readByte();
switch (marker) {
case CLASS_MARKER:
return new JavacRef.JavacClassImpl(in.readBoolean(), in.readLong(), readBytes(in));
case METHOD_MARKER:
return new JavacRef.JavacMethodImpl(readBytes(in), in.readByte(), in.readLong(), readBytes(in));
case FIELD_MARKER:
return new JavacRef.JavacFieldImpl(readBytes(in), in.readLong(), readBytes(in));
default:
throw new IllegalStateException("unknown marker " + marker);
}
}
private void writeBytes(DataOutput out, byte[] bytes) throws IOException {
out.writeInt(bytes.length);
out.write(bytes);
}
private byte[] readBytes(DataInput in) throws IOException {
final int len = in.readInt();
final byte[] buf = new byte[len];
in.readFully(buf);
return buf;
}
};
}
}
@@ -15,14 +15,15 @@
*/
package org.jetbrains.jps.javac.ast.api;
import java.util.Collection;
import java.util.List;
import java.util.Set;
public interface JavacFileReferencesRegistrar {
void initialize();
boolean initialize();
boolean isEnabled();
boolean onlyImports();
void registerFile(String filePath, Set<? extends JavacRef> refs, List<JavacDef> defs);
void registerFile(String filePath, Collection<JavacRef> refs, List<JavacDef> defs);
}
@@ -16,6 +16,7 @@
package org.jetbrains.jps.javac.ast.api;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.util.Convert;
import org.jetbrains.annotations.NotNull;
public interface JavacRef {
@@ -64,7 +65,7 @@ public interface JavacRef {
class JavacClassImpl extends JavacRefBase implements JavacClass {
private boolean myAnonymous;
public JavacClassImpl(byte[] name, long flags, boolean anonymous) {
public JavacClassImpl(boolean anonymous, long flags, byte[] name) {
super(name, flags);
myAnonymous = anonymous;
}
@@ -78,13 +79,18 @@ public interface JavacRef {
public boolean isAnonymous() {
return myAnonymous;
}
@Override
public String toString() {
return Convert.utf2string(getName());
}
}
class JavacMethodImpl extends JavacRefBase implements JavacMethod {
private final byte[] myOwnerName;
private final byte myParamCount;
public JavacMethodImpl(byte[] name, byte[] ownerName, byte paramCount, long flags) {
public JavacMethodImpl(byte[] ownerName, byte paramCount, long flags, byte[] name) {
super(name, flags);
myOwnerName = ownerName;
myParamCount = paramCount;
@@ -99,12 +105,17 @@ public interface JavacRef {
public byte[] getOwnerName() {
return myOwnerName;
}
@Override
public String toString() {
return Convert.utf2string(getOwnerName()) + "." + Convert.utf2string(getName()) + "(" + myParamCount + ")";
}
}
class JavacFieldImpl extends JavacRefBase implements JavacField {
private final byte[] myOwnerName;
public JavacFieldImpl(byte[] name, byte[] ownerName, long flags) {
public JavacFieldImpl(byte[] ownerName, long flags, byte[] name) {
super(name, flags);
myOwnerName = ownerName;
}
@@ -114,6 +125,11 @@ public interface JavacRef {
public byte[] getOwnerName() {
return myOwnerName;
}
@Override
public String toString() {
return Convert.utf2string(getOwnerName()) + "." + Convert.utf2string(getName());
}
}
abstract class JavacSymbolRefBase implements JavacRef {
@@ -29,9 +29,12 @@ import org.jetbrains.jps.backwardRefs.CompilerBackwardReferenceIndex
import org.jetbrains.jps.backwardRefs.LightRef
import org.jetbrains.jps.backwardRefs.index.CompiledFileData
import org.jetbrains.jps.backwardRefs.index.CompilerIndices
import org.jetbrains.jps.builders.BuildResult
import org.jetbrains.jps.builders.JpsBuildTestCase
import org.jetbrains.jps.builders.TestProjectBuilderLogger
import org.jetbrains.jps.builders.logging.BuildLoggingManager
import org.jetbrains.jps.javac.JavaCompilerToolExtension
import org.jetbrains.jps.javac.ast.RefCollectorCompilerToolExtension
import java.io.File
abstract class ReferenceIndexTestBase : JpsBuildTestCase() {
@@ -58,6 +61,22 @@ abstract class ReferenceIndexTestBase : JpsBuildTestCase() {
assertIndexEquals("initialIndex.txt")
}
override fun rebuildAllModules() {
try {
super.rebuildAllModules()
} finally {
(JavaCompilerToolExtension.getExtension(RefCollectorCompilerToolExtension.ID) as RefCollectorCompilerToolExtension).clearRegistrars()
}
}
override fun buildAllModules(): BuildResult {
try {
return super.buildAllModules()
} finally {
(JavaCompilerToolExtension.getExtension(RefCollectorCompilerToolExtension.ID) as RefCollectorCompilerToolExtension).clearRegistrars()
}
}
protected fun renameFile(fileToRename: String, newName: String) {
rename(orCreateProjectDir.path + "/m/" + fileToRename, newName)
}
@@ -18,6 +18,7 @@ package com.intellij.openapi.editor;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.datatransfer.Transferable;
@@ -25,7 +26,7 @@ public abstract class CustomFileDropHandler {
public static final ExtensionPointName<CustomFileDropHandler> CUSTOM_DROP_HANDLER_EP =
ExtensionPointName.create("com.intellij.customFileDropHandler");
public abstract boolean canHandle(@NotNull Transferable t, Editor editor);
public abstract boolean canHandle(@NotNull Transferable t, @Nullable Editor editor);
public abstract boolean handleDrop(@NotNull Transferable t, Editor editor, final Project project);
public abstract boolean handleDrop(@NotNull Transferable t, @Nullable Editor editor, final Project project);
}
@@ -23,6 +23,7 @@ import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
@@ -57,12 +58,49 @@ public class SystemInfo extends SystemInfoRt {
}
// version numbers from http://msdn.microsoft.com/en-us/library/windows/desktop/ms724832.aspx
public static final boolean isWin10OrNewer = isWindows && isOsVersionAtLeast("10.0");
public static final boolean isWin2kOrNewer = isWindows && isOsVersionAtLeast("5.0");
public static final boolean isWinXpOrNewer = isWindows && isOsVersionAtLeast("5.1");
public static final boolean isWinVistaOrNewer = isWindows && isOsVersionAtLeast("6.0");
public static final boolean isWin7OrNewer = isWindows && isOsVersionAtLeast("6.1");
public static final boolean isWin8OrNewer = isWindows && isOsVersionAtLeast("6.2");
/**
* https://msdn.microsoft.com/en-us/commandline/wsl/about
*/
private static final AtomicNullableLazyValue<File> ourWSLBashFile = new AtomicNullableLazyValue<File>() {
@Nullable
@Override
protected File compute() {
if (!isWin10OrNewer) {
return null;
}
String windir = System.getenv().get("windir");
if (StringUtil.isEmpty(windir)) {
return null;
}
return new File(windir + "\\System32\\bash.exe");
}
};
/**
* @return WSL bash file or null if unavailable
*/
@Nullable
public static File getWSLBashFile() {
File bashFile = ourWSLBashFile.getValue();
return bashFile == null || !bashFile.exists() ? null : bashFile;
}
/**
* @return true if we are on Windows 10+ and have WSL installed
*/
public static boolean hasWSL() {
return getWSLBashFile() != null;
}
public static final boolean isXWindow = isUnix && !isMac;
// https://userbase.kde.org/KDE_System_Administration/Environment_Variables#KDE_FULL_SESSION
public static final boolean isKDE = !StringUtil.isEmpty(System.getenv("KDE_FULL_SESSION"));
@@ -20,6 +20,7 @@ import com.intellij.openapi.editor.CustomFileDropHandler;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.datatransfer.Transferable;
import java.io.File;
@@ -28,14 +29,14 @@ import java.util.List;
public class PatchFileDropHandler extends CustomFileDropHandler {
@Override
public boolean canHandle(@NotNull Transferable t, Editor editor) {
public boolean canHandle(@NotNull Transferable t, @Nullable Editor editor) {
List<File> list = FileCopyPasteUtil.getFileList(t);
if (list == null || list.size() != 1) return false;
return PatchFileType.isPatchFile(list.get(0));
}
@Override
public boolean handleDrop(@NotNull Transferable t, Editor editor, @NotNull final Project project) {
public boolean handleDrop(@NotNull Transferable t, @Nullable Editor editor, @NotNull final Project project) {
List<File> list = FileCopyPasteUtil.getFileList(t);
if (list == null || list.size() != 1) return false;
return ApplyPatchAction.showAndGetApplyPatch(project, list.get(0));
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2016 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.
@@ -21,7 +21,7 @@ import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.text.StringUtil;
@@ -43,7 +43,7 @@ abstract class AbstractRegisterFix implements LocalQuickFix, DescriptorUtil.Patc
protected final SmartPsiElementPointer<PsiClass> myPointer;
protected static final Logger LOG = Logger.getInstance(AbstractRegisterFix.class);
public AbstractRegisterFix(@NotNull SmartPsiElementPointer<PsiClass> klass) {
protected AbstractRegisterFix(@NotNull SmartPsiElementPointer<PsiClass> klass) {
myPointer = klass;
}
@@ -52,6 +52,11 @@ abstract class AbstractRegisterFix implements LocalQuickFix, DescriptorUtil.Patc
return DevKitBundle.message("inspections.component.not.registered.quickfix.family");
}
@Override
public boolean startInWriteAction() {
return false;
}
@NotNull
public String getName() {
return DevKitBundle.message("inspections.component.not.registered.quickfix.name", getType());
@@ -76,7 +81,7 @@ abstract class AbstractRegisterFix implements LocalQuickFix, DescriptorUtil.Patc
return;
}
LOG.assertTrue(psiFile != null);
final Module module = ModuleUtil.findModuleForFile(psiFile.getVirtualFile(), project);
final Module module = ModuleUtilCore.findModuleForFile(psiFile.getVirtualFile(), project);
assert module != null;
Runnable command = () -> {
+7 -3
View File
@@ -15,6 +15,7 @@
*/
package org.jetbrains.idea.devkit.util;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.vfs.ReadonlyStatusHandler;
import com.intellij.openapi.vfs.VirtualFile;
@@ -23,6 +24,7 @@ import com.intellij.psi.PsiFile;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ThrowableRunnable;
import com.intellij.util.xml.DomFileElement;
import com.intellij.util.xml.DomManager;
import org.jetbrains.annotations.NotNull;
@@ -67,9 +69,11 @@ public class DescriptorUtil {
throw new IncorrectOperationException(DevKitBundle.message("error.plugin.xml.readonly"));
}
for (XmlFile pluginXml : pluginXmls) {
patcher.patchPluginXml(pluginXml, klass);
}
WriteAction.run((ThrowableRunnable<IncorrectOperationException>)() -> {
for (XmlFile pluginXml : pluginXmls) {
patcher.patchPluginXml(pluginXml, klass);
}
});
}
@Nullable
@@ -5,31 +5,11 @@ import com.jetbrains.edu.learning.settings.StudyOptionsProvider;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class CCOptions implements StudyOptionsProvider {
private JRadioButton myHtmlRadioButton;
private JRadioButton myMarkdownRadioButton;
private JPanel myPanel;
private boolean myIsModified = false;
public CCOptions() {
myHtmlRadioButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
myIsModified = true;
}
});
myMarkdownRadioButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
myIsModified = true;
}
});
}
@Nullable
@Override
@@ -47,12 +27,15 @@ public class CCOptions implements StudyOptionsProvider {
@Override
public boolean isModified() {
return myIsModified;
final boolean htmlAsDefaultTaskFormat = CCSettings.getInstance().useHtmlAsDefaultTaskFormat();
return myHtmlRadioButton.isSelected() != htmlAsDefaultTaskFormat;
}
@Override
public void apply() throws ConfigurationException {
CCSettings.getInstance().setUseHtmlAsDefaultTaskFormat(myHtmlRadioButton.isSelected());
if (isModified()) {
CCSettings.getInstance().setUseHtmlAsDefaultTaskFormat(myHtmlRadioButton.isSelected());
}
}
@Override
@@ -476,23 +476,26 @@ public class StudySerializationUtils {
}
private static JsonObject convertSubtaskInfosToMap(JsonObject stepOptionsJson) {
for (JsonElement taskFileElement : stepOptionsJson.getAsJsonArray(FILES)) {
JsonObject taskFileObject = taskFileElement.getAsJsonObject();
JsonArray placeholders = taskFileObject.getAsJsonArray(PLACEHOLDERS);
for (JsonElement placeholder : placeholders) {
JsonObject placeholderObject = placeholder.getAsJsonObject();
JsonArray infos = placeholderObject.getAsJsonArray(SUBTASK_INFOS);
Map<Integer, JsonObject> objectsToInsert = new HashMap<>();
for (JsonElement info : infos) {
JsonObject object = info.getAsJsonObject();
int index = object.getAsJsonPrimitive(INDEX).getAsInt();
objectsToInsert.put(index, object);
}
placeholderObject.remove(SUBTASK_INFOS);
JsonObject newInfos = new JsonObject();
placeholderObject.add(SUBTASK_INFOS, newInfos);
for (Map.Entry<Integer, JsonObject> entry : objectsToInsert.entrySet()) {
newInfos.add(entry.getKey().toString(), entry.getValue());
final JsonArray files = stepOptionsJson.getAsJsonArray(FILES);
if (files != null) {
for (JsonElement taskFileElement : files) {
JsonObject taskFileObject = taskFileElement.getAsJsonObject();
JsonArray placeholders = taskFileObject.getAsJsonArray(PLACEHOLDERS);
for (JsonElement placeholder : placeholders) {
JsonObject placeholderObject = placeholder.getAsJsonObject();
JsonArray infos = placeholderObject.getAsJsonArray(SUBTASK_INFOS);
Map<Integer, JsonObject> objectsToInsert = new HashMap<>();
for (JsonElement info : infos) {
JsonObject object = info.getAsJsonObject();
int index = object.getAsJsonPrimitive(INDEX).getAsInt();
objectsToInsert.put(index, object);
}
placeholderObject.remove(SUBTASK_INFOS);
JsonObject newInfos = new JsonObject();
placeholderObject.add(SUBTASK_INFOS, newInfos);
for (Map.Entry<Integer, JsonObject> entry : objectsToInsert.entrySet()) {
newInfos.add(entry.getKey().toString(), entry.getValue());
}
}
}
}
@@ -501,14 +504,17 @@ public class StudySerializationUtils {
private static JsonObject convertToSecondVersion(JsonObject stepOptionsJson) {
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
for (JsonElement taskFileElement : stepOptionsJson.getAsJsonArray(FILES)) {
JsonObject taskFileObject = taskFileElement.getAsJsonObject();
JsonArray placeholders = taskFileObject.getAsJsonArray(PLACEHOLDERS);
for (JsonElement placeholder : placeholders) {
JsonObject placeholderObject = placeholder.getAsJsonObject();
convertToAbsoluteOffset(taskFileObject, placeholderObject);
convertMultipleHints(gson, placeholderObject);
convertToSubtaskInfo(placeholderObject);
final JsonArray files = stepOptionsJson.getAsJsonArray(FILES);
if (files != null) {
for (JsonElement taskFileElement : files) {
JsonObject taskFileObject = taskFileElement.getAsJsonObject();
JsonArray placeholders = taskFileObject.getAsJsonArray(PLACEHOLDERS);
for (JsonElement placeholder : placeholders) {
JsonObject placeholderObject = placeholder.getAsJsonObject();
convertToAbsoluteOffset(taskFileObject, placeholderObject);
convertMultipleHints(gson, placeholderObject);
convertToSubtaskInfo(placeholderObject);
}
}
}
return stepOptionsJson;
@@ -182,6 +182,7 @@ public class EduStepicAuthorizedClient {
return stepicUser[0];
}
@Nullable
public static StepicUser login(@NotNull final String email, @NotNull final String password) {
final List<NameValuePair> parameters = new ArrayList<>();
if (password.isEmpty()) return null;
@@ -16,6 +16,8 @@
package com.jetbrains.edu.learning.stepic;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.DocumentAdapter;
@@ -27,68 +29,28 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.Document;
import javax.swing.text.PlainDocument;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
public class StepicStudyOptions implements StudyOptionsProvider {
private static final String DEFAULT_PASSWORD_TEXT = "************";
private static final Logger LOG = Logger.getInstance(StepicStudyOptions.class);
private JTextField myLoginTextField;
private JPasswordField myPasswordField;
private JPanel myPane;
private JBCheckBox myEnableTestingFromSamples;
private boolean myCredentialsModified;
public StepicStudyOptions() {
myPasswordField.getDocument().addDocumentListener(new DocumentAdapter() {
myLoginTextField.getDocument().addDocumentListener(new DocumentAdapter() {
@Override
protected void textChanged(DocumentEvent e) {
myCredentialsModified = true;
erasePassword();
}
});
DocumentListener passwordEraser = new DocumentAdapter() {
@Override
protected void textChanged(DocumentEvent e) {
if (!myCredentialsModified) {
erasePassword();
}
}
};
myLoginTextField.getDocument().addDocumentListener(passwordEraser);
myPasswordField.addFocusListener(new FocusListener() {
@Override
public void focusGained(FocusEvent e) {
if (!myCredentialsModified && !getPassword().isEmpty()) {
erasePassword();
}
}
@Override
public void focusLost(FocusEvent e) {
}
});
myEnableTestingFromSamples.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
myCredentialsModified = true;
}
});
reset();
}
private void erasePassword() {
setPassword("");
myCredentialsModified = true;
}
@NotNull
@@ -107,12 +69,15 @@ public class StepicStudyOptions implements StudyOptionsProvider {
@NotNull
private String getPassword() {
final String passwordText = String.valueOf(myPasswordField.getPassword());
return passwordText.equals(DEFAULT_PASSWORD_TEXT) ? "" : passwordText;
return String.valueOf(myPasswordField.getPassword());
}
private void setPassword(@NotNull final String password) {
myPasswordField.setText(StringUtil.isEmpty(password) ? null : password);
myPasswordField.setText(password);
}
private boolean isTestingFromSamplesEnabled() {
return myEnableTestingFromSamples.isSelected();
}
@Override
@@ -122,9 +87,8 @@ public class StepicStudyOptions implements StudyOptionsProvider {
StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
final StepicUser user = taskManager.getUser();
setLogin(user.getEmail());
setPassword(DEFAULT_PASSWORD_TEXT);
setPassword(user.getPassword());
myEnableTestingFromSamples.setSelected(taskManager.isEnableTestingFromSamples());
resetCredentialsModification();
}
else {
LOG.warn("No study object is opened");
@@ -137,31 +101,42 @@ public class StepicStudyOptions implements StudyOptionsProvider {
}
@Override
public void apply() {
if (myCredentialsModified) {
final Project project = StudyUtils.getStudyProject();
if (project != null) {
StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
taskManager.setEnableTestingFromSamples(myEnableTestingFromSamples.isSelected());
public void apply() throws ConfigurationException {
final Project project = StudyUtils.getStudyProject();
if (project != null) {
StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
if (isTestingFromSamplesEnabled() != taskManager.isEnableTestingFromSamples()){
taskManager.setEnableTestingFromSamples(isTestingFromSamplesEnabled());
}
final StepicUser user = taskManager.getUser();
final boolean isCredentialsModified = !getLogin().equals(user.getEmail()) || !getPassword().equals(user.getPassword());
if (isCredentialsModified) {
final String login = getLogin();
final String password = getPassword();
if (!StringUtil.isEmptyOrSpaces(login) && !StringUtil.isEmptyOrSpaces(password)) {
final StepicUser user = taskManager.getUser();
user.setEmail(login);
user.setPassword(password);
// login to post credentials
final StepicUser stepicUser = EduStepicAuthorizedClient.login(login, password);
if (stepicUser != null) {
taskManager.setUser(stepicUser);
final StepicUser[] stepicUser = new StepicUser[1];
ProgressManager.getInstance().runProcessWithProgressSynchronously(
() -> {
ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true);
stepicUser[0] = StudyUtils.execCancelable(() -> EduStepicAuthorizedClient.login(login, password));
}, "Logging In", true,
project);
if (stepicUser[0] != null && stepicUser[0].getAccessToken() != null) {
taskManager.setUser(stepicUser[0]);
}
else {
throw new ConfigurationException("Unable to login");
}
}
}
else {
LOG.warn("No study object is opened");
}
}
resetCredentialsModification();
else {
LOG.warn("No study object is opened");
}
}
@Nullable
@@ -171,11 +146,15 @@ public class StepicStudyOptions implements StudyOptionsProvider {
}
public boolean isModified() {
return myCredentialsModified;
}
public void resetCredentialsModification() {
myCredentialsModified = false;
final Project project = StudyUtils.getStudyProject();
if (project == null) return false;
final StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
final StepicUser user = taskManager.getUser();
return !getLogin().equals(user.getEmail())
|| !getPassword().equals(user.getPassword())
|| !isTestingFromSamplesEnabled() == taskManager.isEnableTestingFromSamples();
}
private void createUIComponents() {