diff --git a/.idea/libraries/automaton.xml b/.idea/libraries/automaton.xml new file mode 100644 index 000000000000..59072d2a7927 --- /dev/null +++ b/.idea/libraries/automaton.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/RegExpSupport/gen/org/intellij/lang/regexp/_RegExLexer.java b/RegExpSupport/gen/org/intellij/lang/regexp/_RegExLexer.java index 651fc47a6006..802cc4efe2c7 100644 --- a/RegExpSupport/gen/org/intellij/lang/regexp/_RegExLexer.java +++ b/RegExpSupport/gen/org/intellij/lang/regexp/_RegExLexer.java @@ -1,19 +1,3 @@ -/* - * 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. - */ - /* The following code was generated by JFlex 1.7.0-SNAPSHOT tweaked for IntelliJ platform */ /* It's an automatically generated code. Do not modify it. */ @@ -144,12 +128,12 @@ class _RegExLexer implements FlexLexer { /* The ZZ_CMAP_A table has 3056 entries */ static final char ZZ_CMAP_A[] = zzUnpackCMap( - "\10\0\2\65\1\60\1\61\1\62\1\60\22\0\1\55\1\70\1\0\1\71\1\17\1\0\1\64\1\53"+ - "\1\4\1\5\1\21\1\22\1\57\1\47\1\3\1\0\1\2\7\46\2\1\1\63\1\0\1\52\1\67\1\66"+ - "\1\20\1\0\2\26\1\35\1\33\1\43\1\41\1\27\1\50\1\36\2\13\3\56\1\13\1\40\1\42"+ - "\1\31\1\32\2\13\3\31\1\13\1\30\1\10\1\12\1\11\1\16\1\14\1\0\1\25\1\26\1\34"+ - "\1\33\2\25\1\54\1\50\1\36\1\13\1\51\2\13\1\24\1\13\1\37\1\13\1\24\1\31\1\24"+ - "\1\45\2\31\1\44\1\13\1\27\1\6\1\23\1\7\7\0\1\61\24\0\1\13\12\0\1\13\4\0\1"+ + "\10\0\2\66\1\61\1\62\1\63\1\61\22\0\1\56\1\71\1\0\1\72\1\17\1\0\1\65\1\55"+ + "\1\4\1\5\1\21\1\22\1\60\1\51\1\3\1\0\1\2\7\50\2\1\1\64\1\0\1\54\1\70\1\67"+ + "\1\20\1\0\2\30\1\37\1\35\1\45\1\43\1\31\1\52\1\40\2\13\3\57\1\13\1\42\1\44"+ + "\1\33\1\34\2\13\3\33\1\13\1\32\1\10\1\12\1\11\1\16\1\14\1\0\1\25\1\26\1\36"+ + "\1\35\2\25\1\27\1\52\1\40\1\13\1\53\2\13\1\24\1\13\1\41\1\13\1\24\1\33\1\24"+ + "\1\47\2\33\1\46\1\13\1\31\1\6\1\23\1\7\7\0\1\62\24\0\1\13\12\0\1\13\4\0\1"+ "\13\5\0\27\13\1\0\12\13\4\0\14\13\16\0\5\13\7\0\1\13\1\0\1\13\1\0\5\13\1\0"+ "\2\13\2\0\4\13\1\0\1\13\6\0\1\13\1\0\3\13\1\0\1\13\1\0\4\13\1\0\23\13\1\0"+ "\13\13\10\0\6\13\1\0\26\13\2\0\1\13\6\0\10\13\10\0\13\13\5\0\3\13\15\0\12"+ @@ -173,7 +157,7 @@ class _RegExLexer implements FlexLexer { "\1\13\5\0\17\13\1\0\16\13\2\0\14\13\13\0\1\13\15\0\7\13\7\0\16\13\15\0\2\13"+ "\12\15\3\0\3\13\11\0\4\13\1\0\4\13\3\0\2\13\11\0\10\13\1\0\1\13\1\0\1\13\1"+ "\0\1\13\1\0\6\13\1\0\7\13\1\0\1\13\3\0\3\13\1\0\7\13\3\0\4\13\2\0\6\13\14"+ - "\0\2\61\7\0\1\13\15\0\1\13\2\0\1\13\4\0\1\13\2\0\12\13\1\0\1\13\3\0\5\13\6"+ + "\0\2\62\7\0\1\13\15\0\1\13\2\0\1\13\4\0\1\13\2\0\12\13\1\0\1\13\3\0\5\13\6"+ "\0\1\13\1\0\1\13\1\0\1\13\1\0\4\13\1\0\13\13\2\0\4\13\5\0\5\13\4\0\1\13\4"+ "\0\2\13\13\0\5\13\6\0\4\13\3\0\2\13\14\0\10\13\7\0\10\13\1\0\7\13\6\0\2\13"+ "\12\0\5\13\5\0\2\13\3\0\7\13\6\0\3\13\12\15\2\13\13\0\11\13\2\0\27\13\2\0"+ @@ -207,16 +191,17 @@ class _RegExLexer implements FlexLexer { "\1\10\1\36\1\2\1\37\1\40\1\37\1\41\1\42"+ "\1\43\1\44\1\45\1\46\1\1\1\47\1\50\2\51"+ "\1\52\1\53\1\0\1\54\1\55\1\56\1\57\1\60"+ - "\1\10\1\61\1\62\1\63\2\64\1\65\1\66\1\67"+ - "\1\70\1\55\1\71\1\72\2\10\1\73\1\74\1\75"+ - "\1\76\1\77\1\100\1\101\1\102\1\0\1\103\1\104"+ - "\1\105\1\106\1\107\1\0\1\110\1\111\1\112\2\67"+ - "\1\70\1\55\1\113\1\114\1\115\1\116\1\117\1\120"+ - "\1\121\1\122\1\0\1\111\1\123\2\67\1\70\1\124"+ - "\1\125\1\111\1\67\1\70\1\67\1\126\4\67"; + "\1\10\1\61\1\62\1\10\1\62\1\63\2\64\1\65"+ + "\1\66\1\67\1\70\1\55\1\71\1\72\1\10\1\73"+ + "\1\74\1\75\1\76\1\77\1\100\1\101\1\102\1\0"+ + "\1\103\1\104\1\105\1\106\1\107\1\0\1\110\1\111"+ + "\1\0\1\112\1\113\1\114\2\67\1\70\1\55\1\115"+ + "\1\116\1\117\1\120\1\121\1\122\1\0\1\111\1\0"+ + "\1\123\2\67\1\70\1\124\1\125\1\111\1\67\1\70"+ + "\1\67\1\126\4\67"; private static int [] zzUnpackAction() { - int [] result = new int[130]; + int [] result = new int[133]; int offset = 0; offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result); return result; @@ -241,26 +226,26 @@ class _RegExLexer implements FlexLexer { private static final int [] ZZ_ROWMAP = zzUnpackRowMap(); private static final String ZZ_ROWMAP_PACKED_0 = - "\0\0\0\72\0\164\0\256\0\350\0\u0122\0\u015c\0\u0196"+ - "\0\u01d0\0\u020a\0\u0244\0\u027e\0\u02b8\0\u02f2\0\u032c\0\u032c"+ - "\0\u0366\0\u032c\0\u032c\0\u03a0\0\u03da\0\u032c\0\u032c\0\u032c"+ - "\0\u032c\0\u032c\0\u032c\0\u032c\0\u032c\0\u032c\0\u032c\0\u0414"+ - "\0\u032c\0\u044e\0\u032c\0\u0488\0\u032c\0\u032c\0\u032c\0\u032c"+ - "\0\u032c\0\u04c2\0\u032c\0\u04fc\0\u032c\0\u0536\0\u032c\0\u032c"+ - "\0\u03a0\0\u032c\0\u032c\0\u032c\0\u0570\0\u05aa\0\u032c\0\u032c"+ - "\0\u032c\0\u032c\0\u032c\0\u05e4\0\u061e\0\u032c\0\u0658\0\u032c"+ - "\0\u0692\0\u06cc\0\u032c\0\u032c\0\u032c\0\u032c\0\u032c\0\u032c"+ - "\0\u0706\0\u032c\0\u032c\0\u032c\0\u0740\0\u077a\0\u07b4\0\u032c"+ - "\0\u032c\0\u07ee\0\u0828\0\u032c\0\u032c\0\u032c\0\u032c\0\u032c"+ - "\0\u032c\0\u032c\0\u032c\0\u0862\0\u089c\0\u032c\0\u032c\0\u032c"+ - "\0\u032c\0\u08d6\0\u032c\0\u0910\0\u032c\0\u094a\0\u0984\0\u09be"+ - "\0\u09f8\0\u032c\0\u032c\0\u032c\0\u032c\0\u032c\0\u032c\0\u032c"+ - "\0\u032c\0\u0a32\0\u0a6c\0\u032c\0\u0aa6\0\u032c\0\u0ae0\0\u0692"+ - "\0\u032c\0\u032c\0\u0b1a\0\u0b54\0\u0b8e\0\u032c\0\u0bc8\0\u0c02"+ - "\0\u0c3c\0\u0c76"; + "\0\0\0\73\0\166\0\261\0\354\0\u0127\0\u0162\0\u019d"+ + "\0\u01d8\0\u0213\0\u024e\0\u0289\0\u02c4\0\u02ff\0\u033a\0\u033a"+ + "\0\u0375\0\u033a\0\u033a\0\u03b0\0\u03eb\0\u033a\0\u033a\0\u033a"+ + "\0\u033a\0\u033a\0\u033a\0\u033a\0\u033a\0\u033a\0\u033a\0\u0426"+ + "\0\u033a\0\u0461\0\u033a\0\u049c\0\u033a\0\u033a\0\u033a\0\u033a"+ + "\0\u033a\0\u04d7\0\u033a\0\u0512\0\u033a\0\u054d\0\u033a\0\u033a"+ + "\0\u03b0\0\u033a\0\u033a\0\u033a\0\u0588\0\u05c3\0\u033a\0\u033a"+ + "\0\u033a\0\u033a\0\u033a\0\u05fe\0\u0639\0\u033a\0\u0674\0\u033a"+ + "\0\u06af\0\u06ea\0\u033a\0\u033a\0\u033a\0\u033a\0\u0725\0\u0760"+ + "\0\u033a\0\u033a\0\u079b\0\u033a\0\u033a\0\u033a\0\u07d6\0\u0811"+ + "\0\u084c\0\u033a\0\u033a\0\u0887\0\u033a\0\u033a\0\u033a\0\u033a"+ + "\0\u033a\0\u033a\0\u033a\0\u033a\0\u08c2\0\u08fd\0\u033a\0\u033a"+ + "\0\u033a\0\u033a\0\u0938\0\u033a\0\u0973\0\u09ae\0\u033a\0\u033a"+ + "\0\u033a\0\u09e9\0\u0a24\0\u0a5f\0\u0a9a\0\u033a\0\u033a\0\u033a"+ + "\0\u033a\0\u033a\0\u033a\0\u0ad5\0\u0b10\0\u0b4b\0\u033a\0\u0b86"+ + "\0\u033a\0\u0bc1\0\u06af\0\u033a\0\u033a\0\u0bfc\0\u0c37\0\u0c72"+ + "\0\u033a\0\u0cad\0\u0ce8\0\u0d23\0\u0d5e"; private static int [] zzUnpackRowMap() { - int [] result = new int[130]; + int [] result = new int[133]; int offset = 0; offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result); return result; @@ -285,78 +270,81 @@ class _RegExLexer implements FlexLexer { private static final String ZZ_TRANS_PACKED_0 = "\3\17\1\20\1\21\1\22\1\23\1\17\1\24\1\17"+ "\1\25\3\17\1\26\1\27\1\30\1\31\1\32\1\33"+ - "\23\17\1\34\5\17\1\35\2\17\1\36\1\17\1\36"+ - "\2\17\1\36\3\17\1\37\12\17\1\40\57\17\1\41"+ + "\25\17\1\34\4\17\1\35\2\17\1\36\1\17\1\36"+ + "\2\17\1\36\3\17\1\37\12\17\1\40\60\17\1\41"+ "\2\42\4\41\1\43\3\41\1\44\1\41\1\42\1\45"+ - "\5\41\22\44\1\42\1\41\2\44\2\41\1\44\1\41"+ - "\1\44\1\46\12\41\11\47\1\50\46\47\3\0\25\47"+ - "\1\51\41\47\3\0\7\47\6\17\1\23\1\17\1\52"+ - "\1\53\1\54\3\17\1\26\30\17\1\34\10\17\1\55"+ - "\1\17\1\55\1\17\1\56\1\55\4\17\6\57\1\60"+ - "\1\57\1\61\1\57\1\25\15\57\1\62\1\57\1\62"+ - "\2\57\1\62\2\57\1\62\15\57\1\62\13\57\5\63"+ - "\1\64\5\63\1\65\10\63\22\65\1\63\1\66\2\65"+ - "\2\63\1\65\1\63\1\65\4\63\1\67\6\63\60\11"+ - "\1\70\11\11\13\63\1\44\10\63\22\44\2\63\2\44"+ - "\2\63\1\44\1\63\1\44\7\63\1\71\16\63\1\44"+ - "\10\63\22\44\2\63\2\44\1\63\1\72\1\44\1\63"+ - "\1\44\20\63\1\64\5\63\1\44\10\63\22\44\2\63"+ - "\2\44\2\63\1\44\1\63\1\44\14\63\2\42\2\63"+ - "\1\64\5\63\1\44\1\63\1\42\6\63\22\44\1\42"+ - "\1\63\2\44\2\63\1\44\1\63\1\44\13\63\13\73"+ - "\1\44\2\73\1\26\5\73\22\44\2\73\2\44\2\73"+ - "\1\44\1\73\1\44\4\73\1\74\6\73\112\0\1\75"+ - "\62\0\1\76\4\0\1\77\53\0\1\100\1\101\1\102"+ - "\4\103\1\100\1\104\1\100\1\104\1\105\2\100\1\104"+ - "\5\103\2\106\3\107\3\110\1\111\2\112\2\113\1\105"+ - "\1\114\1\105\1\115\1\116\1\117\1\120\1\121\1\122"+ - "\2\100\1\123\1\124\1\105\1\100\1\124\1\100\1\124"+ - "\2\100\1\124\4\100\43\0\1\125\27\0\2\42\12\0"+ - "\1\42\30\0\1\42\24\0\2\44\10\0\3\44\6\0"+ - "\23\44\1\0\2\44\2\0\1\44\1\0\1\44\24\0"+ - "\1\126\4\0\1\77\44\0\1\127\6\0\1\100\1\101"+ - "\1\102\4\103\1\100\1\104\1\130\1\104\1\105\2\100"+ - "\1\104\5\103\2\106\3\107\3\110\1\111\2\112\2\113"+ - "\1\105\1\114\1\105\1\115\1\116\1\117\1\120\1\121"+ - "\1\122\2\100\1\123\1\124\1\105\1\100\1\124\1\100"+ - "\1\124\2\100\1\124\4\100\64\0\1\131\20\0\1\65"+ - "\10\0\22\65\2\0\2\65\2\0\1\65\1\0\1\65"+ - "\26\0\1\66\10\0\22\66\2\0\2\66\2\0\1\66"+ - "\1\0\1\66\24\0\1\132\64\0\1\133\33\0\1\134"+ - "\11\0\1\135\1\136\7\0\1\137\2\0\1\137\1\140"+ - "\1\141\1\142\11\0\1\143\61\0\2\101\43\0\1\101"+ - "\25\0\1\144\43\0\1\144\23\0\72\145\1\0\2\146"+ - "\3\0\1\147\16\0\2\146\4\0\3\146\3\0\1\146"+ - "\1\0\1\146\2\0\1\146\24\0\2\150\22\0\2\150"+ - "\4\0\3\150\3\0\1\150\1\0\1\150\2\0\1\150"+ - "\24\0\1\101\1\151\43\0\1\151\75\0\1\152\1\153"+ - "\70\0\1\154\1\155\70\0\1\156\14\0\1\157\71\0"+ - "\1\160\1\161\1\0\5\162\1\0\64\162\2\0\1\163"+ - "\43\0\1\163\24\0\2\164\22\0\2\164\4\0\3\164"+ - "\3\0\1\164\1\0\1\164\2\0\1\164\24\0\2\165"+ - "\4\0\1\166\15\0\2\165\4\0\3\165\3\0\1\165"+ - "\1\0\1\165\2\0\1\165\24\0\2\167\22\0\2\167"+ - "\4\0\3\167\3\0\1\167\1\0\1\167\2\0\1\167"+ - "\24\0\1\101\1\170\43\0\1\170\23\0\5\162\1\171"+ - "\64\162\2\0\1\172\43\0\1\172\24\0\2\173\4\0"+ - "\1\164\15\0\2\173\4\0\3\173\3\0\1\173\1\0"+ - "\1\173\2\0\1\173\24\0\2\174\22\0\2\174\4\0"+ - "\3\174\3\0\1\174\1\0\1\174\2\0\1\174\24\0"+ - "\2\175\4\0\1\164\15\0\2\175\4\0\3\175\3\0"+ - "\1\175\1\0\1\175\2\0\1\175\24\0\2\176\22\0"+ - "\2\176\4\0\3\176\3\0\1\176\1\0\1\176\2\0"+ - "\1\176\24\0\2\177\4\0\1\164\15\0\2\177\4\0"+ - "\3\177\3\0\1\177\1\0\1\177\2\0\1\177\24\0"+ - "\2\200\4\0\1\164\15\0\2\200\4\0\3\200\3\0"+ - "\1\200\1\0\1\200\2\0\1\200\24\0\2\201\4\0"+ - "\1\164\15\0\2\201\4\0\3\201\3\0\1\201\1\0"+ - "\1\201\2\0\1\201\24\0\2\202\4\0\1\164\15\0"+ - "\2\202\4\0\3\202\3\0\1\202\1\0\1\202\2\0"+ - "\1\202\24\0\2\202\4\0\1\166\15\0\2\202\4\0"+ - "\3\202\3\0\1\202\1\0\1\202\2\0\1\202\23\0"; + "\5\41\24\44\1\42\1\41\2\44\3\41\1\44\1\46"+ + "\12\41\11\47\1\50\47\47\3\0\25\47\1\51\42\47"+ + "\3\0\7\47\6\17\1\23\1\17\1\52\1\53\1\54"+ + "\3\17\1\26\32\17\1\34\7\17\1\55\1\17\1\55"+ + "\1\17\1\56\1\55\4\17\6\57\1\60\1\57\1\61"+ + "\1\57\1\25\17\57\1\62\1\57\1\62\2\57\1\62"+ + "\2\57\1\62\14\57\1\62\13\57\5\63\1\64\5\63"+ + "\1\65\10\63\24\65\1\63\1\66\2\65\3\63\1\65"+ + "\4\63\1\67\6\63\61\11\1\70\11\11\13\63\1\44"+ + "\10\63\24\44\2\63\2\44\3\63\1\44\7\63\1\71"+ + "\16\63\1\44\10\63\24\44\2\63\2\44\1\63\1\72"+ + "\1\63\1\44\20\63\1\64\5\63\1\44\10\63\24\44"+ + "\2\63\2\44\3\63\1\44\14\63\2\42\2\63\1\64"+ + "\5\63\1\44\1\63\1\42\6\63\24\44\1\42\1\63"+ + "\2\44\3\63\1\44\13\63\13\73\1\44\2\73\1\26"+ + "\5\73\24\44\2\73\2\44\3\73\1\44\4\73\1\74"+ + "\6\73\113\0\1\75\63\0\1\76\4\0\1\77\54\0"+ + "\1\100\1\101\1\102\4\103\1\100\1\104\1\100\1\104"+ + "\1\105\2\100\1\104\5\103\2\106\1\107\1\110\3\111"+ + "\3\112\1\113\2\114\2\115\1\105\1\116\1\105\1\117"+ + "\1\120\1\121\1\122\1\123\1\124\2\100\1\125\1\105"+ + "\1\100\1\125\1\100\1\125\2\100\1\125\4\100\45\0"+ + "\1\126\26\0\2\42\12\0\1\42\32\0\1\42\23\0"+ + "\2\44\10\0\3\44\6\0\25\44\1\0\2\44\3\0"+ + "\1\44\24\0\1\127\4\0\1\77\45\0\1\130\6\0"+ + "\1\100\1\101\1\102\4\103\1\100\1\104\1\131\1\104"+ + "\1\105\2\100\1\104\5\103\2\106\1\107\1\110\3\111"+ + "\3\112\1\113\2\114\2\115\1\105\1\116\1\105\1\117"+ + "\1\120\1\121\1\122\1\123\1\124\2\100\1\125\1\105"+ + "\1\100\1\125\1\100\1\125\2\100\1\125\4\100\65\0"+ + "\1\132\20\0\1\65\10\0\24\65\2\0\2\65\3\0"+ + "\1\65\26\0\1\66\10\0\24\66\2\0\2\66\3\0"+ + "\1\66\24\0\1\133\65\0\1\134\35\0\1\135\11\0"+ + "\1\136\1\137\6\0\1\140\2\0\1\140\1\141\1\142"+ + "\1\143\11\0\1\144\62\0\2\101\45\0\1\101\24\0"+ + "\1\145\45\0\1\145\30\0\1\146\140\0\1\147\1\150"+ + "\15\0\73\151\1\0\2\152\3\0\1\153\16\0\2\152"+ + "\1\0\1\152\4\0\3\152\3\0\1\152\1\0\1\152"+ + "\2\0\1\152\23\0\2\154\22\0\2\154\1\0\1\154"+ + "\4\0\3\154\3\0\1\154\1\0\1\154\2\0\1\154"+ + "\23\0\1\101\1\155\45\0\1\155\76\0\1\156\1\157"+ + "\71\0\1\160\13\0\1\161\72\0\1\162\1\163\1\0"+ + "\5\164\1\0\65\164\2\0\1\165\45\0\1\165\51\0"+ + "\1\166\44\0\2\167\22\0\2\167\1\0\1\167\4\0"+ + "\3\167\3\0\1\167\1\0\1\167\2\0\1\167\23\0"+ + "\2\170\4\0\1\171\15\0\2\170\1\0\1\170\4\0"+ + "\3\170\3\0\1\170\1\0\1\170\2\0\1\170\23\0"+ + "\2\172\22\0\2\172\1\0\1\172\4\0\3\172\3\0"+ + "\1\172\1\0\1\172\2\0\1\172\23\0\1\101\1\173"+ + "\45\0\1\173\22\0\5\164\1\174\65\164\2\0\1\175"+ + "\45\0\1\175\31\0\1\111\64\0\2\176\4\0\1\167"+ + "\15\0\2\176\1\0\1\176\4\0\3\176\3\0\1\176"+ + "\1\0\1\176\2\0\1\176\23\0\2\177\22\0\2\177"+ + "\1\0\1\177\4\0\3\177\3\0\1\177\1\0\1\177"+ + "\2\0\1\177\23\0\2\200\4\0\1\167\15\0\2\200"+ + "\1\0\1\200\4\0\3\200\3\0\1\200\1\0\1\200"+ + "\2\0\1\200\23\0\2\201\22\0\2\201\1\0\1\201"+ + "\4\0\3\201\3\0\1\201\1\0\1\201\2\0\1\201"+ + "\23\0\2\202\4\0\1\167\15\0\2\202\1\0\1\202"+ + "\4\0\3\202\3\0\1\202\1\0\1\202\2\0\1\202"+ + "\23\0\2\203\4\0\1\167\15\0\2\203\1\0\1\203"+ + "\4\0\3\203\3\0\1\203\1\0\1\203\2\0\1\203"+ + "\23\0\2\204\4\0\1\167\15\0\2\204\1\0\1\204"+ + "\4\0\3\204\3\0\1\204\1\0\1\204\2\0\1\204"+ + "\23\0\2\205\4\0\1\167\15\0\2\205\1\0\1\205"+ + "\4\0\3\205\3\0\1\205\1\0\1\205\2\0\1\205"+ + "\23\0\2\205\4\0\1\171\15\0\2\205\1\0\1\205"+ + "\4\0\3\205\3\0\1\205\1\0\1\205\2\0\1\205"+ + "\22\0"; private static int [] zzUnpackTrans() { - int [] result = new int[3248]; + int [] result = new int[3481]; int offset = 0; offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result); return result; @@ -397,13 +385,14 @@ class _RegExLexer implements FlexLexer { "\10\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\5\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\11\2\1\6\11\1\1\3\11"+ - "\3\1\2\11\2\1\10\11\1\0\1\1\4\11\1\0"+ - "\1\11\1\1\1\11\4\1\10\11\1\0\1\1\1\11"+ - "\1\1\1\11\2\1\2\11\3\1\1\11\4\1"; + "\2\1\1\11\1\0\1\11\2\1\4\11\2\1\2\11"+ + "\1\1\3\11\3\1\2\11\1\1\10\11\1\0\1\1"+ + "\4\11\1\0\1\11\1\1\1\0\3\11\4\1\6\11"+ + "\1\0\1\1\1\0\1\11\1\1\1\11\2\1\2\11"+ + "\3\1\1\11\4\1"; private static int [] zzUnpackAttribute() { - int [] result = new int[130]; + int [] result = new int[133]; int offset = 0; offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result); return result; @@ -1094,23 +1083,23 @@ class _RegExLexer implements FlexLexer { } case 159: break; case 74: - { if (xmlSchemaMode) { yypushback(1); return RegExpTT.CHAR_CLASS; } else return RegExpTT.CTRL; + { yybegin(NAMED_GROUP); return RegExpTT.RUBY_NAMED_GROUP_CALL; } case 160: break; case 75: - { yybegin(NAMED_GROUP); return RegExpTT.RUBY_NAMED_GROUP_REF; + { yybegin(QUOTED_NAMED_GROUP); return RegExpTT.RUBY_QUOTED_NAMED_GROUP_CALL; } case 161: break; case 76: - { yybegin(QUOTED_NAMED_GROUP); return RegExpTT.RUBY_QUOTED_NAMED_GROUP_REF; + { if (xmlSchemaMode) { yypushback(1); return RegExpTT.CHAR_CLASS; } else return RegExpTT.CTRL; } case 162: break; case 77: - { yybegin(NAMED_GROUP); return RegExpTT.RUBY_NAMED_GROUP_CALL; + { yybegin(NAMED_GROUP); return RegExpTT.RUBY_NAMED_GROUP_REF; } case 163: break; case 78: - { yybegin(QUOTED_NAMED_GROUP); return RegExpTT.RUBY_QUOTED_NAMED_GROUP_CALL; + { yybegin(QUOTED_NAMED_GROUP); return RegExpTT.RUBY_QUOTED_NAMED_GROUP_REF; } case 164: break; case 79: diff --git a/RegExpSupport/src/org/intellij/lang/regexp/DefaultRegExpPropertiesProvider.java b/RegExpSupport/src/org/intellij/lang/regexp/DefaultRegExpPropertiesProvider.java index 4a2e8e48bbab..cfb2b8773944 100644 --- a/RegExpSupport/src/org/intellij/lang/regexp/DefaultRegExpPropertiesProvider.java +++ b/RegExpSupport/src/org/intellij/lang/regexp/DefaultRegExpPropertiesProvider.java @@ -113,6 +113,7 @@ public final class DefaultRegExpPropertiesProvider { {"w", "Word character: [a-zA-Z_0-9]"}, {"W", "Nonword character: [^\\w]"}, {"b", "Word boundary"}, + {"b{g}", "Unicode extended grapheme cluster boundary"}, {"B", "Non-word boundary"}, {"A", "Beginning of the input"}, {"G", "End of the previous match"}, diff --git a/RegExpSupport/src/org/intellij/lang/regexp/RegExpLanguageHost.java b/RegExpSupport/src/org/intellij/lang/regexp/RegExpLanguageHost.java index c530f61e03dd..834332cab4c8 100644 --- a/RegExpSupport/src/org/intellij/lang/regexp/RegExpLanguageHost.java +++ b/RegExpSupport/src/org/intellij/lang/regexp/RegExpLanguageHost.java @@ -36,6 +36,23 @@ public interface RegExpLanguageHost { return true; } + default boolean supportsBoundary(RegExpBoundary boundary) { + switch (boundary.getType()) { + case UNICODE_EXTENDED_GRAPHEME: + return false; + case LINE_START: + case LINE_END: + case WORD: + case NON_WORD: + case BEGIN: + case END: + case END_NO_LINE_TERM: + case PREVIOUS_MATCH: + default: + return true; + } + } + default boolean supportsLiteralBackspace(RegExpChar aChar) { return true; } diff --git a/RegExpSupport/src/org/intellij/lang/regexp/RegExpLanguageHosts.java b/RegExpSupport/src/org/intellij/lang/regexp/RegExpLanguageHosts.java index 2d3bd894144c..eed7db6fa61f 100644 --- a/RegExpSupport/src/org/intellij/lang/regexp/RegExpLanguageHosts.java +++ b/RegExpSupport/src/org/intellij/lang/regexp/RegExpLanguageHosts.java @@ -131,6 +131,11 @@ public final class RegExpLanguageHosts extends ClassExtension nameList = new ArrayList<>(Arrays.asList("d", "D", "s", "S", "w", "W", "b", "B", "A", "G", "Z", "z", "Q", "E", - "t", "n", "r", "f", "a", "e", "h", "H", "v", "V", "R", "X")); + "t", "n", "r", "f", "a", "e", "h", "H", "v", "V", "R", "X", "b{g}")); for (String[] stringArray : DefaultRegExpPropertiesProvider.getInstance().getAllKnownProperties()) { nameList.add("p{" + stringArray[0] + "}"); } diff --git a/RegExpSupport/testData/RETest.xml b/RegExpSupport/testData/RETest.xml index 8478e1cd36dc..e09a6e1f5ed5 100644 --- a/RegExpSupport/testData/RETest.xml +++ b/RegExpSupport/testData/RETest.xml @@ -8,6 +8,10 @@ \;]]> OK + + \b \b{g} \B \A \z \Z \G + OK + diff --git a/build/Install-Linux-tar.txt b/build/Install-Linux-tar.txt index 56c58f668150..914ab278678f 100644 --- a/build/Install-Linux-tar.txt +++ b/build/Install-Linux-tar.txt @@ -21,7 +21,7 @@ INSTALLATION INSTRUCTIONS 4. [OPTIONAL] To adjust the value of the JVM heap size, create ~/.@@system_selector@@/@@product@@.vmoptions (or @@product@@64.vmoptions - if using a 64-bit JDK), and set the -Xms and -Xms paramters. To see how + if using a 64-bit JDK), and set the -Xms and -Xmx parameters. To see how to do this, you can reference the vmoptions file under "{installation home}/bin" as a model. diff --git a/build/groovy/org/jetbrains/intellij/build/CommunityLibraryLicenses.groovy b/build/groovy/org/jetbrains/intellij/build/CommunityLibraryLicenses.groovy index 3e326c95ef8c..abcb0e13be1c 100644 --- a/build/groovy/org/jetbrains/intellij/build/CommunityLibraryLicenses.groovy +++ b/build/groovy/org/jetbrains/intellij/build/CommunityLibraryLicenses.groovy @@ -72,7 +72,7 @@ class CommunityLibraryLicenses { new LibraryLicense(name: "ASM Bytecode Manipulation Framework", libraryName: "ASM", version: "6.0-ALPHA (with patches by JetBrains)", license: "BSD", url: "http://asm.objectweb.org/", licenseUrl: "http://asm.objectweb.org/license.html"), new LibraryLicense(name: "assertJ", version: "3", license: "Apache 2.0", url: "https://github.com/joel-costigliola/assertj-core"), - new LibraryLicense(name: "Automaton", libraryName: "automaton.jar", version: "1.11", license: "BSD", url: "http://www.brics.dk/automaton/", + new LibraryLicense(name: "Automaton", libraryName: "automaton", version: "1.11", license: "BSD", url: "http://www.brics.dk/automaton/", licenseUrl: "http://www.opensource.org/licenses/bsd-license.php"), new LibraryLicense(name: "Axis", libraryName: "axis-1.4", version: "1.4", license: "Apache 2.0", url: "http://ws.apache.org/axis/", licenseUrl: "http://www.apache.org/licenses/LICENSE-2.0.html"), diff --git a/build/groovy/org/jetbrains/intellij/build/impl/DistributionJARsBuilder.groovy b/build/groovy/org/jetbrains/intellij/build/impl/DistributionJARsBuilder.groovy index 1bfb74413527..a40d45abf142 100644 --- a/build/groovy/org/jetbrains/intellij/build/impl/DistributionJARsBuilder.groovy +++ b/build/groovy/org/jetbrains/intellij/build/impl/DistributionJARsBuilder.groovy @@ -62,23 +62,24 @@ class DistributionJARsBuilder { this.allPlugins = allPlugins buildContext.ant.patternset(id: RESOURCES_INCLUDED) { include(name: "**/*.properties") - include(name: "fileTemplates/**/*") - include(name: "inspectionDescriptions/**/*") - include(name: "intentionDescriptions/**/*") - include(name: "tips/**/*") - include(name: "search/**/*") + include(name: "fileTemplates/**") + include(name: "inspectionDescriptions/**") + include(name: "intentionDescriptions/**") + include(name: "tips/**") + include(name: "search/**") } buildContext.ant.patternset(id: RESOURCES_EXCLUDED) { exclude(name: "**/*.properties") - exclude(name: "fileTemplates/**/*") + exclude(name: "fileTemplates/**") exclude(name: "fileTemplates") - exclude(name: "inspectionDescriptions/**/*") + exclude(name: "inspectionDescriptions/**") exclude(name: "inspectionDescriptions") - exclude(name: "intentionDescriptions/**/*") + exclude(name: "intentionDescriptions/**") exclude(name: "intentionDescriptions") - exclude(name: "tips/**/*") + exclude(name: "tips/**") exclude(name: "tips") + exclude(name: "search/**") } } @@ -163,17 +164,23 @@ class DistributionJARsBuilder { jar("forms_rt.jar") { module("forms_rt") } - productLayout.additionalPlatformModules.entrySet().findAll { it.value != "resources_en.jar" }.each { + productLayout.additionalPlatformModules.entrySet().findAll { it.value != productLayout.mainJarName }.each { def moduleName = it.key jar(it.value) { - module(moduleName) + module(moduleName) { + ant.patternset(refid: resourcesExcluded) + } } } jar("resources_en.jar", true) { - productLayout.additionalPlatformModules.entrySet().findAll { it.value == "resources_en.jar"}.each { - modulePatches([it.key]) - module(it.key) + productLayout.additionalPlatformModules.keySet().each { + modulePatches([it]) { + ant.patternset(refid: resourcesIncluded) + } + module(it) { + ant.patternset(refid: resourcesIncluded) + } } module("platform-resources-en") module("coverage-common") { @@ -191,6 +198,14 @@ class DistributionJARsBuilder { module("coverage-common") { ant.patternset(refid: resourcesExcluded) } + productLayout.additionalPlatformModules.entrySet().findAll {it.value == productLayout.mainJarName}.each { + modulePatches([it.key]) { + ant.patternset(refid: resourcesExcluded) + } + module(it.key) { + ant.patternset(refid: resourcesExcluded) + } + } productLayout.additionalJarsToUnpackIntoMainJar.each { ant.zipfileset(src: it) } diff --git a/build/groovy/org/jetbrains/intellij/build/impl/LayoutBuilder.groovy b/build/groovy/org/jetbrains/intellij/build/impl/LayoutBuilder.groovy index d51b5785472a..5f5cc7cc4324 100644 --- a/build/groovy/org/jetbrains/intellij/build/impl/LayoutBuilder.groovy +++ b/build/groovy/org/jetbrains/intellij/build/impl/LayoutBuilder.groovy @@ -144,10 +144,10 @@ class LayoutBuilder { * Include the patched outputs of {@code moduleNames} modules to the current place in the layout. This method is supposed to be called * in the {@code body} of {@link #jar} with 'preserveDuplicates' set to {@code true} */ - def modulePatches(Collection moduleNames) { + def modulePatches(Collection moduleNames, Closure body = {}) { moduleNames.each { moduleOutputPatches.get(it)?.each { - ant.fileset(dir: it) + ant.fileset(dir: it, body) } } } diff --git a/build/mac/signapp.sh b/build/mac/signapp.sh index 06485f36bcd8..b01092c7be9e 100644 --- a/build/mac/signapp.sh +++ b/build/mac/signapp.sh @@ -56,6 +56,7 @@ done # Make sure *.p12 is imported into local KeyChain security unlock-keychain -p ${PASSWORD} /Users/${USERNAME}/Library/Keychains/login.keychain +echo "signing ${EXPLODED}/$BUILD_NAME" codesign -v --deep --force -s "${CODESIGN_STRING}" ${EXPLODED}/"$BUILD_NAME" echo "signing is done" echo "check sign" diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/RendererConfiguration.java b/java/debugger/impl/src/com/intellij/debugger/settings/RendererConfiguration.java index 382853a98de6..5821bd92ed2c 100644 --- a/java/debugger/impl/src/com/intellij/debugger/settings/RendererConfiguration.java +++ b/java/debugger/impl/src/com/intellij/debugger/settings/RendererConfiguration.java @@ -104,7 +104,7 @@ public class RendererConfiguration implements Cloneable, JDOMExternalizable { } public void addRenderer(NodeRenderer renderer) { - myRepresentationNodes.add(renderer); + myRepresentationNodes.add(0, renderer); } @TestOnly diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/UserExpressionDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/UserExpressionDescriptorImpl.java index 4cdb09d9d6ea..9b9e4caa1b85 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/UserExpressionDescriptorImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/UserExpressionDescriptorImpl.java @@ -31,6 +31,7 @@ import com.intellij.debugger.ui.tree.UserExpressionDescriptor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.JavaCodeFragment; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiCodeFragment; import com.intellij.psi.PsiType; @@ -72,7 +73,11 @@ public class UserExpressionDescriptorImpl extends EvaluationDescriptor implement if (psiClassAndType.first == null) { throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.invalid.type.name", myTypeName)); } - return createCodeFragment(psiClassAndType.first); + PsiCodeFragment fragment = createCodeFragment(psiClassAndType.first); + if (fragment instanceof JavaCodeFragment) { + ((JavaCodeFragment)fragment).setThisType(psiClassAndType.second); + } + return fragment; } public ValueDescriptorImpl getParentDescriptor() { diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/CustomFieldInplaceEditor.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/CustomFieldInplaceEditor.java index d82fd16f417f..275dc9035759 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/CustomFieldInplaceEditor.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/CustomFieldInplaceEditor.java @@ -48,7 +48,7 @@ import java.util.List; */ public class CustomFieldInplaceEditor extends XDebuggerTreeInplaceEditor { private final UserExpressionDescriptorImpl myDescriptor; - private final EnumerationChildrenRenderer myRenderer; + protected final EnumerationChildrenRenderer myRenderer; public CustomFieldInplaceEditor(@NotNull XDebuggerTreeNode node, @Nullable UserExpressionDescriptorImpl descriptor, @@ -79,20 +79,26 @@ public class CustomFieldInplaceEditor extends XDebuggerTreeInplaceEditor { @Override protected List> getRendererChildren() { - if (renderer != null) { - return renderer.getChildren(); + if (myRenderer != null) { + return myRenderer.getChildren(); + } + String name = getTypeName(descriptor); + EnumerationChildrenRenderer enumerationChildrenRenderer = new EnumerationChildrenRenderer(); + enumerationChildrenRenderer.setAppendDefaultChildren(true); + + Renderer lastRenderer = descriptor.getLastRenderer(); + if (lastRenderer instanceof CompoundNodeRenderer && + !(((CompoundNodeRenderer)lastRenderer).getChildrenRenderer() instanceof ExpressionChildrenRenderer)) { + ((CompoundNodeRenderer)lastRenderer).setChildrenRenderer(enumerationChildrenRenderer); } else { - String name = getTypeName(descriptor); - EnumerationChildrenRenderer enumerationChildrenRenderer = new EnumerationChildrenRenderer(); - enumerationChildrenRenderer.setAppendDefaultChildren(true); NodeRenderer renderer = NodeRendererSettings.getInstance().createCompoundTypeRenderer(name, name, null, enumerationChildrenRenderer); renderer.setEnabled(true); NodeRendererSettings.getInstance().getCustomRenderers().addRenderer(renderer); NodeRendererSettings.getInstance().fireRenderersChanged(); - return enumerationChildrenRenderer.getChildren(); } + return enumerationChildrenRenderer.getChildren(); } }.show()); } diff --git a/java/debugger/impl/src/org/jetbrains/java/debugger/JavaDebuggerEditorsProvider.java b/java/debugger/impl/src/org/jetbrains/java/debugger/JavaDebuggerEditorsProvider.java index 9146674a158f..f7d00a50ded0 100644 --- a/java/debugger/impl/src/org/jetbrains/java/debugger/JavaDebuggerEditorsProvider.java +++ b/java/debugger/impl/src/org/jetbrains/java/debugger/JavaDebuggerEditorsProvider.java @@ -82,7 +82,7 @@ public class JavaDebuggerEditorsProvider extends XDebuggerEditorsProviderBase { @Nullable PsiElement context, boolean isPhysical) { TextWithImports text = TextWithImportsImpl.fromXExpression(expression); - if (text != null && context != null) { + if (text != null) { CodeFragmentFactory factory = DebuggerUtilsEx.findAppropriateCodeFragmentFactory(text, context); JavaCodeFragment codeFragment = factory.createPresentationCodeFragment(text, context, project); codeFragment.forceResolveScope(GlobalSearchScope.allScope(project)); diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java index ddbf7714d982..1ac91d3db9ea 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java @@ -105,11 +105,12 @@ public class HighlightMethodUtil { PsiUtil.getAccessModifier(superAccessLevel)); TextRange textRange; if (includeRealPositionInfo) { - if (modifierList.hasModifierProperty(PsiModifier.PACKAGE_LOCAL)) { + PsiElement keyword = PsiUtil.findModifierInList(modifierList, accessModifier); + if (keyword == null) { + // in case of package-private or some crazy third-party plugin where some access modifier implied even if it's absent textRange = method.getNameIdentifier().getTextRange(); } else { - PsiElement keyword = PsiUtil.findModifierInList(modifierList, accessModifier); textRange = keyword.getTextRange(); } } @@ -365,7 +366,7 @@ public class HighlightMethodUtil { highlightInfo = checkVarargParameterErasureToBeAccessible((MethodCandidateInfo)resolveResult, methodCall); } - if (highlightInfo == null && resolveResult instanceof MethodCandidateInfo) { + if (highlightInfo == null) { final String errorMessage = ((MethodCandidateInfo)resolveResult).getInferenceErrorMessage(); if (errorMessage != null) { highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).descriptionAndTooltip(errorMessage).range(fixRange).create(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/util/ChangeToAppendUtil.java b/java/java-analysis-impl/src/com/intellij/codeInspection/util/ChangeToAppendUtil.java index 9327bdfbded1..d9dc5f08500b 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/util/ChangeToAppendUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/util/ChangeToAppendUtil.java @@ -26,8 +26,9 @@ public class ChangeToAppendUtil { if (concatenation == null) return null; final PsiType type = appendable.getType(); if (type == null) return null; - final StringBuilder result = - buildAppendExpression(concatenation, type.equalsToText("java.lang.Appendable"), new StringBuilder(appendable.getText())); + final boolean useStringValueOf = !type.equalsToText(CommonClassNames.JAVA_LANG_STRING_BUFFER) && + !type.equalsToText(CommonClassNames.JAVA_LANG_STRING_BUILDER); + final StringBuilder result = buildAppendExpression(concatenation, useStringValueOf, new StringBuilder(appendable.getText())); if (result == null) return null; final PsiElementFactory factory = JavaPsiFacade.getElementFactory(appendable.getProject()); return factory.createExpressionFromText(result.toString(), appendable); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeToAppendFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeToAppendFix.java index 513cb7895ecf..8974dd10291a 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeToAppendFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeToAppendFix.java @@ -24,6 +24,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.InheritanceUtil; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; @@ -35,6 +36,7 @@ public class ChangeToAppendFix implements IntentionAction { private final IElementType myTokenType; private final PsiType myLhsType; private final PsiAssignmentExpression myAssignmentExpression; + private volatile TypeInfo myTypeInfo; public ChangeToAppendFix(@NotNull IElementType eqOpSign, @NotNull PsiType lType, @NotNull PsiAssignmentExpression assignmentExpression) { myTokenType = eqOpSign; @@ -47,9 +49,8 @@ public class ChangeToAppendFix implements IntentionAction { public String getText() { return QuickFixBundle.message("change.to.append.text", ChangeToAppendUtil.buildAppendExpression(myAssignmentExpression.getRExpression(), - myLhsType.equalsToText("java.lang.Appendable"), - new StringBuilder( - myAssignmentExpression.getLExpression().getText()))); + getTypeInfo().myUseStringValueOf, + new StringBuilder(myAssignmentExpression.getLExpression().getText()))); } @NotNull @@ -63,9 +64,7 @@ public class ChangeToAppendFix implements IntentionAction { return JavaTokenType.PLUSEQ == myTokenType && myAssignmentExpression.isValid() && PsiManager.getInstance(project).isInProject(myAssignmentExpression) && - (myLhsType.equalsToText(CommonClassNames.JAVA_LANG_STRING_BUILDER) || - myLhsType.equalsToText(CommonClassNames.JAVA_LANG_STRING_BUFFER) || - myLhsType.equalsToText("java.lang.Appendable")); + getTypeInfo().myAppendable; } @Override @@ -81,4 +80,33 @@ public class ChangeToAppendFix implements IntentionAction { if (appendExpression == null) return; myAssignmentExpression.replace(appendExpression); } + + @NotNull + private TypeInfo getTypeInfo() { + if (myTypeInfo != null) return myTypeInfo; + myTypeInfo = calculateTypeInfo(); + return myTypeInfo; + } + + @NotNull + private TypeInfo calculateTypeInfo() { + if (myLhsType.equalsToText(CommonClassNames.JAVA_LANG_STRING_BUILDER) || + myLhsType.equalsToText(CommonClassNames.JAVA_LANG_STRING_BUFFER)) { + return new TypeInfo(true, false); + } + if (InheritanceUtil.isInheritor(myLhsType, "java.lang.Appendable")) { + return new TypeInfo(true, true); + } + return new TypeInfo(false, false); + } + + private static class TypeInfo { + private final boolean myAppendable; + private final boolean myUseStringValueOf; + + TypeInfo(boolean appendable, boolean useStringValueOf) { + myAppendable = appendable; + myUseStringValueOf = useStringValueOf; + } + } } diff --git a/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationPresentation.java b/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationPresentation.java index d456587fbbb4..43808841410d 100644 --- a/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationPresentation.java +++ b/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationPresentation.java @@ -48,12 +48,14 @@ import com.intellij.ui.HyperlinkAdapter; import com.intellij.ui.ScrollPaneFactory; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashMap; import com.intellij.util.containers.HashSet; import com.intellij.util.text.CharArrayUtil; import com.intellij.util.text.DateFormatUtil; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; +import gnu.trove.TObjectHashingStrategy; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -77,12 +79,28 @@ import java.util.function.Predicate; public class UnusedDeclarationPresentation extends DefaultInspectionToolPresentation { private final Map> myPackageContents = Collections.synchronizedMap(new HashMap>()); - private final Set myIgnoreElements = new HashSet<>(); + private final Set myIgnoreElements = ContainerUtil.newConcurrentSet(TObjectHashingStrategy.IDENTITY); + private final Map myFixedElements = ContainerUtil.newConcurrentMap(TObjectHashingStrategy.IDENTITY); + private WeakUnreferencedFilter myFilter; private DeadHTMLComposer myComposer; @NonNls private static final String DELETE = "delete"; @NonNls private static final String COMMENT = "comment"; - @NonNls private static final String [] HINTS = {COMMENT, DELETE}; + + private enum UnusedDeclarationHint { + COMMENT("Commented out"), + DELETE("Deleted"); + + private final String myDescription; + + UnusedDeclarationHint(String description) { + myDescription = description; + } + + public String getDescription() { + return myDescription; + } + } public UnusedDeclarationPresentation(@NotNull InspectionToolWrapper toolWrapper, @NotNull GlobalInspectionContextImpl context) { super(toolWrapper, context); @@ -150,9 +168,9 @@ public class UnusedDeclarationPresentation extends DefaultInspectionToolPresenta @NonNls Element hintsElement = new Element("hints"); - for (String hint : HINTS) { + for (UnusedDeclarationHint hint : UnusedDeclarationHint.values()) { @NonNls Element hintElement = new Element("hint"); - hintElement.setAttribute("value", hint); + hintElement.setAttribute("value", hint.toString().toLowerCase()); hintsElement.addContent(hintElement); } element.addContent(hintsElement); @@ -206,7 +224,12 @@ public class UnusedDeclarationPresentation extends DefaultInspectionToolPresenta final Project project = getContext().getProject(); if (isDisposed() || project.isDisposed()) return; SafeDeleteHandler.invoke(project, psiElements, false, - () -> removeElements(refElements, project, myToolWrapper)); + () -> { + removeElements(refElements, project, myToolWrapper); + for (RefEntity ref : refElements) { + myFixedElements.put(ref, UnusedDeclarationHint.DELETE); + } + }); }); return false; //refresh after safe delete dialog is closed @@ -278,6 +301,9 @@ public class UnusedDeclarationPresentation extends DefaultInspectionToolPresenta entryPointsManager.removeEntryPoint(refElement); } + for (RefElement ref : deletedRefs) { + myFixedElements.put(ref, UnusedDeclarationHint.COMMENT); + } return true; } } @@ -375,6 +401,27 @@ public class UnusedDeclarationPresentation extends DefaultInspectionToolPresenta return entryPointsNode; } + @NotNull + @Override + public RefElementNode createRefNode(@NotNull RefEntity entity) { + return new RefElementNode(entity, this) { + @Nullable + @Override + public String getCustomizedTailText() { + final UnusedDeclarationHint hint = myFixedElements.get(getElement()); + if (hint != null) { + return hint.getDescription(); + } + return super.getCustomizedTailText(); + } + + @Override + public boolean isQuickFixAppliedFromView() { + return myFixedElements.containsKey(getElement()); + } + }; + } + @Override public void updateContent() { getTool().checkForReachableRefs(getContext()); diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/LeafBlock.java b/java/java-impl/src/com/intellij/psi/formatter/java/LeafBlock.java index c5062a9af68f..a151885ec5a2 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/LeafBlock.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/LeafBlock.java @@ -18,13 +18,16 @@ package com.intellij.psi.formatter.java; import com.intellij.formatting.*; import com.intellij.lang.ASTNode; import com.intellij.openapi.util.TextRange; +import com.intellij.psi.formatter.common.ExtraRangesProvider; +import com.intellij.psi.formatter.common.NodeIndentRangesCalculator; import com.intellij.psi.impl.source.codeStyle.ShiftIndentInsideHelper; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; -public class LeafBlock implements ASTBlock{ +public class LeafBlock implements ASTBlock, ExtraRangesProvider { private int myStartOffset = -1; private final ASTNode myNode; private final Wrap myWrap; @@ -108,4 +111,16 @@ public class LeafBlock implements ASTBlock{ myStartOffset = startOffset; // if (startOffset != -1) assert startOffset == myNode.getTextRange().getStartOffset(); } + + @Override + @Nullable + public List getExtraRangesToFormat(@NotNull FormattingRangesInfo info) { + int startOffset = getTextRange().getStartOffset(); + if (info.isOnInsertedLine(startOffset) && myNode.getTextLength() == 1 && myNode.textContains('}')) { + ASTNode parent = myNode.getTreeParent(); + return new NodeIndentRangesCalculator(parent).calculateExtraRanges(); + } + return null; + } + } diff --git a/java/java-impl/src/com/intellij/psi/impl/JavaRegExpHost.java b/java/java-impl/src/com/intellij/psi/impl/JavaRegExpHost.java index 82f99be64274..c86d0792906b 100644 --- a/java/java-impl/src/com/intellij/psi/impl/JavaRegExpHost.java +++ b/java/java-impl/src/com/intellij/psi/impl/JavaRegExpHost.java @@ -24,10 +24,7 @@ import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.psi.PsiElement; import org.intellij.lang.regexp.DefaultRegExpPropertiesProvider; import org.intellij.lang.regexp.RegExpLanguageHost; -import org.intellij.lang.regexp.psi.RegExpChar; -import org.intellij.lang.regexp.psi.RegExpGroup; -import org.intellij.lang.regexp.psi.RegExpNamedGroupRef; -import org.intellij.lang.regexp.psi.RegExpSimpleClass; +import org.intellij.lang.regexp.psi.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -94,6 +91,24 @@ public class JavaRegExpHost implements RegExpLanguageHost { return hasAtLeastJdkVersion(regExpChar, JavaSdkVersion.JDK_1_7); } + @Override + public boolean supportsBoundary(RegExpBoundary boundary) { + switch (boundary.getType()) { + case UNICODE_EXTENDED_GRAPHEME: + return hasAtLeastJdkVersion(boundary, JavaSdkVersion.JDK_1_9); + case LINE_START: + case LINE_END: + case WORD: + case NON_WORD: + case BEGIN: + case END: + case END_NO_LINE_TERM: + case PREVIOUS_MATCH: + default: + return true; + } + } + @Override public boolean supportsSimpleClass(RegExpSimpleClass simpleClass) { switch(simpleClass.getKind()) { diff --git a/java/java-tests/testSrc/com/intellij/psi/formatter/java/FormatWithContextTest.kt b/java/java-tests/testSrc/com/intellij/psi/formatter/java/FormatWithContextTest.kt index 9eae257769a9..ea46a45738cd 100644 --- a/java/java-tests/testSrc/com/intellij/psi/formatter/java/FormatWithContextTest.kt +++ b/java/java-tests/testSrc/com/intellij/psi/formatter/java/FormatWithContextTest.kt @@ -114,7 +114,7 @@ class X { } - fun `ignore test inserted closing brace`() { + fun `test inserted closing brace`() { myLineRange = TextRange(6, 6) check( """ diff --git a/lib/jna-platform.jar b/lib/jna-platform.jar index c01edecd6717..f3a9fd2b2294 100644 Binary files a/lib/jna-platform.jar and b/lib/jna-platform.jar differ diff --git a/lib/jna.jar b/lib/jna.jar index da2ffe17b67d..a943670981d8 100644 Binary files a/lib/jna.jar and b/lib/jna.jar differ diff --git a/lib/src/jna-platform.zip b/lib/src/jna-platform.zip index 21ebc8e1acca..9fc9f0ca4b73 100644 Binary files a/lib/src/jna-platform.zip and b/lib/src/jna-platform.zip differ diff --git a/lib/src/jna.zip b/lib/src/jna.zip index df0617bf9cf1..b09a18be654e 100644 Binary files a/lib/src/jna.zip and b/lib/src/jna.zip differ diff --git a/platform/annotations/common/src/org/jetbrains/annotations/Contract.java b/platform/annotations/common/src/org/jetbrains/annotations/Contract.java index 003d541746a2..361a95ba130d 100644 --- a/platform/annotations/common/src/org/jetbrains/annotations/Contract.java +++ b/platform/annotations/common/src/org/jetbrains/annotations/Contract.java @@ -26,7 +26,7 @@ import java.lang.annotation.*; * clause ::= args '->' effect
* args ::= ((arg ',')* arg )?
* arg ::= value-constraint
- * value-constraint ::= 'any' | 'null' | '!null' | 'false' | 'true'
+ * value-constraint ::= '_' | 'null' | '!null' | 'false' | 'true'
* effect ::= value-constraint | 'fail'

* * The constraints denote the following:
diff --git a/platform/core-api/core-api.iml b/platform/core-api/core-api.iml index 0877129ba99f..b56ccffb06aa 100644 --- a/platform/core-api/core-api.iml +++ b/platform/core-api/core-api.iml @@ -14,15 +14,7 @@ - - - - - - - - - + \ No newline at end of file diff --git a/platform/core-api/src/com/intellij/lexer/FlexAdapter.java b/platform/core-api/src/com/intellij/lexer/FlexAdapter.java index c8b5b90688ea..e57d2b3f188b 100644 --- a/platform/core-api/src/com/intellij/lexer/FlexAdapter.java +++ b/platform/core-api/src/com/intellij/lexer/FlexAdapter.java @@ -18,6 +18,7 @@ package com.intellij.lexer; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.TokenType; import com.intellij.psi.tree.IElementType; +import com.intellij.util.SystemProperties; import org.jetbrains.annotations.NotNull; /** @@ -26,6 +27,7 @@ import org.jetbrains.annotations.NotNull; public class FlexAdapter extends LexerBase { private static final Logger LOG = Logger.getInstance(FlexAdapter.class); + private static final boolean logLexerErrors = SystemProperties.getBooleanProperty("log.flex.adapter.errors", true); // Used by Upsource private final FlexLexer myFlex; @@ -105,13 +107,17 @@ public class FlexAdapter extends LexerBase { myTokenType = myFlex.advance(); myTokenEnd = myFlex.getTokenEnd(); } - catch (Exception e) { - LOG.error(myFlex.getClass().getName(), e); + catch (Exception e) { + if (logLexerErrors) { + LOG.error(myFlex.getClass().getName(), e); + } myTokenType = TokenType.WHITE_SPACE; myTokenEnd = myBufferEnd; } catch (Error e) { - LOG.error(myFlex.getClass().getName(), e); + if (logLexerErrors) { + LOG.error(myFlex.getClass().getName(), e); + } myTokenType = TokenType.WHITE_SPACE; myTokenEnd = myBufferEnd; } diff --git a/platform/credential-store/src/FileCredentialStore.kt b/platform/credential-store/src/FileCredentialStore.kt index 31549209f850..4ce3dac4440a 100644 --- a/platform/credential-store/src/FileCredentialStore.kt +++ b/platform/credential-store/src/FileCredentialStore.kt @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.ide.passwordSafe +package com.intellij.credentialStore +import com.intellij.ide.passwordSafe.PasswordStorage import com.intellij.ide.passwordSafe.impl.providers.masterKey.windows.WindowsCryptUtils import com.intellij.openapi.application.PathManager -import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream import com.intellij.openapi.util.io.setOwnerPermissions @@ -29,15 +29,19 @@ import java.nio.file.NoSuchFileException import java.nio.file.Path import java.nio.file.Paths import java.security.Key -import java.security.MessageDigest -import java.security.SecureRandom import java.util.Base64 import java.util.concurrent.atomic.AtomicBoolean import javax.crypto.spec.SecretKeySpec -internal val LOG = Logger.getInstance(FileCredentialStore::class.java) +class FileCredentialStore(keyToValue: Map? = null, baseDirectory: Path = Paths.get(PathManager.getConfigPath()), var memoryOnly: Boolean = false) : PasswordStorage, CredentialStore { + override fun get(key: String) = getPassword(null, key) + + override fun set(key: String, password: ByteArray?) { + val string = password?.toString(Charsets.UTF_8) + password?.fill(0) + setPassword(key, string) + } -class FileCredentialStore(keyToValue: Map? = null, baseDirectory: Path = Paths.get(PathManager.getConfigPath()), var memoryOnly: Boolean = false) : PasswordStorage { private val db = ContainerUtil.newConcurrentMap() private val dbFile = baseDirectory.resolve("pdb") @@ -132,10 +136,10 @@ class FileCredentialStore(keyToValue: Map? = null, baseDirectory override fun getPassword(requestor: Class<*>?, key: String): String? { val rawKey = getRawKey(key, requestor) - // try old key - as hash var value = db.get(rawKey) - if (value == null) { - value = db.remove(toOldKey(MessageDigest.getInstance("SHA-256").digest(rawKey.toByteArray()))) + if (value == null && (requestor != null || key.contains('/'))) { + // try old key - as hash + value = db.remove(toOldKey(rawKey)) if (value != null) { db.put(rawKey, value) needToSave.set(true) @@ -163,16 +167,6 @@ class FileCredentialStore(keyToValue: Map? = null, baseDirectory } } -internal fun getRawKey(key: String, requestor: Class<*>?) = if (requestor == null) key else "${requestor.name}/$key" - -internal fun toOldKey(hash: ByteArray) = "old-hashed-key|" + Base64.getEncoder().encodeToString(hash) - -internal fun generate(): ByteArray { - val bytes = ByteArray(16) - SecureRandom().nextBytes(bytes) - return bytes -} - interface MasterKeyStorage { fun get(): ByteArray? diff --git a/platform/credential-store/src/MacOsCredentialStore.kt b/platform/credential-store/src/MacOsCredentialStore.kt deleted file mode 100644 index 6eb22ed36c93..000000000000 --- a/platform/credential-store/src/MacOsCredentialStore.kt +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 com.intellij.ide.passwordSafe - -import com.intellij.ide.passwordSafe.macOs.deleteGenericPassword -import com.intellij.ide.passwordSafe.macOs.findGenericPassword -import com.intellij.ide.passwordSafe.macOs.isMacOsCredentialStoreSupported -import com.intellij.ide.passwordSafe.macOs.saveGenericPassword -import com.intellij.openapi.diagnostic.catchAndLog -import com.intellij.util.SystemProperties -import java.security.MessageDigest - -private class MacOsCredentialStore(serviceName: String) : PasswordStorage { - private val serviceName = serviceName.toByteArray() - - override fun getPassword(requestor: Class<*>?, key: String): String? { - val rawKey = getRawKey(key, requestor) - // try old key - as hash - @Suppress("CanBeVal") - var value: String? - try { - value = findGenericPassword(serviceName, rawKey) - } - catch (e: Throwable) { - LOG.error(e) - return null - } - - if (value == null) { - LOG.catchAndLog { - val oldKey = toOldKey(MessageDigest.getInstance("SHA-256").digest(rawKey.toByteArray())) - value = findGenericPassword(serviceName, oldKey) - if (value != null) { - LOG.catchAndLog { deleteGenericPassword(serviceName, oldKey) } - saveGenericPassword(serviceName, key, value!!) - } - } - } - return value - } - - override fun setPassword(requestor: Class<*>?, key: String, value: String?) { - LOG.catchAndLog { - val rawKey = getRawKey(key, requestor) - if (value == null) { - deleteGenericPassword(serviceName, rawKey) - } - else { - saveGenericPassword(serviceName, rawKey, value) - } - } - } -} - -private class MacOsCredentialStoreFactory : CredentialStoreFactory { - override fun create(): PasswordStorage? { - if (isMacOsCredentialStoreSupported && SystemProperties.getBooleanProperty("use.mac.keychain", true)) { - return MacOsCredentialStore("IntelliJ Platform") - } - return null - } -} \ No newline at end of file diff --git a/platform/credential-store/src/PasswordSafeConfigurable.kt b/platform/credential-store/src/PasswordSafeConfigurable.kt index c75ad7197708..6fcf8afb4dfd 100644 --- a/platform/credential-store/src/PasswordSafeConfigurable.kt +++ b/platform/credential-store/src/PasswordSafeConfigurable.kt @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.ide.passwordSafe.config +package com.intellij.credentialStore +import com.intellij.credentialStore.PasswordSafeSettings.ProviderType import com.intellij.ide.passwordSafe.PasswordSafe -import com.intellij.ide.passwordSafe.config.PasswordSafeSettings.ProviderType import com.intellij.ide.passwordSafe.impl.PasswordSafeImpl import com.intellij.layout.* import com.intellij.layout.CCFlags.* diff --git a/platform/credential-store/src/PasswordSafeImpl.kt b/platform/credential-store/src/PasswordSafeImpl.kt index 977164bd5fe8..a40a4ec7d91d 100644 --- a/platform/credential-store/src/PasswordSafeImpl.kt +++ b/platform/credential-store/src/PasswordSafeImpl.kt @@ -15,10 +15,13 @@ */ package com.intellij.ide.passwordSafe.impl -import com.intellij.ide.passwordSafe.* -import com.intellij.ide.passwordSafe.config.PasswordSafeSettings -import com.intellij.ide.passwordSafe.config.PasswordSafeSettings.ProviderType -import com.intellij.ide.passwordSafe.config.PasswordSafeSettingsListener +import com.intellij.credentialStore.FileCredentialStore +import com.intellij.credentialStore.LOG +import com.intellij.credentialStore.PasswordSafeSettings +import com.intellij.credentialStore.PasswordSafeSettings.ProviderType +import com.intellij.credentialStore.PasswordSafeSettingsListener +import com.intellij.ide.passwordSafe.PasswordSafe +import com.intellij.ide.passwordSafe.PasswordStorage import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ex.ApplicationInfoEx import com.intellij.openapi.components.SettingsSavingComponent @@ -126,7 +129,7 @@ class PasswordSafeImpl(/* public - backward compatibility */val settings: Passwo private fun createPersistentCredentialStore(existing: FileCredentialStore? = null, convertFileStore: Boolean = false): PasswordStorage { LOG.catchAndLog { - for (factory in CredentialStoreFactory.CREDENTIAL_STORE_FACTORY.extensions) { + for (factory in com.intellij.credentialStore.CredentialStoreFactory.CREDENTIAL_STORE_FACTORY.extensions) { val store = factory.create() ?: continue if (convertFileStore) { LOG.catchAndLog { diff --git a/platform/platform-impl/src/com/intellij/ide/passwordSafe/CredentialStoreFactory.java b/platform/credential-store/src/com/intellij/credentialStore/CredentialStoreFactory.java similarity index 90% rename from platform/platform-impl/src/com/intellij/ide/passwordSafe/CredentialStoreFactory.java rename to platform/credential-store/src/com/intellij/credentialStore/CredentialStoreFactory.java index 58a76ab90949..b9cc5a0e85a2 100644 --- a/platform/platform-impl/src/com/intellij/ide/passwordSafe/CredentialStoreFactory.java +++ b/platform/credential-store/src/com/intellij/credentialStore/CredentialStoreFactory.java @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.ide.passwordSafe; +package com.intellij.credentialStore; +import com.intellij.ide.passwordSafe.PasswordStorage; import com.intellij.openapi.extensions.ExtensionPointName; import org.jetbrains.annotations.Nullable; diff --git a/platform/credential-store/src/com/intellij/ide/passwordSafe/config/PasswordSafeSettings.java b/platform/credential-store/src/com/intellij/credentialStore/PasswordSafeSettings.java similarity index 98% rename from platform/credential-store/src/com/intellij/ide/passwordSafe/config/PasswordSafeSettings.java rename to platform/credential-store/src/com/intellij/credentialStore/PasswordSafeSettings.java index 946ccb3388a9..080f8429ff57 100644 --- a/platform/credential-store/src/com/intellij/ide/passwordSafe/config/PasswordSafeSettings.java +++ b/platform/credential-store/src/com/intellij/credentialStore/PasswordSafeSettings.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.ide.passwordSafe.config; +package com.intellij.credentialStore; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.PersistentStateComponent; diff --git a/platform/credential-store/src/credentialStore.kt b/platform/credential-store/src/credentialStore.kt new file mode 100644 index 000000000000..470d4b54724d --- /dev/null +++ b/platform/credential-store/src/credentialStore.kt @@ -0,0 +1,35 @@ +/* + * 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 com.intellij.credentialStore + +import com.intellij.openapi.diagnostic.Logger +import java.security.MessageDigest +import java.util.* + +internal val LOG = Logger.getInstance(CredentialStore::class.java) + +internal interface CredentialStore { + fun get(key: String): String? + + // passed byte array will be cleared + fun set(key: String, password: ByteArray?) +} + +internal fun getRawKey(key: String, requestor: Class<*>?) = if (requestor == null) key else "${requestor.name}/$key" + +internal fun toOldKey(hash: ByteArray) = "old-hashed-key|" + Base64.getEncoder().encodeToString(hash) + +internal fun toOldKey(newKey: String) = toOldKey(MessageDigest.getInstance("SHA-256").digest(newKey.toByteArray())) \ No newline at end of file diff --git a/platform/credential-store/src/dbV1Convertor.kt b/platform/credential-store/src/dbV1Convertor.kt index 6503ffecd35b..fc7ee0d33f10 100644 --- a/platform/credential-store/src/dbV1Convertor.kt +++ b/platform/credential-store/src/dbV1Convertor.kt @@ -13,10 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.ide.passwordSafe +package com.intellij.credentialStore import com.intellij.ide.ApplicationLoadListener -import com.intellij.ide.passwordSafe.config.PasswordSafeSettings import com.intellij.ide.passwordSafe.impl.providers.ByteArrayWrapper import com.intellij.ide.passwordSafe.impl.providers.EncryptionUtil import com.intellij.ide.passwordSafe.impl.providers.masterKey.EnterPasswordComponent diff --git a/platform/credential-store/src/libraries/linuxSecretLibrary.kt b/platform/credential-store/src/libraries/linuxSecretLibrary.kt new file mode 100644 index 000000000000..e1cb25a67186 --- /dev/null +++ b/platform/credential-store/src/libraries/linuxSecretLibrary.kt @@ -0,0 +1,91 @@ +package com.intellij.credentialStore.linux + +import com.intellij.credentialStore.CredentialStore +import com.intellij.credentialStore.LOG +import com.intellij.jna.DisposableMemory +import com.sun.jna.Library +import com.sun.jna.Native +import com.sun.jna.Pointer + +private val LIBRARY by lazy { Native.loadLibrary("secret-1", SecretLibrary::class.java) as SecretLibrary } + +private const val SECRET_SCHEMA_NONE = 0 +private const val SECRET_SCHEMA_ATTRIBUTE_STRING = 0 + +// explicitly create pointer to be explicitly dispose it to avoid sensitive data in the memory +internal fun stringPointer(data: ByteArray): DisposableMemory { + val pointer = DisposableMemory(data.size + 1L) + pointer.write(0, data, 0, data.size) + pointer.setByte(data.size.toLong(), 0.toByte()) + return pointer +} + +// we use default collection, it seems no way to use custom +internal class SecretCredentialStore(schemeName: String) : CredentialStore { + private val keyAttributeNamePointer by lazy { stringPointer("key".toByteArray()) } + private val scheme by lazy { LIBRARY.secret_schema_new(schemeName, SECRET_SCHEMA_NONE, keyAttributeNamePointer, SECRET_SCHEMA_ATTRIBUTE_STRING, null) } + + override fun get(key: String): String? { + val keyPointer = stringPointer(key.toByteArray()) + return checkError("secret_password_lookup_sync") { errorRef -> + LIBRARY.secret_password_lookup_sync(scheme, null, errorRef, keyAttributeNamePointer, keyPointer, null) + } + } + + override fun set(key: String, password: ByteArray?) { + val keyPointer = stringPointer(key.toByteArray()) + + if (password == null) { + checkError("secret_password_store_sync") { errorRef -> + LIBRARY.secret_password_clear_sync(scheme, null, errorRef, keyAttributeNamePointer, keyPointer, null) + } + return + } + + val passwordPointer = stringPointer(password) + password.fill(0) + + checkError("secret_password_store_sync") { errorRef -> + try { + LIBRARY.secret_password_store_sync(scheme, null, keyPointer, passwordPointer, null, errorRef, keyAttributeNamePointer, keyPointer, null) + } + finally { + passwordPointer.dispose() + keyPointer.dispose() + } + } + } +} + +private inline fun checkError(method: String, task: (errorRef: Array) -> T): T { + val errorRef = arrayOf(null) + val result = task(errorRef) + val error = errorRef.get(0) + if (error != null && error.code !== 0) { + LOG.error("$method error code ${error.code}, error message ${error.message}") + } + return result +} + +// we use sync API to simplify - client will use postponed write +private interface SecretLibrary : Library { + fun secret_schema_new(name: String, flags: Int, vararg attributes: Any?): Pointer + + fun secret_password_store_sync(scheme: Pointer, collection: Pointer?, label: Pointer, password: Pointer, cancellable: Pointer?, error: Array, vararg attributes: Pointer?) + + fun secret_password_lookup_sync(scheme: Pointer, cancellable: Pointer?, error: Array, vararg attributes: Pointer?): String + + fun secret_password_clear_sync(scheme: Pointer, cancellable: Pointer?, error: Array, vararg attributes: Pointer?) +} + +@Suppress("unused") +class GErrorStruct : com.sun.jna.Structure() { + @JvmField + var domain = 0 + @JvmField + var code = 0 + @JvmField + var message: String? = null + + override fun getFieldOrder() = listOf("domain", "code", "message") +} \ No newline at end of file diff --git a/platform/credential-store/src/macOsKeychainLibrary.kt b/platform/credential-store/src/libraries/macOsKeychainLibrary.kt similarity index 66% rename from platform/credential-store/src/macOsKeychainLibrary.kt rename to platform/credential-store/src/libraries/macOsKeychainLibrary.kt index c6d7a7047a91..11df303e0df3 100644 --- a/platform/credential-store/src/macOsKeychainLibrary.kt +++ b/platform/credential-store/src/libraries/macOsKeychainLibrary.kt @@ -13,10 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.ide.passwordSafe.macOs +package com.intellij.credentialStore.macOs -import com.intellij.ide.passwordSafe.LOG +import com.intellij.credentialStore.CredentialStore +import com.intellij.credentialStore.LOG import com.intellij.openapi.util.SystemInfo +import com.sun.jna.Library import com.sun.jna.Native import com.sun.jna.Pointer @@ -27,12 +29,25 @@ private val LIBRARY by lazy { Native.loadLibrary("Security", MacOsKeychainLibrary::class.java) as MacOsKeychainLibrary } -fun saveGenericPassword(serviceName: ByteArray, accountName: String, password: String) { - saveGenericPassword(serviceName, accountName, password.toByteArray()) -} +private const val errSecItemNotFound = -25300 +private const val errSecInvalidRecord = -67701 -private fun saveGenericPassword(serviceName: ByteArray, accountName: String, passwordData: ByteArray) { - saveGenericPassword(serviceName, accountName, passwordData, passwordData.size) +internal class KeyChainCredentialStore(serviceName: String) : CredentialStore { + private val serviceName = serviceName.toByteArray() + + override fun get(key: String): String? { + return findGenericPassword(serviceName, key) + } + + override fun set(key: String, password: ByteArray?) { + if (password == null) { + deleteGenericPassword(serviceName, key) + return + } + + saveGenericPassword(serviceName, key, password, password.size) + password.fill(0) + } } fun findGenericPassword(serviceName: ByteArray, accountName: String): String? { @@ -50,17 +65,22 @@ fun findGenericPassword(serviceName: ByteArray, accountName: String): String? { fun deleteGenericPassword(serviceName: ByteArray, accountName: String) { val itemRef = arrayOf(null) val accountNameBytes = accountName.toByteArray() - checkForError("find (for delete)", LIBRARY.SecKeychainFindGenericPassword(null, serviceName.size, serviceName, accountNameBytes.size, accountNameBytes, null, null, itemRef)) - val pointer = itemRef[0] + val code = LIBRARY.SecKeychainFindGenericPassword(null, serviceName.size, serviceName, accountNameBytes.size, accountNameBytes, null, null, itemRef) + if (code == errSecItemNotFound || code == errSecInvalidRecord) { + return + } + + checkForError("find (for delete)", code) + val pointer = itemRef.get(0) if (pointer != null) { checkForError("delete", LIBRARY.SecKeychainItemDelete(pointer)) LIBRARY.CFRelease(pointer) } } -// http://developer.apple.com/mac/library/DOCUMENTATION/Security/Reference/keychainservices/Reference/reference.html +// https://developer.apple.com/library/mac/documentation/Security/Reference/keychainservices/index.html // It is very, very important to use CFRelease/SecKeychainItemFreeContent You must do it, otherwise you can get "An invalid record was encountered." -interface MacOsKeychainLibrary : com.sun.jna.Library { +private interface MacOsKeychainLibrary : Library { fun SecKeychainAddGenericPassword(keychain: Pointer?, serviceNameLength: Int, serviceName: ByteArray, accountNameLength: Int, accountName: ByteArray, passwordLength: Int, passwordData: ByteArray, itemRef: Pointer? = null): Int fun SecKeychainItemModifyContent(/*SecKeychainItemRef*/ itemRef: Pointer, /*SecKeychainAttributeList**/ attrList: Pointer?, length: Int, data: ByteArray): Int @@ -89,35 +109,38 @@ interface MacOsKeychainLibrary : com.sun.jna.Library { fun SecKeychainItemFreeContent(/*SecKeychainAttributeList*/attrList: Pointer?, data: Pointer?) } -private fun saveGenericPassword(serviceName: ByteArray, accountName: String, password: ByteArray, passwordSize: Int) { +fun saveGenericPassword(serviceName: ByteArray, accountName: String, password: ByteArray, passwordSize: Int = password.size) { val accountNameBytes = accountName.toByteArray() val itemRef = arrayOf(null) - checkForError("find (for save)", LIBRARY.SecKeychainFindGenericPassword(null, serviceName.size, serviceName, accountNameBytes.size, accountNameBytes, null, null, itemRef)) + val library = LIBRARY + checkForError("find (for save)", library.SecKeychainFindGenericPassword(null, serviceName.size, serviceName, accountNameBytes.size, accountNameBytes, null, null, itemRef)) val pointer = itemRef[0] if (pointer == null) { - checkForError("save (new)", LIBRARY.SecKeychainAddGenericPassword(null, serviceName.size, serviceName, accountNameBytes.size, accountNameBytes, passwordSize, password)) + checkForError("save (new)", library.SecKeychainAddGenericPassword(null, serviceName.size, serviceName, accountNameBytes.size, accountNameBytes, passwordSize, password)) } else { - checkForError("save (update)", LIBRARY.SecKeychainItemModifyContent(pointer, null, passwordSize, password)) - LIBRARY.CFRelease(pointer) + checkForError("save (update)", library.SecKeychainItemModifyContent(pointer, null, passwordSize, password)) + library.CFRelease(pointer) } } private fun checkForError(message: String, code: Int) { - if (code != 0 && code != /* errSecItemNotFound, always returned from find it seems */-25300) { - val translated = LIBRARY.SecCopyErrorMessageString(code, null) - val builder = StringBuilder(message).append(": ") - if (translated == null) { - builder.append(code) - } - else { - val buf = CharArray(LIBRARY.CFStringGetLength(translated).toInt()) - for (i in 0..buf.size - 1) { - buf[i] = LIBRARY.CFStringGetCharacterAtIndex(translated, i.toLong()) - } - LIBRARY.CFRelease(translated) - builder.append(buf).append(" (").append(code).append(')') - } - LOG.error(builder.toString()) + if (code == 0 || code == errSecItemNotFound) { + return } + + val translated = LIBRARY.SecCopyErrorMessageString(code, null) + val builder = StringBuilder(message).append(": ") + if (translated == null) { + builder.append(code) + } + else { + val buf = CharArray(LIBRARY.CFStringGetLength(translated).toInt()) + for (i in 0..buf.size - 1) { + buf[i] = LIBRARY.CFStringGetCharacterAtIndex(translated, i.toLong()) + } + LIBRARY.CFRelease(translated) + builder.append(buf).append(" (").append(code).append(')') + } + LOG.error(builder.toString()) } \ No newline at end of file diff --git a/platform/credential-store/src/nativeCredentialStore.kt b/platform/credential-store/src/nativeCredentialStore.kt new file mode 100644 index 000000000000..ef0255be4885 --- /dev/null +++ b/platform/credential-store/src/nativeCredentialStore.kt @@ -0,0 +1,119 @@ +/* + * 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 com.intellij.credentialStore + +import com.intellij.credentialStore.linux.SecretCredentialStore +import com.intellij.credentialStore.macOs.KeyChainCredentialStore +import com.intellij.credentialStore.macOs.isMacOsCredentialStoreSupported +import com.intellij.ide.passwordSafe.PasswordStorage +import com.intellij.openapi.diagnostic.catchAndLog +import com.intellij.openapi.util.SystemInfo +import com.intellij.util.SystemProperties +import com.intellij.util.concurrency.QueueProcessor +import com.intellij.util.containers.ContainerUtil + +private const val nullPassword = "\u0000" + +private class CredentialStoreWrapper(private val store: CredentialStore) : PasswordStorage { + private val fallbackStore = lazy { FileCredentialStore(memoryOnly = true) } + + private val queueProcessor = QueueProcessor<() -> Unit>({ + it() + }) + + private val postponedCredentials = ContainerUtil.newConcurrentMap() + + override fun getPassword(requestor: Class<*>?, key: String): String? { + val rawKey = getRawKey(key, requestor) + + postponedCredentials.get(rawKey)?.let { + return if (it == nullPassword) null else it + } + + var store = if (fallbackStore.isInitialized()) fallbackStore.value else store + + // try old key - as hash + @Suppress("CanBeVal") + var value: String? + try { + value = store.get(rawKey) + } + catch (e: UnsatisfiedLinkError) { + store = fallbackStore.value + LOG.error(e) + value = store.get(rawKey) + } + catch (e: Throwable) { + LOG.error(e) + return null + } + + if (value == null) { + LOG.catchAndLog { + val oldKey = toOldKey(rawKey) + value = store.get(oldKey) + if (value != null) { + LOG.catchAndLog { store.set(oldKey, null) } + store.set(key, value!!.toByteArray()) + } + } + } + return value + } + + override fun setPassword(requestor: Class<*>?, key: String, value: String?) { + LOG.catchAndLog { + val store = if (fallbackStore.isInitialized()) fallbackStore.value else store + val rawKey = getRawKey(key, requestor) + val passwordData = value?.toByteArray() + if (fallbackStore.isInitialized()) { + store.set(rawKey, passwordData) + } + else { + postponedCredentials.put(rawKey, value ?: nullPassword) + queueProcessor.add { + if (!fallbackStore.isInitialized()) { + LOG.catchAndLog { + store.set(rawKey, passwordData) + postponedCredentials.remove(rawKey) + return@add + } + } + fallbackStore.value.set(rawKey, passwordData) + postponedCredentials.remove(rawKey) + } + } + } + } +} + +private class MacOsCredentialStoreFactory : CredentialStoreFactory { + override fun create(): PasswordStorage? { + if (isMacOsCredentialStoreSupported && SystemProperties.getBooleanProperty("use.mac.keychain", true)) { + return CredentialStoreWrapper(KeyChainCredentialStore("IntelliJ Platform")) + } + return null + } +} + +private class LinuxSecretCredentialStoreFactory : CredentialStoreFactory { + override fun create(): PasswordStorage? { + if (SystemInfo.isLinux && SystemProperties.getBooleanProperty("use.linux.keychain", true)) { + return CredentialStoreWrapper(SecretCredentialStore("com.intellij.credentialStore.Credential")) + } + return null + } +} \ No newline at end of file diff --git a/platform/credential-store/test/FileCredentialStoreTest.kt b/platform/credential-store/test/FileCredentialStoreTest.kt index eb24fc26b816..d04364800102 100644 --- a/platform/credential-store/test/FileCredentialStoreTest.kt +++ b/platform/credential-store/test/FileCredentialStoreTest.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.ide.passwordSafe +package com.intellij.credentialStore import com.intellij.testFramework.RuleChain import com.intellij.testFramework.TemporaryDirectory diff --git a/platform/credential-store/test/LinuxSecretTest.kt b/platform/credential-store/test/LinuxSecretTest.kt new file mode 100644 index 000000000000..5abe45740229 --- /dev/null +++ b/platform/credential-store/test/LinuxSecretTest.kt @@ -0,0 +1,32 @@ +package com.intellij.credentialStore.linux + +import com.intellij.openapi.util.SystemInfo +import com.intellij.testFramework.UsefulTestCase +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test +import java.math.BigInteger +import java.util.* + +class LinuxSecretTest { + @Test + fun test() { + if (!SystemInfo.isLinux || UsefulTestCase.IS_UNDER_TEAMCITY) { + return + } + + val store = SecretCredentialStore("com.intellij.test") + val pass = BigInteger(128, Random()).toString(32) + store.set("test", pass.toByteArray()) + assertThat(store.get("test")).isEqualTo(pass) + + store.set("test", null) + assertThat(store.get("test")).isNull() + + val unicodePassword = "Gr\u00FCnwald" + store.set("test", unicodePassword.toByteArray()) + assertThat(store.get("test")).isEqualTo(unicodePassword) + + store.set(unicodePassword, pass.toByteArray()) + assertThat(store.get(unicodePassword)).isEqualTo(pass) + } +} \ No newline at end of file diff --git a/platform/credential-store/test/MasterPasswordMigrationTest.kt b/platform/credential-store/test/MasterPasswordMigrationTest.kt index 0a966f3b144a..36360051ec06 100644 --- a/platform/credential-store/test/MasterPasswordMigrationTest.kt +++ b/platform/credential-store/test/MasterPasswordMigrationTest.kt @@ -13,14 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.ide.passwordSafe +package com.intellij.credentialStore import com.intellij.ide.passwordSafe.impl.providers.masterKey.PasswordDatabase import com.intellij.openapi.util.JDOMUtil import com.intellij.testFramework.ApplicationRule import com.intellij.testFramework.runInEdtAndWait import com.intellij.util.xmlb.XmlSerializer - import org.assertj.core.api.Assertions.assertThat import org.junit.ClassRule import org.junit.Test diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionRVContentProvider.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionRVContentProvider.java index 4f9b181adc52..35f2ca182bb8 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionRVContentProvider.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionRVContentProvider.java @@ -74,7 +74,7 @@ public abstract class InspectionRVContentProvider { @NotNull public RefElementNode createNode(@NotNull InspectionToolPresentation presentation) { - return ReadAction.compute(() -> new RefElementNode(myEntity, presentation)); + return ReadAction.compute(() -> presentation.createRefNode(myEntity)); } @Nullable diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/QuickFixAction.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/QuickFixAction.java index f3ef73f63605..1efc1db12d9c 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/QuickFixAction.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/QuickFixAction.java @@ -271,7 +271,7 @@ public class QuickFixAction extends AnAction implements CustomComponentAction { } } - private static void refreshViews(@NotNull Project project, @NotNull RefEntity[] refElements, @NotNull InspectionToolWrapper toolWrapper) { + protected static void refreshViews(@NotNull Project project, @NotNull RefEntity[] refElements, @NotNull InspectionToolWrapper toolWrapper) { final Set ignoredElements = new HashSet<>(); for (RefEntity element : refElements) { final PsiElement psiElement = element instanceof RefElement ? ((RefElement)element).getElement() : null; diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionToolPresentation.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionToolPresentation.java index 4c5244ac361c..32712db58793 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionToolPresentation.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionToolPresentation.java @@ -48,6 +48,12 @@ public interface InspectionToolPresentation extends ProblemDescriptionsProcessor @NotNull InspectionTreeNode parentNode, final boolean showStructure, final boolean groupBySeverity); + + @NotNull + default RefElementNode createRefNode(@NotNull RefEntity entity) { + return new RefElementNode(entity, this); + } + void updateContent(); boolean hasReportedProblems(); diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTreeCellRenderer.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTreeCellRenderer.java index 907a726033d1..040f413da079 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTreeCellRenderer.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTreeCellRenderer.java @@ -69,7 +69,7 @@ class InspectionTreeCellRenderer extends ColoredTreeCellRenderer { if (node.isExcluded(myView.getExcludedManager())) { return attributes.derive(attributes.getStyle() | SimpleTextAttributes.STYLE_STRIKEOUT, null, null, null); } - if (node instanceof ProblemDescriptionNode && ((ProblemDescriptionNode)node).isQuickFixAppliedFromView()) { + if (node instanceof SuppressableInspectionTreeNode && ((SuppressableInspectionTreeNode)node).isQuickFixAppliedFromView()) { return attributes.derive(-1, SimpleTextAttributes.GRAYED_ATTRIBUTES.getFgColor(), null, null); } if (!node.isValid()) { diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/ProblemDescriptionNode.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/ProblemDescriptionNode.java index a4d7bb6bdb14..f431cd7c79a4 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/ProblemDescriptionNode.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/ProblemDescriptionNode.java @@ -169,6 +169,7 @@ public class ProblemDescriptionNode extends SuppressableInspectionTreeNode { return XmlStringUtil.stripHtml(ProblemDescriptorUtil.renderDescriptionMessage(descriptor, element, TRIM_AT_TREE_END)); } + @Override public boolean isQuickFixAppliedFromView() { return (myDescriptor != null && myPresentation.isProblemResolved(getElement(), myDescriptor)) && !isAlreadySuppressedFromView(); } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/RefElementNode.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/RefElementNode.java index 68a0d15c9479..a92de6a639e9 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/RefElementNode.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/RefElementNode.java @@ -134,6 +134,11 @@ public class RefElementNode extends SuppressableInspectionTreeNode { super.visitProblemSeverities(counter); } + @Override + public boolean isQuickFixAppliedFromView() { + return false; + } + @Nullable @Override public String getCustomizedTailText() { diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/SuppressableInspectionTreeNode.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/SuppressableInspectionTreeNode.java index c8e6928a6691..1c0110b9745d 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/SuppressableInspectionTreeNode.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/SuppressableInspectionTreeNode.java @@ -60,6 +60,8 @@ public abstract class SuppressableInspectionTreeNode extends CachedInspectionTre return usrObj != null && myView.getSuppressedNodes(myPresentation.getToolWrapper().getShortName()).contains(usrObj); } + public abstract boolean isQuickFixAppliedFromView(); + @Nullable @Override public String getCustomizedTailText() { diff --git a/platform/lang-impl/src/com/intellij/execution/impl/RunConfigurationBeforeRunProvider.java b/platform/lang-impl/src/com/intellij/execution/impl/RunConfigurationBeforeRunProvider.java index 2010b73d2968..4252c2d346e2 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/RunConfigurationBeforeRunProvider.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/RunConfigurationBeforeRunProvider.java @@ -43,6 +43,7 @@ import com.intellij.ui.components.JBList; import com.intellij.ui.components.JBScrollPane; import com.intellij.util.Consumer; import com.intellij.util.concurrency.Semaphore; +import com.intellij.util.containers.ContainerUtil; import org.jdom.Attribute; import org.jdom.Element; import org.jetbrains.annotations.NotNull; @@ -190,22 +191,13 @@ extends BeforeRunTaskProvider targets = ApplicationManager.getApplication().runReadAction(new Computable>() { - @Override - public List compute() { - return ExecutionTargetManager.getTargetsFor(env.getProject(), settings); - } - }); - - if (targets.isEmpty()) { + ExecutionTarget compatibleTarget = getCompatibleTarget(env, settings); + if (compatibleTarget == null) { return false; } - final ExecutionEnvironment environment = builder.target(targets.get(0)).build(); + final ExecutionEnvironment environment = builder.target(compatibleTarget).build(); environment.setExecutionId(env.getExecutionId()); - if (!ExecutionTargetManager.canRun(settings, environment.getExecutionTarget())) { - return false; - } if (!environment.getRunner().canRun(executorId, environment.getRunProfile())) { return false; @@ -216,6 +208,22 @@ extends BeforeRunTaskProvider targets = ApplicationManager.getApplication().runReadAction(new Computable>() { + @Override + public List compute() { + return ExecutionTargetManager.getTargetsFor(env.getProject(), settings); + } + }); + + return ContainerUtil.getFirstItem(targets); + } + public static boolean doRunTask(final String executorId, final ExecutionEnvironment environment, ProgramRunner runner) { final Semaphore targetDone = new Semaphore(); final Ref result = new Ref<>(false); diff --git a/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesAction.java b/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesAction.java index 75cb5c7c25ce..33e36b1fa833 100644 --- a/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesAction.java +++ b/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesAction.java @@ -407,11 +407,6 @@ public class ShowUsagesAction extends AnAction implements PopupAction { public UsageNode valueOf(UsageNode node) { return node; } - - @Override - public boolean isCellEditable(UsageNode node) { - return usageView.isOriginUsage(node.getUsage()); - } }; List> list = Collections.nCopies(cols, o); return list.toArray(new ColumnInfo[list.size()]); @@ -652,7 +647,7 @@ public class ShowUsagesAction extends AnAction implements PopupAction { new DumbAwareAction() { @Override public void actionPerformed(@NotNull AnActionEvent e) { - cancel(popup); + cancel(popup[0]); showDialogAndFindUsages(handler, popupPosition, editor, maxUsages); } }.registerCustomShortcutSet(new CustomShortcutSet(shortcut.getFirstKeyStroke()), table); @@ -662,13 +657,13 @@ public class ShowUsagesAction extends AnAction implements PopupAction { new DumbAwareAction() { @Override public void actionPerformed(@NotNull AnActionEvent e) { - cancel(popup); + cancel(popup[0]); searchEverywhere(options, handler, editor, popupPosition, maxUsages); } }.registerCustomShortcutSet(new CustomShortcutSet(shortcut.getFirstKeyStroke()), table); } - InplaceButton settingsButton = createSettingsButton(handler, popupPosition, editor, maxUsages, () -> cancel(popup)); + InplaceButton settingsButton = createSettingsButton(handler, popupPosition, editor, maxUsages, () -> cancel(popup[0])); ActiveComponent spinningProgress = new ActiveComponent.Adapter() { @Override @@ -726,7 +721,7 @@ public class ShowUsagesAction extends AnAction implements PopupAction { @Override public void actionPerformed(@NotNull AnActionEvent e) { hideHints(); - cancel(popup); + cancel(popup[0]); FindUsagesManager findUsagesManager = ((FindManagerImpl)FindManager.getInstance(usageView.getProject())).getFindUsagesManager(); findUsagesManager.findUsages(handler.getPrimaryElements(), handler.getSecondaryElements(), handler, options, FindSettings.getInstance().isSkipResultsWithOneUsage()); @@ -747,9 +742,9 @@ public class ShowUsagesAction extends AnAction implements PopupAction { }; } - private static void cancel(@Nullable JBPopup... popup) { - if (popup != null && popup.length>0 && popup[0] != null) { - popup[0].cancel(); + private static void cancel(@Nullable JBPopup popup) { + if (popup != null) { + popup.cancel(); } } @@ -957,6 +952,13 @@ public class ShowUsagesAction extends AnAction implements PopupAction { newSelection = table.getSelectedRow(); } else { + // do not pre-select the usage under caret by default + if (newSelection == 0 && table.getModel().getRowCount() > 1) { + Object valueInTopRow = table.getModel().getValueAt(0, 0); + if (valueInTopRow instanceof UsageNode && usageView.isOriginUsage(((UsageNode)valueInTopRow).getUsage())) { + newSelection++; + } + } table.getSelectionModel().setSelectionInterval(newSelection, newSelection); } ScrollingUtil.ensureIndexIsVisible(table, newSelection, 0); diff --git a/platform/lang-impl/src/com/intellij/ide/navigationToolbar/ui/AbstractNavBarUI.java b/platform/lang-impl/src/com/intellij/ide/navigationToolbar/ui/AbstractNavBarUI.java index d30d9d2535b1..4e3d744f5735 100644 --- a/platform/lang-impl/src/com/intellij/ide/navigationToolbar/ui/AbstractNavBarUI.java +++ b/platform/lang-impl/src/com/intellij/ide/navigationToolbar/ui/AbstractNavBarUI.java @@ -307,7 +307,7 @@ public abstract class AbstractNavBarUI implements NavBarUI { @Override public void doPaintNavBarPanel(Graphics2D g, Rectangle r, boolean mainToolbarVisible, boolean undocked) { g.setColor(getBackgroundColor()); - if (!UIUtil.isUnderAquaLookAndFeel() && mainToolbarVisible) { + if (mainToolbarVisible) { g.fillRect(0, 0, r.width, r.height); } } diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java index 66fff05abaf9..b70a8eafa159 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java @@ -53,7 +53,10 @@ import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.*; -import com.intellij.openapi.util.*; +import com.intellij.openapi.util.ActionCallback; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.IdeFocusManager; @@ -88,7 +91,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import javax.swing.border.CompoundBorder; import javax.swing.border.EmptyBorder; import javax.swing.event.DocumentEvent; import javax.swing.event.ListSelectionEvent; @@ -389,9 +391,6 @@ public abstract class ChooseByNameBase { if (myModel.getPromptText() != null) { JLabel label = new JLabel(myModel.getPromptText()); - if (UIUtil.isUnderAquaLookAndFeel()) { - label.setBorder(new CompoundBorder(new EmptyBorder(0, 9, 0, 0), label.getBorder())); - } label.setFont(UIUtil.getLabelFont().deriveFont(Font.BOLD)); caption2Tools.add(label, BorderLayout.WEST); } diff --git a/platform/platform-api/platform-api.iml b/platform/platform-api/platform-api.iml index fa8a000b28c8..2e3cac31a1e1 100644 --- a/platform/platform-api/platform-api.iml +++ b/platform/platform-api/platform-api.iml @@ -16,17 +16,9 @@ + - - - - - - - - - diff --git a/platform/platform-api/src/com/intellij/openapi/fileChooser/FileChooser.java b/platform/platform-api/src/com/intellij/openapi/fileChooser/FileChooser.java index c51fef0c4b22..65d8582e2d28 100644 --- a/platform/platform-api/src/com/intellij/openapi/fileChooser/FileChooser.java +++ b/platform/platform-api/src/com/intellij/openapi/fileChooser/FileChooser.java @@ -87,7 +87,7 @@ public class FileChooser { @Nullable final Project project, @Nullable final VirtualFile toSelect, @NotNull final Consumer> callback) { - chooseFiles(descriptor, project, KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(), toSelect, callback); + chooseFiles(descriptor, project, KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow(), toSelect, callback); } /** diff --git a/platform/platform-api/src/com/intellij/openapi/ui/ComboBox.java b/platform/platform-api/src/com/intellij/openapi/ui/ComboBox.java index b320b9f85a7b..59eeaae20025 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/ComboBox.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/ComboBox.java @@ -17,7 +17,6 @@ package com.intellij.openapi.ui; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; -import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.ui.IdeBorderFactory; import com.intellij.ui.components.JBList; @@ -27,7 +26,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.ComboPopup; import javax.swing.table.TableCellEditor; import java.awt.*; @@ -242,14 +240,7 @@ public class ComboBox extends ComboBoxWithWidePopup implements AWTEventLis } public final void setEditor(final ComboBoxEditor editor) { - ComboBoxEditor _editor = editor; - if (SystemInfo.isMac && UIUtil.isUnderAquaLookAndFeel()) { - if (editor instanceof UIResource) { - _editor = new FixedComboBoxEditor(); - } - } - - super.setEditor(new MyEditor(this, _editor)); + super.setEditor(new MyEditor(this, editor)); } public final Dimension getMinimumSize() { @@ -266,23 +257,6 @@ public class ComboBox extends ComboBoxWithWidePopup implements AWTEventLis return new Dimension(width, UIUtil.fixComboBoxHeight(preferredSize.height)); } - @Override - public Insets getInsets() { - Insets insets = super.getInsets(); - if (SystemInfo.isMac && UIUtil.isUnderAquaLookAndFeel() && isEditable) { - insets.right += 2; - } - return insets; - } - - @Override - public boolean hasFocus() { - if (SystemInfo.isMac && UIUtil.isUnderAquaLookAndFeel() && myPaintingNow && isEditable) { - return false; - } - return super.hasFocus(); - } - protected Dimension getOriginalPreferredSize() { return super.getPreferredSize(); } diff --git a/platform/platform-api/src/com/intellij/openapi/ui/popup/BalloonBuilder.java b/platform/platform-api/src/com/intellij/openapi/ui/popup/BalloonBuilder.java index 41db010dbfe6..bad5404519b7 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/popup/BalloonBuilder.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/popup/BalloonBuilder.java @@ -98,6 +98,8 @@ public interface BalloonBuilder { @NotNull BalloonBuilder setRequestFocus(boolean requestFocus); + BalloonBuilder setHideOnCloseClick(boolean hideOnCloseClick); + /** * Links target balloon life cycle to the given object. I.e. current balloon will be auto-hide and collected as soon * as given anchor is disposed. diff --git a/platform/platform-api/src/com/intellij/util/io/CompressedBytesReadAwareGZIPInputStream.java b/platform/platform-api/src/com/intellij/util/io/CountingGZIPInputStream.java similarity index 61% rename from platform/platform-api/src/com/intellij/util/io/CompressedBytesReadAwareGZIPInputStream.java rename to platform/platform-api/src/com/intellij/util/io/CountingGZIPInputStream.java index 6c1a487e1ee1..287d0a5b3354 100644 --- a/platform/platform-api/src/com/intellij/util/io/CompressedBytesReadAwareGZIPInputStream.java +++ b/platform/platform-api/src/com/intellij/util/io/CountingGZIPInputStream.java @@ -19,61 +19,62 @@ import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.io.InputStream; -import java.util.concurrent.atomic.AtomicLong; import java.util.zip.GZIPInputStream; -public class CompressedBytesReadAwareGZIPInputStream extends GZIPInputStream { - private final BytesReadAwareInputStream myInputStream; +/** + * A stream for reading compressed data in the GZIP file format. + * Total amount of compressed read bytes can be accessed via {@link #getCompressedBytesRead()}. + * + * Note that this implementation is not thread safe. + */ +public class CountingGZIPInputStream extends GZIPInputStream { + private final CountingInputStream myInputStream; - private CompressedBytesReadAwareGZIPInputStream(@NotNull BytesReadAwareInputStream inputStream) throws IOException { + private CountingGZIPInputStream(@NotNull CountingInputStream inputStream) throws IOException { super(inputStream); myInputStream = inputStream; } public long getCompressedBytesRead() { - return myInputStream.myBytesRead.get(); + return myInputStream.myBytesRead; } @NotNull - public static CompressedBytesReadAwareGZIPInputStream create(@NotNull InputStream inputStream) throws IOException { - return new CompressedBytesReadAwareGZIPInputStream(new BytesReadAwareInputStream(inputStream)); + public static CountingGZIPInputStream create(@NotNull InputStream inputStream) throws IOException { + return new CountingGZIPInputStream(new CountingInputStream(inputStream)); } - private static class BytesReadAwareInputStream extends InputStream { + private static class CountingInputStream extends InputStream { private final InputStream myInputStream; - private final AtomicLong myBytesRead = new AtomicLong(0); + private long myBytesRead = 0; - public BytesReadAwareInputStream(@NotNull InputStream inputStream) { + public CountingInputStream(@NotNull InputStream inputStream) { myInputStream = inputStream; } public int read() throws IOException { - long bytesReadBefore = myBytesRead.get(); int data = myInputStream.read(); - myBytesRead.compareAndSet(bytesReadBefore, bytesReadBefore + 1); + myBytesRead++; return data; } @Override public int read(@NotNull byte[] b) throws IOException { - long bytesReadBefore = myBytesRead.get(); int bytesRead = myInputStream.read(b); - myBytesRead.compareAndSet(bytesReadBefore, bytesReadBefore + bytesRead); + myBytesRead += bytesRead; return bytesRead; } @Override public int read(@NotNull byte[] b, int off, int len) throws IOException { - long bytesReadBefore = myBytesRead.get(); int bytesRead = myInputStream.read(b, off, len); - myBytesRead.compareAndSet(bytesReadBefore, bytesReadBefore + bytesRead); + myBytesRead += bytesRead; return bytesRead; } public long skip(long n) throws IOException { - long bytesReadBefore = myBytesRead.get(); long bytesSkipped = myInputStream.skip(n); - myBytesRead.compareAndSet(bytesReadBefore, bytesReadBefore + bytesSkipped); + myBytesRead += bytesSkipped; return bytesSkipped; } diff --git a/platform/platform-api/src/com/intellij/util/io/HttpRequests.java b/platform/platform-api/src/com/intellij/util/io/HttpRequests.java index 3db597dcec5d..98952a16bb33 100644 --- a/platform/platform-api/src/com/intellij/util/io/HttpRequests.java +++ b/platform/platform-api/src/com/intellij/util/io/HttpRequests.java @@ -278,8 +278,7 @@ public final class HttpRequests { if (myInputStream == null) { myInputStream = getConnection().getInputStream(); if (myBuilder.myGzip && "gzip".equalsIgnoreCase(getConnection().getContentEncoding())) { - //noinspection IOResourceOpenedButNotSafelyClosed - myInputStream = CompressedBytesReadAwareGZIPInputStream.create(myInputStream); + myInputStream = CountingGZIPInputStream.create(myInputStream); } } return myInputStream; diff --git a/platform/platform-api/src/com/intellij/util/net/NetUtils.java b/platform/platform-api/src/com/intellij/util/net/NetUtils.java index 1c02684465ac..e8e9405c42b8 100644 --- a/platform/platform-api/src/com/intellij/util/net/NetUtils.java +++ b/platform/platform-api/src/com/intellij/util/net/NetUtils.java @@ -21,7 +21,7 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.util.SystemInfo; import com.intellij.util.ObjectUtils; import com.intellij.util.SystemProperties; -import com.intellij.util.io.CompressedBytesReadAwareGZIPInputStream; +import com.intellij.util.io.CountingGZIPInputStream; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -188,7 +188,7 @@ public class NetUtils { indicator.setIndeterminate(true); } } - CompressedBytesReadAwareGZIPInputStream gzipStream = ObjectUtils.tryCast(inputStream, CompressedBytesReadAwareGZIPInputStream.class); + CountingGZIPInputStream gzipStream = ObjectUtils.tryCast(inputStream, CountingGZIPInputStream.class); final byte[] buffer = new byte[8 * 1024]; int count; int bytesWritten = 0; diff --git a/platform/platform-impl/src/com/intellij/ide/ui/customization/CustomizationUtil.java b/platform/platform-impl/src/com/intellij/ide/ui/customization/CustomizationUtil.java index 4d9e745381fe..4e19b7758d2d 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/customization/CustomizationUtil.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/customization/CustomizationUtil.java @@ -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. @@ -137,11 +137,13 @@ public class CustomizationUtil { @Override public boolean accept(Object node) { DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)node; - if (treeNode.isLeaf()) { + Object userObject = treeNode.getUserObject(); + if (treeNode.isLeaf() && !(userObject instanceof Group)) { return true; } - final ActionUrl url = getActionUrl(new TreePath(treeNode.getPath()), 0); - url.getGroupPath().add(((Group)treeNode.getUserObject()).getName()); + ActionUrl url = getActionUrl(new TreePath(treeNode.getPath()), 0); + String groupName = ((Group)userObject).getName(); + url.getGroupPath().add(groupName); final TreePath treePath = getTreePath(defaultTree, url); if (treePath != null) { final DefaultMutableTreeNode visited = (DefaultMutableTreeNode)treePath.getLastPathComponent(); @@ -152,7 +154,7 @@ public class CustomizationUtil { //customizations at the new place url.getGroupPath().remove(url.getParentGroup()); if (actions.contains(url)){ - url.getGroupPath().add(((Group)treeNode.getUserObject()).getName()); + url.getGroupPath().add(groupName); actions.addAll(schema.getChildActions(url)); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java b/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java index 9dd912540ece..e15b6351e343 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java @@ -54,6 +54,7 @@ import com.intellij.ui.mac.foundation.MacUtil; import com.intellij.util.IJSwingUtilities; import com.intellij.util.ReflectionUtil; import com.intellij.util.ui.JBInsets; +import com.intellij.util.ui.OwnerOptional; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -175,18 +176,16 @@ public class DialogWrapperPeerImpl extends DialogWrapperPeer implements FocusTra */ protected DialogWrapperPeerImpl(@NotNull DialogWrapper wrapper, @NotNull Component parent, boolean canBeParent) { myWrapper = wrapper; - if (!parent.isShowing()) { - throw new IllegalArgumentException("parent must be showing: " + parent); - } + myWindowManager = null; Application application = ApplicationManager.getApplication(); if (application != null && application.hasComponent(WindowManager.class)) { myWindowManager = (WindowManagerEx)WindowManager.getInstance(); } - Window owner = parent instanceof Window ? (Window)parent : (Window)SwingUtilities.getAncestorOfClass(Window.class, parent); - if (!(owner instanceof Dialog) && !(owner instanceof Frame)) { - owner = JOptionPane.getRootFrame(); + Window owner = OwnerOptional.fromComponent(parent).get(); + if (!owner.isShowing()) { + throw new IllegalArgumentException("owner must be showing: " + owner); } createDialog(owner, canBeParent); } @@ -219,7 +218,7 @@ public class DialogWrapperPeerImpl extends DialogWrapperPeer implements FocusTra @Deprecated public DialogWrapperPeerImpl(@NotNull DialogWrapper wrapper,final Window owner, final boolean canBeParent, final boolean applicationModalIfPossible) { - this(wrapper, owner, canBeParent, applicationModalIfPossible ? DialogWrapper.IdeModalityType.IDE : DialogWrapper.IdeModalityType.PROJECT); + this(wrapper, owner, canBeParent, applicationModalIfPossible ? DialogWrapper.IdeModalityType.IDE : DialogWrapper.IdeModalityType.PROJECT); } @Override @@ -496,7 +495,7 @@ public class DialogWrapperPeerImpl extends DialogWrapperPeer implements FocusTra if (StackingPopupDispatcher.getInstance().isPopupFocused()) return; JTree tree = UIUtil.getParentOfType(JTree.class, focusOwner); JTable table = UIUtil.getParentOfType(JTable.class, focusOwner); - + if (tree != null || table != null) { if (hasNoEditingTreesOrTablesUpward(focusOwner)) { e.getPresentation().setEnabled(true); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/StripeButton.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/StripeButton.java index 8572df500c3a..c33b3833f172 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/StripeButton.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/StripeButton.java @@ -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. @@ -184,8 +184,14 @@ public final class StripeButton extends AnchoredButton implements ActionListener myDragPane = findLayeredPane(e); if (myDragPane == null) return; - final BufferedImage image = UIUtil.createImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); - paint(image.getGraphics()); + int width = getWidth() - 1; // -1 because StripeButtonUI.paint will not paint 1 pixel in case (anchor == ToolWindowAnchor.LEFT) + int height = getHeight() - 1; // -1 because StripeButtonUI.paint will not paint 1 pixel in case (anchor.isHorizontal()) + BufferedImage image = UIUtil.createImage(width, height, BufferedImage.TYPE_INT_RGB); + Graphics graphics = image.getGraphics(); + graphics.setColor(UIUtil.getBgFillColor(getParent())); + graphics.fillRect(0, 0, width, height); + paint(graphics); + graphics.dispose(); myDragButtonImage = new JLabel(new JBImageIcon(image)) { public String toString() { diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/WelcomeBalloonLayoutImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/WelcomeBalloonLayoutImpl.java index 61bdca9a5b22..b36fa6982a78 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/WelcomeBalloonLayoutImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/WelcomeBalloonLayoutImpl.java @@ -105,8 +105,8 @@ public class WelcomeBalloonLayoutImpl extends BalloonLayoutImpl { }); myPopupBalloon = - new BalloonImpl(pane, BORDER_COLOR, new Insets(0, 0, 0, 0), FILL_COLOR, true, false, false, false, true, 0, false, false, null, - false, 0, 0, 0, 0, false, null, null, false, false, false, null, false); + new BalloonImpl(pane, BORDER_COLOR, new Insets(0, 0, 0, 0), FILL_COLOR, true, false, false, true, false, true, 0, false, false, + null, false, 0, 0, 0, 0, false, null, null, false, false, false, null, false); myPopupBalloon.setAnimationEnabled(false); myPopupBalloon.setShadowBorderProvider( new NotificationBalloonShadowBorderProvider(FILL_COLOR, BORDER_COLOR)); diff --git a/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java b/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java index 674277664414..3c080a48cde8 100644 --- a/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java @@ -157,7 +157,7 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { } } - if (UIUtil.isCloseClick(me)) { + if (myHideOnCloseClick && UIUtil.isCloseClick(me)) { if (isInsideBalloon(me)) { hide(); me.consume(); @@ -291,6 +291,7 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { private Runnable myHideListener; private final boolean myHideOnKey; private final boolean myHideOnAction; + private final boolean myHideOnCloseClick; private final boolean myRequestFocus; private Component myOriginalFocusOwner; private final boolean myEnableButtons; @@ -302,6 +303,7 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { boolean hideOnMouse, boolean hideOnKey, boolean hideOnAction, + boolean hideOnCloseClick, boolean showPointer, boolean enableButtons, long fadeoutTime, @@ -328,6 +330,7 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { myHideOnMouse = hideOnMouse; myHideOnKey = hideOnKey; myHideOnAction = hideOnAction; + myHideOnCloseClick = hideOnCloseClick; myShowPointer = showPointer; myEnableButtons = enableButtons; myHideOnFrameResize = hideOnFrameResize; diff --git a/platform/platform-impl/src/com/intellij/ui/Win7TaskBar.java b/platform/platform-impl/src/com/intellij/ui/Win7TaskBar.java index 413c09e1d5bd..4621570a73de 100644 --- a/platform/platform-impl/src/com/intellij/ui/Win7TaskBar.java +++ b/platform/platform-impl/src/com/intellij/ui/Win7TaskBar.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 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. @@ -15,6 +15,7 @@ */ package com.intellij.ui; +import com.intellij.jna.DisposableMemory; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.wm.IdeFrame; @@ -70,17 +71,6 @@ class Win7TaskBar { boolean FlashWindow(WinDef.HWND hwnd, boolean bInvert); } - private static class MyMemory extends Memory { - private MyMemory(long size) { - super(size); - } - - @Override - public synchronized void dispose() { - super.dispose(); - } - } - private static boolean ourInitialized = true; static { @@ -162,7 +152,7 @@ class Win7TaskBar { return new Object(); } - MyMemory memory = new MyMemory(ico.length); + DisposableMemory memory = new DisposableMemory(ico.length); try { memory.write(0, ico, 0, ico.length); diff --git a/platform/platform-impl/src/com/intellij/ui/mac/MacPathChooserDialog.java b/platform/platform-impl/src/com/intellij/ui/mac/MacPathChooserDialog.java index 843caae83081..37f85924d2e8 100644 --- a/platform/platform-impl/src/com/intellij/ui/mac/MacPathChooserDialog.java +++ b/platform/platform-impl/src/com/intellij/ui/mac/MacPathChooserDialog.java @@ -26,18 +26,15 @@ import com.intellij.openapi.fileChooser.FileChooserDescriptor; import com.intellij.openapi.fileChooser.PathChooserDialog; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.ui.popup.StackingPopupDispatcher; -import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.IdeFrame; import com.intellij.openapi.wm.WindowManager; import com.intellij.ui.UIBundle; -import com.intellij.ui.popup.AbstractPopup; import com.intellij.util.ArrayUtil; import com.intellij.util.Consumer; -import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.OwnerOptional; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -54,23 +51,25 @@ import java.util.stream.Stream; public class MacPathChooserDialog implements PathChooserDialog { - private final FileDialog myFileDialog; + private FileDialog myFileDialog; private final FileChooserDescriptor myFileChooserDescriptor; private final Component myParent; private final String myTitle; public MacPathChooserDialog(FileChooserDescriptor descriptor, Component parent, Project project) { - //StackingPopupDispatcher.getInstance().hidePersistentPopups(); - //myDisposeActions.add(() -> StackingPopupDispatcher.getInstance().restorePersistentPopups()); - - myFileDialog = parent != null - ? createFileDialogWithOwner(findOwnerByComponent(parent), descriptor.getTitle(), FileDialog.LOAD) - : createFileDialogWithoutOwner(descriptor.getTitle(), FileDialog.LOAD); - myFileChooserDescriptor = descriptor; myParent = parent; myTitle = getChooserTitle(descriptor); + + Consumer

dialogConsumer = owner -> myFileDialog = new FileDialog(owner, myTitle, FileDialog.LOAD); + Consumer frameConsumer = owner -> myFileDialog = new FileDialog(owner, myTitle, FileDialog.LOAD); + + OwnerOptional + .fromComponent(parent) + .ifDialog(dialogConsumer) + .ifFrame(frameConsumer) + .ifNull(frameConsumer); } private static String getChooserTitle(final FileChooserDescriptor descriptor) { @@ -139,64 +138,6 @@ public class MacPathChooserDialog implements PathChooserDialog { } } - @NotNull - private static Window findOwnerByComponent(@NotNull Component component) { - return (component instanceof Window) ? (Window) component : SwingUtilities.getWindowAncestor(component); - } - - @NotNull - private static FileDialog createFileDialogWithOwner(@NotNull Window owner, String title, int mode) { - FileDialog fileDialog; - - IdePopupManager manager = IdeEventQueue.getInstance().getPopupManager(); - - if (manager.isPopupWindow(owner)) { - - manager.closeAllPopups(); - - owner = owner.getOwner(); - - while (owner != null - && !(owner instanceof Dialog) - && !(owner instanceof Frame)) { - owner = owner.getOwner(); - } - } - - if (owner instanceof Dialog) { - Dialog ownerDialog = (Dialog)owner; - if (ownerDialog.isModal()) { - owner = ownerDialog; - } - else { - while (owner instanceof Dialog && !((Dialog)owner).isModal()) { - owner = owner.getOwner(); - } - } - } - - if (owner == null) { - fileDialog = createFileDialogWithoutOwner(title, mode); - } - else { - if (owner instanceof Frame) { - if (owner instanceof IdeFrame.Child) { - IdeFrame.Child ideFrameChild = (IdeFrame.Child)owner; - owner = WindowManager.getInstance().getFrame(ideFrameChild.getProject()); - } - fileDialog = new FileDialog((Frame)owner, title, mode); - } - else if (owner instanceof Dialog) { - fileDialog = new FileDialog((Dialog)owner, title, mode); - } - else { - throw new RuntimeException("Owner should be a frame or a dialog"); - } - } - - return fileDialog; - } - @NotNull private static FileDialog createFileDialogWithoutOwner(String title, int load) { // This is bad. But sometimes we do not have any windows at all. diff --git a/platform/platform-impl/src/com/intellij/ui/popup/BalloonPopupBuilderImpl.java b/platform/platform-impl/src/com/intellij/ui/popup/BalloonPopupBuilderImpl.java index 37c1648d24e7..e307808c73c1 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/BalloonPopupBuilderImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/BalloonPopupBuilderImpl.java @@ -61,6 +61,7 @@ public class BalloonPopupBuilderImpl implements BalloonBuilder { private int myPositionChangeXShift; private int myPositionChangeYShift; private boolean myHideOnAction = true; + private boolean myHideOnCloseClick = true; private boolean myDialogMode; private String myTitle; private Insets myContentInsets = JBUI.insets(2); @@ -153,6 +154,12 @@ public class BalloonPopupBuilderImpl implements BalloonBuilder { return this; } + @Override + public BalloonBuilder setHideOnCloseClick(boolean hideOnCloseClick) { + myHideOnCloseClick = hideOnCloseClick; + return this; + } + @NotNull @Override public BalloonBuilder setAnimationCycle(int time) { @@ -256,10 +263,10 @@ public class BalloonPopupBuilderImpl implements BalloonBuilder { @Override public Balloon createBalloon() { final BalloonImpl result = new BalloonImpl( - myContent, myBorder, myBorderInsets, myFill, myHideOnMouseOutside, myHideOnKeyOutside, myHideOnAction, myShowCallout, myCloseButtonEnabled, - myFadeoutTime, myHideOnFrameResize, myHideOnLinkClick, myClickHandler, myCloseOnClick, myAnimationCycle, myCalloutShift, - myPositionChangeXShift, myPositionChangeYShift, myDialogMode, myTitle, myContentInsets, myShadow, mySmallVariant, myBlockClicks, - myLayer, myRequestFocus); + myContent, myBorder, myBorderInsets, myFill, myHideOnMouseOutside, myHideOnKeyOutside, myHideOnAction, myHideOnCloseClick, + myShowCallout, myCloseButtonEnabled, myFadeoutTime, myHideOnFrameResize, myHideOnLinkClick, myClickHandler, myCloseOnClick, + myAnimationCycle, myCalloutShift, myPositionChangeXShift, myPositionChangeYShift, myDialogMode, myTitle, myContentInsets, myShadow, + mySmallVariant, myBlockClicks, myLayer, myRequestFocus); if (myStorage != null && myAnchor != null) { List balloons = myStorage.get(myAnchor); diff --git a/platform/platform-impl/src/com/intellij/util/ui/OwnerOptional.java b/platform/platform-impl/src/com/intellij/util/ui/OwnerOptional.java new file mode 100644 index 000000000000..ad88b852ea0f --- /dev/null +++ b/platform/platform-impl/src/com/intellij/util/ui/OwnerOptional.java @@ -0,0 +1,104 @@ +/* + * 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 com.intellij.util.ui; + +import com.intellij.ide.IdeEventQueue; +import com.intellij.ide.IdePopupManager; +import com.intellij.openapi.wm.IdeFrame; +import com.intellij.openapi.wm.WindowManager; +import com.intellij.util.Consumer; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; + +/** + * @author Denis Fokin + */ +public class OwnerOptional { + + @NotNull + private static Window findOwnerByComponent(@NotNull Component component) { + return (component instanceof Window) ? (Window) component : SwingUtilities.getWindowAncestor(component); + } + + private Window myPermanentOwner; + + private OwnerOptional(Window permanentOwner) { + this.myPermanentOwner = permanentOwner; + } + + public static OwnerOptional fromComponent (Component parentComponent) { + + Window owner = findOwnerByComponent(parentComponent); + + IdePopupManager manager = IdeEventQueue.getInstance().getPopupManager(); + + if (manager.isPopupWindow(owner)) { + + manager.closeAllPopups(); + + owner = owner.getOwner(); + + while (owner != null + && !(owner instanceof Dialog) + && !(owner instanceof Frame)) { + owner = owner.getOwner(); + } + } + + if (owner instanceof Dialog) { + Dialog ownerDialog = (Dialog)owner; + if (ownerDialog.isModal()) { + owner = ownerDialog; + } + else { + while (owner instanceof Dialog && !((Dialog)owner).isModal()) { + owner = owner.getOwner(); + } + } + } + + return new OwnerOptional(owner); + } + + public OwnerOptional ifDialog(Consumer consumer) { + if (myPermanentOwner instanceof Dialog) { + consumer.consume((Dialog)myPermanentOwner); + } + return this; + } + + public OwnerOptional ifNull(Consumer consumer) { + consumer.consume((Frame)this.myPermanentOwner); + return this; + } + + public OwnerOptional ifFrame(Consumer consumer) { + if (myPermanentOwner instanceof Frame) { + if (myPermanentOwner instanceof IdeFrame.Child) { + IdeFrame.Child ideFrameChild = (IdeFrame.Child)myPermanentOwner; + myPermanentOwner = WindowManager.getInstance().getFrame(ideFrameChild.getProject()); + } + consumer.consume((Frame)this.myPermanentOwner); + } + return this; + } + + public Window get() { + return myPermanentOwner; + } +} diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties index 57356e1e7612..1351b80e42e8 100644 --- a/platform/platform-resources-en/src/messages/ActionsBundle.properties +++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties @@ -1027,6 +1027,10 @@ action.XDebugger.RemoveWatch.text=Remove Watch action.XDebugger.RemoveAllWatches.text=Remove All Watches action.XDebugger.NewWatch.text=New Watch... action.XDebugger.EditWatch.text=Edit... +action.XDebugger.CopyWatch.text=Duplicate Watch +action.XDebugger.MoveWatchUp.text=Move Watch Up +action.XDebugger.MoveWatchDown.text=Move Watch Down +action.XDebugger.SwitchWatchesInVariables.text=Show watches in variables tab action.XDebugger.AutoTooltip.text=Value auto tooltip action.XDebugger.AutoTooltip.description=Toggles if value tooltip is shown automatically on mouse over action.XDebugger.Inline.text=Show Values Inline diff --git a/platform/platform-resources-en/src/messages/XDebuggerBundle.properties b/platform/platform-resources-en/src/messages/XDebuggerBundle.properties index cfff4041837e..4b2ab388d593 100644 --- a/platform/platform-resources-en/src/messages/XDebuggerBundle.properties +++ b/platform/platform-resources-en/src/messages/XDebuggerBundle.properties @@ -70,7 +70,7 @@ xdebugger.button.evaluate=E&valuate xdebugger.evaluate.dialog.close=Close xdebugger.dialog.title.evaluate.expression=Evaluate Expression xdebugger.evaluate.label.expression=Expression: -xdebugger.evaluate.addtowatches.hint=Use Control+Shift+Enter to add to Watches +xdebugger.evaluate.addtowatches.hint=Use {0} to add to Watches dialog.title.evaluate.code.fragment=Evaluate Code Fragment button.text.code.fragment.mode=Code Fragment &Mode button.text.expression.mode=Expression &Mode @@ -111,7 +111,6 @@ stack.frame.loading.text=Loading\u2026 invalid.frame= xdebugger.drag.text.0.elements={0} elements xdebugger.drop.text.add.to.watches=Add to watches -debugger.session.tab.show.watches.in.variables=Show watches in variables tab debugger.frames.not.available=Frames are not available debugger.threads.not.available=Threads are not available diff --git a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml index 1bc33437797c..e48484a9b1c8 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml @@ -269,6 +269,6 @@ - + diff --git a/platform/platform-resources/src/META-INF/PlatformExtensions.xml b/platform/platform-resources/src/META-INF/PlatformExtensions.xml index 46d115378e82..0432a2ac4d79 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensions.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensions.xml @@ -159,13 +159,13 @@ - + - + - @@ -444,6 +444,7 @@ - + + diff --git a/platform/platform-resources/src/idea/LangActions.xml b/platform/platform-resources/src/idea/LangActions.xml index 7a0725c44749..695e5f8b4b60 100644 --- a/platform/platform-resources/src/idea/LangActions.xml +++ b/platform/platform-resources/src/idea/LangActions.xml @@ -738,6 +738,10 @@ text="Duplicate Watch" use-shortcut-of="EditorDuplicate"/> + + + @@ -817,6 +821,10 @@ + + + + diff --git a/platform/platform-tests/testSrc/com/intellij/ui/BalloonTest.java b/platform/platform-tests/testSrc/com/intellij/ui/BalloonTest.java index 94171191a854..544484168aba 100644 --- a/platform/platform-tests/testSrc/com/intellij/ui/BalloonTest.java +++ b/platform/platform-tests/testSrc/com/intellij/ui/BalloonTest.java @@ -60,7 +60,7 @@ public class BalloonTest { //pane.setBorder(new LineBorder(Color.blue)); balloon.set(new BalloonImpl( - new JLabel("Content"), Color.black, null , MessageType.ERROR.getPopupBackground(), true, true, true, true, true, 0, true, false, null, + new JLabel("Content"), Color.black, null , MessageType.ERROR.getPopupBackground(), true, true, true, true, true, true, 0, true, false, null, false, 500, 25, 0, 0, false, "This is the title", JBUI.insets(2), true, false, false, Balloon.Layer.normal, false)); balloon.get().setShowPointer(true); diff --git a/platform/structure-view-api/structure-view-api.iml b/platform/structure-view-api/structure-view-api.iml deleted file mode 100644 index 199e5f92fca9..000000000000 --- a/platform/structure-view-api/structure-view-api.iml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/platform/structure-view-impl/structure-view-impl.iml b/platform/structure-view-impl/structure-view-impl.iml index 81bda1deb7ea..b7f0815825c2 100644 --- a/platform/structure-view-impl/structure-view-impl.iml +++ b/platform/structure-view-impl/structure-view-impl.iml @@ -15,15 +15,7 @@ - - - - - - - - - + \ No newline at end of file diff --git a/platform/testFramework/src/com/intellij/testFramework/LightProjectDescriptor.java b/platform/testFramework/src/com/intellij/testFramework/LightProjectDescriptor.java index 74cc4b5508ef..78d8c3f00285 100644 --- a/platform/testFramework/src/com/intellij/testFramework/LightProjectDescriptor.java +++ b/platform/testFramework/src/com/intellij/testFramework/LightProjectDescriptor.java @@ -59,7 +59,7 @@ public class LightProjectDescriptor { @NotNull public Module createMainModule(@NotNull Project project) { - return createModule(project, "light_idea_test_case.iml"); + return createModule(project, FileUtil.join(FileUtil.getTempDirectory(), "light_idea_test_case.iml")); } protected Module createModule(@NotNull Project project, @NotNull String moduleFilePath) { @@ -69,7 +69,7 @@ public class LightProjectDescriptor { //temporary workaround for IDEA-147530: otherwise if someone saved module with this name before the created module will get its settings FileUtil.delete(imlFile); } - return ModuleManager.getInstance(project).newModule(imlFile.getAbsolutePath(), getModuleType().getId()); + return ModuleManager.getInstance(project).newModule(moduleFilePath, getModuleType().getId()); }); } diff --git a/platform/util/src/com/intellij/jna.kt b/platform/util/src/com/intellij/jna.kt new file mode 100644 index 000000000000..e40e82326cf5 --- /dev/null +++ b/platform/util/src/com/intellij/jna.kt @@ -0,0 +1,25 @@ +/* + * 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 com.intellij.jna + +import com.sun.jna.Memory + +class DisposableMemory(size: Long) : Memory(size) { + @Synchronized public override fun dispose() { + super.dispose() + } +} + diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointManagerImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointManagerImpl.java index d249f55eec1e..3224335c9964 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointManagerImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointManagerImpl.java @@ -238,14 +238,15 @@ public class XBreakpointManagerImpl implements XBreakpointManager, PersistentSta @NotNull public > Collection getBreakpoints(@NotNull final XBreakpointType type) { ApplicationManager.getApplication().assertReadAccessAllowed(); - Collection> breakpoints = myBreakpoints.get(type); - Collection regular = breakpoints != null ? Collections.unmodifiableCollection((Collection)breakpoints) : Collections.emptyList(); - - final XBreakpointBase defaultBreakpoint = myDefaultBreakpoints.get(type); - if (defaultBreakpoint == null) return regular; List result = new ArrayList<>(); - result.add((B)defaultBreakpoint); - result.addAll(regular); + B defaultBreakpoint = getDefaultBreakpoint(type); + if (defaultBreakpoint != null) { + result.add(defaultBreakpoint); + } + Collection> breakpoints = myBreakpoints.get(type); + if (breakpoints != null) { + result.addAll((Collection)breakpoints); + } return result; } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/ExpressionInputComponent.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/ExpressionInputComponent.java index 1e4b9e7a937c..f54c83f0227e 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/ExpressionInputComponent.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/ExpressionInputComponent.java @@ -22,6 +22,7 @@ import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CustomShortcutSet; import com.intellij.openapi.editor.ex.util.EditorUtil; +import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.FixedSizeButton; import com.intellij.openapi.ui.popup.PopupStep; @@ -72,7 +73,9 @@ public class ExpressionInputComponent extends EvaluationInputComponent { } }); myMainPanel.add(historyButton, BorderLayout.EAST); - final JBLabel help = new JBLabel(XDebuggerBundle.message("xdebugger.evaluate.addtowatches.hint"), SwingConstants.RIGHT); + final JBLabel help = new JBLabel(XDebuggerBundle.message("xdebugger.evaluate.addtowatches.hint", + KeymapUtil.getKeystrokeText(XDebuggerEvaluationDialog.ADD_WATCH_KEYSTROKE)), + SwingConstants.RIGHT); help.setBorder(JBUI.Borders.empty(2, 0, 6, 0)); help.setComponentStyle(UIUtil.ComponentStyle.SMALL); help.setFontColor(UIUtil.FontColor.BRIGHTER); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/XDebuggerEvaluationDialog.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/XDebuggerEvaluationDialog.java index dce46002610a..c59fd426c230 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/XDebuggerEvaluationDialog.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/XDebuggerEvaluationDialog.java @@ -22,6 +22,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.openapi.wm.WindowManager; @@ -58,6 +59,10 @@ import java.awt.event.KeyEvent; public class XDebuggerEvaluationDialog extends DialogWrapper { public static final DataKey KEY = DataKey.create("DEBUGGER_EVALUATION_DIALOG"); + //can not use new SHIFT_DOWN_MASK etc because in this case ActionEvent modifiers do not match + private static final int ADD_WATCH_MODIFIERS = (SystemInfo.isMac ? InputEvent.META_MASK : InputEvent.CTRL_MASK) | InputEvent.SHIFT_MASK; + static KeyStroke ADD_WATCH_KEYSTROKE = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, ADD_WATCH_MODIFIERS); + private final JPanel myMainPanel; private final JPanel myResultPanel; private final XDebuggerTreePanel myTreePanel; @@ -120,7 +125,7 @@ public class XDebuggerEvaluationDialog extends DialogWrapper { doOKAction(); addToWatches(); } - }.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK)), getRootPane(), myDisposable); + }.registerCustomShortcutSet(new CustomShortcutSet(ADD_WATCH_KEYSTROKE), getRootPane(), myDisposable); new AnAction() { @Override @@ -171,7 +176,7 @@ public class XDebuggerEvaluationDialog extends DialogWrapper { @Override public void actionPerformed(ActionEvent e) { super.actionPerformed(e); - if ((e.getModifiers() & (InputEvent.SHIFT_MASK | InputEvent.CTRL_MASK)) == (InputEvent.SHIFT_MASK | InputEvent.CTRL_MASK)) { + if ((e.getModifiers() & ADD_WATCH_MODIFIERS) == ADD_WATCH_MODIFIERS) { addToWatches(); } } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XWatchesView.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XWatchesView.java index c061b8b39fe1..b024434dd150 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XWatchesView.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XWatchesView.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 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. @@ -17,6 +17,7 @@ package com.intellij.xdebugger.impl.frame; import com.intellij.openapi.actionSystem.DataKey; import com.intellij.xdebugger.XExpression; +import com.intellij.xdebugger.impl.ui.tree.nodes.WatchNode; import com.intellij.xdebugger.impl.ui.tree.nodes.XDebuggerTreeNode; import org.jetbrains.annotations.NotNull; diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XWatchesViewImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XWatchesViewImpl.java index a63504cd92db..a71bf5422c22 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XWatchesViewImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XWatchesViewImpl.java @@ -15,7 +15,6 @@ */ package com.intellij.xdebugger.impl.frame; -import com.intellij.icons.AllIcons; import com.intellij.ide.DataManager; import com.intellij.ide.dnd.DnDEvent; import com.intellij.ide.dnd.DnDManager; @@ -23,11 +22,14 @@ import com.intellij.ide.dnd.DnDNativeTarget; import com.intellij.openapi.CompositeDisposable; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl; import com.intellij.openapi.ide.CopyPasteManager; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.EmptyRunnable; -import com.intellij.openapi.util.SystemInfo; -import com.intellij.ui.*; +import com.intellij.ui.CaptionPanel; +import com.intellij.ui.ClickListener; +import com.intellij.ui.DoubleClickListener; +import com.intellij.ui.ListenerUtil; import com.intellij.ui.border.CustomLineBorder; import com.intellij.util.Alarm; import com.intellij.util.containers.ContainerUtil; @@ -40,7 +42,7 @@ import com.intellij.xdebugger.frame.XStackFrame; import com.intellij.xdebugger.impl.XDebugSessionImpl; import com.intellij.xdebugger.impl.actions.XDebuggerActions; import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl; -import com.intellij.xdebugger.impl.frame.actions.XWatchesTreeActionBase; +import com.intellij.xdebugger.impl.ui.DebuggerSessionTabBase; import com.intellij.xdebugger.impl.ui.DebuggerUIUtil; import com.intellij.xdebugger.impl.ui.XDebugSessionData; import com.intellij.xdebugger.impl.ui.XDebugSessionTab; @@ -57,7 +59,10 @@ import javax.swing.event.TreeSelectionListener; import javax.swing.tree.TreePath; import java.awt.*; import java.awt.datatransfer.DataFlavor; -import java.awt.event.*; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -75,8 +80,6 @@ public class XWatchesViewImpl extends XVariablesView implements DnDNativeTarget, super(session); myWatchesInVariables = watchesInVariables; - ActionManager actionManager = ActionManager.getInstance(); - XDebuggerTree tree = getTree(); createNewRootNode(null); @@ -101,74 +104,19 @@ public class XWatchesViewImpl extends XVariablesView implements DnDNativeTarget, } }.registerCustomShortcutSet(CommonShortcuts.getPaste(), tree, myDisposables); - ToolbarDecorator decorator = ToolbarDecorator.createDecorator(getTree()); + ActionToolbarImpl toolbar = (ActionToolbarImpl)ActionManager.getInstance().createActionToolbar( + ActionPlaces.DEBUGGER_TOOLBAR, + DebuggerSessionTabBase.getCustomizedActionGroup(XDebuggerActions.WATCHES_TREE_TOOLBAR_GROUP), + !myWatchesInVariables); + toolbar.setBorder(new CustomLineBorder(CaptionPanel.CNT_ACTIVE_BORDER_COLOR, 0, 0, + myWatchesInVariables ? 0 : 1, + myWatchesInVariables ? 1 : 0)); + toolbar.setTargetComponent(tree); - decorator.setAddAction(button -> executeAction(XDebuggerActions.XNEW_WATCH)); - decorator.setAddIcon(AllIcons.Debugger.NewWatch); - decorator.setAddActionName(actionManager.getAction(XDebuggerActions.XNEW_WATCH).getTemplatePresentation().getText()); - - AnAction removeWatchAction = actionManager.getAction(XDebuggerActions.XREMOVE_WATCH); - decorator.setRemoveAction(button -> executeAction(XDebuggerActions.XREMOVE_WATCH)); - decorator.setRemoveActionName(removeWatchAction.getTemplatePresentation().getText()); - - decorator.setRemoveActionUpdater(e -> { - removeWatchAction.update(e); - return e.getPresentation().isEnabled(); - }); - decorator.addExtraAction(AnActionButton.fromAction(actionManager.getAction(XDebuggerActions.XCOPY_WATCH))); - decorator.addExtraAction( - new ToggleActionButton(XDebuggerBundle.message("debugger.session.tab.show.watches.in.variables"), AllIcons.Debugger.Watches) { - @Override - public boolean isSelected(AnActionEvent e) { - XDebugSessionTab tab = session.getSessionTab(); - return tab == null || tab.isWatchesInVariables(); - } - - @Override - public void setSelected(AnActionEvent e, boolean state) { - XDebugSessionTab tab = session.getSessionTab(); - if (tab != null) { - tab.setWatchesInVariables(!tab.isWatchesInVariables()); - } - } - }); - decorator.setMoveUpAction(button -> { - List nodes = XWatchesTreeActionBase.getSelectedNodes(getTree(), WatchNode.class); - assert nodes.size() == 1; - myRootNode.moveUp(nodes.get(0)); - updateSessionData(); - }); - decorator.setMoveUpActionUpdater(e -> { - List nodes = XWatchesTreeActionBase.getSelectedNodes(getTree(), WatchNode.class); - if (nodes.size() != 1) return false; - return myRootNode.getIndex(nodes.get(0)) > 0; - }); - decorator.setMoveUpActionName("Move Watch Up"); - decorator.setMoveDownAction(button -> { - List nodes = XWatchesTreeActionBase.getSelectedNodes(getTree(), WatchNode.class); - assert nodes.size() == 1; - myRootNode.moveDown(nodes.get(0)); - updateSessionData(); - }); - decorator.setMoveDownActionUpdater(e -> { - List nodes = XWatchesTreeActionBase.getSelectedNodes(getTree(), WatchNode.class); - if (nodes.size() != 1) return false; - return myRootNode.getIndex(nodes.get(0)) < myRootNode.getWatchChildren().size() - 1; - }); - decorator.setMoveDownActionName("Move Watch Down"); - CustomLineBorder border = new CustomLineBorder(CaptionPanel.CNT_ACTIVE_BORDER_COLOR, - SystemInfo.isMac ? 1 : 0, 0, - SystemInfo.isMac ? 0 : 1, 0); - decorator.setToolbarBorder(border); - decorator.setPanelBorder(BorderFactory.createEmptyBorder()); - getPanel().removeAll(); - if (myWatchesInVariables) { - decorator.setToolbarPosition(ActionToolbarPosition.LEFT); - } - else { + if (!myWatchesInVariables) { getTree().getEmptyText().setText(XDebuggerBundle.message("debugger.no.watches")); } - getPanel().add(decorator.createPanel()); + getPanel().add(toolbar.getComponent(), myWatchesInVariables ? BorderLayout.WEST : BorderLayout.NORTH); installEditListeners(); } @@ -362,6 +310,16 @@ public class XWatchesViewImpl extends XVariablesView implements DnDNativeTarget, updateSessionData(); } + public void moveWatchUp(WatchNode node) { + myRootNode.moveUp(node); + updateSessionData(); + } + + public void moveWatchDown(WatchNode node) { + myRootNode.moveDown(node); + updateSessionData(); + } + public void updateSessionData() { List watchExpressions = ContainerUtil.newArrayList(); List children = myRootNode.getWatchChildren(); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/actions/XMoveWatchDown.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/actions/XMoveWatchDown.java new file mode 100644 index 000000000000..f1d91c9721ff --- /dev/null +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/actions/XMoveWatchDown.java @@ -0,0 +1,56 @@ +/* + * 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 com.intellij.xdebugger.impl.frame.actions; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.ui.CommonActionsPanel; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.xdebugger.impl.frame.XWatchesView; +import com.intellij.xdebugger.impl.frame.XWatchesViewImpl; +import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; +import com.intellij.xdebugger.impl.ui.tree.nodes.WatchNodeImpl; +import com.intellij.xdebugger.impl.ui.tree.nodes.WatchesRootNode; +import com.intellij.xdebugger.impl.ui.tree.nodes.XDebuggerTreeNode; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * @author egor + */ +public class XMoveWatchDown extends XWatchesTreeActionBase { + public XMoveWatchDown() { + getTemplatePresentation().setIcon(CommonActionsPanel.Buttons.DOWN.getIcon()); + } + + protected boolean isEnabled(@NotNull AnActionEvent e, @NotNull XDebuggerTree tree) { + List nodes = getSelectedNodes(tree, WatchNodeImpl.class); + if (nodes.size() == 1) { + XDebuggerTreeNode root = tree.getRoot(); + if (root instanceof WatchesRootNode) { + return root.getIndex(nodes.get(0)) < ((WatchesRootNode)root).getWatchChildren().size() - 1; + } + } + return false; + } + + @Override + protected void perform(@NotNull AnActionEvent e, @NotNull XDebuggerTree tree, @NotNull XWatchesView watchesView) { + if (watchesView instanceof XWatchesViewImpl) { + ((XWatchesViewImpl)watchesView).moveWatchDown(ContainerUtil.getFirstItem(getSelectedNodes(tree, WatchNodeImpl.class))); + } + } +} diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/actions/XMoveWatchUp.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/actions/XMoveWatchUp.java new file mode 100644 index 000000000000..f465098c4c73 --- /dev/null +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/actions/XMoveWatchUp.java @@ -0,0 +1,56 @@ +/* + * 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 com.intellij.xdebugger.impl.frame.actions; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.ui.CommonActionsPanel; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.xdebugger.impl.frame.XWatchesView; +import com.intellij.xdebugger.impl.frame.XWatchesViewImpl; +import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; +import com.intellij.xdebugger.impl.ui.tree.nodes.WatchNodeImpl; +import com.intellij.xdebugger.impl.ui.tree.nodes.WatchesRootNode; +import com.intellij.xdebugger.impl.ui.tree.nodes.XDebuggerTreeNode; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * @author egor + */ +public class XMoveWatchUp extends XWatchesTreeActionBase { + public XMoveWatchUp() { + getTemplatePresentation().setIcon(CommonActionsPanel.Buttons.UP.getIcon()); + } + + protected boolean isEnabled(@NotNull AnActionEvent e, @NotNull XDebuggerTree tree) { + List nodes = getSelectedNodes(tree, WatchNodeImpl.class); + if (nodes.size() == 1) { + XDebuggerTreeNode root = tree.getRoot(); + if (root instanceof WatchesRootNode) { + return root.getIndex(nodes.get(0)) > 0; + } + } + return false; + } + + @Override + protected void perform(@NotNull AnActionEvent e, @NotNull XDebuggerTree tree, @NotNull XWatchesView watchesView) { + if (watchesView instanceof XWatchesViewImpl) { + ((XWatchesViewImpl)watchesView).moveWatchUp(ContainerUtil.getFirstItem(getSelectedNodes(tree, WatchNodeImpl.class))); + } + } +} diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/actions/XRemoveWatchAction.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/actions/XRemoveWatchAction.java index c34fe36ab0d2..e2648a2835f8 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/actions/XRemoveWatchAction.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/actions/XRemoveWatchAction.java @@ -15,6 +15,7 @@ */ package com.intellij.xdebugger.impl.frame.actions; +import com.intellij.openapi.actionSystem.ActionPlaces; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.xdebugger.impl.frame.XWatchesView; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; @@ -30,7 +31,13 @@ public class XRemoveWatchAction extends XWatchesTreeActionBase { @Override public void update(AnActionEvent e) { XDebuggerTree tree = XDebuggerTree.getTree(e); - e.getPresentation().setEnabledAndVisible(tree != null && !getSelectedNodes(tree, WatchNodeImpl.class).isEmpty()); + boolean enabled = tree != null && !getSelectedNodes(tree, WatchNodeImpl.class).isEmpty(); + if (ActionPlaces.DEBUGGER_TOOLBAR.equals(e.getPlace())) { + e.getPresentation().setEnabled(enabled); + } + else { + e.getPresentation().setEnabledAndVisible(enabled); + } } @Override diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/actions/XSwitchWatchesInVariables.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/actions/XSwitchWatchesInVariables.java new file mode 100644 index 000000000000..3ea71554b867 --- /dev/null +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/actions/XSwitchWatchesInVariables.java @@ -0,0 +1,39 @@ +/* + * 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 com.intellij.xdebugger.impl.frame.actions; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.xdebugger.impl.ui.XDebugSessionTab; + +/** + * @author egor + */ +public class XSwitchWatchesInVariables extends ToggleAction { + @Override + public boolean isSelected(AnActionEvent e) { + XDebugSessionTab tab = e.getData(XDebugSessionTab.TAB_KEY); + return tab == null || tab.isWatchesInVariables(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + XDebugSessionTab tab = e.getData(XDebugSessionTab.TAB_KEY); + if (tab != null) { + tab.setWatchesInVariables(!tab.isWatchesInVariables()); + } + } +} diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/DebuggerSessionTabBase.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/DebuggerSessionTabBase.java index 172ff0eb20c9..3fe7b233c63b 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/DebuggerSessionTabBase.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/DebuggerSessionTabBase.java @@ -56,7 +56,7 @@ public abstract class DebuggerSessionTabBase extends RunTab { .initFocusContent(DebuggerContentInfo.CONSOLE_CONTENT, LayoutViewOptions.STARTUP, new LayoutAttractionPolicy.FocusOnce(false)); } - protected static ActionGroup getCustomizedActionGroup(final String id) { + public static ActionGroup getCustomizedActionGroup(final String id) { return (ActionGroup)CustomActionsSchema.getInstance().getCorrectedAction(id); } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebugTabCustomizableActionGroupProvider.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebugTabCustomizableActionGroupProvider.java index e0839f75621e..71d7744e639c 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebugTabCustomizableActionGroupProvider.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebugTabCustomizableActionGroupProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 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. @@ -26,5 +26,6 @@ public class XDebugTabCustomizableActionGroupProvider extends CustomizableAction public void registerGroups(CustomizableActionGroupRegistrar registrar) { registrar.addCustomizableActionGroup(XDebuggerActions.TOOL_WINDOW_TOP_TOOLBAR_GROUP, "Debug Tool Window Top Toolbar"); registrar.addCustomizableActionGroup(XDebuggerActions.TOOL_WINDOW_LEFT_TOOLBAR_GROUP, "Debug Tool Window Left Toolbar"); + registrar.addCustomizableActionGroup(XDebuggerActions.WATCHES_TREE_TOOLBAR_GROUP, "Debug Watches Toolbar"); } } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml index 89799f565fb3..4f0034eac1c1 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml @@ -2527,22 +2527,22 @@ key="replace.diamond.with.explicit.type.arguments.name" groupBundle="messages.InspectionsBundle" groupKey="group.names.code.style.issues" enabledByDefault="true" level="INFORMATION" implementationClass="com.siyeh.ig.style.ReplaceDiamondWithExplicitTypeArgumentsInspection"/> - + implementationClass="com.siyeh.ig.style.LambdaCanBeReplacedWithAnonymousInspection"/> - - + + implementationClass="com.siyeh.ig.exceptions.MultiCatchCanBeSplitInspection"/> '{'...'}' replace.diamond.with.explicit.type.arguments.name=Replace diamond with explicit type arguments replace.diamond.with.explicit.type.arguments.descriptor=Replace '<>' with explicit type arguments -replace.lambda.with.anonymous.name=Replace lambda with anonymous class -replace.lambda.with.anonymous.descriptor=Replace lambda with anonymous class +lambda.can.be.replaced.with.anonymous.name=Lambda can be replaced with anonymous class +lambda.can.be.replaced.with.anonymous.quickfix=Replace lambda with anonymous class method.ref.can.be.replaced.with.lambda.name=Method reference can be replaced with lambda method.ref.can.be.replaced.with.lambda.quickfix=Replace method reference with lambda -split.try.with.multiple.resources.name=Split Try Statement with Multiple Resources -split.try.with.multiple.resources.descriptor=Split 'try' statement with multiple resources -split.multi.catch.name=Split Multi-Catch into Separate Catch Blocks -split.multi.catch.descriptor=Split multi-catch into separate 'catch' blocks +try.statement.with.multiple.resources.name=Try statement with multiple resources can be split +try.statement.with.multiple.resources.quickfix=Split 'try' statement with multiple resources +multi.catch.can.be.split.name=Multi-catch can be split into separate catch blocks +multi.catch.can.be.split.quickfix=Split multi-catch into separate 'catch' blocks assertion.can.be.if.name=Assertion can be replaced with if statement assert.can.be.if.quickfix=Replace 'assert' with 'if' statement if.can.be.assertion.name=If statement can be replaced with assertion diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/exceptions/SplitMultiCatchInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/exceptions/MultiCatchCanBeSplitInspection.java similarity index 93% rename from plugins/InspectionGadgets/src/com/siyeh/ig/exceptions/SplitMultiCatchInspection.java rename to plugins/InspectionGadgets/src/com/siyeh/ig/exceptions/MultiCatchCanBeSplitInspection.java index c95b650fe1d7..a2407caa469f 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/exceptions/SplitMultiCatchInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/exceptions/MultiCatchCanBeSplitInspection.java @@ -30,19 +30,19 @@ import org.jetbrains.annotations.Nullable; import static com.intellij.util.ObjectUtils.assertNotNull; -public class SplitMultiCatchInspection extends BaseInspection { +public class MultiCatchCanBeSplitInspection extends BaseInspection { @Nls @NotNull @Override public String getDisplayName() { - return InspectionGadgetsBundle.message("split.multi.catch.name"); + return InspectionGadgetsBundle.message("multi.catch.can.be.split.name"); } @NotNull @Override protected String buildErrorString(Object... infos) { - return InspectionGadgetsBundle.message("split.multi.catch.descriptor"); + return getDisplayName(); } @Override @@ -123,7 +123,7 @@ public class SplitMultiCatchInspection extends BaseInspection { @NotNull @Override public String getName() { - return InspectionGadgetsBundle.message("split.multi.catch.descriptor"); + return InspectionGadgetsBundle.message("multi.catch.can.be.split.quickfix"); } @Nls diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/exceptions/SplitTryWithMultipleResourcesInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/exceptions/TryStatementWithMultipleResourcesInspection.java similarity index 92% rename from plugins/InspectionGadgets/src/com/siyeh/ig/exceptions/SplitTryWithMultipleResourcesInspection.java rename to plugins/InspectionGadgets/src/com/siyeh/ig/exceptions/TryStatementWithMultipleResourcesInspection.java index 61de687454b6..1f6c41871dc3 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/exceptions/SplitTryWithMultipleResourcesInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/exceptions/TryStatementWithMultipleResourcesInspection.java @@ -31,19 +31,19 @@ import org.jetbrains.annotations.Nullable; /** * @author Bas Leijdekkers */ -public class SplitTryWithMultipleResourcesInspection extends BaseInspection { +public class TryStatementWithMultipleResourcesInspection extends BaseInspection { @Nls @NotNull @Override public String getDisplayName() { - return InspectionGadgetsBundle.message("split.try.with.multiple.resources.name"); + return InspectionGadgetsBundle.message("try.statement.with.multiple.resources.name"); } @NotNull @Override protected String buildErrorString(Object... infos) { - return InspectionGadgetsBundle.message("split.try.with.multiple.resources.descriptor"); + return getDisplayName(); } @Override @@ -136,7 +136,7 @@ public class SplitTryWithMultipleResourcesInspection extends BaseInspection { @NotNull @Override public String getName() { - return InspectionGadgetsBundle.message("split.try.with.multiple.resources.descriptor"); + return InspectionGadgetsBundle.message("try.statement.with.multiple.resources.quickfix"); } @Nls diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/style/ReplaceLambdaWithAnonymousInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/style/LambdaCanBeReplacedWithAnonymousInspection.java similarity index 89% rename from plugins/InspectionGadgets/src/com/siyeh/ig/style/ReplaceLambdaWithAnonymousInspection.java rename to plugins/InspectionGadgets/src/com/siyeh/ig/style/LambdaCanBeReplacedWithAnonymousInspection.java index 6233797e070e..decc3bfb1950 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/style/ReplaceLambdaWithAnonymousInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/style/LambdaCanBeReplacedWithAnonymousInspection.java @@ -28,7 +28,6 @@ import com.intellij.psi.util.PsiTypesUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.RedundantCastUtil; import com.intellij.refactoring.util.RefactoringChangeUtil; -import com.intellij.util.containers.HashMap; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; @@ -39,20 +38,20 @@ import org.jetbrains.annotations.Nullable; import java.util.*; -public class ReplaceLambdaWithAnonymousInspection extends BaseInspection { - private static final Logger LOG = Logger.getInstance("#" + ReplaceLambdaWithAnonymousInspection.class.getName()); +public class LambdaCanBeReplacedWithAnonymousInspection extends BaseInspection { + private static final Logger LOG = Logger.getInstance("#" + LambdaCanBeReplacedWithAnonymousInspection.class.getName()); @Nls @NotNull @Override public String getDisplayName() { - return InspectionGadgetsBundle.message("replace.lambda.with.anonymous.name"); + return InspectionGadgetsBundle.message("lambda.can.be.replaced.with.anonymous.name"); } @NotNull @Override protected String buildErrorString(Object... infos) { - return InspectionGadgetsBundle.message("replace.lambda.with.anonymous.descriptor"); + return getDisplayName(); } @Override @@ -179,19 +178,16 @@ public class ReplaceLambdaWithAnonymousInspection extends BaseInspection { private static class LambdaToAnonymousVisitor extends BaseInspectionVisitor { @Override - public void visitParameterList(PsiParameterList parameterList) { - super.visitParameterList(parameterList); - if (isConvertibleLambdaExpression(parameterList.getParent())) { - registerError(parameterList); - } - } - - @Override - public void visitJavaToken(PsiJavaToken token) { - super.visitJavaToken(token); - if (token.getTokenType() == JavaTokenType.ARROW) { - if (isConvertibleLambdaExpression(token.getParent())) { - registerError(token); + public void visitLambdaExpression(PsiLambdaExpression lambdaExpression) { + super.visitLambdaExpression(lambdaExpression); + if (isConvertibleLambdaExpression(lambdaExpression)) { + PsiParameterList parameterList = lambdaExpression.getParameterList(); + PsiElement nextElement = PsiTreeUtil.skipSiblingsForward(parameterList, PsiWhiteSpace.class, PsiComment.class); + if (nextElement instanceof PsiJavaToken && ((PsiJavaToken)nextElement).getTokenType() == JavaTokenType.ARROW) { + registerErrorAtRange(parameterList, nextElement); + } + else { + registerError(parameterList); } } } @@ -244,22 +240,22 @@ public class ReplaceLambdaWithAnonymousInspection extends BaseInspection { @NotNull @Override public String getName() { - return InspectionGadgetsBundle.message("replace.lambda.with.anonymous.descriptor"); + return InspectionGadgetsBundle.message("lambda.can.be.replaced.with.anonymous.quickfix"); } @Nls @NotNull @Override public String getFamilyName() { - return InspectionGadgetsBundle.message("replace.lambda.with.anonymous.name"); + return getName(); } @Override protected void doFix(Project project, ProblemDescriptor descriptor) { - final PsiElement element = descriptor.getPsiElement(); + final PsiElement element = descriptor.getStartElement(); final PsiElement parent = element.getParent(); if (parent instanceof PsiLambdaExpression) { - ReplaceLambdaWithAnonymousInspection.doFix(project, (PsiLambdaExpression)parent); + LambdaCanBeReplacedWithAnonymousInspection.doFix(project, (PsiLambdaExpression)parent); } } } diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/LambdaCanBeReplacedWithAnonymous.html b/plugins/InspectionGadgets/src/inspectionDescriptions/LambdaCanBeReplacedWithAnonymous.html new file mode 100644 index 000000000000..a4d0f7935a78 --- /dev/null +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/LambdaCanBeReplacedWithAnonymous.html @@ -0,0 +1,13 @@ + + +This inspection reports lambda expressions which can be replaced with anonymous class. +

The quick fix expands lambda expression to the anonymous class. +

For example the lambda expression s -> System.out.println(s) can be expanded to the following class: +

new Consumer<String>() {
+  @Override
+  public void accept(String s) {
+    System.out.println(s);
+  }
+}
+ + \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/MethodRefCanBeReplacedWithLambda.html b/plugins/InspectionGadgets/src/inspectionDescriptions/MethodRefCanBeReplacedWithLambda.html index 0160b839a8f6..154b400da14c 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/MethodRefCanBeReplacedWithLambda.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/MethodRefCanBeReplacedWithLambda.html @@ -1,6 +1,8 @@ -This inspection reports method references. +This inspection reports method references, like MyClass::myMethod and myObject::myMethod.

The quick fix for the inspection replaces the method reference with an equivalent lambda expression that invokes the method. +

For example, the method reference System.out::println is replaced with +s -> System.out.println(s) \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/MultiCatchCanBeSplit.html b/plugins/InspectionGadgets/src/inspectionDescriptions/MultiCatchCanBeSplit.html new file mode 100644 index 000000000000..79db054c69d6 --- /dev/null +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/MultiCatchCanBeSplit.html @@ -0,0 +1,18 @@ + + +This inspection reports multi-catch sections, like catch (IOException|RuntimeException e) {...} +

The quick fix splits multi-catch section into separate catch blocks. + +

For example, the multi-catch section +

catch (IOException|RuntimeException e) {
+  /*handle the exception*/
+}
+is transformed into the following: +
catch (IOException e) {
+  /*handle the exception*/
+}
+catch (RuntimeException e) {
+  /*handle the exception*/
+}
+ + diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/ReplaceLambdaWithAnonymous.html b/plugins/InspectionGadgets/src/inspectionDescriptions/ReplaceLambdaWithAnonymous.html deleted file mode 100644 index 1346d7e1e2e6..000000000000 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/ReplaceLambdaWithAnonymous.html +++ /dev/null @@ -1,5 +0,0 @@ - - -This inspection expands lambda expression to the anonymous class - - \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/SplitMultiCatch.html b/plugins/InspectionGadgets/src/inspectionDescriptions/SplitMultiCatch.html deleted file mode 100644 index e54812932fc8..000000000000 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/SplitMultiCatch.html +++ /dev/null @@ -1,6 +0,0 @@ - - -This inspection splits a multi-catch section into separate catch blocks. -

- - diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/SplitTryWithMultipleResources.html b/plugins/InspectionGadgets/src/inspectionDescriptions/SplitTryWithMultipleResources.html deleted file mode 100644 index f52392d04636..000000000000 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/SplitTryWithMultipleResources.html +++ /dev/null @@ -1,6 +0,0 @@ - - -This inspection looks for try statements with multiple resources, -and suggests to split them into nested try-with-resources statements. - - diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/TryStatementWithMultipleResources.html b/plugins/InspectionGadgets/src/inspectionDescriptions/TryStatementWithMultipleResources.html new file mode 100644 index 000000000000..3d8c0b20c461 --- /dev/null +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/TryStatementWithMultipleResources.html @@ -0,0 +1,18 @@ + + +This inspection reports try statements with multiple resources. +

The quick fix suggests to split such statements into nested try-with-resources statements. + +

Example of try statement with multiple resources: +

try (FileInputStream in = new FileInputStream("in.txt");
+     FileOutputStream out = new FileOutputStream("out.txt")) {
+  /*read and write*/
+}
+

The example above is transformed into the following: +

try (FileInputStream in = new FileInputStream("in.txt")) {
+  try (FileOutputStream out = new FileOutputStream("out.txt")) {
+    /*read and write*/
+  }
+}
+ + diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/performance/concatenation_inside_append/PrintWriterAppend.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/performance/concatenation_inside_append/PrintWriterAppend.after.java new file mode 100644 index 000000000000..0a1ce4d87b1a --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/performance/concatenation_inside_append/PrintWriterAppend.after.java @@ -0,0 +1,10 @@ +package com.siyeh.igfixes.performance.concatenation_inside_append; + +import java.io.PrintWriter; + +class PrintWriterAppend { + + void foo(PrintWriter printWriter, int year, int season) { + printWriter.append("this is intellij idea ").append(String.valueOf(year)).append(".").append(String.valueOf(season)).append(" version"); + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/performance/concatenation_inside_append/PrintWriterAppend.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/performance/concatenation_inside_append/PrintWriterAppend.java new file mode 100644 index 000000000000..3c8d5da0dd67 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/performance/concatenation_inside_append/PrintWriterAppend.java @@ -0,0 +1,10 @@ +package com.siyeh.igfixes.performance.concatenation_inside_append; + +import java.io.PrintWriter; + +class PrintWriterAppend { + + void foo(PrintWriter printWriter, int year, int season) { + printWriter.append("this is intellij idea " + year + "." + season + " version"); + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/exceptions/SplitMultiCatchFixTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/exceptions/MultiCatchCanBeSplitFixTest.java similarity index 78% rename from plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/exceptions/SplitMultiCatchFixTest.java rename to plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/exceptions/MultiCatchCanBeSplitFixTest.java index 300ee466f0e1..e2fb562bfe8e 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/exceptions/SplitMultiCatchFixTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/exceptions/MultiCatchCanBeSplitFixTest.java @@ -17,14 +17,14 @@ package com.siyeh.ig.fixes.exceptions; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.IGQuickFixesTestCase; -import com.siyeh.ig.exceptions.SplitMultiCatchInspection; +import com.siyeh.ig.exceptions.MultiCatchCanBeSplitInspection; -public class SplitMultiCatchFixTest extends IGQuickFixesTestCase { +public class MultiCatchCanBeSplitFixTest extends IGQuickFixesTestCase { @Override protected void setUp() throws Exception { super.setUp(); - myFixture.enableInspections(new SplitMultiCatchInspection()); - myDefaultHint = InspectionGadgetsBundle.message("split.multi.catch.descriptor"); + myFixture.enableInspections(new MultiCatchCanBeSplitInspection()); + myDefaultHint = InspectionGadgetsBundle.message("multi.catch.can.be.split.quickfix"); myRelativePath = "exceptions/split_multi_catch"; } diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/exceptions/SplitTryWithMultipleResourcesFixTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/exceptions/TryStatementWithMultipleResourcesFixTest.java similarity index 78% rename from plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/exceptions/SplitTryWithMultipleResourcesFixTest.java rename to plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/exceptions/TryStatementWithMultipleResourcesFixTest.java index 06dc41ed65ab..c4b39503674a 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/exceptions/SplitTryWithMultipleResourcesFixTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/exceptions/TryStatementWithMultipleResourcesFixTest.java @@ -17,17 +17,17 @@ package com.siyeh.ig.fixes.exceptions; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.IGQuickFixesTestCase; -import com.siyeh.ig.exceptions.SplitTryWithMultipleResourcesInspection; +import com.siyeh.ig.exceptions.TryStatementWithMultipleResourcesInspection; /** * @author Bas Leijdekkers */ -public class SplitTryWithMultipleResourcesFixTest extends IGQuickFixesTestCase { +public class TryStatementWithMultipleResourcesFixTest extends IGQuickFixesTestCase { @Override protected void setUp() throws Exception { super.setUp(); - myFixture.enableInspections(new SplitTryWithMultipleResourcesInspection()); - myDefaultHint = InspectionGadgetsBundle.message("split.try.with.multiple.resources.descriptor"); + myFixture.enableInspections(new TryStatementWithMultipleResourcesInspection()); + myDefaultHint = InspectionGadgetsBundle.message("try.statement.with.multiple.resources.quickfix"); myRelativePath = "exceptions/try_multiple_resource"; } diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/performance/ReplaceWithChainedAppendFixTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/performance/ReplaceWithChainedAppendFixTest.java index 594833e059a1..7c93c8b14d7c 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/performance/ReplaceWithChainedAppendFixTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/performance/ReplaceWithChainedAppendFixTest.java @@ -34,4 +34,16 @@ public class ReplaceWithChainedAppendFixTest extends IGQuickFixesTestCase { public void testUnresolvedMethod() { doTest(); } + public void testPrintWriterAppend() { + myFixture.addClass("package java.lang;" + + "public interface Appendable {}"); + myFixture.addClass("package java.io;" + + "public class PrintWriter extends java.lang.Appendable {" + + "@Override" + + "public PrintWriter append(CharSequence csq) throws IOException {" + + " return null;" + + "}}"); + doTest(); + } + } diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/style/ReplaceLambdaWithAnonymousFixTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/style/LambdaCanBeReplacedWithAnonymousFixTest.java similarity index 87% rename from plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/style/ReplaceLambdaWithAnonymousFixTest.java rename to plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/style/LambdaCanBeReplacedWithAnonymousFixTest.java index ed9a8b544444..886371c41de7 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/style/ReplaceLambdaWithAnonymousFixTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/style/LambdaCanBeReplacedWithAnonymousFixTest.java @@ -17,14 +17,14 @@ package com.siyeh.ig.fixes.style; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.IGQuickFixesTestCase; -import com.siyeh.ig.style.ReplaceLambdaWithAnonymousInspection; +import com.siyeh.ig.style.LambdaCanBeReplacedWithAnonymousInspection; -public class ReplaceLambdaWithAnonymousFixTest extends IGQuickFixesTestCase { +public class LambdaCanBeReplacedWithAnonymousFixTest extends IGQuickFixesTestCase { @Override protected void setUp() throws Exception { super.setUp(); - myFixture.enableInspections(new ReplaceLambdaWithAnonymousInspection()); - myDefaultHint = InspectionGadgetsBundle.message("replace.lambda.with.anonymous.descriptor"); + myFixture.enableInspections(new LambdaCanBeReplacedWithAnonymousInspection()); + myDefaultHint = InspectionGadgetsBundle.message("lambda.can.be.replaced.with.anonymous.quickfix"); } public void testSimpleRunnable() { diff --git a/plugins/groovy/groovy-psi/resources/inspectionDescriptions/GrUnnecessaryDefModifier.html b/plugins/groovy/groovy-psi/resources/inspectionDescriptions/GrUnnecessaryDefModifier.html index 8fb7292ff9ac..becbb1e826f6 100644 --- a/plugins/groovy/groovy-psi/resources/inspectionDescriptions/GrUnnecessaryDefModifier.html +++ b/plugins/groovy/groovy-psi/resources/inspectionDescriptions/GrUnnecessaryDefModifier.html @@ -1,6 +1,6 @@ -Reports unnecessary def modifier when it is used with explicit type declaration. +Reports unnecessary def modifiers when used with explicit type declaration.

For example:

diff --git a/plugins/groovy/groovy-psi/resources/inspectionDescriptions/GrUnnecessaryPublicModifier.html b/plugins/groovy/groovy-psi/resources/inspectionDescriptions/GrUnnecessaryPublicModifier.html
new file mode 100644
index 000000000000..41eb3532fcdf
--- /dev/null
+++ b/plugins/groovy/groovy-psi/resources/inspectionDescriptions/GrUnnecessaryPublicModifier.html
@@ -0,0 +1,5 @@
+
+
+Reports unnecessary public modifiers.
+
+
\ No newline at end of file
diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/GroovyBundle.properties b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/GroovyBundle.properties
index 7a2bd26fa80b..1ce2a6d362b3 100644
--- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/GroovyBundle.properties
+++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/GroovyBundle.properties
@@ -187,8 +187,8 @@ interface.must.have.no.static.method=Interface must have no static method
 only.abstract.class.can.have.abstract.method=Only abstract class can have abstract method
 anonymous.class.cannot.have.abstract.method=Anonymous class cannot have abstract method
 illegal.combination.of.modifiers.volatile.and.final=Illegal combination of modifiers 'volatile' and 'final'
-variable.cannot.be.native=Variable cannot have modifier 'native'
-variable.cannot.be.abstract=Variable cannot have modifier 'abstract'
+variable.cannot.be=Variable cannot have modifier ''{0}''
+remove.modifier=Remove ''{0}''
 not.abstract.method.should.have.body=Not abstract method should have body
 cannot.create.class.error.text=Cannot Create Class ''{0}'': {1}
 cannot.create.class.error.title=Cannot Create Class
diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java
index 3555e7449f20..41a2fcff169d 100644
--- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java
+++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java
@@ -57,6 +57,7 @@ import org.jetbrains.plugins.groovy.annotator.checkers.AnnotationChecker;
 import org.jetbrains.plugins.groovy.annotator.checkers.CustomAnnotationChecker;
 import org.jetbrains.plugins.groovy.annotator.intentions.*;
 import org.jetbrains.plugins.groovy.codeInspection.bugs.GrModifierFix;
+import org.jetbrains.plugins.groovy.codeInspection.bugs.GrRemoveModifierFix;
 import org.jetbrains.plugins.groovy.config.GroovyConfigUtils;
 import org.jetbrains.plugins.groovy.findUsages.LiteralConstructorReference;
 import org.jetbrains.plugins.groovy.highlighter.GroovySyntaxHighlighter;
@@ -105,6 +106,10 @@ import org.jetbrains.plugins.groovy.lang.resolve.ast.InheritConstructorContribut
 
 import java.util.*;
 
+import static org.jetbrains.plugins.groovy.annotator.UtilKt.checkModifierIsNotAllowed;
+import static org.jetbrains.plugins.groovy.annotator.UtilKt.checkVariableModifiers;
+import static org.jetbrains.plugins.groovy.annotator.UtilKt.registerFix;
+
 /**
  * @author ven
  */
@@ -695,7 +700,7 @@ public class GroovyAnnotator extends GroovyElementVisitor {
         final String superPresentation = GroovyPresentationUtil.getSignaturePresentation(signature);
         final String superQName = getQNameOfMember(superMethod);
 
-        final PsiElement modifier = PsiUtil.findModifierInList(method.getModifierList(), currentModifier);
+        final PsiElement modifier = method.getModifierList().getModifier(currentModifier);
         holder.createErrorAnnotation(
           modifier != null? modifier : method.getNameIdentifierGroovy(),
           GroovyBundle.message("method.0.cannot.have.weaker.access.privileges.1.than.2.in.3.4", currentPresentation, currentModifier, superPresentation, superQName, superModifier)
@@ -951,9 +956,12 @@ public class GroovyAnnotator extends GroovyElementVisitor {
     else if (parent instanceof GrTypeDefinition) {
       checkTypeDefinitionModifiers(myHolder, (GrTypeDefinition)parent);
     }
-    else if (parent instanceof GrVariableDeclaration && parent.getParent() instanceof GrTypeDefinition) {
+    else if (parent instanceof GrVariableDeclaration && parent.getParent() instanceof GrTypeDefinitionBody) {
       checkFieldModifiers(myHolder, (GrVariableDeclaration)parent);
     }
+    else if (parent instanceof GrVariableDeclaration) {
+      checkVariableModifiers(myHolder, ((GrVariableDeclaration)parent));
+    }
     else if (parent instanceof GrClassInitializer) {
       checkClassInitializerModifiers(myHolder, modifierList);
     }
@@ -993,9 +1001,6 @@ public class GroovyAnnotator extends GroovyElementVisitor {
       registerFix(annotation, new GrModifierFix(member, PsiModifier.FINAL, true, false, GrModifierFix.MODIFIER_LIST), modifierList);
     }
 
-    checkModifierIsNotAllowed(modifierList, PsiModifier.NATIVE, GroovyBundle.message("variable.cannot.be.native"), holder);
-    checkModifierIsNotAllowed(modifierList, PsiModifier.ABSTRACT, GroovyBundle.message("variable.cannot.be.abstract"), holder);
-
     if (member.getContainingClass() instanceof GrInterfaceDefinition) {
       checkModifierIsNotAllowed(modifierList,
                                 PsiModifier.PRIVATE, GroovyBundle.message("interface.members.are.not.allowed.to.be", PsiModifier.PRIVATE), holder);
@@ -1005,35 +1010,6 @@ public class GroovyAnnotator extends GroovyElementVisitor {
     }
   }
 
-  private static void registerFix(Annotation annotation, LocalQuickFix fix, PsiElement place) {
-    final InspectionManager manager = InspectionManager.getInstance(place.getProject());
-    assert !place.getTextRange().isEmpty() : place.getContainingFile().getName();
-
-    final ProblemDescriptor descriptor = manager.createProblemDescriptor(place, place, annotation.getMessage(),
-                                                                         annotation.getHighlightType(), true, LocalQuickFix.EMPTY_ARRAY);
-    final TextRange range = TextRange.create(annotation.getStartOffset(), annotation.getEndOffset());
-    annotation.registerFix(fix, range, null, descriptor);
-  }
-
-  private static void checkModifierIsNotAllowed(@NotNull GrModifierList modifierList,
-                                                @NotNull @GrModifier.GrModifierConstant String modifier,
-                                                @Nullable String message,
-                                                @NotNull AnnotationHolder holder) {
-    checkModifierIsNotAllowedImpl(modifierList, modifier, message, holder, false);
-  }
-
-  private static void checkModifierIsNotAllowedImpl(@NotNull GrModifierList modifierList,
-                                                    @NotNull @GrModifier.GrModifierConstant String modifier,
-                                                    @Nullable String message,
-                                                    @NotNull AnnotationHolder holder,
-                                                    final boolean explicit) {
-    if (explicit ? modifierList.hasModifierProperty(modifier) : modifierList.hasExplicitModifier(modifier)) {
-      PsiElement modifierOrList = getModifierOrList(modifierList, modifier);
-      final Annotation annotation = holder.createErrorAnnotation(modifierOrList, message);
-      registerFix(annotation, new GrModifierFix((PsiMember)modifierList.getParent(), modifier, true, false, GrModifierFix.MODIFIER_LIST), modifierList);
-    }
-  }
-
   private static void checkAnnotationAttributeType(GrTypeElement element, AnnotationHolder holder) {
     if (element instanceof GrBuiltInTypeElement) return;
 
@@ -1867,7 +1843,7 @@ public class GroovyAnnotator extends GroovyElementVisitor {
 
   @NotNull
   private static PsiElement getModifierOrList(@NotNull GrModifierList modifiersList, @GrModifier.GrModifierConstant final String modifier) {
-    PsiElement m = PsiUtil.findModifierInList(modifiersList, modifier);
+    PsiElement m = modifiersList.getModifier(modifier);
     return m != null ? m : modifiersList;
   }
 
diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/annotator/util.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/annotator/util.kt
new file mode 100644
index 000000000000..8031181f7273
--- /dev/null
+++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/annotator/util.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.plugins.groovy.annotator
+
+import com.intellij.codeInspection.InspectionManager
+import com.intellij.codeInspection.LocalQuickFix
+import com.intellij.lang.annotation.Annotation
+import com.intellij.lang.annotation.AnnotationHolder
+import com.intellij.openapi.util.TextRange
+import com.intellij.psi.PsiElement
+import org.jetbrains.plugins.groovy.GroovyBundle
+import org.jetbrains.plugins.groovy.codeInspection.bugs.GrRemoveModifierFix
+import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifier
+import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifier.GrModifierConstant
+import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration
+
+val VARIABLE_MODIFIERS = setOf(GrModifier.DEF, GrModifier.FINAL)
+
+internal fun checkVariableModifiers(holder: AnnotationHolder, variableDeclaration: GrVariableDeclaration) {
+  val modifierList = variableDeclaration.modifierList
+  for (modifier in GrModifier.GROOVY_MODIFIERS) {
+    if (modifier in VARIABLE_MODIFIERS) continue
+    checkModifierIsNotAllowed(modifierList, modifier, GroovyBundle.message("variable.cannot.be", modifier), holder)
+  }
+}
+
+internal fun checkModifierIsNotAllowed(modifierList: GrModifierList,
+                                       @GrModifierConstant modifier: String,
+                                       message: String?,
+                                       holder: AnnotationHolder) {
+  val modifierElement = modifierList.getModifier(modifier) ?: return
+  val annotation = holder.createErrorAnnotation(modifierElement, message)
+  val fix = GrRemoveModifierFix(modifier, GroovyBundle.message("remove.modifier", modifier))
+  registerFix(annotation, fix, modifierElement)
+}
+
+internal fun registerFix(annotation: Annotation, fix: LocalQuickFix, place: PsiElement) {
+  val manager = InspectionManager.getInstance(place.project)
+  assert(!place.textRange.isEmpty) { place.containingFile.name }
+  val descriptor = manager.createProblemDescriptor(place, place, annotation.message, annotation.highlightType, true)
+  val range = TextRange.create(annotation.startOffset, annotation.endOffset)
+  annotation.registerFix(fix, range, null, descriptor)
+}
\ No newline at end of file
diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/GroovyInspectionBundle.properties b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/GroovyInspectionBundle.properties
index ee93219b0a79..2639a6539614 100644
--- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/GroovyInspectionBundle.properties
+++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/GroovyInspectionBundle.properties
@@ -12,14 +12,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-
 ########################################################################################################################
 # Unuzed imports
 ########################################################################################################################
 unused.import=Unused import
 optimize.imports=Optimize imports
 optimize.all.imports=Optimize unused import statements
-
 ########################################################################################################################
 # Create class fix
 ########################################################################################################################
@@ -29,20 +27,15 @@ destination.package=Destination package:
 dialog.create.class.label.0=Create class ''{0}''
 second.unsafe.call=Second unsafe call
 call.can.throw.npe=Calling chain can throw NullPointerException
-
 groovy.dfa.issues=Data flow issues
 other=Other
-
 unused.assignment=Unused Assignment
 unused.assignment.tooltip=Assignment is not used
 unused.variable=Variable is not used
-
 unused.inc.dec=Unused Incrementing or Decrementing
 variable.can.be.final=Variable can be final
 variable.can.be.final.tooltip=Variable ''{0}'' can be final
 parameter.can.be.final.tooltip=Parameter ''{0}'' can be final
-
-
 groovy.probable.bugs=Probable bugs
 equals.between.inconvertible.types='equals()' between objects of inconvertible types
 equals.between.inconvertible.types.tooltip={0} between objects of inconvertible types ''{1}'' and ''{2}''
@@ -62,7 +55,6 @@ unresolved.type.status=Type ''{0}'' is not resolved
 resolved.type.status=Type ''{0}'' is resolved
 no.type.specified=No type specified
 dynamic.element=Dynamic Element
-
 ########################################################################################################################
 # InspectorGroovy properties
 ########################################################################################################################
@@ -70,14 +62,11 @@ pointless.boolean.problem.descriptor=#ref can be simplified to ''{0}'' #loc
 pointless.arithmetic.error.message=#ref can be replaced with ''{0}'' #loc
 pointless.boolean.display.name=Pointless boolean expression
 pointless.boolean.quickfix=Simplify
-
 Cannot.perform.undo.operation=Cannot perform undo operation
 Undo.disable=Undo Disabled
 field.already.defined=Variables with field names
 clashing.getters=Clashing getters
-
 access.to.inaccessible.element=Access to inaccessible element
-
 type.doesnt.contain.method=Type ''{0}'' cannot be iterated in range because it does not have method ''{1}''
 incorrect.range.argument=Incorrect range arguments
 type.doesnt.implemnt.comparable=Type ''{0}'' doesn''t implement Comparable
@@ -125,3 +114,5 @@ ignore.when.catch.parameter.is.named.ignore.or.ignored=Ignore when catch paramet
 no.applicable.signature.found=No applicable signature found
 expected.type.0=Expected ''{0}'', found ''{1}''
 declare.explicit.implementations.of.trait=Declare explicit implementations of trait
+unnecessary.modifier.description=Modifier ''{0}'' is not necessary
+unnecessary.modifier.remove=Remove unnecessary ''{0}''
\ No newline at end of file
diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/bugs/GrRemoveModifierFix.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/bugs/GrRemoveModifierFix.java
new file mode 100644
index 000000000000..ce292191d85d
--- /dev/null
+++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/bugs/GrRemoveModifierFix.java
@@ -0,0 +1,40 @@
+/*
+ * 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.plugins.groovy.codeInspection.bugs;
+
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.codeStyle.CodeStyleManager;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.plugins.groovy.codeInspection.GroovyInspectionBundle;
+import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifier;
+
+public class GrRemoveModifierFix extends GrModifierFix {
+
+  public GrRemoveModifierFix(@NotNull @GrModifier.GrModifierConstant String modifier) {
+    this(modifier, GroovyInspectionBundle.message("unnecessary.modifier.remove", modifier));
+  }
+
+  public GrRemoveModifierFix(@NotNull @GrModifier.GrModifierConstant String modifier, @NotNull String text) {
+    super(text, modifier, false, GrModifierFix.MODIFIER_LIST_CHILD);
+  }
+
+  @Override
+  protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException {
+    CodeStyleManager.getInstance(project).performActionWithFormatterDisabled((Runnable)() -> super.doFix(project, descriptor));
+  }
+}
diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/style/GrUnnecessaryDefModifierInspection.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/style/GrUnnecessaryDefModifierInspection.java
index d05cbe5a478e..eb3098f30aff 100644
--- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/style/GrUnnecessaryDefModifierInspection.java
+++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/style/GrUnnecessaryDefModifierInspection.java
@@ -20,41 +20,40 @@ import com.intellij.codeInspection.ProblemsHolder;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiElementVisitor;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.plugins.groovy.codeInspection.GroovyInspectionBundle;
 import org.jetbrains.plugins.groovy.codeInspection.GroovySuppressableInspectionTool;
 import org.jetbrains.plugins.groovy.codeInspection.bugs.GrModifierFix;
-import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor;
-import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementVisitor;
+import org.jetbrains.plugins.groovy.codeInspection.bugs.GrRemoveModifierFix;
+import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifier;
 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList;
-import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
-import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration;
-import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
-import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
+import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
 
 public class GrUnnecessaryDefModifierInspection extends GroovySuppressableInspectionTool {
 
-  private static final GrModifierFix FIX = new GrModifierFix(
-    "Remove 'def'", GrModifier.DEF, false, GrModifierFix.MODIFIER_LIST_CHILD
-  );
+  private static final GrModifierFix FIX = new GrRemoveModifierFix(GrModifier.DEF);
 
   @NotNull
   @Override
   public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
-    return new GroovyPsiElementVisitor(new GroovyElementVisitor() {
+    return new PsiElementVisitor() {
       @Override
-      public void visitModifierList(GrModifierList modifierList) {
-        PsiElement parent = modifierList.getParent();
-        if (!(parent instanceof GrMethod) && !(parent instanceof GrParameter) && !(parent instanceof GrVariableDeclaration)) return;
+      public void visitElement(PsiElement modifier) {
+        if (modifier.getNode().getElementType() != GroovyTokenTypes.kDEF) return;
 
-        PsiElement modifier = modifierList.getModifier(GrModifier.DEF);
-        if (modifier == null) return;
+        PsiElement list = modifier.getParent();
+        if (!(list instanceof GrModifierList)) return;
 
-        if (parent instanceof GrMethod && ((GrMethod)parent).getReturnTypeElementGroovy() != null ||
-            parent instanceof GrVariable && ((GrVariable)parent).getTypeElementGroovy() != null ||
-            parent instanceof GrVariableDeclaration && ((GrVariableDeclaration)parent).getTypeElementGroovy() != null) {
-          holder.registerProblem(modifier, "'def' is not necessary", ProblemHighlightType.LIKE_UNUSED_SYMBOL, FIX);
-        }
+        PsiElement owner = list.getParent();
+        if (!PsiUtil.modifierListMayBeEmpty(owner)) return;
+
+        holder.registerProblem(
+          modifier,
+          GroovyInspectionBundle.message("unnecessary.modifier.description", GrModifier.DEF),
+          ProblemHighlightType.LIKE_UNUSED_SYMBOL,
+          FIX
+        );
       }
-    });
+    };
   }
 }
diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/style/GrUnnecessaryPublicModifierInspection.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/style/GrUnnecessaryPublicModifierInspection.java
new file mode 100644
index 000000000000..50794b28bfc1
--- /dev/null
+++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/style/GrUnnecessaryPublicModifierInspection.java
@@ -0,0 +1,61 @@
+/*
+ * 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.plugins.groovy.codeInspection.style;
+
+import com.intellij.codeInspection.LocalQuickFix;
+import com.intellij.codeInspection.ProblemHighlightType;
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiElementVisitor;
+import com.intellij.psi.PsiModifier;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.plugins.groovy.codeInspection.GroovyInspectionBundle;
+import org.jetbrains.plugins.groovy.codeInspection.GroovySuppressableInspectionTool;
+import org.jetbrains.plugins.groovy.codeInspection.bugs.GrRemoveModifierFix;
+import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
+import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration;
+
+public class GrUnnecessaryPublicModifierInspection extends GroovySuppressableInspectionTool {
+
+  private static final LocalQuickFix FIX = new GrRemoveModifierFix(PsiModifier.PUBLIC);
+
+  @NotNull
+  @Override
+  public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
+    return new PsiElementVisitor() {
+      @Override
+      public void visitElement(PsiElement modifier) {
+        if (modifier.getNode().getElementType() != GroovyTokenTypes.kPUBLIC) return;
+
+        PsiElement list = modifier.getParent();
+        if (!(list instanceof GrModifierList)) return;
+
+        PsiElement parent = list.getParent();
+        // Do not mark public on fields as unnecessary
+        // It may be put there explicitly to prevent getter/setter generation.
+        if (parent instanceof GrVariableDeclaration) return;
+
+        holder.registerProblem(
+          modifier,
+          GroovyInspectionBundle.message("unnecessary.modifier.description", PsiModifier.PUBLIC),
+          ProblemHighlightType.LIKE_UNUSED_SYMBOL,
+          FIX
+        );
+      }
+    };
+  }
+}
diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/modifiers/GrModifierListImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/modifiers/GrModifierListImpl.java
index 1afc02b9aeba..d40b9683e63b 100644
--- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/modifiers/GrModifierListImpl.java
+++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/modifiers/GrModifierListImpl.java
@@ -53,6 +53,7 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.GrStubElementBase;
 import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
 import org.jetbrains.plugins.groovy.lang.psi.stubs.GrModifierListStub;
 import org.jetbrains.plugins.groovy.lang.psi.util.GrTraitUtil;
+import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
 
 import java.util.ArrayList;
 import java.util.Map;
@@ -293,6 +294,9 @@ public class GrModifierListImpl extends GrStubElementBase im
     else {
       setModifierPropertyInternal(name, doSet);
     }
+    if (isEmptyModifierList() && !PsiUtil.modifierListMayBeEmpty(this.getParent())) {
+      setModifierPropertyInternal(GrModifier.DEF, true);
+    }
   }
 
   @NotNull
diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrVariableDeclarationImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrVariableDeclarationImpl.java
index 2de43582c213..07d32596237e 100644
--- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrVariableDeclarationImpl.java
+++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrVariableDeclarationImpl.java
@@ -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.
@@ -231,10 +231,10 @@ public class GrVariableDeclarationImpl extends GrStubElementBase impl
   private PsiElement findSuitableModifier() {
     final GrModifierList list = getModifierList();
 
-    PsiElement defModifier = PsiUtil.findModifierInList(list, GrModifier.DEF);
+    PsiElement defModifier = list.getModifier(GrModifier.DEF);
     if (defModifier != null) return defModifier;
 
-    PsiElement finalModifier = PsiUtil.findModifierInList(list, PsiModifier.FINAL);
+    PsiElement finalModifier = list.getModifier(PsiModifier.FINAL);
     if (finalModifier != null) return finalModifier;
 
     for (PsiElement element : list.getModifiers()) {
diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/synthetic/GrAccessorMethodImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/synthetic/GrAccessorMethodImpl.java
index 09fe465fab21..8f479706c7a8 100644
--- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/synthetic/GrAccessorMethodImpl.java
+++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/synthetic/GrAccessorMethodImpl.java
@@ -76,7 +76,7 @@ public class GrAccessorMethodImpl extends LightMethodBuilder implements GrAccess
       addModifier(PsiModifier.FINAL);
     }
 
-    if (myProperty.hasModifierProperty(PsiModifier.ABSTRACT)) {
+    if (myProperty.hasModifierProperty(PsiModifier.ABSTRACT) && GrTraitUtil.isTrait(myProperty.getContainingClass())) {
       addModifier(PsiModifier.ABSTRACT);
     }
 
diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java
index 17c96ea013e4..427b033f6864 100644
--- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java
+++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java
@@ -106,12 +106,28 @@ public class PsiUtil {
   private PsiUtil() {
   }
 
-  @Nullable
-  public static PsiElement findModifierInList(@NotNull GrModifierList list, @GrModifier.GrModifierConstant @NotNull String modifier) {
-    for (PsiElement element : list.getModifiers()) {
-      if (modifier.equals(element.getText())) return element;
+  /**
+   * @param owner modifier list owner
+   * @return 
    + *
  • {@code true} when owner has explicit type or it's not required for owner to have explicit type
  • + *
  • {@code false} when doesn't have explicit type and it's required to have a type or modifier
  • + *
  • {@code defaultValue} for the other owners
  • + *
+ */ + public static boolean modifierListMayBeEmpty(@Nullable PsiElement owner) { + if (owner instanceof GrParameter) { + return true; } - return null; + if (owner instanceof GrMethod) { + return ((GrMethod)owner).getReturnTypeElementGroovy() != null || ((GrMethod)owner).isConstructor(); + } + else if (owner instanceof GrVariable) { + return ((GrVariable)owner).getTypeElementGroovy() != null; + } + else if (owner instanceof GrVariableDeclaration) { + return ((GrVariableDeclaration)owner).getTypeElementGroovy() != null; + } + return true; } @Nullable diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index bf3e6adc379b..2f2d61270cdb 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -826,6 +826,9 @@ + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/declaration/GrSetStrongTypeIntention.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/declaration/GrSetStrongTypeIntention.java index 78d936edda87..f6aeb1fcf8eb 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/declaration/GrSetStrongTypeIntention.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/declaration/GrSetStrongTypeIntention.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 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. @@ -184,7 +184,7 @@ public class GrSetStrongTypeIntention extends Intention { GrModifierList modifierList = getModifierList(parent); if (modifierList != null && modifierList.hasModifierProperty(GrModifier.DEF) && modifierList.getModifiers().length == 1) { - PsiElement modifier = PsiUtil.findModifierInList(modifierList, GrModifier.DEF); + PsiElement modifier = modifierList.getModifier(GrModifier.DEF); LOG.assertTrue(modifier != null); int modifierOffset = modifier.getTextRange().getEndOffset() - elementToBuildTemplateOn.getTextRange().getStartOffset(); return new TypeInfo(modifier, modifierOffset); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/variable/GrInplaceVariableIntroducer.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/variable/GrInplaceVariableIntroducer.java index ef4fa4cd7683..a2f24a2428f6 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/variable/GrInplaceVariableIntroducer.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/variable/GrInplaceVariableIntroducer.java @@ -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. @@ -31,11 +31,12 @@ import com.intellij.ui.NonFocusableCheckBox; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifier; +import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement; import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.SupertypeConstraint; import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.TypeConstraint; -import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; import org.jetbrains.plugins.groovy.refactoring.GroovyNameSuggestionUtil; import org.jetbrains.plugins.groovy.refactoring.introduce.GrAbstractInplaceIntroducer; import org.jetbrains.plugins.groovy.refactoring.introduce.GrFinalListener; @@ -169,11 +170,21 @@ public abstract class GrInplaceVariableIntroducer extends GrAbstractInplaceIntro TypeConstraint[] constraints = initializerType != null && !initializerType.equals(PsiType.NULL) ? new SupertypeConstraint[]{SupertypeConstraint.create(initializerType)} : TypeConstraint.EMPTY_ARRAY; ChooseTypeExpression typeExpression = new ChooseTypeExpression(constraints, variable.getManager(), variable.getResolveScope(), true, GroovyApplicationSettings.getInstance().INTRODUCE_LOCAL_SELECT_DEF); - PsiElement element = variable.getTypeElementGroovy() != null ? variable.getTypeElementGroovy() - : PsiUtil.findModifierInList(variable.getModifierList(), GrModifier.DEF); + PsiElement element = getTypeELementOrDef(variable); + if (element == null) return; builder.replaceElement(element, "Variable_type", typeExpression, true, true); } + @Nullable + private static PsiElement getTypeELementOrDef(@NotNull GrVariable variable) { + GrTypeElement typeElement = variable.getTypeElementGroovy(); + if (typeElement != null) return typeElement; + + GrModifierList modifierList = variable.getModifierList(); + if (modifierList != null) return modifierList.getModifier(GrModifier.DEF); + return null; + } + @Override protected GroovyIntroduceVariableSettings getSettings() { return new GroovyIntroduceVariableSettings() { diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/codeInspection/style/GrUnnecessaryDefModifierInspectionTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/codeInspection/style/GrUnnecessaryDefModifierInspectionTest.groovy index 7376bc73c347..54399d083677 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/codeInspection/style/GrUnnecessaryDefModifierInspectionTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/codeInspection/style/GrUnnecessaryDefModifierInspectionTest.groovy @@ -23,28 +23,34 @@ import org.jetbrains.plugins.groovy.LightGroovyTestCase @CompileStatic public class GrUnnecessaryDefModifierInspectionTest extends LightGroovyTestCase { - LightProjectDescriptor projectDescriptor = GroovyLightProjectDescriptor.GROOVY_LATEST + final LightProjectDescriptor projectDescriptor = GroovyLightProjectDescriptor.GROOVY_LATEST void 'test highlighting and fix'() { fixture.with { enableInspections GrUnnecessaryDefModifierInspection configureByText '_.groovy', '''\ -def foo(def Object a) {} -def boolean baz(def a) {} -def Object bar +def foo(def Object a) {} +def baw(def a) {} +def boolean baz(a) {} +def Object bar def baf def (int a, b) = [1, 2] +class A { + def A() {} +} ''' checkHighlighting() launchAction findSingleIntention("Fix all 'Unnecessary 'def''") checkResult '''\ def foo(Object a) {} - -boolean baz(def a) {} - +def baw(a) {} +boolean baz(a) {} Object bar def baf def (int a, b) = [1, 2] +class A { + A() {} +} ''' } } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/codeInspection/style/GrUnnecessaryPublicModifierInspectionTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/codeInspection/style/GrUnnecessaryPublicModifierInspectionTest.groovy new file mode 100644 index 000000000000..02d1f1bbd17b --- /dev/null +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/codeInspection/style/GrUnnecessaryPublicModifierInspectionTest.groovy @@ -0,0 +1,58 @@ +/* + * 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.plugins.groovy.codeInspection.style + +import com.intellij.testFramework.LightProjectDescriptor +import groovy.transform.CompileStatic +import org.jetbrains.plugins.groovy.GroovyLightProjectDescriptor +import org.jetbrains.plugins.groovy.LightGroovyTestCase + +@CompileStatic +public class GrUnnecessaryPublicModifierInspectionTest extends LightGroovyTestCase { + + final LightProjectDescriptor projectDescriptor = GroovyLightProjectDescriptor.GROOVY_LATEST + + void 'test highlighting and fix'() { + fixture.with { + enableInspections GrUnnecessaryPublicModifierInspection + configureByText '_.groovy', '''\ +public class A { + public A() {} + public foo() {} + public x + public class B {} +} +public enum E {} +public interface I {} +''' + checkHighlighting() + launchAction findSingleIntention("Fix all 'Unnecessary 'public''") + checkResult '''\ +class A { + A() {} + def foo() {} + public x + class B {} +} +enum E {} +interface I {} +''' + } + } +} \ No newline at end of file diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/Groovy23HighlightingTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/Groovy23HighlightingTest.groovy index 1dd7e3954151..eed26f6917fa 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/Groovy23HighlightingTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/Groovy23HighlightingTest.groovy @@ -216,5 +216,16 @@ trait T { ''' } + void 'test abstract property in class'() { + fixture.with { + configureByText '_.groovy', '''\ +class A { + abstract f +} +''' + checkHighlighting() + } + } + final InspectionProfileEntry[] customInspections = [new GroovyAssignabilityCheckInspection(), new GrUnresolvedAccessInspection()] } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/GroovyHighlightingTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/GroovyHighlightingTest.groovy index 64469f0740a4..5ca01d3186cd 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/GroovyHighlightingTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/GroovyHighlightingTest.groovy @@ -2007,4 +2007,8 @@ def doParse() { def foo = new Rrrrrrrr() {} ''' } + + void testLocalVariableModifiers() { doTest() } + + void testFieldModifiers() { doTest() } } diff --git a/plugins/groovy/testdata/highlighting/FieldModifiers.groovy b/plugins/groovy/testdata/highlighting/FieldModifiers.groovy new file mode 100644 index 000000000000..e68c9e3d8604 --- /dev/null +++ b/plugins/groovy/testdata/highlighting/FieldModifiers.groovy @@ -0,0 +1,30 @@ +class Simple { + def a0 = 1 + protected a1 = 1 + private a2 = 1 + static a3 = 1 + abstract a4 = 1 + final a5 = 1 + native a6 = 1 + synchronized a7 = 1 + strictfp a8 = 1 + transient a9 = 1 + volatile a10 = 1 +} + +class Combinations { + public private a + private protected b + protected public c + volatile final g +} + +class Duplicates { + public public a +} + +interface I { + private a + public b + protected c +} diff --git a/plugins/groovy/testdata/highlighting/LocalVariableModifiers.groovy b/plugins/groovy/testdata/highlighting/LocalVariableModifiers.groovy new file mode 100644 index 000000000000..6c39fbfcd3bf --- /dev/null +++ b/plugins/groovy/testdata/highlighting/LocalVariableModifiers.groovy @@ -0,0 +1,13 @@ +def foo() { + def a0 = 1 + protected a1 = 1 + private a2 = 1 + static a3 = 1 + abstract a4 = 1 + final a5 = 1 + native a6 = 1 + synchronized a7 = 1 + strictfp a8 = 1 + transient a9 = 1 + volatile a10 = 1 +} diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/wizards/MavenModuleBuilder.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/wizards/MavenModuleBuilder.java index 1817fcf8f59c..81862156a5f2 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/wizards/MavenModuleBuilder.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/wizards/MavenModuleBuilder.java @@ -31,6 +31,7 @@ import com.intellij.openapi.roots.ui.configuration.ModulesProvider; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import icons.MavenIcons; @@ -231,4 +232,14 @@ public class MavenModuleBuilder extends ModuleBuilder implements SourcePathsBuil Disposer.register(parentDisposable, step); return step; } + + @Nullable + @Override + public ModuleWizardStep modifySettingsStep(@NotNull SettingsStep settingsStep) { + final JTextField moduleNameField = settingsStep.getModuleNameField(); + if (moduleNameField != null && myProjectId != null && myProjectId.getArtifactId() != null) { + moduleNameField.setText(StringUtil.sanitizeJavaIdentifier(myProjectId.getArtifactId())); + } + return super.modifySettingsStep(settingsStep); + } } diff --git a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditorHighlighter.java b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditorHighlighter.java index db59674c8c5d..979980d4d3b6 100644 --- a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditorHighlighter.java +++ b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditorHighlighter.java @@ -107,6 +107,7 @@ public class ResourceBundleEditorHighlighter implements BackgroundEditorHighligh new SmartList<>(); final IProperty[] properties = files.stream().map(f -> f.findPropertyByKey(key)).filter(Objects::nonNull).toArray(IProperty[]::new); + if (properties.length == 0) return false; for (InspectionVisitorWrapper v : visitors) { final ResourceBundleEditorProblemDescriptor[] problemDescriptors = v.getProblemVisitor().apply(properties); if (!ArrayUtil.isEmpty(problemDescriptors)) { diff --git a/plugins/settings-repository/src/IcsManager.kt b/plugins/settings-repository/src/IcsManager.kt index 5c936d80c8a7..e1c2e0037ff3 100644 --- a/plugins/settings-repository/src/IcsManager.kt +++ b/plugins/settings-repository/src/IcsManager.kt @@ -17,8 +17,8 @@ package org.jetbrains.settingsRepository import com.intellij.configurationStore.StateStorageManagerImpl import com.intellij.configurationStore.StreamProvider +import com.intellij.credentialStore.macOs.isMacOsCredentialStoreSupported import com.intellij.ide.ApplicationLoadListener -import com.intellij.ide.passwordSafe.macOs.isMacOsCredentialStoreSupported import com.intellij.openapi.application.Application import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.PathManager diff --git a/plugins/settings-repository/src/git/JGitCredentialsProvider.kt b/plugins/settings-repository/src/git/JGitCredentialsProvider.kt index 6d4a632de241..3395e1bfd453 100644 --- a/plugins/settings-repository/src/git/JGitCredentialsProvider.kt +++ b/plugins/settings-repository/src/git/JGitCredentialsProvider.kt @@ -15,7 +15,7 @@ */ package org.jetbrains.settingsRepository.git -import com.intellij.ide.passwordSafe.macOs.isMacOsCredentialStoreSupported +import com.intellij.credentialStore.macOs.isMacOsCredentialStoreSupported import com.intellij.openapi.ui.MessageDialogBuilder import com.intellij.openapi.ui.Messages import com.intellij.openapi.util.NotNullLazyValue diff --git a/plugins/settings-repository/src/keychain/OsXCredentialsStore.kt b/plugins/settings-repository/src/keychain/OsXCredentialsStore.kt index bcfed4ffbf5d..b806818ad6cb 100644 --- a/plugins/settings-repository/src/keychain/OsXCredentialsStore.kt +++ b/plugins/settings-repository/src/keychain/OsXCredentialsStore.kt @@ -15,9 +15,9 @@ */ package org.jetbrains.keychain -import com.intellij.ide.passwordSafe.macOs.deleteGenericPassword -import com.intellij.ide.passwordSafe.macOs.findGenericPassword -import com.intellij.ide.passwordSafe.macOs.saveGenericPassword +import com.intellij.credentialStore.macOs.deleteGenericPassword +import com.intellij.credentialStore.macOs.findGenericPassword +import com.intellij.credentialStore.macOs.saveGenericPassword import com.intellij.openapi.util.PasswordUtil import gnu.trove.THashMap @@ -36,7 +36,7 @@ class OsXCredentialsStore(serviceName: String) : CredentialsStore { } val accountName: String = sshKeyFile ?: host - var credentials = accountToCredentials[accountName] + var credentials = accountToCredentials.get(accountName) if (credentials != null) { return credentials } @@ -70,12 +70,12 @@ class OsXCredentialsStore(serviceName: String) : CredentialsStore { override fun save(host: String?, credentials: Credentials, sshKeyFile: String?) { val accountName: String = sshKeyFile ?: host!! val oldCredentials = accountToCredentials.put(accountName, credentials) - if (credentials.equals(oldCredentials)) { + if (credentials == oldCredentials) { return } val data = if (sshKeyFile == null) "${PasswordUtil.encodePassword(credentials.id)}@${PasswordUtil.encodePassword(credentials.token)}" else credentials.token!! - saveGenericPassword(getServiceName(sshKeyFile), accountName, data) + saveGenericPassword(getServiceName(sshKeyFile), accountName, data.toByteArray()) } override fun reset(host: String) { diff --git a/plugins/tasks/tasks-tests/test/com/intellij/tasks/impl/gson/MandatoryTest.java b/plugins/tasks/tasks-tests/test/com/intellij/tasks/impl/gson/MandatoryTest.java index 3acfb66fc2f8..3f167b744e53 100644 --- a/plugins/tasks/tasks-tests/test/com/intellij/tasks/impl/gson/MandatoryTest.java +++ b/plugins/tasks/tasks-tests/test/com/intellij/tasks/impl/gson/MandatoryTest.java @@ -21,17 +21,17 @@ import org.jetbrains.annotations.NotNull; public class MandatoryTest extends TestCase { public void testSuccess1() { - String json = "{ mandatory = \"text\" }"; + String json = "{ \"mandatory\" : \"text\" }"; doTest(json, Class1.class, true); } public void testSuccess2() { - String json = "{ simple = \"text2\", mandatory = \"text\" }"; + String json = "{ \"simple\" : \"text2\", \"mandatory\" : \"text\" }"; doTest(json, Class1.class, true); } public void testSuccess3() { - String json = "{ unknown = \"text2\", mandatory = \"text\" }"; + String json = "{ \"unknown\" : \"text2\", \"mandatory\" : \"text\" }"; doTest(json, Class1.class, true); } @@ -41,57 +41,57 @@ public class MandatoryTest extends TestCase { } public void testFailure1() { - String json = "{ simple = \"text2\" }"; + String json = "{ \"simple\" : \"text2\" }"; doTest(json, Class1.class, false); } public void testFailure2() { - String json = "{ unknown = \"text2\" }"; + String json = "{ \"unknown\" : \"text2\" }"; doTest(json, Class1.class, false); } public void testFailure3() { - String json = "{ mandatory = null }"; + String json = "{ \"mandatory\" : null }"; doTest(json, Class1.class, false); } public void testSuccessEnclosed1() { - String json = "{ mandatory = { mandatory = \"text\" } }"; + String json = "{ \"mandatory\" : { \"mandatory\" : \"text\" } }"; doTest(json, Class2.class, true); } public void testSuccessEnclosed2() { - String json = "{ mandatory = { mandatory = \"text\" }, simple = { mandatory = \"text\" } }"; + String json = "{ \"mandatory\" : { \"mandatory\" : \"text\" }, \"simple\" : { \"mandatory\" : \"text\" } }"; doTest(json, Class2.class, true); } public void testSuccessEnclosed3() { - String json = "{ mandatory = { mandatory = \"text\" }, simple = null }"; + String json = "{ \"mandatory\" : { \"mandatory\" : \"text\" }, \"simple\" : null }"; doTest(json, Class2.class, true); } public void testFailureEnclosed1() { - String json = "{ simple = { mandatory = \"text\" } }"; + String json = "{ \"simple\" : { \"mandatory\" : \"text\" } }"; doTest(json, Class2.class, false); } public void testFailureEnclosed2() { - String json = "{ mandatory = null }"; + String json = "{ \"mandatory\" : null }"; doTest(json, Class2.class, false); } public void testFailureEnclosed3() { - String json = "{ mandatory = { simple = \"text\" } }"; + String json = "{ \"mandatory\" : { \"simple\" : \"text\" } }"; doTest(json, Class2.class, false); } public void testFailureEnclosed4() { - String json = "{ mandatory = { mandatory = null, simple = \"text\" } }"; + String json = "{ \"mandatory\" : { \"mandatory\" : null, \"simple\" : \"text\" } }"; doTest(json, Class2.class, false); } public void testFailureEnclosed5() { - String json = "{ mandatory = { mandatory = \"text\" }, simple = { simple = \"text\" } }"; + String json = "{ \"mandatory\" : { \"mandatory\" : \"text\" }, \"simple\" : { \"simple\" : \"text\" } }"; doTest(json, Class2.class, false); } diff --git a/python/helpers/docstring_formatter.py b/python/helpers/docstring_formatter.py new file mode 100644 index 000000000000..f0de7b316591 --- /dev/null +++ b/python/helpers/docstring_formatter.py @@ -0,0 +1,397 @@ +import os +import re +import sys +import textwrap + +import six +from six import text_type, u + +ENCODING = 'utf-8' +_stdin = os.fdopen(sys.stdin.fileno(), 'rb') +_stdout = os.fdopen(sys.stdout.fileno(), 'wb') +_stderr = os.fdopen(sys.stderr.fileno(), 'wb') + + +def read_safe(): + return _stdin.read().decode(ENCODING) + + +def print_safe(s, error=False): + stream = _stderr if error else _stdout + stream.write(s.encode(ENCODING)) + stream.flush() + + +def format_rest(docstring): + from docutils import nodes + from docutils.core import publish_string + from docutils.frontend import OptionParser + from docutils.nodes import Text, field_body, field_name + from docutils.parsers.rst import directives + from docutils.parsers.rst.directives.admonitions import BaseAdmonition + from docutils.writers import Writer + from docutils.writers.html4css1 import HTMLTranslator, Writer as HTMLWriter + + # Copied from the Sphinx' sources. Docutils doesn't handle "seealso" directives by default. + class seealso(nodes.Admonition, nodes.Element): + """Custom "see also" admonition.""" + + class SeeAlso(BaseAdmonition): + """ + An admonition mentioning things to look at as reference. + """ + node_class = seealso + + directives.register_directive('seealso', SeeAlso) + + class RestHTMLTranslator(HTMLTranslator): + settings = None + + def __init__(self, document): + # Copied from epydoc.markup.restructuredtext._EpydocHTMLTranslator + if self.settings is None: + settings = OptionParser([HTMLWriter()]).get_default_values() + self.__class__.settings = settings + document.settings = self.settings + + HTMLTranslator.__init__(self, document) + + def visit_document(self, node): + pass + + def depart_document(self, node): + pass + + def visit_docinfo(self, node): + pass + + def depart_docinfo(self, node): + pass + + def unimplemented_visit(self, node): + pass + + def visit_field_name(self, node): + atts = {} + if self.in_docinfo: + atts['class'] = 'docinfo-name' + else: + atts['class'] = 'field-name' + + self.context.append('') + atts['align'] = "right" + self.body.append(self.starttag(node, 'th', '', **atts)) + + def visit_field_body(self, node): + self.body.append(self.starttag(node, 'td', '', CLASS='field-body')) + parent_text = node.parent[0][0].astext() + if hasattr(node.parent, "type"): + self.body.append("(") + self.body.append(self.starttag(node, 'a', '', + href='psi_element://#typename#' + node.parent.type)) + self.body.append(node.parent.type) + self.body.append("") + self.body.append(") ") + elif parent_text.startswith("type "): + index = parent_text.index("type ") + type_string = parent_text[index + len("type ")] + self.body.append(self.starttag(node, 'a', '', + href='psi_element://#typename#' + type_string)) + elif parent_text.startswith("rtype"): + type_string = node.children[0][0].astext() + self.body.append(self.starttag(node, 'a', '', + href='psi_element://#typename#' + type_string)) + + self.set_class_on_child(node, 'first', 0) + field = node.parent + if (self.compact_field_list or + isinstance(field.parent, nodes.docinfo) or + field.parent.index(field) == len(field.parent) - 1): + # If we are in a compact list, the docinfo, or if this is + # the last field of the field list, do not add vertical + # space after last element. + self.set_class_on_child(node, 'last', -1) + + def depart_field_body(self, node): + if node.parent[0][0].astext().startswith("type "): + self.body.append("") + HTMLTranslator.depart_field_body(self, node) + + def visit_reference(self, node): + atts = {} + if 'refuri' in node: + atts['href'] = node['refuri'] + if self.settings.cloak_email_addresses and atts['href'].startswith('mailto:'): + atts['href'] = self.cloak_mailto(atts['href']) + self.in_mailto = True + # atts['class'] += ' external' + else: + assert 'refid' in node, 'References must have "refuri" or "refid" attribute.' + atts['href'] = '#' + node['refid'] + atts['class'] += ' internal' + if not isinstance(node.parent, nodes.TextElement): + assert len(node) == 1 and isinstance(node[0], nodes.image) + atts['class'] += ' image-reference' + self.body.append(self.starttag(node, 'a', '', **atts)) + + def starttag(self, node, tagname, suffix='\n', **attributes): + attr_dicts = [attributes] + if isinstance(node, nodes.Node): + attr_dicts.append(node.attributes) + if isinstance(node, dict): + attr_dicts.append(node) + # Munge each attribute dictionary. Unfortunately, we need to + # iterate through attributes one at a time because some + # versions of docutils don't case-normalize attributes. + for attr_dict in attr_dicts: + # For some reason additional classes in bullet list make it render poorly. + # Such lists are used to render multiple return values in Numpy docstrings by Napoleon. + if tagname == 'ul' and isinstance(node.parent, field_body): + attr_dict.pop('class', None) + attr_dict.pop('classes', None) + continue + + for (key, val) in attr_dict.items(): + # Prefix all CSS classes with "rst-"; and prefix all + # names with "rst-" to avoid conflicts. + if key.lower() in ('class', 'id', 'name'): + attr_dict[key] = 'rst-%s' % val + elif key.lower() in ('classes', 'ids', 'names'): + attr_dict[key] = ['rst-%s' % cls for cls in val] + elif key.lower() == 'href': + if attr_dict[key][:1] == '#': + attr_dict[key] = '#rst-%s' % attr_dict[key][1:] + + if tagname == 'th' and isinstance(node, field_name): + attributes['valign'] = 'top' + + # For headings, use class="heading" + if re.match(r'^h\d+$', tagname): + attributes['class'] = ' '.join([attributes.get('class', ''), 'heading']).strip() + return HTMLTranslator.starttag(self, node, tagname, suffix, **attributes) + + def visit_rubric(self, node): + self.body.append(self.starttag(node, 'h1', '', CLASS='rubric')) + + def depart_rubric(self, node): + self.body.append('\n') + + def visit_note(self, node): + self.body.append('

Note

\n') + + def depart_note(self, node): + pass + + def visit_seealso(self, node): + self.body.append('

See Also

\n') + + def depart_seealso(self, node): + pass + + def visit_field_list(self, node): + fields = {} + for n in node.children: + if not n.children: + continue + child = n.children[0] + rawsource = child.rawsource + if rawsource.startswith("param "): + index = rawsource.index("param ") + if not child.children: + continue + param_name = rawsource[index + len("param "):] + param_type = None + parts = param_name.rsplit(None, 1) + if len(parts) == 2: + param_type, param_name = parts + # Strip leading escaped asterisks for vararg parameters in Google code style docstrings + param_name = re.sub(r'\\\*', '*', param_name) + child.children[0] = Text(param_name) + fields[param_name] = n + if param_type: + n.type = param_type + if rawsource == "return": + fields["return"] = n + + for n in node.children: + if len(n.children) < 2: + continue + field_name, field_body = n.children[0], n.children[1] + rawsource = field_name.rawsource + if rawsource.startswith("type "): + index = rawsource.index("type ") + name = re.sub(r'\\\*', '*', rawsource[index + len("type "):]) + if name in fields: + fields[name].type = self._strip_markup(field_body.astext())[1] + node.children.remove(n) + if rawsource == "rtype": + if "return" in fields: + fields["return"].type = self._strip_markup(field_body.astext())[1] + node.children.remove(n) + + HTMLTranslator.visit_field_list(self, node) + + def unknown_visit(self, node): + """ Ignore unknown nodes """ + + def unknown_departure(self, node): + """ Ignore unknown nodes """ + + def visit_problematic(self, node): + # Don't insert hyperlinks to nowhere for e.g. unclosed asterisks + if not self._is_text_wrapper(node): + return HTMLTranslator.visit_problematic(self, node) + + directive, text = self._strip_markup(node.astext()) + if directive and directive[1:-1] in ('exc', 'class'): + self.body.append(self.starttag(node, 'a', '', href='psi_element://#typename#' + text)) + self.body.append(text) + self.body.append('') + else: + self.body.append(text) + raise nodes.SkipNode + + @staticmethod + def _strip_markup(text): + m = re.match(r'(:\w+)?(:\S+:)?`(.+?)`', text) + if m: + _, directive, trimmed = m.groups('') + return directive, trimmed + return None, text + + def depart_problematic(self, node): + if not self._is_text_wrapper(node): + return HTMLTranslator.depart_problematic(self, node) + + def visit_Text(self, node): + text = node.astext() + encoded = self.encode(text) + if not isinstance(node.parent, (nodes.literal, nodes.literal_block)): + encoded = encoded.replace('---', '—').replace('--', '–') + if self.in_mailto and self.settings.cloak_email_addresses: + encoded = self.cloak_email(encoded) + self.body.append(encoded) + + def _is_text_wrapper(self, node): + return len(node.children) == 1 and isinstance(node.children[0], Text) + + def visit_block_quote(self, node): + self.body.append(self.emptytag(node, "br")) + + def depart_block_quote(self, node): + pass + + def visit_literal(self, node): + """Process text to prevent tokens from wrapping.""" + self.body.append(self.starttag(node, 'tt', '', CLASS='docutils literal')) + text = node.astext() + for token in self.words_and_spaces.findall(text): + if token.strip(): + self.body.append('%s' + % self.encode(token)) + elif token in ('\n', ' '): + # Allow breaks at whitespace: + self.body.append(token) + else: + # Protect runs of multiple spaces; the last space can wrap: + self.body.append(' ' * (len(token) - 1) + ' ') + self.body.append('') + raise nodes.SkipNode + + class _DocumentPseudoWriter(Writer): + def __init__(self): + self.document = None + Writer.__init__(self) + + def translate(self): + self.output = '' + + writer = _DocumentPseudoWriter() + publish_string(docstring, writer=writer, settings_overrides={'report_level': 10000, + 'halt_level': 10000, + 'warning_stream': None, + 'docinfo_xform': False}) + document = writer.document + document.settings.xml_declaration = None + visitor = RestHTMLTranslator(document) + document.walkabout(visitor) + return u('').join(visitor.body) + + +def format_google(docstring): + from sphinxcontrib.napoleon import GoogleDocstring + transformed = text_type(GoogleDocstring(textwrap.dedent(docstring))) + return format_rest(transformed) + + +def format_numpy(docstring): + from sphinxcontrib.napoleon import NumpyDocstring + transformed = text_type(NumpyDocstring(textwrap.dedent(docstring))) + return format_rest(transformed) + + +def format_epytext(docstring): + if six.PY3: + return u('Epydoc is not compatible with Python 3 interpreter') + + import epydoc.markup.epytext + from epydoc.markup import DocstringLinker + from epydoc.markup.epytext import parse_docstring, ParseError, _colorize + + def _add_para(doc, para_token, stack, indent_stack, errors): + """Colorize the given paragraph, and add it to the DOM tree.""" + para = _colorize(doc, para_token, errors) + if para_token.inline: + para.attribs['inline'] = True + stack[-1].children.append(para) + + epydoc.markup.epytext._add_para = _add_para + ParseError.is_fatal = lambda self: False + + errors = [] + + class EmptyLinker(DocstringLinker): + def translate_indexterm(self, indexterm): + return "" + + def translate_identifier_xref(self, identifier, label=None): + return identifier + + docstring = parse_docstring(docstring, errors) + docstring, fields = docstring.split_fields() + html = docstring.to_html(EmptyLinker()) + + if errors and not html: + # It's not possible to recover original stacktraces of the errors + error_lines = '\n'.join(text_type(e) for e in errors) + raise Exception('Error parsing docstring. Probable causes:\n' + error_lines) + + return html + + +def main(): + args = sys.argv[1:] + + docstring_format = args[0] if args else 'rest' + if len(args) > 1: + try: + f = open(args[1], 'rb') + text = f.read().decode('utf-8') + finally: + f.close() + else: + text = read_safe() + + formatter = { + 'rest': format_rest, + 'google': format_google, + 'numpy': format_numpy, + 'epytext': format_epytext + }.get(docstring_format, format_rest) + + html = formatter(text) + print_safe(html) + + +if __name__ == '__main__': + main() diff --git a/python/helpers/epydoc_formatter.py b/python/helpers/epydoc_formatter.py deleted file mode 100644 index 3d30e1ee3ba4..000000000000 --- a/python/helpers/epydoc_formatter.py +++ /dev/null @@ -1,46 +0,0 @@ -import sys -from epydoc.markup import DocstringLinker -from epydoc.markup.epytext import parse_docstring, ParseError, _colorize -import epydoc.markup.epytext - -def _add_para(doc, para_token, stack, indent_stack, errors): - """Colorize the given paragraph, and add it to the DOM tree.""" - para = _colorize(doc, para_token, errors) - if para_token.inline: - para.attribs['inline'] = True - stack[-1].children.append(para) - -epydoc.markup.epytext._add_para = _add_para - -def is_fatal(): - return False - -ParseError.is_fatal = is_fatal - -try: - src = sys.stdin.read() - errors = [] - - class EmptyLinker(DocstringLinker): - def translate_indexterm(self, indexterm): - return "" - - def translate_identifier_xref(self, identifier, label=None): - return identifier - - docstring = parse_docstring(src, errors) - docstring, fields = docstring.split_fields() - html = docstring.to_html(EmptyLinker()) - - if errors and not html: - sys.stderr.write("Error parsing docstring:\n") - for error in errors: - sys.stderr.write(str(error) + "\n") - sys.exit(1) - - sys.stdout.write(html) - sys.stdout.flush() -except: - exc_type, exc_value, exc_traceback = sys.exc_info() - sys.stderr.write("Error calculating docstring: " + str(exc_value)) - sys.exit(1) diff --git a/python/helpers/google_formatter.py b/python/helpers/google_formatter.py deleted file mode 100644 index 296e8a2a0f53..000000000000 --- a/python/helpers/google_formatter.py +++ /dev/null @@ -1,14 +0,0 @@ -import sys -import textwrap - -import rest_formatter -from sphinxcontrib.napoleon.docstring import GoogleDocstring - - -def main(text=None): - src = sys.stdin.read() if text is None else text - rest_formatter.main(str(GoogleDocstring(textwrap.dedent(src)))) - - -if __name__ == '__main__': - main() diff --git a/python/helpers/numpy_formatter.py b/python/helpers/numpy_formatter.py deleted file mode 100644 index 9a02e31a33bb..000000000000 --- a/python/helpers/numpy_formatter.py +++ /dev/null @@ -1,14 +0,0 @@ -import sys -import textwrap - -import rest_formatter -from sphinxcontrib.napoleon.docstring import NumpyDocstring - - -def main(text=None): - src = sys.stdin.read() if text is None else text - rest_formatter.main(str(NumpyDocstring(textwrap.dedent(src)))) - - -if __name__ == '__main__': - main() diff --git a/python/helpers/rest_formatter.py b/python/helpers/rest_formatter.py deleted file mode 100644 index 68826f4cbeae..000000000000 --- a/python/helpers/rest_formatter.py +++ /dev/null @@ -1,326 +0,0 @@ -import os -import re -import sys - -from docutils import nodes -from docutils.core import publish_string -from docutils.frontend import OptionParser -from docutils.nodes import Text, field_body, field_name, rubric -from docutils.parsers.rst import directives -from docutils.parsers.rst.directives.admonitions import BaseAdmonition -from docutils.writers.html4css1 import HTMLTranslator, Writer as HTMLWriter -from docutils.writers import Writer - -ENCODING = 'utf-8' -_stdin = os.fdopen(sys.stdin.fileno(), 'rb') -_stdout = os.fdopen(sys.stdout.fileno(), 'wb') - - -def read_safe(): - return _stdin.read().decode(ENCODING) - - -def print_safe(s): - _stdout.write(s.encode(ENCODING)) - _stdout.flush() - - -# Copied from the Sphinx' sources. Docutils doesn't handle "seealso" directives by default. -class seealso(nodes.Admonition, nodes.Element): - """Custom "see also" admonition.""" - - -class SeeAlso(BaseAdmonition): - """ - An admonition mentioning things to look at as reference. - """ - node_class = seealso - - -directives.register_directive('seealso', SeeAlso) - - -class RestHTMLTranslator(HTMLTranslator): - settings = None - - def __init__(self, document): - # Copied from epydoc.markup.restructuredtext._EpydocHTMLTranslator - if self.settings is None: - settings = OptionParser([HTMLWriter()]).get_default_values() - self.__class__.settings = settings - document.settings = self.settings - - HTMLTranslator.__init__(self, document) - - def visit_document(self, node): - pass - - def depart_document(self, node): - pass - - def visit_docinfo(self, node): - pass - - def depart_docinfo(self, node): - pass - - def unimplemented_visit(self, node): - pass - - def visit_field_name(self, node): - atts = {} - if self.in_docinfo: - atts['class'] = 'docinfo-name' - else: - atts['class'] = 'field-name' - - self.context.append('') - atts['align'] = "right" - self.body.append(self.starttag(node, 'th', '', **atts)) - - def visit_field_body(self, node): - self.body.append(self.starttag(node, 'td', '', CLASS='field-body')) - parent_text = node.parent[0][0].astext() - if hasattr(node.parent, "type"): - self.body.append("(") - self.body.append(self.starttag(node, 'a', '', - href='psi_element://#typename#' + node.parent.type)) - self.body.append(node.parent.type) - self.body.append("") - self.body.append(") ") - elif parent_text.startswith("type "): - index = parent_text.index("type ") - type_string = parent_text[index + len("type ")] - self.body.append(self.starttag(node, 'a', '', - href='psi_element://#typename#' + type_string)) - elif parent_text.startswith("rtype"): - type_string = node.children[0][0].astext() - self.body.append(self.starttag(node, 'a', '', - href='psi_element://#typename#' + type_string)) - - self.set_class_on_child(node, 'first', 0) - field = node.parent - if (self.compact_field_list or - isinstance(field.parent, nodes.docinfo) or - field.parent.index(field) == len(field.parent) - 1): - # If we are in a compact list, the docinfo, or if this is - # the last field of the field list, do not add vertical - # space after last element. - self.set_class_on_child(node, 'last', -1) - - def depart_field_body(self, node): - if node.parent[0][0].astext().startswith("type "): - self.body.append("") - HTMLTranslator.depart_field_body(self, node) - - def visit_reference(self, node): - atts = {} - if 'refuri' in node: - atts['href'] = node['refuri'] - if self.settings.cloak_email_addresses and atts['href'].startswith('mailto:'): - atts['href'] = self.cloak_mailto(atts['href']) - self.in_mailto = True - # atts['class'] += ' external' - else: - assert 'refid' in node, 'References must have "refuri" or "refid" attribute.' - atts['href'] = '#' + node['refid'] - atts['class'] += ' internal' - if not isinstance(node.parent, nodes.TextElement): - assert len(node) == 1 and isinstance(node[0], nodes.image) - atts['class'] += ' image-reference' - self.body.append(self.starttag(node, 'a', '', **atts)) - - def starttag(self, node, tagname, suffix='\n', **attributes): - attr_dicts = [attributes] - if isinstance(node, nodes.Node): - attr_dicts.append(node.attributes) - if isinstance(node, dict): - attr_dicts.append(node) - # Munge each attribute dictionary. Unfortunately, we need to - # iterate through attributes one at a time because some - # versions of docutils don't case-normalize attributes. - for attr_dict in attr_dicts: - # For some reason additional classes in bullet list make it render poorly. - # Such lists are used to render multiple return values in Numpy docstrings by Napoleon. - if tagname == 'ul' and isinstance(node.parent, field_body): - attr_dict.pop('class', None) - attr_dict.pop('classes', None) - continue - - for (key, val) in attr_dict.items(): - # Prefix all CSS classes with "rst-"; and prefix all - # names with "rst-" to avoid conflicts. - if key.lower() in ('class', 'id', 'name'): - attr_dict[key] = 'rst-%s' % val - elif key.lower() in ('classes', 'ids', 'names'): - attr_dict[key] = ['rst-%s' % cls for cls in val] - elif key.lower() == 'href': - if attr_dict[key][:1] == '#': - attr_dict[key] = '#rst-%s' % attr_dict[key][1:] - - if tagname == 'th' and isinstance(node, field_name): - attributes['valign'] = 'top' - - # For headings, use class="heading" - if re.match(r'^h\d+$', tagname): - attributes['class'] = ' '.join([attributes.get('class', ''), 'heading']).strip() - return HTMLTranslator.starttag(self, node, tagname, suffix, **attributes) - - def visit_rubric(self, node): - self.body.append(self.starttag(node, 'h1', '', CLASS='rubric')) - - def depart_rubric(self, node): - self.body.append('\n') - - def visit_note(self, node): - self.body.append('

Note

\n') - - def depart_note(self, node): - pass - - def visit_seealso(self, node): - self.body.append('

See Also

\n') - - def depart_seealso(self, node): - pass - - def visit_field_list(self, node): - fields = {} - for n in node.children: - if not n.children: - continue - child = n.children[0] - rawsource = child.rawsource - if rawsource.startswith("param "): - index = rawsource.index("param ") - if not child.children: - continue - param_name = rawsource[index + len("param "):] - param_type = None - parts = param_name.rsplit(None, 1) - if len(parts) == 2: - param_type, param_name = parts - # Strip leading escaped asterisks for vararg parameters in Google code style docstrings - param_name = re.sub(r'\\\*', '*', param_name) - child.children[0] = Text(param_name) - fields[param_name] = n - if param_type: - n.type = param_type - if rawsource == "return": - fields["return"] = n - - for n in node.children: - if len(n.children) < 2: - continue - field_name, field_body = n.children[0], n.children[1] - rawsource = field_name.rawsource - if rawsource.startswith("type "): - index = rawsource.index("type ") - name = re.sub(r'\\\*', '*', rawsource[index + len("type "):]) - if name in fields: - fields[name].type = self._strip_markup(field_body.astext())[1] - node.children.remove(n) - if rawsource == "rtype": - if "return" in fields: - fields["return"].type = self._strip_markup(field_body.astext())[1] - node.children.remove(n) - - HTMLTranslator.visit_field_list(self, node) - - def unknown_visit(self, node): - """ Ignore unknown nodes """ - - def unknown_departure(self, node): - """ Ignore unknown nodes """ - - def visit_problematic(self, node): - # Don't insert hyperlinks to nowhere for e.g. unclosed asterisks - if not self._is_text_wrapper(node): - return HTMLTranslator.visit_problematic(self, node) - - directive, text = self._strip_markup(node.astext()) - if directive and directive[1:-1] in ('exc', 'class'): - self.body.append(self.starttag(node, 'a', '', href='psi_element://#typename#' + text)) - self.body.append(text) - self.body.append('') - else: - self.body.append(text) - raise nodes.SkipNode - - @staticmethod - def _strip_markup(text): - m = re.match(r'(:\w+)?(:\S+:)?`(.+?)`', text) - if m: - _, directive, trimmed = m.groups('') - return directive, trimmed - return None, text - - def depart_problematic(self, node): - if not self._is_text_wrapper(node): - return HTMLTranslator.depart_problematic(self, node) - - def visit_Text(self, node): - text = node.astext() - encoded = self.encode(text) - if not isinstance(node.parent, (nodes.literal, nodes.literal_block)): - encoded = encoded.replace('---', '—').replace('--', '–') - if self.in_mailto and self.settings.cloak_email_addresses: - encoded = self.cloak_email(encoded) - self.body.append(encoded) - - def _is_text_wrapper(self, node): - return len(node.children) == 1 and isinstance(node.children[0], Text) - - def visit_block_quote(self, node): - self.body.append(self.emptytag(node, "br")) - - def depart_block_quote(self, node): - pass - - def visit_literal(self, node): - """Process text to prevent tokens from wrapping.""" - self.body.append(self.starttag(node, 'tt', '', CLASS='docutils literal')) - text = node.astext() - for token in self.words_and_spaces.findall(text): - if token.strip(): - self.body.append('%s' - % self.encode(token)) - elif token in ('\n', ' '): - # Allow breaks at whitespace: - self.body.append(token) - else: - # Protect runs of multiple spaces; the last space can wrap: - self.body.append(' ' * (len(token) - 1) + ' ') - self.body.append('') - raise nodes.SkipNode - - -def format_docstring(docstring): - class _DocumentPseudoWriter(Writer): - def __init__(self): - self.document = None - Writer.__init__(self) - - def translate(self): - self.output = '' - - writer = _DocumentPseudoWriter() - publish_string(docstring, writer=writer, settings_overrides={'report_level': 10000, - 'halt_level': 10000, - 'warning_stream': None, - 'docinfo_xform': False}) - document = writer.document - document.settings.xml_declaration = None - visitor = RestHTMLTranslator(document) - document.walkabout(visitor) - return ''.join(visitor.body) - - -def main(text=None): - src = read_safe() if text is None else text - html = format_docstring(src) - print_safe(html) - - -if __name__ == '__main__': - main() diff --git a/python/ipnb/resources/META-INF/ipython-notebook.xml b/python/ipnb/resources/META-INF/ipython-notebook.xml index 7a2fb65cc1ca..324ef88f4e76 100644 --- a/python/ipnb/resources/META-INF/ipython-notebook.xml +++ b/python/ipnb/resources/META-INF/ipython-notebook.xml @@ -18,6 +18,7 @@ + + diff --git a/python/src/com/jetbrains/python/PythonHelper.java b/python/src/com/jetbrains/python/PythonHelper.java index 91867b915b1a..383057e7498d 100644 --- a/python/src/com/jetbrains/python/PythonHelper.java +++ b/python/src/com/jetbrains/python/PythonHelper.java @@ -68,10 +68,7 @@ public enum PythonHelper implements HelperPackage { BUILDOUT_ENGULFER("pycharm", "buildout_engulfer"), - EPYDOC_FORMATTER("epydoc_formatter.py"), - REST_FORMATTER("rest_formatter.py"), - GOOGLE_FORMATTER("google_formatter.py"), - NUMPY_FORMATTER("numpy_formatter.py"), + DOCSTRING_FORMATTER("docstring_formatter.py"), EXTRA_SYSPATH("extra_syspath.py"), SYSPATH("syspath.py"), diff --git a/python/src/com/jetbrains/python/documentation/docstrings/DocStringFormat.java b/python/src/com/jetbrains/python/documentation/docstrings/DocStringFormat.java index caa5ec15a0a1..43f32c57f3ab 100644 --- a/python/src/com/jetbrains/python/documentation/docstrings/DocStringFormat.java +++ b/python/src/com/jetbrains/python/documentation/docstrings/DocStringFormat.java @@ -16,7 +16,6 @@ package com.jetbrains.python.documentation.docstrings; import com.intellij.psi.PsiElement; -import com.intellij.util.Function; import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -32,11 +31,11 @@ public enum DocStringFormat { /** * @see DocStringUtil#ensureNotPlainDocstringFormat(PsiElement) */ - PLAIN("Plain"), - EPYTEXT("Epytext"), - REST("reStructuredText"), - NUMPY("NumPy"), - GOOGLE("Google"); + PLAIN("Plain", ""), + EPYTEXT("Epytext", "epytext"), + REST("reStructuredText", "rest"), + NUMPY("NumPy", "numpy"), + GOOGLE("Google", "google"); public static final List ALL_NAMES = getAllNames(); @@ -67,10 +66,12 @@ public enum DocStringFormat { return ObjectUtils.notNull(fromName(name), PLAIN); } - String myName; + private final String myName; + private final String myFormatterCommand; - DocStringFormat(@NotNull String name) { + DocStringFormat(@NotNull String name, @NotNull String formatterCommand) { myName = name; + myFormatterCommand = formatterCommand; } @NotNull @@ -78,4 +79,8 @@ public enum DocStringFormat { return myName; } + @NotNull + public String getFormatterCommand() { + return myFormatterCommand; + } } diff --git a/python/src/com/jetbrains/python/documentation/docstrings/PyStructuredDocstringFormatter.java b/python/src/com/jetbrains/python/documentation/docstrings/PyStructuredDocstringFormatter.java index a24d45d9ee21..95bc1d2ed2d8 100644 --- a/python/src/com/jetbrains/python/documentation/docstrings/PyStructuredDocstringFormatter.java +++ b/python/src/com/jetbrains/python/documentation/docstrings/PyStructuredDocstringFormatter.java @@ -25,7 +25,6 @@ import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.psi.PsiElement; -import com.jetbrains.python.HelperPackage; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PythonHelper; import com.jetbrains.python.psi.PyIndentUtil; @@ -70,37 +69,24 @@ public class PyStructuredDocstringFormatter { final String preparedDocstring = PyIndentUtil.removeCommonIndent(docstring, true).trim(); - final HelperPackage formatter; - final StructuredDocString structuredDocString; final DocStringFormat format = DocStringUtil.guessDocStringFormat(preparedDocstring, element); - if (format == DocStringFormat.GOOGLE) { - formatter = PythonHelper.GOOGLE_FORMATTER; - structuredDocString = DocStringUtil.parseDocStringContent(DocStringFormat.GOOGLE, preparedDocstring); - } - else if (format == DocStringFormat.NUMPY) { - formatter = PythonHelper.NUMPY_FORMATTER; - structuredDocString = DocStringUtil.parseDocStringContent(DocStringFormat.NUMPY, preparedDocstring); - } - else if (format == DocStringFormat.EPYTEXT) { - formatter = PythonHelper.EPYDOC_FORMATTER; - structuredDocString = DocStringUtil.parseDocStringContent(DocStringFormat.EPYTEXT, preparedDocstring); - result.add(formatStructuredDocString(structuredDocString)); - } - else if (format == DocStringFormat.REST) { - formatter = PythonHelper.REST_FORMATTER; - structuredDocString = DocStringUtil.parseDocStringContent(DocStringFormat.REST, preparedDocstring); - } - - else { + if (format == DocStringFormat.PLAIN) { return null; } - final String output = runExternalTool(module, formatter, preparedDocstring); + final StructuredDocString structuredDocString = DocStringUtil.parseDocStringContent(format, preparedDocstring); + + final String output = runExternalTool(module, format, preparedDocstring); if (output != null) { - result.add(0, output); + result.add(output); } else { - result.add(0, structuredDocString.getDescription()); + result.add(structuredDocString.getDescription()); + } + + // Information about parameters in Epytext-style docstrings are formatter on our side + if (format == DocStringFormat.EPYTEXT) { + result.add(formatStructuredDocString(structuredDocString)); } return result; @@ -108,11 +94,11 @@ public class PyStructuredDocstringFormatter { @Nullable private static String runExternalTool(@NotNull final Module module, - @NotNull final HelperPackage formatter, + @NotNull final DocStringFormat format, @NotNull final String docstring) { final Sdk sdk; final String missingInterpreterMessage; - if (formatter == PythonHelper.EPYDOC_FORMATTER) { + if (format == DocStringFormat.EPYTEXT) { sdk = PythonSdkType.findPython2Sdk(module); missingInterpreterMessage = PyBundle.message("QDOC.epydoc.python2.sdk.not.found"); } @@ -121,7 +107,7 @@ public class PyStructuredDocstringFormatter { missingInterpreterMessage = PyBundle.message("QDOC.sdk.not.found"); } if (sdk == null) { - LOG.warn("Python SDK for docstring formatter " + formatter + " is not found"); + LOG.warn("Python SDK for docstring formatter " + format + " is not found"); return "

" + missingInterpreterMessage + "

"; } @@ -132,9 +118,10 @@ public class PyStructuredDocstringFormatter { final byte[] data = new byte[encoded.limit()]; encoded.get(data); - final GeneralCommandLine commandLine = formatter.newCommandLine(sdk, Lists.newArrayList()); + final ArrayList arguments = Lists.newArrayList(format.getFormatterCommand()); + final GeneralCommandLine commandLine = PythonHelper.DOCSTRING_FORMATTER.newCommandLine(sdk, arguments); commandLine.setCharset(DEFAULT_CHARSET); - + LOG.debug("Command for launching docstring formatter: " + commandLine.getCommandLineString()); final ProcessOutput output = PySdkUtil.getProcessOutput(commandLine, new File(sdkHome).getParent(), null, 5000, data, false); diff --git a/python/src/com/jetbrains/python/packaging/PyPIPackageUtil.java b/python/src/com/jetbrains/python/packaging/PyPIPackageUtil.java index 196219ecce02..20e066313647 100644 --- a/python/src/com/jetbrains/python/packaging/PyPIPackageUtil.java +++ b/python/src/com/jetbrains/python/packaging/PyPIPackageUtil.java @@ -86,7 +86,15 @@ public class PyPIPackageUtil { LOG.error("Cannot find \"packages\". " + e.getMessage()); } } + + /** + * Prevents simultaneous updates of {@link PyPackageService#PY_PACKAGES} + * because the corresponding response contains tons of data and multiple + * queries at the same time can cause memory issues. + */ + private final Object myPyPIPackageCacheUpdateLock = new Object(); + private PyPIPackageUtil() { try { final DefaultXmlRpcTransportFactory factory = new PyPIXmlRpcTransportFactory(new URL(PYPI_URL)); @@ -141,7 +149,7 @@ public class PyPIPackageUtil { for (String pyPackage : packagesList) { if (simpleIndex) { - final Pair nameVersion = splitNameVersion(pyPackage); + final Pair nameVersion = splitNameVersion(StringUtil.trimTrailing(pyPackage, '/')); ourAdditionalPackageNames.add(new RepoPackage(nameVersion.getFirst(), url, nameVersion.getSecond())); } else { @@ -414,9 +422,11 @@ public class PyPIPackageUtil { @NotNull public Map loadAndGetPackages() throws IOException { Map pyPIPackages = getPyPIPackages(); - if (pyPIPackages.isEmpty()) { - updatePyPICache(PyPackageService.getInstance()); - pyPIPackages = getPyPIPackages(); + synchronized (myPyPIPackageCacheUpdateLock) { + if (pyPIPackages.isEmpty()) { + updatePyPICache(PyPackageService.getInstance()); + pyPIPackages = getPyPIPackages(); + } } return pyPIPackages; } diff --git a/python/src/com/jetbrains/python/validation/Pep8ExternalAnnotator.java b/python/src/com/jetbrains/python/validation/Pep8ExternalAnnotator.java index 529d0121599b..af08f42538bd 100644 --- a/python/src/com/jetbrains/python/validation/Pep8ExternalAnnotator.java +++ b/python/src/com/jetbrains/python/validation/Pep8ExternalAnnotator.java @@ -68,6 +68,7 @@ import org.jetbrains.annotations.Nullable; import java.io.File; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.regex.Matcher; @@ -88,12 +89,30 @@ public class Pep8ExternalAnnotator extends ExternalAnnotator p.isProblemSuppressed(problem, file, element)); + } + private static boolean crossesLineBoundary(@Nullable Document document, String text, TextRange problemRange) { int start = problemRange.getStartOffset(); int end = problemRange.getEndOffset(); diff --git a/python/src/com/jetbrains/python/validation/Pep8ProblemSuppressor.java b/python/src/com/jetbrains/python/validation/Pep8ProblemSuppressor.java new file mode 100644 index 000000000000..d18139121f03 --- /dev/null +++ b/python/src/com/jetbrains/python/validation/Pep8ProblemSuppressor.java @@ -0,0 +1,45 @@ +/* + * 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 com.jetbrains.python.validation; + +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Custom filter that allows to selectively suppress warnings and errors produced by pycodestyle.py (former pep8.py). + * Note that by using {@link com.jetbrains.python.inspections.PythonVisitorFilter} you can disable PEP 8 inspection for concrete files + * altogether. + * + * @author Mikhail Golubev + * @see Pep8ExternalAnnotator + * @see com.jetbrains.python.inspections.PythonVisitorFilter + */ +public interface Pep8ProblemSuppressor { + ExtensionPointName EP_NAME = ExtensionPointName.create("Pythonid.pep8ProblemSuppressor"); + + /** + * @param problem a single problem returned by the script and extracted from its output + * @param file PSI file where the inspection operates + * @param targetElement PSI element found in the place of a problem on which annotation is going be attached in the editor + * @return whether notification about this problem should be hidden in the editor + */ + boolean isProblemSuppressed(@NotNull Pep8ExternalAnnotator.Problem problem, + @NotNull PsiFile file, + @Nullable PsiElement targetElement); +} diff --git a/spellchecker/src/com/intellij/spellchecker/xml/XmlSpellcheckingStrategy.java b/spellchecker/src/com/intellij/spellchecker/xml/XmlSpellcheckingStrategy.java index 47478266656d..96ca42e13c64 100644 --- a/spellchecker/src/com/intellij/spellchecker/xml/XmlSpellcheckingStrategy.java +++ b/spellchecker/src/com/intellij/spellchecker/xml/XmlSpellcheckingStrategy.java @@ -2,7 +2,12 @@ package com.intellij.spellchecker.xml; import com.intellij.codeInspection.SuppressQuickFix; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.templateLanguages.TemplateLanguage; +import com.intellij.psi.xml.XmlToken; +import com.intellij.psi.xml.XmlTokenType; import com.intellij.spellchecker.tokenizer.SuppressibleSpellcheckingStrategy; +import com.intellij.spellchecker.tokenizer.Tokenizer; import com.intellij.util.xml.DomElement; import com.intellij.util.xml.DomUtil; import org.jetbrains.annotations.NotNull; @@ -12,6 +17,17 @@ import org.jetbrains.annotations.NotNull; */ public class XmlSpellcheckingStrategy extends SuppressibleSpellcheckingStrategy { + @NotNull + @Override + public Tokenizer getTokenizer(PsiElement element) { + if (element instanceof XmlToken && ((XmlToken)element).getTokenType() == XmlTokenType.XML_DATA_CHARACTERS) { + PsiFile file = element.getContainingFile(); + if (file == null || file.getLanguage() instanceof TemplateLanguage) + return EMPTY_TOKENIZER; + } + return super.getTokenizer(element); + } + @Override public boolean isSuppressedFor(@NotNull PsiElement element, @NotNull String name) { DomElement domElement = DomUtil.getDomElement(element);