diff --git a/java/java-tests/testData/properties/enterhandler/afterCaretAtMultipleWhitespaces.properties b/java/java-tests/testData/properties/enterhandler/afterCaretAtAfterMultipleWhitespaces.properties similarity index 57% rename from java/java-tests/testData/properties/enterhandler/afterCaretAtMultipleWhitespaces.properties rename to java/java-tests/testData/properties/enterhandler/afterCaretAtAfterMultipleWhitespaces.properties index ba36aa2b395d..c865d7c6a963 100644 --- a/java/java-tests/testData/properties/enterhandler/afterCaretAtMultipleWhitespaces.properties +++ b/java/java-tests/testData/properties/enterhandler/afterCaretAtAfterMultipleWhitespaces.properties @@ -1,3 +1,3 @@ # "_ignore" "true" -message=hello \ +message=hello \ world diff --git a/java/java-tests/testData/properties/enterhandler/afterCaretAtBeforeMultipleWhitespaces.properties b/java/java-tests/testData/properties/enterhandler/afterCaretAtBeforeMultipleWhitespaces.properties new file mode 100644 index 000000000000..71742abe7478 --- /dev/null +++ b/java/java-tests/testData/properties/enterhandler/afterCaretAtBeforeMultipleWhitespaces.properties @@ -0,0 +1,3 @@ +# "_ignore" "true" +message=hello\ + \ world diff --git a/java/java-tests/testData/properties/enterhandler/afterCaretAtMiddleMultipleWhitespaces.properties b/java/java-tests/testData/properties/enterhandler/afterCaretAtMiddleMultipleWhitespaces.properties new file mode 100644 index 000000000000..78abf375c77b --- /dev/null +++ b/java/java-tests/testData/properties/enterhandler/afterCaretAtMiddleMultipleWhitespaces.properties @@ -0,0 +1,3 @@ +# "_ignore" "true" +message=hello \ + \ world diff --git a/java/java-tests/testData/properties/enterhandler/beforeCaretAtAfterMultipleWhitespaces.properties b/java/java-tests/testData/properties/enterhandler/beforeCaretAtAfterMultipleWhitespaces.properties new file mode 100644 index 000000000000..26295f5cb7fc --- /dev/null +++ b/java/java-tests/testData/properties/enterhandler/beforeCaretAtAfterMultipleWhitespaces.properties @@ -0,0 +1,2 @@ +# "_ignore" "true" +message=hello world diff --git a/java/java-tests/testData/properties/enterhandler/beforeCaretAtBeforeMultipleWhitespaces.properties b/java/java-tests/testData/properties/enterhandler/beforeCaretAtBeforeMultipleWhitespaces.properties new file mode 100644 index 000000000000..405117efd4e1 --- /dev/null +++ b/java/java-tests/testData/properties/enterhandler/beforeCaretAtBeforeMultipleWhitespaces.properties @@ -0,0 +1,2 @@ +# "_ignore" "true" +message=hello world diff --git a/java/java-tests/testData/properties/enterhandler/beforeCaretAtMiddleMultipleWhitespaces.properties b/java/java-tests/testData/properties/enterhandler/beforeCaretAtMiddleMultipleWhitespaces.properties new file mode 100644 index 000000000000..073c57d6ca8f --- /dev/null +++ b/java/java-tests/testData/properties/enterhandler/beforeCaretAtMiddleMultipleWhitespaces.properties @@ -0,0 +1,2 @@ +# "_ignore" "true" +message=hello world diff --git a/java/java-tests/testData/properties/enterhandler/beforeCaretAtMultipleWhitespaces.properties b/java/java-tests/testData/properties/enterhandler/beforeCaretAtMultipleWhitespaces.properties deleted file mode 100644 index fc9b2800cc53..000000000000 --- a/java/java-tests/testData/properties/enterhandler/beforeCaretAtMultipleWhitespaces.properties +++ /dev/null @@ -1,2 +0,0 @@ -# "_ignore" "true" -message=hello world diff --git a/java/java-tests/testData/properties/joinlines/afterJoinWithEmptyLineWIthUnclosedBackslash.properties b/java/java-tests/testData/properties/joinlines/afterJoinWithEmptyLineWIthUnclosedBackslash.properties new file mode 100644 index 000000000000..b9c72db1d728 --- /dev/null +++ b/java/java-tests/testData/properties/joinlines/afterJoinWithEmptyLineWIthUnclosedBackslash.properties @@ -0,0 +1,2 @@ +# "_ignore" "true" +message=hello, world \ No newline at end of file diff --git a/java/java-tests/testData/properties/joinlines/afterJoinWithLeadingWhitespaceLine.properties b/java/java-tests/testData/properties/joinlines/afterJoinWithLeadingWhitespaceLine.properties new file mode 100644 index 000000000000..d7d58b98b482 --- /dev/null +++ b/java/java-tests/testData/properties/joinlines/afterJoinWithLeadingWhitespaceLine.properties @@ -0,0 +1,2 @@ +# "_ignore" "true" +message=hello world diff --git a/java/java-tests/testData/properties/joinlines/afterJoinWithMultipleLeadingWhitespacesLine.properties b/java/java-tests/testData/properties/joinlines/afterJoinWithMultipleLeadingWhitespacesLine.properties new file mode 100644 index 000000000000..d7d58b98b482 --- /dev/null +++ b/java/java-tests/testData/properties/joinlines/afterJoinWithMultipleLeadingWhitespacesLine.properties @@ -0,0 +1,2 @@ +# "_ignore" "true" +message=hello world diff --git a/java/java-tests/testData/properties/joinlines/afterJoiningWithEmptyLine.properties b/java/java-tests/testData/properties/joinlines/afterJoiningWithEmptyLine.properties index 352d5085c594..209c463aad8e 100644 --- a/java/java-tests/testData/properties/joinlines/afterJoiningWithEmptyLine.properties +++ b/java/java-tests/testData/properties/joinlines/afterJoiningWithEmptyLine.properties @@ -1,5 +1,3 @@ # "_ignore" "true" -# hello -# world message=hello \ world diff --git a/java/java-tests/testData/properties/joinlines/beforeJoinWithEmptyLineWIthUnclosedBackslash.properties b/java/java-tests/testData/properties/joinlines/beforeJoinWithEmptyLineWIthUnclosedBackslash.properties new file mode 100644 index 000000000000..49d4655668d9 --- /dev/null +++ b/java/java-tests/testData/properties/joinlines/beforeJoinWithEmptyLineWIthUnclosedBackslash.properties @@ -0,0 +1,2 @@ +# "_ignore" "true" +message=hello, world\ diff --git a/java/java-tests/testData/properties/joinlines/beforeJoinWithLeadingWhitespaceLine.properties b/java/java-tests/testData/properties/joinlines/beforeJoinWithLeadingWhitespaceLine.properties new file mode 100644 index 000000000000..f801e8cd1bbd --- /dev/null +++ b/java/java-tests/testData/properties/joinlines/beforeJoinWithLeadingWhitespaceLine.properties @@ -0,0 +1,3 @@ +# "_ignore" "true" +message=hello \ + \ world diff --git a/java/java-tests/testData/properties/joinlines/beforeJoinWithMultipleLeadingWhitespacesLine.properties b/java/java-tests/testData/properties/joinlines/beforeJoinWithMultipleLeadingWhitespacesLine.properties new file mode 100644 index 000000000000..91e75464d40f --- /dev/null +++ b/java/java-tests/testData/properties/joinlines/beforeJoinWithMultipleLeadingWhitespacesLine.properties @@ -0,0 +1,3 @@ +# "_ignore" "true" +message=hello\ + \ world diff --git a/java/java-tests/testData/properties/joinlines/beforeJoiningWithEmptyLine.properties b/java/java-tests/testData/properties/joinlines/beforeJoiningWithEmptyLine.properties index 95f2df45755f..31bf21656085 100644 --- a/java/java-tests/testData/properties/joinlines/beforeJoiningWithEmptyLine.properties +++ b/java/java-tests/testData/properties/joinlines/beforeJoiningWithEmptyLine.properties @@ -1,6 +1,4 @@ # "_ignore" "true" -# hello -# world message=hello \ \ world diff --git a/plugins/java-i18n/testData/highlighting/InvalidEscape.properties b/plugins/java-i18n/testData/highlighting/InvalidEscape.properties index 180cbcba6ae7..214198a201ff 100644 --- a/plugins/java-i18n/testData/highlighting/InvalidEscape.properties +++ b/plugins/java-i18n/testData/highlighting/InvalidEscape.properties @@ -1,5 +1,7 @@ -\ a\ b\ =x\x +\ a\ b\ =x\x a\n\r\b2 = x\\\n\r v -spaceinthemiddle=a\ b -ii\dnnvalidkey -framingSpaces = \ xxx\ \ \ \ No newline at end of file +spaceinthemiddle=a\ b +ii\dnnvalidkey +framingSpaces = \ xxx\ \ \ +hello=world,\ + \ universe \ No newline at end of file diff --git a/plugins/properties/properties-psi-api/src/com/intellij/lang/properties/PropertiesHighlighter.java b/plugins/properties/properties-psi-api/src/com/intellij/lang/properties/PropertiesHighlighter.java index ad206c72454b..d297380f9117 100644 --- a/plugins/properties/properties-psi-api/src/com/intellij/lang/properties/PropertiesHighlighter.java +++ b/plugins/properties/properties-psi-api/src/com/intellij/lang/properties/PropertiesHighlighter.java @@ -19,7 +19,6 @@ import java.util.Map; public class PropertiesHighlighter extends SyntaxHighlighterBase { private static final Map keys1; - private static final Map keys2; @Override @NotNull @@ -46,10 +45,7 @@ public class PropertiesHighlighter extends SyntaxHighlighterBase { "PROPERTIES.KEY_VALUE_SEPARATOR", DefaultLanguageHighlighterColors.OPERATION_SIGN ); - public static final TextAttributesKey PROPERTIES_VALID_STRING_ESCAPE = TextAttributesKey.createTextAttributesKey( - "PROPERTIES.VALID_STRING_ESCAPE", - DefaultLanguageHighlighterColors.VALID_STRING_ESCAPE - ); + public static final TextAttributesKey PROPERTIES_INVALID_STRING_ESCAPE = TextAttributesKey.createTextAttributesKey( "PROPERTIES.INVALID_STRING_ESCAPE", DefaultLanguageHighlighterColors.INVALID_STRING_ESCAPE @@ -57,22 +53,18 @@ public class PropertiesHighlighter extends SyntaxHighlighterBase { static { keys1 = new HashMap<>(); - keys2 = new HashMap<>(); keys1.put(PropertiesTokenTypes.VALUE_CHARACTERS, PROPERTY_VALUE); keys1.put(PropertiesTokenTypes.END_OF_LINE_COMMENT, PROPERTY_COMMENT); keys1.put(PropertiesTokenTypes.KEY_CHARACTERS, PROPERTY_KEY); keys1.put(PropertiesTokenTypes.KEY_VALUE_SEPARATOR, PROPERTY_KEY_VALUE_SEPARATOR); - keys1.put(StringEscapesTokenTypes.VALID_STRING_ESCAPE_TOKEN, PROPERTIES_VALID_STRING_ESCAPE); - // in fact all back-slashed escapes are allowed - keys1.put(StringEscapesTokenTypes.INVALID_CHARACTER_ESCAPE_TOKEN, PROPERTIES_INVALID_STRING_ESCAPE); keys1.put(StringEscapesTokenTypes.INVALID_UNICODE_ESCAPE_TOKEN, PROPERTIES_INVALID_STRING_ESCAPE); } @Override public TextAttributesKey @NotNull [] getTokenHighlights(IElementType tokenType) { - return SyntaxHighlighterBase.pack(keys1.get(tokenType), keys2.get(tokenType)); + return SyntaxHighlighterBase.pack(keys1.get(tokenType)); } public static final Map> DISPLAY_NAMES = ContainerUtil.>immutableMapBuilder() @@ -80,7 +72,6 @@ public class PropertiesHighlighter extends SyntaxHighlighterBase { .put(PROPERTY_VALUE, new Pair<>(PropertiesBundle.message("options.properties.attribute.descriptor.property.value"), null)) .put(PROPERTY_KEY_VALUE_SEPARATOR, new Pair<>(PropertiesBundle.message("options.properties.attribute.descriptor.key.value.separator"), null)) .put(PROPERTY_COMMENT, new Pair<>(PropertiesBundle.message("options.properties.attribute.descriptor.comment"), null)) - .put(PROPERTIES_VALID_STRING_ESCAPE, new Pair<>(PropertiesBundle.message("options.properties.attribute.descriptor.valid.string.escape"), null)) .put(PROPERTIES_INVALID_STRING_ESCAPE, Pair.create(PropertiesBundle.message("options.properties.attribute.descriptor.invalid.string.escape"), HighlightSeverity.WARNING)) .build(); } diff --git a/plugins/properties/src/com/intellij/lang/properties/EnterInPropertiesFileHandler.java b/plugins/properties/src/com/intellij/lang/properties/EnterInPropertiesFileHandler.java index ede28149f4e1..d73570d98cdb 100644 --- a/plugins/properties/src/com/intellij/lang/properties/EnterInPropertiesFileHandler.java +++ b/plugins/properties/src/com/intellij/lang/properties/EnterInPropertiesFileHandler.java @@ -49,11 +49,12 @@ final class EnterInPropertiesFileHandler extends EnterHandlerDelegateAdapter { final IElementType elementType = psiAtOffset == null ? null : psiAtOffset.getNode().getElementType(); if (elementType == PropertiesTokenTypes.VALUE_CHARACTERS) { - toInsert = "\\\n "; - // if the split at a whitespace, move the caret forward to keep all the whitespaces in the same line - if (text.charAt(caretOffset) == ' ') { - final int leadingWhitespaces = getLeadingWhitespacesNumber(text.substring(caretOffset)); - caretOffset += leadingWhitespaces; + if (text.charAt(caretOffset) == ' ' || text.charAt(caretOffset) == '\t') { + // escape the whitespace on the next line like "\ " + toInsert = "\\\n \\"; + } + else { + toInsert = "\\\n "; } } else if (elementType == PropertiesTokenTypes.END_OF_LINE_COMMENT && "#!".indexOf(document.getText().charAt(caretOffset)) == -1) { @@ -70,13 +71,4 @@ final class EnterInPropertiesFileHandler extends EnterHandlerDelegateAdapter { editor.getSelectionModel().removeSelection(); } - static int getLeadingWhitespacesNumber(String text) { - final long leadingWhitespaces = text - .chars() - .takeWhile(c -> c == ' ') - .count(); - - return (int)leadingWhitespaces; - } - } diff --git a/plugins/properties/src/com/intellij/lang/properties/PropertiesJoinLinesHandler.java b/plugins/properties/src/com/intellij/lang/properties/PropertiesJoinLinesHandler.java index 819fd0832be2..37f3fbf88882 100644 --- a/plugins/properties/src/com/intellij/lang/properties/PropertiesJoinLinesHandler.java +++ b/plugins/properties/src/com/intellij/lang/properties/PropertiesJoinLinesHandler.java @@ -19,20 +19,39 @@ import com.intellij.codeInsight.editorActions.JoinLinesHandlerDelegate; import com.intellij.lang.properties.psi.PropertiesFile; import com.intellij.openapi.editor.Document; import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class PropertiesJoinLinesHandler implements JoinLinesHandlerDelegate { @Override - public int tryJoinLines(@NotNull final Document doc, @NotNull final PsiFile psiFile, int start, final int end) { + public int tryJoinLines(@NotNull final Document doc, @NotNull final PsiFile psiFile, int start, int end) { if (!(psiFile instanceof PropertiesFile)) return -1; - // strip continuation char and two leading whitespaces - if (PropertiesUtil.isUnescapedBackSlashAtTheEnd(doc.getText().substring(0, start + 1))) { - final String text = doc.getText().substring(start + 1); - final int leadingWhitespaces = EnterInPropertiesFileHandler.getLeadingWhitespacesNumber(text); - doc.deleteString(start, start + 1 + leadingWhitespaces); - start--; + final String documentText = doc.getText(); + final String documentTextTillEndOfFirstLine = documentText.substring(0, start + 1); + if (!PropertiesUtil.isUnescapedBackSlashAtTheEnd(documentTextTillEndOfFirstLine)) return CANNOT_JOIN; + + if (end < documentText.length() && startsWithEscapedWhitespace(documentText.substring(end))) { + // if the second line starts with escaped whitespace (e.g. '\ '), then remove it too + end ++; } - return start + 1; + + // strip the continuation char '\', + // the leading whitespaces on the second line and + // the optional '\' if the text on the second line starts with '\ ' + doc.deleteString(start, end); + return start; } -} + + @Contract(value = "null -> false", pure = true) + public static boolean startsWithEscapedWhitespace(@Nullable String text) { + if (text == null) return false; + if (text.length() < 2) return false; + + final char backslash = text.charAt(0); + final char whitespace = text.charAt(1); + + return backslash == '\\' && (whitespace == ' ' || whitespace == '\t'); + } +} \ No newline at end of file diff --git a/plugins/properties/tests/testData/propertiesFile/joinLines/PropertiesBackSlashSlash_after.properties b/plugins/properties/tests/testData/propertiesFile/joinLines/PropertiesBackSlashSlash_after.properties index c7df67c00b79..855d661904de 100644 --- a/plugins/properties/tests/testData/propertiesFile/joinLines/PropertiesBackSlashSlash_after.properties +++ b/plugins/properties/tests/testData/propertiesFile/joinLines/PropertiesBackSlashSlash_after.properties @@ -1 +1 @@ -a=B\\ x=Y +a=B\\ x=Y diff --git a/plugins/properties/tests/testData/propertiesFile/joinLines/Properties_after.properties b/plugins/properties/tests/testData/propertiesFile/joinLines/Properties_after.properties index 94297e7d2a6a..da7371eb68c6 100644 --- a/plugins/properties/tests/testData/propertiesFile/joinLines/Properties_after.properties +++ b/plugins/properties/tests/testData/propertiesFile/joinLines/Properties_after.properties @@ -1 +1 @@ -a=B x=Y +a=B x=Y