diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettings.java b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettings.java
index fd7fe543f17c..04797764bc7e 100644
--- a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettings.java
+++ b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettings.java
@@ -403,6 +403,7 @@ public class CodeStyleSettings extends CommonCodeStyleSettings implements Clonea
"a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,span,strike,strong,sub,sup,textarea,tt,u,var";
@NonNls public String HTML_DONT_ADD_BREAKS_IF_INLINE_CONTENT = "title,h1,h2,h3,h4,h5,h6,p";
public QuoteStyle HTML_QUOTE_STYLE = QuoteStyle.Double;
+ public boolean HTML_ENFORCE_QUOTES = false;
// ---------------------------------------------------------------------------------------
diff --git a/platform/platform-resources-en/src/messages/ApplicationBundle.properties b/platform/platform-resources-en/src/messages/ApplicationBundle.properties
index 4a8c3bad5ad4..98684cec09d7 100644
--- a/platform/platform-resources-en/src/messages/ApplicationBundle.properties
+++ b/platform/platform-resources-en/src/messages/ApplicationBundle.properties
@@ -90,6 +90,7 @@ checkbox.parentheses.around.method.arguments=Add parentheses around method argum
checkbox.rename.local.variables.inplace=Enable in-place mode
checkbox.rename.local.variables.preselect=Preselect old name
generated.quote.marks=Generated quote marks:
+generated.quote.enforce.format=Enforce on format
editbox.keep.blank.lines=Keep blank lines:
checkbox.keep.white.spaces=Keep white spaces
checkbox.align.text=Align text
diff --git a/platform/platform-resources/src/META-INF/XmlPlugin.xml b/platform/platform-resources/src/META-INF/XmlPlugin.xml
index 1a244d0e4dcc..8e1583f9a1cb 100644
--- a/platform/platform-resources/src/META-INF/XmlPlugin.xml
+++ b/platform/platform-resources/src/META-INF/XmlPlugin.xml
@@ -234,6 +234,7 @@
+
diff --git a/platform/platform-resources/src/codeStyle/preview/preview.html.template b/platform/platform-resources/src/codeStyle/preview/preview.html.template
index a716ad0366f5..bed8287f60e6 100644
--- a/platform/platform-resources/src/codeStyle/preview/preview.html.template
+++ b/platform/platform-resources/src/codeStyle/preview/preview.html.template
@@ -12,12 +12,12 @@
-
+
-
-
+
+
-
+
- Home
- Products
@@ -75,7 +75,7 @@
"If you are a C# developer you simply owe it to yourself to get this tool. It has
- dramatically made my life better (SERIOUSLY!). I put it up there with amazing tools…"
+ dramatically made my life better (SERIOUSLY!). I put it up there with amazing tools�"
Damon Wilder Carr,
Chief Technologist and CEO,
Agilerfactor
@@ -121,7 +121,7 @@
Refactoring can significantly improve your code design and efficiency. ReSharper's automated refactoring
support takes care of code consistency and compilability after even the most dramatic modifications.
ReSharper supports the following types of refactoring:
- More about this feature…
+ More about this feature�
To get the full story on ReSharpers feature set, please visit the Features page.
@@ -131,7 +131,7 @@
handwork, giving you more time to focus on the task at hand. Its robust set of features for automatic error-checking
and code correction cuts development time and increases your efficiency. You'll find that ReSharper quickly
pays back it's cost in increased developer productivity and improved code quality.
-
The wait is over… ReSharper is here and now C# developers can experience what we mean when we say
+
The wait is over� ReSharper is here and now C# developers can experience what we mean when we say
"Develop with pleasure!" Download a copy today!
diff --git a/xml/impl/src/com/intellij/application/options/CodeStyleHtmlPanel.form b/xml/impl/src/com/intellij/application/options/CodeStyleHtmlPanel.form
index 8c53c6bd7664..e281cbe66218 100644
--- a/xml/impl/src/com/intellij/application/options/CodeStyleHtmlPanel.form
+++ b/xml/impl/src/com/intellij/application/options/CodeStyleHtmlPanel.form
@@ -3,7 +3,7 @@
-
+
@@ -40,7 +40,7 @@
-
+
@@ -194,6 +194,14 @@
+
+
+
+
+
+
+
+
diff --git a/xml/impl/src/com/intellij/application/options/CodeStyleHtmlPanel.java b/xml/impl/src/com/intellij/application/options/CodeStyleHtmlPanel.java
index 3b971b9cd921..63d4c96a1cd8 100644
--- a/xml/impl/src/com/intellij/application/options/CodeStyleHtmlPanel.java
+++ b/xml/impl/src/com/intellij/application/options/CodeStyleHtmlPanel.java
@@ -29,6 +29,7 @@ import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.ui.EnumComboBoxModel;
+import com.intellij.ui.components.JBCheckBox;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.util.ArrayUtil;
import com.intellij.util.PlatformIcons;
@@ -68,6 +69,7 @@ public class CodeStyleHtmlPanel extends CodeStyleAbstractPanel {
private JBScrollPane myJBScrollPane;
private JPanel myRightMarginPanel;
private JComboBox myQuotesCombo;
+ private JBCheckBox myEnforceQuotesBox;
private RightMarginForm myRightMarginForm;
public CodeStyleHtmlPanel(CodeStyleSettings settings) {
@@ -91,7 +93,14 @@ public class CodeStyleHtmlPanel extends CodeStyleAbstractPanel {
myInlineElementsTagNames.getTextField().setColumns(5);
myDontBreakIfInlineContent.getTextField().setColumns(5);
-
+ myQuotesCombo.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ boolean quotesRequired = !CodeStyleSettings.QuoteStyle.None.equals(myQuotesCombo.getSelectedItem());
+ myEnforceQuotesBox.setEnabled(quotesRequired);
+ if (!quotesRequired) myEnforceQuotesBox.setSelected(false);
+ }
+ });
addPanelToWatch(myPanel);
}
@@ -166,6 +175,7 @@ public class CodeStyleHtmlPanel extends CodeStyleAbstractPanel {
settings.HTML_KEEP_LINE_BREAKS = myShouldKeepBlankLines.isSelected();
settings.HTML_KEEP_LINE_BREAKS_IN_TEXT = myShouldKeepLineBreaksInText.isSelected();
settings.HTML_QUOTE_STYLE = (CodeStyleSettings.QuoteStyle)myQuotesCombo.getSelectedItem();
+ settings.HTML_ENFORCE_QUOTES = myEnforceQuotesBox.isSelected();
myRightMarginForm.apply(settings);
}
@@ -206,6 +216,7 @@ public class CodeStyleHtmlPanel extends CodeStyleAbstractPanel {
myKeepWhiteSpacesTagNames.setText(settings.HTML_KEEP_WHITESPACES_INSIDE);
myRightMarginForm.reset(settings);
myQuotesCombo.setSelectedItem(settings.HTML_QUOTE_STYLE);
+ myEnforceQuotesBox.setSelected(settings.HTML_ENFORCE_QUOTES);
}
@Override
@@ -280,7 +291,8 @@ public class CodeStyleHtmlPanel extends CodeStyleAbstractPanel {
return true;
}
- return myRightMarginForm.isModified(settings);
+ return myRightMarginForm.isModified(settings) ||
+ myEnforceQuotesBox.isSelected() != settings.HTML_ENFORCE_QUOTES;
}
@Override
diff --git a/xml/impl/src/com/intellij/lang/html/HtmlQuotesFormatPreprocessor.java b/xml/impl/src/com/intellij/lang/html/HtmlQuotesFormatPreprocessor.java
new file mode 100644
index 000000000000..d2553e0b4cb5
--- /dev/null
+++ b/xml/impl/src/com/intellij/lang/html/HtmlQuotesFormatPreprocessor.java
@@ -0,0 +1,172 @@
+/*
+ * 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.lang.html;
+
+
+import com.intellij.lang.ASTNode;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.XmlRecursiveElementVisitor;
+import com.intellij.psi.codeStyle.CodeStyleSettings;
+import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
+import com.intellij.psi.impl.source.codeStyle.PreFormatProcessor;
+import com.intellij.psi.tree.IElementType;
+import com.intellij.psi.xml.XmlAttributeValue;
+import com.intellij.psi.xml.XmlTokenType;
+import com.intellij.util.DocumentUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class HtmlQuotesFormatPreprocessor implements PreFormatProcessor {
+ @NotNull
+ @Override
+ public TextRange process(@NotNull ASTNode node, @NotNull TextRange range) {
+ PsiElement psiElement = node.getPsi();
+ if (psiElement != null &&
+ psiElement.isValid() &&
+ psiElement.getLanguage().is(HTMLLanguage.INSTANCE)) {
+ CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(psiElement.getProject());
+ CodeStyleSettings.QuoteStyle quoteStyle = settings.HTML_QUOTE_STYLE;
+ if (quoteStyle != CodeStyleSettings.QuoteStyle.None && settings.HTML_ENFORCE_QUOTES) {
+ HtmlQuotesConverter converter = new HtmlQuotesConverter(quoteStyle, psiElement, range);
+ Document document = converter.getDocument();
+ if (document != null) {
+ DocumentUtil.executeInBulk(document, true, converter);
+ }
+ return converter.getTextRange();
+ }
+ }
+ return range;
+ }
+
+
+ private static class HtmlQuotesConverter extends XmlRecursiveElementVisitor implements Runnable {
+ private TextRange myTextRange;
+ private final Document myDocument;
+ private final PsiDocumentManager myDocumentManager;
+ private final PsiElement myElement;
+ private int myDelta = 0;
+ private final char myQuoteChar;
+
+ private HtmlQuotesConverter(CodeStyleSettings.QuoteStyle style,
+ @NotNull PsiElement element,
+ @NotNull TextRange textRange) {
+ Project project = element.getProject();
+ PsiFile file = element.getContainingFile();
+ myElement = element;
+ myTextRange = new TextRange(textRange.getStartOffset(), textRange.getEndOffset());
+ myDocumentManager = PsiDocumentManager.getInstance(project);
+ myDocument = myDocumentManager.getDocument(file);
+ switch (style) {
+ case Single:
+ myQuoteChar = '\'';
+ break;
+ case Double:
+ myQuoteChar = '"';
+ break;
+ default:
+ myQuoteChar = 0;
+ }
+ }
+
+ public TextRange getTextRange() {
+ return myTextRange.grown(myDelta);
+ }
+
+ public Document getDocument() {
+ return myDocument;
+ }
+
+ @Override
+ public void visitXmlAttributeValue(XmlAttributeValue value) {
+ if (myTextRange.contains(value.getTextRange())) {
+ PsiElement child = value.getFirstChild();
+ if (child != null &&
+ !containsQuoteChars(value) // For now we skip values containing quotes to be inserted/replaced
+ ) {
+ String newValue = null;
+ if (child.getNode().getElementType() == XmlTokenType.XML_ATTRIBUTE_VALUE_START_DELIMITER) {
+ PsiElement lastChild = value.getLastChild();
+ if (lastChild != null && lastChild.getNode().getElementType() == XmlTokenType.XML_ATTRIBUTE_VALUE_END_DELIMITER) {
+ CharSequence delimiterChars = child.getNode().getChars();
+ if (delimiterChars.length() == 1) {
+ char existingQuote = delimiterChars.charAt(0);
+ if (existingQuote != myQuoteChar) {
+ newValue = convertQuotes(value);
+ }
+ }
+ }
+ }
+ else if (child.getNode().getElementType() == XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN
+ && child == value.getLastChild()) {
+ newValue = surroundWithQuotes(value);
+ }
+ if (newValue != null) {
+ int startOffset = value.getTextRange().getStartOffset() + myDelta;
+ int endOffset = value.getTextRange().getEndOffset() + myDelta;
+ myDocument.replaceString(startOffset, endOffset, newValue);
+ myDelta += newValue.length() - value.getTextLength();
+ }
+ }
+ }
+ }
+
+ @Nullable
+ private String convertQuotes(@NotNull XmlAttributeValue value) {
+ String currValue = value.getNode().getChars().toString();
+ if (currValue.length() >= 2) {
+ return myQuoteChar + currValue.substring(1, currValue.length() - 1) + myQuoteChar;
+ }
+ return null;
+ }
+
+ @NotNull
+ private String surroundWithQuotes(@NotNull XmlAttributeValue value) {
+ String currValue = value.getNode().getChars().toString();
+ return myQuoteChar + currValue + myQuoteChar;
+ }
+
+ private boolean containsQuoteChars(@NotNull XmlAttributeValue value) {
+ for (PsiElement child = value.getFirstChild(); child != null; child = child.getNextSibling()) {
+ if (!isDelimiter(child.getNode().getElementType())) {
+ CharSequence valueChars = child.getNode().getChars();
+ for (int i = 0; i < valueChars.length(); i ++) {
+ if (valueChars.charAt(i) == myQuoteChar) return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private static boolean isDelimiter(@NotNull IElementType elementType) {
+ return elementType == XmlTokenType.XML_ATTRIBUTE_VALUE_START_DELIMITER ||
+ elementType == XmlTokenType.XML_ATTRIBUTE_VALUE_END_DELIMITER;
+ }
+
+ @Override
+ public void run() {
+ if (myDocument != null) {
+ myDocumentManager.doPostponedOperationsAndUnblockDocument(myDocument);
+ myElement.accept(this);
+ myDocumentManager.commitDocument(myDocument);
+ }
+ }
+ }
+}