mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-05-06 13:20:53 +07:00
[properties] IDEA-215803 Join and split string works incorrectly inside property files when cursor inside property value
This patch amends PropertiesHighlighter with not highlighting invalid string escapes since Properties are meant to ignore backslashes if they don't precede a single quote, double quote or backslash character. This patch amends EnterInPropertiesFileHandler with splitting a property value right at the place of the caret and escaping optional whitespaces with a backslash on the next line if the caret is at a whitespace. Signed-off-by: Nikita Eshkeev <nikita.eshkeev@jetbrains.com> GitOrigin-RevId: 0d8b66c63db1d81a6deada3c26b808fa3b61d46b
This commit is contained in:
committed by
intellij-monorepo-bot
parent
d60d8682fa
commit
292c890cc7
@@ -1,3 +1,3 @@
|
||||
# "_ignore" "true"
|
||||
message=hello \
|
||||
message=hello \
|
||||
world
|
||||
@@ -0,0 +1,3 @@
|
||||
# "_ignore" "true"
|
||||
message=hello\
|
||||
\ world
|
||||
@@ -0,0 +1,3 @@
|
||||
# "_ignore" "true"
|
||||
message=hello \
|
||||
\ world
|
||||
@@ -0,0 +1,2 @@
|
||||
# "_ignore" "true"
|
||||
message=hello <caret>world
|
||||
@@ -0,0 +1,2 @@
|
||||
# "_ignore" "true"
|
||||
message=hello<caret> world
|
||||
@@ -0,0 +1,2 @@
|
||||
# "_ignore" "true"
|
||||
message=hello <caret> world
|
||||
@@ -1,2 +0,0 @@
|
||||
# "_ignore" "true"
|
||||
message=hello <caret> world
|
||||
@@ -0,0 +1,2 @@
|
||||
# "_ignore" "true"
|
||||
message=hello, world
|
||||
@@ -0,0 +1,2 @@
|
||||
# "_ignore" "true"
|
||||
message=hello world
|
||||
@@ -0,0 +1,2 @@
|
||||
# "_ignore" "true"
|
||||
message=hello world
|
||||
@@ -1,5 +1,3 @@
|
||||
# "_ignore" "true"
|
||||
# hello
|
||||
# world
|
||||
message=hello \
|
||||
world
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# "_ignore" "true"
|
||||
<caret>message=hello, world\
|
||||
@@ -0,0 +1,3 @@
|
||||
# "_ignore" "true"
|
||||
<caret>message=hello \
|
||||
\ world
|
||||
@@ -0,0 +1,3 @@
|
||||
# "_ignore" "true"
|
||||
<caret>message=hello\
|
||||
\ world
|
||||
@@ -1,6 +1,4 @@
|
||||
# "_ignore" "true"
|
||||
# hello
|
||||
# world
|
||||
<caret>message=hello \
|
||||
\
|
||||
world
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
\ a\ b\ =x<warning descr="Invalid string escape">\x</warning>
|
||||
\ a\ b\ =x\x
|
||||
a\n\r\b2 = x\\\n\r v
|
||||
spaceinthemiddle=a<warning descr="Invalid string escape">\ </warning>b
|
||||
ii<warning descr="Invalid string escape">\d</warning>nnvalidkey
|
||||
framingSpaces = \ xxx\ \ \
|
||||
spaceinthemiddle=a\ b
|
||||
ii\dnnvalidkey
|
||||
framingSpaces = \ xxx\ \ \
|
||||
hello=world,\
|
||||
\ universe
|
||||
@@ -19,7 +19,6 @@ import java.util.Map;
|
||||
|
||||
public class PropertiesHighlighter extends SyntaxHighlighterBase {
|
||||
private static final Map<IElementType, TextAttributesKey> keys1;
|
||||
private static final Map<IElementType, TextAttributesKey> 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<TextAttributesKey, Pair<@Nls String, HighlightSeverity>> DISPLAY_NAMES = ContainerUtil.<TextAttributesKey, Pair<@Nls String, HighlightSeverity>>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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
a=B\\<caret> x=Y
|
||||
a=B\\<caret> x=Y
|
||||
|
||||
@@ -1 +1 @@
|
||||
a=B<caret> x=Y
|
||||
a=B<caret> x=Y
|
||||
|
||||
Reference in New Issue
Block a user