mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
WEB-12316 PhpStorm performing awful when typing in HTML zones in PHP files
Reimplement calculation of emmet abbreviation. See original implementation: https://github.com/emmetio/emmet/blob/master/lib/utils/action.js
This commit is contained in:
+35
-1
@@ -15,13 +15,20 @@
|
||||
*/
|
||||
package com.intellij.codeInsight.template.emmet.generators;
|
||||
|
||||
import com.intellij.codeInsight.template.CustomTemplateCallback;
|
||||
import com.intellij.codeInsight.template.emmet.ZenCodingTemplate;
|
||||
import com.intellij.codeInsight.template.emmet.tokens.TemplateToken;
|
||||
import com.intellij.codeInsight.template.impl.TemplateImpl;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.Couple;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.xml.XmlDocument;
|
||||
import com.intellij.psi.xml.XmlFile;
|
||||
import com.intellij.psi.xml.XmlTag;
|
||||
import com.intellij.psi.xml.XmlTokenType;
|
||||
import com.intellij.xml.util.HtmlUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -36,7 +43,9 @@ public abstract class XmlZenCodingGenerator extends ZenCodingGenerator {
|
||||
@Override
|
||||
public TemplateImpl generateTemplate(@NotNull TemplateToken token, boolean hasChildren, @NotNull PsiElement context) {
|
||||
String s = toString(token, hasChildren, context);
|
||||
TemplateImpl template = token.getTemplate().copy();
|
||||
TemplateImpl tokenTemplate = token.getTemplate();
|
||||
assert tokenTemplate != null;
|
||||
TemplateImpl template = tokenTemplate.copy();
|
||||
template.setString(s);
|
||||
return template;
|
||||
}
|
||||
@@ -77,4 +86,29 @@ public abstract class XmlZenCodingGenerator extends ZenCodingGenerator {
|
||||
|
||||
@Override
|
||||
public abstract boolean isMyContext(@NotNull PsiElement context, boolean wrapping);
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String computeTemplateKey(@NotNull CustomTemplateCallback callback) {
|
||||
Editor editor = callback.getEditor();
|
||||
int currentOffset = editor.getCaretModel().getOffset();
|
||||
int startOffset = editor.getDocument().getLineStartOffset(editor.getCaretModel().getLogicalPosition().line);
|
||||
|
||||
CharSequence documentText = editor.getDocument().getCharsSequence();
|
||||
PsiElement prevVisibleLeaf = callback.getContext();
|
||||
while (prevVisibleLeaf != null) {
|
||||
TextRange textRange = prevVisibleLeaf.getTextRange();
|
||||
if (textRange.getEndOffset() <= startOffset) {
|
||||
break;
|
||||
}
|
||||
if (prevVisibleLeaf.getNode().getElementType() == XmlTokenType.XML_TAG_END) {
|
||||
startOffset = textRange.getEndOffset();
|
||||
break;
|
||||
}
|
||||
prevVisibleLeaf = PsiTreeUtil.prevVisibleLeaf(prevVisibleLeaf);
|
||||
}
|
||||
|
||||
String key = computeKey(documentText.subSequence(startOffset, currentOffset));
|
||||
return !StringUtil.isEmpty(key) && ZenCodingTemplate.checkTemplateKey(key, callback, this) ? key : null;
|
||||
}
|
||||
}
|
||||
|
||||
+55
-86
@@ -25,12 +25,8 @@ import com.intellij.codeInsight.template.impl.TemplateImpl;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.options.UnnamedConfigurable;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiErrorElement;
|
||||
import com.intellij.psi.PsiWhiteSpace;
|
||||
import com.intellij.psi.impl.source.tree.LeafPsiElement;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import com.intellij.psi.xml.XmlTokenType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -43,9 +39,7 @@ import java.util.List;
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public abstract class ZenCodingGenerator {
|
||||
private static final ExtensionPointName<ZenCodingGenerator> EP_NAME =
|
||||
new ExtensionPointName<ZenCodingGenerator>("com.intellij.xml.zenCodingGenerator");
|
||||
private static final TokenSet VALID_LEAF_TYPES = TokenSet.create(XmlTokenType.XML_DATA_CHARACTERS, XmlTokenType.XML_CHAR_ENTITY_REF);
|
||||
private static final ExtensionPointName<ZenCodingGenerator> EP_NAME = new ExtensionPointName<ZenCodingGenerator>("com.intellij.xml.zenCodingGenerator");
|
||||
|
||||
public abstract TemplateImpl generateTemplate(@NotNull TemplateToken token, boolean hasChildren, @NotNull PsiElement context);
|
||||
|
||||
@@ -75,96 +69,71 @@ public abstract class ZenCodingGenerator {
|
||||
@Nullable
|
||||
public String computeTemplateKey(@NotNull CustomTemplateCallback callback) {
|
||||
Editor editor = callback.getEditor();
|
||||
final int currentOffset = editor.getCaretModel().getOffset();
|
||||
final CharSequence documentText = editor.getDocument().getCharsSequence();
|
||||
PsiElement element = callback.getContext();
|
||||
int line = editor.getCaretModel().getLogicalPosition().line;
|
||||
int lineStart = editor.getDocument().getLineStartOffset(line);
|
||||
int elementStart = -1;
|
||||
do {
|
||||
PsiElement e = element;
|
||||
while ((e instanceof LeafPsiElement && VALID_LEAF_TYPES.contains(((LeafPsiElement)e).getElementType())) ||
|
||||
e instanceof PsiWhiteSpace || e instanceof PsiErrorElement) {
|
||||
elementStart = e.getTextRange().getStartOffset();
|
||||
e = e.getPrevSibling();
|
||||
}
|
||||
if (elementStart >= 0) {
|
||||
int startOffset = Math.max(elementStart, lineStart);
|
||||
String key = computeKey(startOffset, currentOffset, documentText);
|
||||
if (key != null) {
|
||||
while (key.length() > 0 && !ZenCodingTemplate.checkTemplateKey(key, callback, this)) {
|
||||
key = key.substring(1);
|
||||
}
|
||||
if (key.length() > 0) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
element = element.getParent();
|
||||
}
|
||||
while (element != null && elementStart > lineStart);
|
||||
return null;
|
||||
int currentOffset = editor.getCaretModel().getOffset();
|
||||
int startOffset = editor.getDocument().getLineStartOffset(editor.getCaretModel().getLogicalPosition().line);
|
||||
String key = computeKey(editor.getDocument().getCharsSequence().subSequence(startOffset, currentOffset));
|
||||
return !StringUtil.isEmpty(key) && ZenCodingTemplate.checkTemplateKey(key, callback, this) ? key : null;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
protected static String computeKey(int startOffset, int currentOffset, CharSequence documentText) {
|
||||
if (currentOffset < startOffset || startOffset > documentText.length() || currentOffset > documentText.length()) {
|
||||
return null;
|
||||
}
|
||||
String s = documentText.subSequence(startOffset, currentOffset).toString();
|
||||
int index = 0;
|
||||
while (index < s.length() && Character.isWhitespace(s.charAt(index))) {
|
||||
index++;
|
||||
}
|
||||
String key = s.substring(index);
|
||||
int lastWhitespaceIndex = -1;
|
||||
int lastQuoteIndex = -1;
|
||||
int lastApostropheIndex = -1;
|
||||
boolean inBrackets = false;
|
||||
int bracesStack = 0;
|
||||
protected String computeKey(@NotNull CharSequence text) {
|
||||
int currentOffset = text.length();
|
||||
int groupCount = 0;
|
||||
int bracketCount = 0;
|
||||
int textCount = 0;
|
||||
|
||||
for (int i = 0; i < key.length(); i++) {
|
||||
char c = key.charAt(i);
|
||||
if (lastQuoteIndex >= 0 || lastApostropheIndex >= 0) {
|
||||
if (c == '"') {
|
||||
lastQuoteIndex = -1;
|
||||
}
|
||||
else if (c == '\'') lastApostropheIndex = -1;
|
||||
}
|
||||
else if (Character.isWhitespace(c)) {
|
||||
lastWhitespaceIndex = i;
|
||||
}
|
||||
else if (c == '"') {
|
||||
lastQuoteIndex = i;
|
||||
}
|
||||
else if (c == '\'') {
|
||||
lastApostropheIndex = i;
|
||||
while (currentOffset > 0) {
|
||||
currentOffset--;
|
||||
char c = text.charAt(currentOffset);
|
||||
|
||||
if (c == ']') {
|
||||
bracketCount++;
|
||||
}
|
||||
else if (c == '[') {
|
||||
inBrackets = true;
|
||||
if (bracketCount == 0) {
|
||||
currentOffset++;
|
||||
break;
|
||||
}
|
||||
bracketCount--;
|
||||
}
|
||||
else if (c == ']' && inBrackets) {
|
||||
lastWhitespaceIndex = -1;
|
||||
inBrackets = false;
|
||||
else if (c == '}') {
|
||||
textCount++;
|
||||
}
|
||||
else if (c == '{') {
|
||||
bracesStack++;
|
||||
if (textCount == 0) {
|
||||
currentOffset++;
|
||||
break;
|
||||
}
|
||||
textCount--;
|
||||
}
|
||||
else if (c == '}' && bracesStack > 0) {
|
||||
bracesStack--;
|
||||
if (bracesStack == 0) {
|
||||
lastWhitespaceIndex = -1;
|
||||
else if (c == ')') {
|
||||
groupCount++;
|
||||
}
|
||||
else if (c == '(') {
|
||||
if (groupCount == 0) {
|
||||
currentOffset++;
|
||||
break;
|
||||
}
|
||||
groupCount--;
|
||||
}
|
||||
else {
|
||||
if (bracketCount > 0 || textCount > 0) {
|
||||
// respect all characters inside attribute sets or text nodes
|
||||
continue;
|
||||
}
|
||||
if (!isAllowedChar(c)) {
|
||||
currentOffset++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lastQuoteIndex >= 0 || lastApostropheIndex >= 0) {
|
||||
int max = Math.max(lastQuoteIndex, lastApostropheIndex);
|
||||
return max < key.length() - 1 ? key.substring(max) : null;
|
||||
}
|
||||
if (lastWhitespaceIndex >= 0 && lastWhitespaceIndex < key.length() - 1) {
|
||||
return key.substring(lastWhitespaceIndex + 1);
|
||||
}
|
||||
return key;
|
||||
return groupCount == 0 && textCount == 0 && bracketCount == 0 && currentOffset >= 0 && currentOffset < text.length()
|
||||
? text.subSequence(currentOffset, text.length()).toString().replaceFirst("^[*+>^]+", "")
|
||||
: null;
|
||||
}
|
||||
|
||||
private static boolean isAllowedChar(char c) {
|
||||
return (Character.isDigit(c) || Character.isLetter(c) || StringUtil.containsChar("/>+^[](){}#.*:$-_!@|%", c));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
Reference in New Issue
Block a user