Merge remote-tracking branch 'origin/master'

This commit is contained in:
Dmitry Trofimov
2014-12-19 23:12:20 +01:00
34 changed files with 752 additions and 599 deletions
@@ -34,14 +34,14 @@ import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.util.io.HttpRequests;
import com.intellij.util.SystemProperties;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.net.NetUtils;
import com.intellij.util.io.HttpRequests;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.regex.Pattern;
@@ -167,21 +167,8 @@ public class InternetAttachSourceProvider implements AttachSourcesProvider {
}
try {
File tmpDownload = HttpRequests.request(artifactUrl).connect(new HttpRequests.RequestProcessor<File>() {
@Override
public File process(@NotNull HttpRequests.Request request) throws IOException {
File tmpDownload = FileUtil.createTempFile(libSourceDir, "download.", ".tmp", false, false);
OutputStream out = new BufferedOutputStream(new FileOutputStream(tmpDownload));
try {
NetUtils.copyStreamContent(indicator, request.getInputStream(), out, request.getConnection().getContentLength());
}
finally {
out.close();
}
return tmpDownload;
}
});
File tmpDownload = FileUtil.createTempFile(libSourceDir, "download.", ".tmp", false, false);
HttpRequests.request(artifactUrl).saveToFile(tmpDownload, indicator);
if (!sourceFile.exists() && !tmpDownload.renameTo(sourceFile)) {
LOG.warn("Failed to rename file " + tmpDownload + " to " + sourceFileName);
}
@@ -102,7 +102,7 @@ public class JavaDocExternalFilter extends AbstractExternalFilter {
}
@Override
protected RefConvertor[] getRefConvertors() {
protected RefConvertor[] getRefConverters() {
return myReferenceConvertors;
}
@@ -18,7 +18,10 @@ package com.intellij.psi.augment;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiType;
import com.intellij.psi.PsiTypeElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
@@ -44,4 +47,28 @@ public abstract class PsiAugmentProvider {
return result;
}
/**
* Extends {@link PsiTypeElement#getType()} so type could be retrieved from external place
* e.g. from variable initializer in lombok case (http://projectlombok.org/features/val.html)
*
* @param typeElement place where inference takes place,
* also nested PsiTypeElement-s (e.g. for List<String> PsiTypeElements corresponding to both List and String would be suggested)
* @return inferred type or null, if inference is not applicable
*/
@Nullable
protected PsiType inferType(PsiTypeElement typeElement) {
return null;
}
@Nullable
public static PsiType getInferredType(PsiTypeElement typeElement) {
for (PsiAugmentProvider provider : Extensions.getExtensions(EP_NAME)) {
final PsiType type = provider.inferType(typeElement);
if (type != null) {
return type;
}
}
return null;
}
}
@@ -17,6 +17,7 @@ package com.intellij.psi.impl.source;
import com.intellij.lang.ASTNode;
import com.intellij.psi.*;
import com.intellij.psi.augment.PsiAugmentProvider;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
import com.intellij.psi.impl.source.tree.CompositePsiElement;
@@ -76,6 +77,11 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl
}
private PsiType calculateType() {
final PsiType inferredType = PsiAugmentProvider.getInferredType(this);
if (inferredType != null) {
return inferredType;
}
PsiType type = null;
SmartList<PsiAnnotation> annotations = new SmartList<PsiAnnotation>();
@@ -0,0 +1,25 @@
import lombok.val;
import java.util.*;
class Test {
<error descr="Incompatible types. Found: 'int', required: 'lombok.val'">val field = 0;</error>
void method(val param) {
<error descr="Incompatible types. Found: 'lombok.val', required: 'int'">int p = param;</error>
val i = 0;
int j = i + 1;
val a = new ArrayList<String>();
Object o = a.get(0);
val b = new ArrayList<>();
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.String'">String s = b.get(0);</error>
o = b.get(0);
for (val v : a) {
String vStr = v;
}
}
}
@@ -0,0 +1,96 @@
/*
* Copyright 2000-2014 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.codeInsight.daemon;
import com.intellij.JavaTestUtil;
import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
import com.intellij.psi.*;
import com.intellij.psi.augment.PsiAugmentProvider;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.testFramework.PlatformTestUtil;
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
public class PsiAugmentProviderTest extends LightCodeInsightFixtureTestCase {
private static final String LOMBOK_VAL_FQN = "lombok.val";
private static final String LOMBOK_VAL_SHORT_NAME = "val";
public void testLombokVal() {
PlatformTestUtil.registerExtension(PsiAugmentProvider.EP_NAME, new PsiAugmentProvider() {
@NotNull
@Override
public <Psi extends PsiElement> List<Psi> getAugments(@NotNull PsiElement element, @NotNull Class<Psi> type) {
return Collections.<Psi>emptyList();
}
@Nullable
@Override
protected PsiType inferType(PsiTypeElement typeElement) {
final PsiElement parent = typeElement.getParent();
if (parent instanceof PsiLocalVariable && ((PsiLocalVariable)parent).getInitializer() != null ||
parent instanceof PsiParameter && ((PsiParameter)parent).getDeclarationScope() instanceof PsiForeachStatement) {
final String text = typeElement.getText();
if (LOMBOK_VAL_SHORT_NAME.equals(text) || LOMBOK_VAL_FQN.equals(text)) {
final PsiJavaCodeReferenceElement referenceElement = typeElement.getInnermostComponentReferenceElement();
if (referenceElement != null) {
final PsiElement resolve = referenceElement.resolve();
if (resolve instanceof PsiClass) {
if (parent instanceof PsiLocalVariable) {
final PsiExpression initializer = ((PsiVariable)parent).getInitializer();
assertNotNull(initializer);
final PsiType initializerType = initializer.getType();
if (initializer instanceof PsiNewExpression) {
final PsiJavaCodeReferenceElement reference = ((PsiNewExpression)initializer).getClassOrAnonymousClassReference();
if (reference != null) {
final PsiReferenceParameterList parameterList = reference.getParameterList();
if (parameterList != null) {
final PsiTypeElement[] elements = parameterList.getTypeParameterElements();
if (elements.length == 1 && elements[0].getType() instanceof PsiDiamondType) {
return TypeConversionUtil.erasure(initializerType);
}
}
}
}
return initializerType;
}
final PsiForeachStatement foreachStatement = (PsiForeachStatement)((PsiParameter)parent).getDeclarationScope();
assertNotNull(foreachStatement);
final PsiExpression iteratedValue = foreachStatement.getIteratedValue();
if (iteratedValue != null) {
return JavaGenericsUtil.getCollectionItemType(iteratedValue);
}
}
}
}
}
return null;
}
}, myTestRootDisposable);
myFixture.addClass("package lombok; public @interface val{}");
myFixture.testHighlighting(false, false, false, getTestName(false) + ".java");
}
@Override
protected String getTestDataPath() {
return JavaTestUtil.getJavaTestDataPath() + "/codeInsight/daemonCodeAnalyzer/augment";
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2014 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.
@@ -71,12 +71,12 @@ public class ExternalJavadocPresentationTest extends LightCodeInsightTestCase {
}
@Override
public void doBuildFromStream(String surl, Reader input, StringBuffer data, boolean search4Encoding) throws IOException {
super.doBuildFromStream(surl, input, data, search4Encoding);
public void doBuildFromStream(String url, Reader input, StringBuilder data, boolean search4Encoding) throws IOException {
super.doBuildFromStream(url, input, data, search4Encoding);
}
}
JavadocExternalTestFilter filter = new JavadocExternalTestFilter(getProject());
final StringBuffer extractedData = new StringBuffer();
StringBuilder extractedData = new StringBuilder();
filter.doBuildFromStream(url, new StringReader(LoadTextUtil.loadText(pageTextFile).toString()), extractedData, false);
assertEquals(LoadTextUtil.loadText(expectedTextFile).toString(), extractedData.toString());
}
@@ -20,8 +20,6 @@ import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.util.ProgressIndicatorBase;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Trinity;
@@ -31,8 +29,8 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.*;
import com.intellij.psi.PsiElement;
import com.intellij.util.SystemProperties;
import com.intellij.util.io.UrlConnectionUtil;
import com.intellij.util.net.HttpConfigurable;
import com.intellij.util.ThrowableConsumer;
import com.intellij.util.io.HttpRequests;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -40,54 +38,53 @@ import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.Locale;
import java.util.concurrent.Future;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
/**
* @author db
* @since May 2, 2003
*/
public abstract class AbstractExternalFilter {
private static final Logger LOG = Logger.getInstance(AbstractExternalFilter.class);
private static final boolean EXTRACT_IMAGES_FROM_JARS = SystemProperties.getBooleanProperty("extract.doc.images", true);
@NotNull public static final String QUICK_DOC_DIR_NAME = "quickdoc";
@NotNull
@NonNls
public static final String QUICK_DOC_DIR_NAME = "quickdoc";
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.javadoc.JavaDocExternalFilter");
private static final Pattern ourClassDataStartPattern = Pattern.compile("START OF CLASS DATA", Pattern.CASE_INSENSITIVE);
private static final Pattern ourClassDataEndPattern = Pattern.compile("SUMMARY ========", Pattern.CASE_INSENSITIVE);
private static final Pattern ourClassDataStartPattern = Pattern.compile("START OF CLASS DATA", Pattern.CASE_INSENSITIVE);
private static final Pattern ourClassDataEndPattern = Pattern.compile("SUMMARY ========", Pattern.CASE_INSENSITIVE);
private static final Pattern ourNonClassDataEndPattern = Pattern.compile("<A NAME=", Pattern.CASE_INSENSITIVE);
protected static @NonNls final Pattern ourAnchorsuffix = Pattern.compile("#(.*)$");
protected static @NonNls final Pattern ourHTMLFilesuffix = Pattern.compile("/([^/]*[.][hH][tT][mM][lL]?)$");
private static @NonNls final Pattern ourAnnihilator = Pattern.compile("/[^/^.]*/[.][.]/");
private static @NonNls final Pattern ourIMGselector =
@NonNls
protected static final Pattern ourAnchorSuffix = Pattern.compile("#(.*)$");
protected static @NonNls final Pattern ourHtmlFileSuffix = Pattern.compile("/([^/]*[.][hH][tT][mM][lL]?)$");
private static @NonNls final Pattern ourAnnihilator = Pattern.compile("/[^/^.]*/[.][.]/");
private static @NonNls final Pattern ourImgSelector =
Pattern.compile("<IMG[ \\t\\n\\r\\f]+SRC=\"([^>]*?)\"", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
private static @NonNls final Pattern ourPathInsideJarPattern = Pattern.compile(
private static @NonNls final Pattern ourPathInsideJarPattern = Pattern.compile(
String.format("%s(.+\\.jar)!/(.+?)[^/]+", JarFileSystem.PROTOCOL_PREFIX),
Pattern.CASE_INSENSITIVE | Pattern.DOTALL
);
private static @NonNls final String JAR_PROTOCOL = "jar:";
@NonNls private static final String HR = "<HR>";
@NonNls private static final String P = "<P>";
@NonNls private static final String DL = "<DL>";
@NonNls protected static final String H2 = "</H2>";
@NonNls protected static final String HTML_CLOSE = "</HTML>";
@NonNls protected static final String HTML = "<HTML>";
@NonNls private static final String BR = "<BR>";
@NonNls private static final String DT = "<DT>";
private static final Pattern CHARSET_META_PATTERN =
private static @NonNls final String JAR_PROTOCOL = "jar:";
@NonNls private static final String HR = "<HR>";
@NonNls private static final String P = "<P>";
@NonNls private static final String DL = "<DL>";
@NonNls protected static final String H2 = "</H2>";
@NonNls protected static final String HTML_CLOSE = "</HTML>";
@NonNls protected static final String HTML = "<HTML>";
@NonNls private static final String BR = "<BR>";
@NonNls private static final String DT = "<DT>";
private static final Pattern CHARSET_META_PATTERN =
Pattern.compile("<meta[^>]+\\s*charset=\"?([\\w\\-]*)\\s*\">", Pattern.CASE_INSENSITIVE);
private static final String FIELD_SUMMARY = "<!-- =========== FIELD SUMMARY =========== -->";
private static final String CLASS_SUMMARY = "<div class=\"summary\">";
private final HttpConfigurable myHttpConfigurable = HttpConfigurable.getInstance();
private static final String FIELD_SUMMARY = "<!-- =========== FIELD SUMMARY =========== -->";
private static final String CLASS_SUMMARY = "<div class=\"summary\">";
protected static abstract class RefConvertor {
@NotNull private final Pattern mySelector;
@NotNull
private final Pattern mySelector;
public RefConvertor(@NotNull Pattern selector) {
mySelector = selector;
@@ -124,7 +121,7 @@ public abstract class AbstractExternalFilter {
}
}
protected final RefConvertor myIMGConvertor = new RefConvertor(ourIMGselector) {
protected final RefConvertor myIMGConvertor = new RefConvertor(ourImgSelector) {
@Override
protected String convertReference(String root, String href) {
if (StringUtil.startsWithChar(href, '#')) {
@@ -192,7 +189,7 @@ public abstract class AbstractExternalFilter {
root = VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, "/" + path);
}
}
return ourHTMLFilesuffix.matcher(root).replaceAll("/") + href;
return ourHtmlFileSuffix.matcher(root).replaceAll("/") + href;
}
};
@@ -210,39 +207,40 @@ public abstract class AbstractExternalFilter {
public String correctRefs(String root, String read) {
String result = read;
for (RefConvertor myReferenceConvertor : getRefConvertors()) {
for (RefConvertor myReferenceConvertor : getRefConverters()) {
result = myReferenceConvertor.refFilter(root, result);
}
return result;
}
protected abstract RefConvertor[] getRefConvertors();
protected abstract RefConvertor[] getRefConverters();
@Nullable
private static Reader getReaderByUrl(final String surl, final HttpConfigurable httpConfigurable, final ProgressIndicator pi)
throws IOException
{
if (surl.startsWith(JAR_PROTOCOL)) {
VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(BrowserUtil.getDocURL(surl));
private static void getReaderByUrl(@NotNull String url, final @NotNull ThrowableConsumer<Reader, IOException> consumer)
throws IOException {
if (url.startsWith(JAR_PROTOCOL)) {
VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(BrowserUtil.getDocURL(url));
if (file != null) {
consumer.consume(new StringReader(VfsUtilCore.loadText(file)));
}
return;
}
if (file == null) {
final URL parsedUrl = BrowserUtil.getURL(url);
if (parsedUrl == null) {
return;
}
HttpRequests.request(parsedUrl.toString()).connect(new HttpRequests.RequestProcessor<Void>() {
@Override
public Void process(@NotNull HttpRequests.Request request) throws IOException {
String contentEncoding = guessEncoding(parsedUrl);
InputStream inputStream = request.getInputStream();
//noinspection IOResourceOpenedButNotSafelyClosed
consumer.consume(contentEncoding != null ? new MyReader(inputStream, contentEncoding) : new MyReader(inputStream));
return null;
}
return new StringReader(VfsUtilCore.loadText(file));
}
URL url = BrowserUtil.getURL(surl);
if (url == null) {
return null;
}
final URLConnection urlConnection = httpConfigurable.openConnection(url.toString());
final String contentEncoding = guessEncoding(url);
final InputStream inputStream =
pi != null ? UrlConnectionUtil.getConnectionInputStreamWithException(urlConnection, pi) : urlConnection.getInputStream();
//noinspection IOResourceOpenedButNotSafelyClosed
return contentEncoding != null ? new MyReader(inputStream, contentEncoding) : new MyReader(inputStream);
});
}
private static String guessEncoding(URL url) {
@@ -264,31 +262,34 @@ public abstract class AbstractExternalFilter {
catch (IOException ignored) {
}
finally {
if (reader != null)
if (reader != null) {
try {
reader.close();
}
catch (IOException ignored) {
}
}
}
return result;
}
@Nullable
@SuppressWarnings({"HardCodedStringLiteral"})
public String getExternalDocInfo(final String surl) throws Exception {
public String getExternalDocInfo(final String url) throws Exception {
Application app = ApplicationManager.getApplication();
if (!app.isUnitTestMode() && app.isDispatchThread() || app.isWriteAccessAllowed()) {
LOG.error("May block indefinitely: shouldn't be called from EDT or under write lock");
return null;
}
if (surl == null) return null;
if (url == null) {
return null;
}
if (MyJavadocFetcher.isFree()) {
final MyJavadocFetcher fetcher = new MyJavadocFetcher(surl, new MyDocBuilder() {
final MyJavadocFetcher fetcher = new MyJavadocFetcher(url, new MyDocBuilder() {
@Override
public void buildFromStream(String surl, Reader input, StringBuffer result) throws IOException {
doBuildFromStream(surl, input, result);
public void buildFromStream(String url, Reader input, StringBuilder result) throws IOException {
doBuildFromStream(url, input, result);
}
});
final Future<?> fetcherFuture = app.executeOnPooledThread(fetcher);
@@ -304,7 +305,7 @@ public abstract class AbstractExternalFilter {
throw exception;
}
final String docText = correctRefs(ourAnchorsuffix.matcher(surl).replaceAll(""), fetcher.getData());
final String docText = correctRefs(ourAnchorSuffix.matcher(url).replaceAll(""), fetcher.getData());
if (LOG.isDebugEnabled()) {
LOG.debug("Filtered JavaDoc: " + docText + "\n");
}
@@ -318,13 +319,12 @@ public abstract class AbstractExternalFilter {
return getExternalDocInfo(docURL);
}
protected void doBuildFromStream(String surl, Reader input, StringBuffer data) throws IOException {
doBuildFromStream(surl, input, data, true);
protected void doBuildFromStream(String url, Reader input, StringBuilder data) throws IOException {
doBuildFromStream(url, input, data, true);
}
protected void doBuildFromStream(String surl, Reader input, StringBuffer data, boolean search4Encoding) throws IOException {
BufferedReader buf = new BufferedReader(input);
Trinity<Pattern, Pattern, Boolean> settings = getParseSettings(surl);
protected void doBuildFromStream(final String url, Reader input, final StringBuilder data, boolean search4Encoding) throws IOException {
Trinity<Pattern, Pattern, Boolean> settings = getParseSettings(url);
@NonNls Pattern startSection = settings.first;
@NonNls Pattern endSection = settings.second;
boolean useDt = settings.third;
@@ -349,6 +349,8 @@ public abstract class AbstractExternalFilter {
String read;
String contentEncoding = null;
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
BufferedReader buf = new BufferedReader(input);
do {
read = buf.readLine();
if (read != null && search4Encoding && read.contains("charset")) {
@@ -360,21 +362,23 @@ public abstract class AbstractExternalFilter {
}
while (read != null && !startSection.matcher(StringUtil.toUpperCase(read)).find());
if (input instanceof MyReader && contentEncoding != null) {
if (!contentEncoding.equalsIgnoreCase(CharsetToolkit.UTF8) &&
!contentEncoding.equals(((MyReader)input).getEncoding()))
{ //restart page parsing with correct encoding
Reader stream;
try {
stream = getReaderByUrl(surl, myHttpConfigurable, new ProgressIndicatorBase());
}
catch (ProcessCanceledException e) {
return;
}
data.delete(0, data.length());
doBuildFromStream(surl, new MyReader(((MyReader)stream).getInputStream(), contentEncoding), data, false);
if (input instanceof MyReader && contentEncoding != null && !contentEncoding.equalsIgnoreCase(CharsetToolkit.UTF8) &&
!contentEncoding.equals(((MyReader)input).getEncoding())) {
//restart page parsing with correct encoding
try {
final String finalContentEncoding = contentEncoding;
getReaderByUrl(url, new ThrowableConsumer<Reader, IOException>() {
@Override
public void consume(Reader reader) throws IOException {
data.delete(0, data.length());
doBuildFromStream(url, new MyReader(((MyReader)reader).getInputStream(), finalContentEncoding), data, false);
}
});
}
catch (ProcessCanceledException e) {
return;
}
return;
}
if (read == null) {
@@ -386,7 +390,7 @@ public abstract class AbstractExternalFilter {
boolean skip = false;
do {
if (StringUtil.toUpperCase(read).contains(H2) && !read.toUpperCase().contains("H2")) { // read=class name in <H2>
if (StringUtil.toUpperCase(read).contains(H2) && !read.toUpperCase(Locale.ENGLISH).contains("H2")) { // read=class name in <H2>
data.append(H2);
skip = true;
}
@@ -403,8 +407,7 @@ public abstract class AbstractExternalFilter {
data.append(DL);
StringBuffer classDetails = new StringBuffer();
StringBuilder classDetails = new StringBuilder();
while (((read = buf.readLine()) != null) && !StringUtil.toUpperCase(read).equals(HR) && !StringUtil.toUpperCase(read).equals(P)) {
if (reachTheEnd(data, read, classDetails)) return;
appendLine(classDetails, read);
@@ -438,9 +441,9 @@ public abstract class AbstractExternalFilter {
/**
* Decides what settings should be used for parsing content represented by the given url.
*
* @param url url which points to the target content
* @return following data: (start interested data boundary pattern; end interested data boundary pattern;
* replace table data by &lt;dt&gt;)
* @param url url which points to the target content
* @return following data: (start interested data boundary pattern; end interested data boundary pattern;
* replace table data by &lt;dt&gt;)
*/
@NotNull
protected Trinity<Pattern, Pattern, Boolean> getParseSettings(@NotNull String url) {
@@ -448,7 +451,7 @@ public abstract class AbstractExternalFilter {
Pattern endSection = ourClassDataEndPattern;
boolean useDt = true;
Matcher anchorMatcher = ourAnchorsuffix.matcher(url);
Matcher anchorMatcher = ourAnchorSuffix.matcher(url);
if (anchorMatcher.find()) {
useDt = false;
startSection = Pattern.compile(Pattern.quote("<a name=\"" + anchorMatcher.group(1) + "\""), Pattern.CASE_INSENSITIVE);
@@ -457,10 +460,9 @@ public abstract class AbstractExternalFilter {
return Trinity.create(startSection, endSection, useDt);
}
private static boolean reachTheEnd(StringBuffer data, String read, StringBuffer classDetails) {
private static boolean reachTheEnd(StringBuilder data, String read, StringBuilder classDetails) {
if (StringUtil.indexOfIgnoreCase(read, FIELD_SUMMARY, 0) != -1 ||
StringUtil.indexOfIgnoreCase(read, CLASS_SUMMARY, 0) != -1)
{
StringUtil.indexOfIgnoreCase(read, CLASS_SUMMARY, 0) != -1) {
data.append(classDetails);
data.append(HTML_CLOSE);
return true;
@@ -470,8 +472,9 @@ public abstract class AbstractExternalFilter {
@Nullable
static String parseContentEncoding(@NotNull String htmlLine) {
if (!htmlLine.contains("charset"))
if (!htmlLine.contains("charset")) {
return null;
}
final Matcher matcher = CHARSET_META_PATTERN.matcher(htmlLine);
if (matcher.find()) {
return matcher.group(1);
@@ -479,28 +482,27 @@ public abstract class AbstractExternalFilter {
return null;
}
private static void appendLine(final StringBuffer buffer, final String read) {
private static void appendLine(StringBuilder buffer, final String read) {
buffer.append(read);
buffer.append("\n");
}
private interface MyDocBuilder {
void buildFromStream(String surl, Reader input, StringBuffer result) throws IOException;
void buildFromStream(String url, Reader input, StringBuilder result) throws IOException;
}
private static class MyJavadocFetcher implements Runnable {
private static boolean ourFree = true;
private final StringBuffer data = new StringBuffer();
private final String surl;
private static boolean ourFree = true;
private final StringBuilder data = new StringBuilder();
private final String url;
private final MyDocBuilder myBuilder;
private final Exception[] myExceptions = new Exception[1];
private final HttpConfigurable myHttpConfigurable;
private Exception myException;
public MyJavadocFetcher(final String surl, MyDocBuilder builder) {
this.surl = surl;
public MyJavadocFetcher(String url, MyDocBuilder builder) {
this.url = url;
myBuilder = builder;
//noinspection AssignmentToStaticFieldFromInstanceMethod
ourFree = false;
myHttpConfigurable = HttpConfigurable.getInstance();
}
public static boolean isFree() {
@@ -514,51 +516,36 @@ public abstract class AbstractExternalFilter {
@Override
public void run() {
try {
if (surl == null) {
if (url == null) {
return;
}
Reader stream = null;
try {
stream = getReaderByUrl(surl, myHttpConfigurable, new ProgressIndicatorBase());
getReaderByUrl(url, new ThrowableConsumer<Reader, IOException>() {
@Override
public void consume(Reader reader) throws IOException {
myBuilder.buildFromStream(url, reader, data);
}
});
}
catch (ProcessCanceledException e) {
return;
catch (ProcessCanceledException ignored) {
}
catch (IOException e) {
myExceptions[0] = e;
}
if (stream == null) {
return;
}
try {
myBuilder.buildFromStream(surl, stream, data);
}
catch (final IOException e) {
myExceptions[0] = e;
}
finally {
try {
stream.close();
}
catch (IOException e) {
myExceptions[0] = e;
}
myException = e;
}
}
finally {
//noinspection AssignmentToStaticFieldFromInstanceMethod
ourFree = true;
}
}
public Exception getException() {
return myExceptions[0];
return myException;
}
public void cleanup() {
myExceptions[0] = null;
myException = null;
}
}
@@ -37,19 +37,19 @@ import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.*;
import com.intellij.util.io.HttpRequests;
import com.intellij.util.concurrency.BoundedTaskExecutor;
import com.intellij.util.containers.hash.LinkedHashMap;
import com.intellij.util.download.DownloadableFileDescription;
import com.intellij.util.download.FileDownloader;
import com.intellij.util.io.HttpRequests;
import com.intellij.util.net.IOExceptionDialog;
import com.intellij.util.net.NetUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.ide.PooledThreadExecutor;
import javax.swing.*;
import java.io.*;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
@@ -315,22 +315,8 @@ public class FileDownloaderImpl implements FileDownloader {
return existingFile;
}
File tempFile = FileUtil.createTempFile("download.", ".tmp");
boolean deleteFile = true;
OutputStream out = new BufferedOutputStream(new FileOutputStream(tempFile));
try {
indicator.setText2(IdeBundle.message("progress.download.file.text", description.getPresentableFileName(), presentableUrl));
indicator.setIndeterminate(size == -1);
NetUtils.copyStreamContent(indicator, request.getInputStream(), out, size);
deleteFile = false;
return tempFile;
}
finally {
out.close();
if (deleteFile) {
FileUtil.delete(tempFile);
}
}
indicator.setText2(IdeBundle.message("progress.download.file.text", description.getPresentableFileName(), presentableUrl));
return request.saveToFile(FileUtil.createTempFile("download.", ".tmp"), indicator);
}
});
}
@@ -19,17 +19,21 @@ import com.intellij.ide.IdeBundle;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.SystemProperties;
import com.intellij.util.net.HttpConfigurable;
import com.intellij.util.net.NetUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import java.io.IOException;
import java.io.InputStream;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLClassLoader;
@@ -58,6 +62,11 @@ public abstract class HttpRequests {
InputStream getInputStream() throws IOException;
boolean isSuccessful() throws IOException;
@NotNull
File saveToFile(@NotNull File file, @Nullable ProgressIndicator indicator) throws IOException;
byte[] toBytes(@Nullable ProgressIndicator indicator) throws IOException;
}
public interface RequestProcessor<T> {
@@ -154,6 +163,26 @@ public abstract class HttpRequests {
return errorValue;
}
}
public void saveToFile(@NotNull final File file, @Nullable final ProgressIndicator indicator) throws IOException {
connect(new HttpRequests.RequestProcessor<Void>() {
@Override
public Void process(@NotNull HttpRequests.Request request) throws IOException {
request.saveToFile(file, indicator);
return null;
}
});
}
@NotNull
public byte[] toBytes(@Nullable final ProgressIndicator indicator) throws IOException {
return connect(new HttpRequests.RequestProcessor<byte[]>() {
@Override
public byte[] process(@NotNull HttpRequests.Request request) throws IOException {
return request.toBytes(indicator);
}
});
}
}
@NotNull
@@ -216,6 +245,53 @@ public abstract class HttpRequests {
((HttpURLConnection)myConnection).disconnect();
}
}
@NotNull
public byte[] toBytes(@Nullable ProgressIndicator indicator) throws IOException {
int contentLength = getConnection().getContentLength();
BufferExposingByteArrayOutputStream out = new BufferExposingByteArrayOutputStream(contentLength > 0 ? contentLength : 32 * 1024);
NetUtils.copyStreamContent(indicator, getInputStream(), out, contentLength);
return ArrayUtil.realloc(out.getInternalBuffer(), out.size());
}
@NotNull
public File saveToFile(@NotNull File file, @Nullable ProgressIndicator indicator) throws IOException {
OutputStream out = null;
boolean deleteFile = true;
try {
if (indicator != null) {
indicator.checkCanceled();
}
FileUtilRt.createParentDirs(file);
out = new FileOutputStream(file);
NetUtils.copyStreamContent(indicator, getInputStream(), out, getConnection().getContentLength());
deleteFile = false;
}
catch (IOException e) {
URLConnection connection = getConnection();
String errorMessage = "Cannot download '" + builder.myUrl + ", headers: " + connection.getHeaderFields();
if (connection instanceof HttpURLConnection) {
HttpURLConnection httpConnection = (HttpURLConnection)connection;
errorMessage += "', response code: " + httpConnection.getResponseCode()
+ ", response message: " + httpConnection.getResponseMessage();
}
throw new IOException(errorMessage, e);
}
finally {
try {
if (out != null) {
out.close();
}
}
finally {
if (deleteFile) {
FileUtilRt.delete(file);
}
}
}
return file;
}
}
RequestImpl request = new RequestImpl();
@@ -178,12 +178,11 @@ class HttpProxySettingsUi implements ConfigurableUi<HttpConfigurable> {
//noinspection ConstantConditions
HttpRequests.request(answer)
.readTimeout(3 * 1000)
.connect(new HttpRequests.RequestProcessor<Object>() {
.connect(new HttpRequests.RequestProcessor<Void>() {
@Override
public Object process(@NotNull HttpRequests.Request request) throws IOException {
int responseCode = ((HttpURLConnection)request.getConnection()).getResponseCode();
if (responseCode != HttpURLConnection.HTTP_OK) {
exceptionReference.set(new IOException("Error code: " + responseCode));
public Void process(@NotNull HttpRequests.Request request) throws IOException {
if (!request.isSuccessful()) {
exceptionReference.set(new IOException("Error code: " + ((HttpURLConnection)request.getConnection()).getResponseCode()));
}
return null;
}
@@ -27,7 +27,6 @@ import com.intellij.openapi.util.BuildNumber;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.io.HttpRequests;
import com.intellij.util.io.URLUtil;
import com.intellij.util.net.NetUtils;
import org.apache.http.client.utils.URIBuilder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -36,7 +35,10 @@ import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URISyntaxException;
import java.net.URLConnection;
@@ -125,13 +127,7 @@ public class RepositoryHelper {
if (pluginListFile != null) {
synchronized (RepositoryHelper.class) {
FileUtil.ensureExists(pluginListFile.getParentFile());
OutputStream output = new FileOutputStream(pluginListFile);
try {
NetUtils.copyStreamContent(indicator, request.getInputStream(), output, connection.getContentLength());
}
finally {
output.close();
}
request.saveToFile(pluginListFile, indicator);
return loadPluginList(pluginListFile);
}
}
@@ -35,12 +35,12 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.PathUtil;
import com.intellij.util.io.HttpRequests;
import com.intellij.util.io.ZipUtil;
import com.intellij.util.net.NetUtils;
import org.apache.http.client.utils.URIBuilder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.io.File;
import java.io.IOException;
import java.net.*;
import java.util.List;
import java.util.UUID;
@@ -254,19 +254,9 @@ public class PluginDownloader {
return HttpRequests.request(myPluginUrl).gzip(false).forceHttps(myForceHttps).connect(new HttpRequests.RequestProcessor<File>() {
@Override
public File process(@NotNull HttpRequests.Request request) throws IOException {
indicator.checkCanceled();
URLConnection connection = request.getConnection();
OutputStream fileOut = new FileOutputStream(file);
try {
NetUtils.copyStreamContent(indicator, request.getInputStream(), fileOut, connection.getContentLength());
}
finally {
fileOut.close();
}
request.saveToFile(file, indicator);
if (myFileName == null) {
myFileName = guessFileName(connection, file);
myFileName = guessFileName(request.getConnection(), file);
}
File newFile = new File(file.getParentFile(), myFileName);
@@ -43,7 +43,6 @@ import com.intellij.util.PlatformUtils;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.io.HttpRequests;
import com.intellij.util.io.URLUtil;
import com.intellij.util.net.NetUtils;
import com.intellij.util.ui.UIUtil;
import org.apache.http.client.utils.URIBuilder;
import org.jdom.Document;
@@ -54,7 +53,9 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.event.HyperlinkEvent;
import java.io.*;
import java.io.File;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
@@ -587,15 +588,7 @@ public final class UpdateChecker {
File tempFile = HttpRequests.request(url).gzip(false).forceHttps(forceHttps).connect(new HttpRequests.RequestProcessor<File>() {
@Override
public File process(@NotNull HttpRequests.Request request) throws IOException {
File tempFile = FileUtil.createTempFile("ij.platform.", ".patch", true);
OutputStream output = new BufferedOutputStream(new FileOutputStream(tempFile));
try {
NetUtils.copyStreamContent(indicator, request.getInputStream(), output, request.getConnection().getContentLength());
}
finally {
output.close();
}
return tempFile;
return request.saveToFile(FileUtil.createTempFile("ij.platform.", ".patch", true), indicator);
}
});
@@ -21,6 +21,7 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeRegistry;
import com.intellij.openapi.fileTypes.FileTypes;
import com.intellij.openapi.progress.util.AbstractProgressIndicatorExBase;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsBundle;
import com.intellij.openapi.vfs.VirtualFile;
@@ -30,7 +31,8 @@ import com.intellij.util.io.HttpRequests;
import com.intellij.util.net.ssl.CertificateManager;
import org.jetbrains.annotations.NotNull;
import java.io.*;
import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
public class DefaultRemoteContentProvider extends RemoteContentProvider {
@@ -63,39 +65,20 @@ public class DefaultRemoteContentProvider extends RemoteContentProvider {
.connect(new HttpRequests.RequestProcessor<Object>() {
@Override
public Object process(@NotNull HttpRequests.Request request) throws IOException {
HttpURLConnection connection = (HttpURLConnection)request.getConnection();
int responseCode = connection.getResponseCode();
if (responseCode != HttpURLConnection.HTTP_OK) {
throw new IOException(IdeBundle.message("error.connection.failed.with.http.code.N", responseCode));
if (!request.isSuccessful()) {
throw new IOException(IdeBundle.message("error.connection.failed.with.http.code.N", ((HttpURLConnection)request.getConnection()).getResponseCode()));
}
int size = connection.getContentLength();
OutputStream output = new BufferedOutputStream(new FileOutputStream(file));
try {
callback.setProgressText(VfsBundle.message("download.progress.downloading", presentableUrl), size == -1);
if (size != -1) {
int size = request.getConnection().getContentLength();
callback.setProgressText(VfsBundle.message("download.progress.downloading", presentableUrl), size == -1);
request.saveToFile(file, new AbstractProgressIndicatorExBase() {
@Override
public void setFraction(double fraction) {
callback.setProgressFraction(0);
}
});
int count;
byte[] buf = new byte[4096];
int total = 0;
while ((count = request.getInputStream().read(buf)) > 0) {
if (callback.isCancelled()) {
return null;
}
total += count;
if (size > 0) {
callback.setProgressFraction((double)total / size);
}
output.write(buf, 0, count);
}
}
finally {
output.close();
}
FileType fileType = RemoteFileUtil.getFileType(connection.getContentType());
FileType fileType = RemoteFileUtil.getFileType(request.getConnection().getContentType());
if (fileType == FileTypes.PLAIN_TEXT) {
FileType fileTypeByFileName = FileTypeRegistry.getInstance().getFileTypeByFileName(PathUtilRt.getFileName(url.getPath()));
if (fileTypeByFileName != FileTypes.UNKNOWN) {
@@ -217,6 +217,8 @@ emmet.configuration.title=Emmet
emmet.enable.label=&Enable XML Emmet
emmet.filters.enabled.by.default=Filters enabled by default
emmet.enable.preview=Enable &abbreviation preview
emmet.allow.compact.boolean.attributes=Allow &compact boolean attributes in HTML (use <div attr> instead of <div attr="attr">)
emmet.boolean.attributes=Boolean attributes
emmet.expand.abbreviation.with=Expand &abbreviation with
title.cannot.create.html.file=Cannot create HTML file
@@ -18,11 +18,9 @@ import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.util.io.HttpRequests;
import com.intellij.util.net.NetUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
@@ -141,25 +139,15 @@ public abstract class AbstractAttachSourceProvider implements AttachSourcesProvi
@Override
public ActionCallback perform(List<LibraryOrderEntry> orderEntriesContainingFile) {
final ActionCallback callback = new ActionCallback();
Task task = new Task.Backgroundable(myProject, "Downloading sources...", true) {
@Override
public void run(@NotNull final ProgressIndicator indicator) {
final ByteArrayOutputStream out;
final byte[] bytes;
try {
LOG.info("Downloading sources JAR: " + myUrl);
indicator.checkCanceled();
out = HttpRequests.request(myUrl).connect(new HttpRequests.RequestProcessor<ByteArrayOutputStream>() {
@Override
public ByteArrayOutputStream process(@NotNull HttpRequests.Request request) throws IOException {
int contentLength = request.getConnection().getContentLength();
ByteArrayOutputStream out = new ByteArrayOutputStream(contentLength > 0 ? contentLength : 100 * 1024);
NetUtils.copyStreamContent(indicator, request.getInputStream(), out, contentLength);
return out;
}
});
bytes = HttpRequests.request(myUrl).toBytes(indicator);
}
catch (IOException e) {
LOG.warn(e);
@@ -183,7 +171,7 @@ public abstract class AbstractAttachSourceProvider implements AttachSourcesProvi
public void run() {
AccessToken accessToken = WriteAction.start();
try {
storeFile(out.toByteArray());
storeFile(bytes);
}
finally {
accessToken.finish();
@@ -30,13 +30,10 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.util.io.HttpRequests;
import com.intellij.util.net.NetUtils;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.Set;
@@ -68,19 +65,10 @@ public abstract class DownloadManager {
file = HttpRequests.request(location).connect(new HttpRequests.RequestProcessor<File>() {
@Override
public File process(@NotNull HttpRequests.Request request) throws IOException {
int total = request.getConnection().getContentLength();
String name = Integer.toHexString(System.identityHashCode(this)) + "_" +
Integer.toHexString(location.hashCode()) + "_" +
location.substring(location.lastIndexOf('/') + 1);
File file = new File(myResourcePath, name.lastIndexOf('.') == -1 ? name + ".xml" : name);
OutputStream out = new FileOutputStream(file);
try {
NetUtils.copyStreamContent(myProgress, request.getInputStream(), out, total);
}
finally {
out.close();
}
return file;
return request.saveToFile(new File(myResourcePath, name.lastIndexOf('.') == -1 ? name + ".xml" : name), myProgress);
}
});
@@ -249,7 +249,7 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
<template description="&lt;script&gt;...&lt;/script&gt;" name="script" toReformat="true" toShortenFQNames="true" value="&lt;script&gt;$END$&lt;/script&gt;">
<template description="&lt;script&gt;...&lt;/script&gt;" name="script" toReformat="true" toShortenFQNames="true" value="&lt;script !src=&quot;&quot;&gt;$END$&lt;/script&gt;">
<context>
<option name="HTML_TEXT" value="true"/>
</context>
@@ -15,7 +15,6 @@
*/
package com.intellij.application.options.emmet;
import com.intellij.codeInsight.template.emmet.filters.BemEmmetFilter;
import com.intellij.codeInsight.template.emmet.filters.ZenCodingFilter;
import com.intellij.codeInsight.template.impl.TemplateSettings;
import com.intellij.openapi.components.*;
@@ -24,7 +23,6 @@ import com.intellij.util.xmlb.XmlSerializerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.Set;
@State(
@@ -32,32 +30,12 @@ import java.util.Set;
storages = @Storage(file = StoragePathMacros.APP_CONFIG + "/emmet.xml")
)
public class EmmetOptions implements PersistentStateComponent<EmmetOptions> {
/**
* @deprecated delete after IDEA 14
*/
private boolean myBemFilterEnabledByDefault = false;
private boolean myEmmetEnabled = true;
private int myEmmetExpandShortcut = TemplateSettings.TAB_CHAR;
private boolean myFuzzySearchEnabled = true;
private boolean myAutoInsertCssPrefixedEnabled = true;
private boolean myPreviewEnabled = false;
private boolean myCompactBooleanAllowed = true;
private Set<String> myBooleanAttributes = ContainerUtil.newHashSet("contenteditable", "seamless");
private Set<String> myFiltersEnabledByDefault = ContainerUtil.newHashSet();
@NotNull
private Map<String, Integer> prefixes = ContainerUtil.newHashMap();
/**
* @deprecated delete after IDEA 14
*/
public boolean isBemFilterEnabledByDefault() {
return myBemFilterEnabledByDefault;
}
/**
* @deprecated delete after IDEA 14
*/
public void setBemFilterEnabledByDefault(boolean enableBemFilterByDefault) {
myBemFilterEnabledByDefault = enableBemFilterByDefault;
}
@NotNull
public Set<String> getFiltersEnabledByDefault() {
@@ -70,7 +48,7 @@ public class EmmetOptions implements PersistentStateComponent<EmmetOptions> {
public boolean isFilterEnabledByDefault(@NotNull ZenCodingFilter filter) {
return myFiltersEnabledByDefault.contains(filter.getSuffix());
}
}
public void setEmmetExpandShortcut(int emmetExpandShortcut) {
myEmmetExpandShortcut = emmetExpandShortcut;
@@ -87,7 +65,23 @@ public class EmmetOptions implements PersistentStateComponent<EmmetOptions> {
public void setPreviewEnabled(boolean previewEnabled) {
myPreviewEnabled = previewEnabled;
}
public boolean isCompactBooleanAllowed() {
return myCompactBooleanAllowed;
}
public void setCompactBooleanAllowed(boolean compactBooleanAllowed) {
myCompactBooleanAllowed = compactBooleanAllowed;
}
public Set<String> getBooleanAttributes() {
return myBooleanAttributes;
}
public void setBooleanAttributes(@NotNull Set<String> booleanAttributes) {
myBooleanAttributes = booleanAttributes;
}
public boolean isEmmetEnabled() {
return myEmmetEnabled;
}
@@ -96,30 +90,6 @@ public class EmmetOptions implements PersistentStateComponent<EmmetOptions> {
myEmmetEnabled = emmetEnabled;
}
@Deprecated
//use {@link CssEmmetOptions}
public boolean isAutoInsertCssPrefixedEnabled() {
return myAutoInsertCssPrefixedEnabled;
}
@Deprecated
//use {@link CssEmmetOptions}
public void setAutoInsertCssPrefixedEnabled(boolean autoInsertCssPrefixedEnabled) {
myAutoInsertCssPrefixedEnabled = autoInsertCssPrefixedEnabled;
}
@Deprecated
//use {@link CssEmmetOptions}
public void setFuzzySearchEnabled(boolean fuzzySearchEnabled) {
myFuzzySearchEnabled = fuzzySearchEnabled;
}
@Deprecated
//use {@link CssEmmetOptions}
public boolean isFuzzySearchEnabled() {
return myFuzzySearchEnabled;
}
@Nullable
@Override
public EmmetOptions getState() {
@@ -129,28 +99,9 @@ public class EmmetOptions implements PersistentStateComponent<EmmetOptions> {
@Override
public void loadState(final EmmetOptions state) {
XmlSerializerUtil.copyBean(state, this);
// todo delete after IDEA 14
if (myFiltersEnabledByDefault.isEmpty() && myBemFilterEnabledByDefault) {
myFiltersEnabledByDefault.add(BemEmmetFilter.SUFFIX);
}
}
public static EmmetOptions getInstance() {
return ServiceManager.getService(EmmetOptions.class);
}
@NotNull
@Deprecated
//use {@link CssEmmetOptions}
public Map<String, Integer> getPrefixes() {
return prefixes;
}
@SuppressWarnings("UnusedDeclaration")
@Deprecated
//use {@link CssEmmetOptions}
public void setPrefixes(@NotNull Map<String, Integer> prefixes) {
this.prefixes = prefixes;
}
}
@@ -3,12 +3,12 @@
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="130"/>
<xy x="20" y="20" width="999" height="135"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<grid id="369dc" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="369dc" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
@@ -38,7 +38,7 @@
<grid id="cdbb7" binding="myFiltersListPanel" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
@@ -51,6 +51,14 @@
</component>
</children>
</grid>
<component id="3bd4e" class="com.intellij.ui.components.JBCheckBox" binding="myAllowCompactBooleanAttributesJBCheckBox" default-binding="true">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="9" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/XmlBundle" key="emmet.allow.compact.boolean.attributes"/>
</properties>
</component>
</children>
</grid>
</children>
@@ -40,6 +40,7 @@ public class XmlEmmetConfigurable implements UnnamedConfigurable, Disposable, Co
private JBCheckBox myEnablePreviewJBCheckBox;
private CheckBoxList<ZenCodingFilter> myFiltersCheckBoxList;
private JPanel myFiltersListPanel;
private JBCheckBox myAllowCompactBooleanAttributesJBCheckBox;
public XmlEmmetConfigurable() {
myEnableEmmetJBCheckBox.addActionListener(new ActionListener() {
@@ -47,6 +48,7 @@ public class XmlEmmetConfigurable implements UnnamedConfigurable, Disposable, Co
public void actionPerformed(ActionEvent e) {
boolean selected = myEnableEmmetJBCheckBox.isSelected();
myEnablePreviewJBCheckBox.setEnabled(selected);
myAllowCompactBooleanAttributesJBCheckBox.setEnabled(selected);
myFiltersCheckBoxList.setEnabled(selected);
}
});
@@ -74,6 +76,7 @@ public class XmlEmmetConfigurable implements UnnamedConfigurable, Disposable, Co
EmmetOptions emmetOptions = EmmetOptions.getInstance();
return emmetOptions.isEmmetEnabled() != myEnableEmmetJBCheckBox.isSelected() ||
emmetOptions.isPreviewEnabled() != myEnablePreviewJBCheckBox.isSelected() ||
emmetOptions.isCompactBooleanAllowed() != myAllowCompactBooleanAttributesJBCheckBox.isSelected() ||
!emmetOptions.getFiltersEnabledByDefault().equals(enabledFilters());
}
@@ -82,6 +85,7 @@ public class XmlEmmetConfigurable implements UnnamedConfigurable, Disposable, Co
EmmetOptions emmetOptions = EmmetOptions.getInstance();
emmetOptions.setEmmetEnabled(myEnableEmmetJBCheckBox.isSelected());
emmetOptions.setPreviewEnabled(myEnablePreviewJBCheckBox.isSelected());
emmetOptions.setCompactBooleanAllowed(myAllowCompactBooleanAttributesJBCheckBox.isSelected());
emmetOptions.setFiltersEnabledByDefault(enabledFilters());
}
@@ -92,6 +96,8 @@ public class XmlEmmetConfigurable implements UnnamedConfigurable, Disposable, Co
myEnableEmmetJBCheckBox.setSelected(emmetOptions.isEmmetEnabled());
myEnablePreviewJBCheckBox.setEnabled(emmetOptions.isEmmetEnabled());
myEnablePreviewJBCheckBox.setSelected(emmetOptions.isPreviewEnabled());
myAllowCompactBooleanAttributesJBCheckBox.setEnabled(emmetOptions.isEmmetEnabled());
myAllowCompactBooleanAttributesJBCheckBox.setSelected(emmetOptions.isCompactBooleanAllowed());
Set<String> enabledByDefault = emmetOptions.getFiltersEnabledByDefault();
for (ZenCodingFilter filter : ZenCodingFilter.getInstances()) {
@@ -46,9 +46,9 @@ import com.intellij.psi.search.PsiElementProcessor;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.*;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.io.HttpRequests;
import com.intellij.util.net.HttpConfigurable;
import com.intellij.util.net.IOExceptionDialog;
import com.intellij.util.net.NetUtils;
import com.intellij.xml.XmlBundle;
import com.intellij.xml.util.XmlUtil;
import org.jetbrains.annotations.NonNls;
@@ -56,10 +56,10 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
/**
@@ -515,30 +515,17 @@ public class FetchExtResourceAction extends BaseExtResourceAction implements Wat
}
@Nullable
private static FetchResult fetchData(final Project project, final String dtdUrl, ProgressIndicator indicator) throws IOException {
private static FetchResult fetchData(final Project project, final String dtdUrl, final ProgressIndicator indicator) throws IOException {
try {
URL url = new URL(dtdUrl);
HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();
urlConnection.addRequestProperty("accept", "text/xml,application/xml,text/html,*/*");
int contentLength = urlConnection.getContentLength();
ByteArrayOutputStream out = new ByteArrayOutputStream();
InputStream in = urlConnection.getInputStream();
String contentType;
try {
contentType = urlConnection.getContentType();
NetUtils.copyStreamContent(indicator, in, out, contentLength);
}
finally {
in.close();
}
FetchResult result = new FetchResult();
result.bytes = out.toByteArray();
result.contentType = contentType;
return result;
HttpRequests.request(dtdUrl).accept("text/xml,application/xml,text/html,*/*").connect(new HttpRequests.RequestProcessor<FetchResult>() {
@Override
public FetchResult process(@NotNull HttpRequests.Request request) throws IOException {
FetchResult result = new FetchResult();
result.bytes = request.toBytes(indicator);
result.contentType = request.getConnection().getContentType();
return result;
}
});
}
catch (MalformedURLException e) {
if (!ApplicationManager.getApplication().isUnitTestMode()) {
@@ -228,4 +228,13 @@ public abstract class EmmetParser {
}
return null;
}
@Nullable
protected ZenCodingToken nextToken(int i) {
if (myIndex + i < myTokens.size()) {
return myTokens.get(myIndex + i);
}
return null;
}
}
@@ -29,6 +29,7 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlTag;
import com.intellij.psi.xml.XmlTokenType;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.Stack;
import com.intellij.xml.util.HtmlUtil;
import org.jetbrains.annotations.NonNls;
@@ -41,11 +42,10 @@ import java.util.regex.Pattern;
import static com.intellij.openapi.util.text.StringUtil.startsWithIgnoreCase;
/**
* User: zolotov
* Date: 2/7/13
*/
public class XmlEmmetParser extends EmmetParser {
public static final String DEFAULT_ATTRIBUTE_NAME = "%default";
public static final String BOOLEAN_ATTRIBUTE_VALUE = "%boolean";
private static final String DEFAULT_TAG = "div";
private static final int DEFAULT_LOREM_LENGTH = 30;
private static final Pattern LOREM_PATTERN = Pattern.compile("(lorem|lipsum)(\\d*)");
@@ -89,6 +89,28 @@ public class XmlEmmetParser extends EmmetParser {
}
}
@Nullable
private String getAttributeNameByToken(ZenCodingToken token) {
if (!(token instanceof IdentifierToken)) {
return null;
}
String name = ((IdentifierToken)token).getText();
if (name.isEmpty()) {
return null;
}
final XmlTag tag = XmlElementFactory.getInstance(myCallback.getProject()).createTagFromText("<tag " + name + "=''/>");
XmlAttribute[] attributes = tag.getAttributes();
if (attributes.length == 1) {
return attributes[0].getName();
}
else {
return null;
}
}
@NotNull
private static String getAttributeValueByToken(@Nullable ZenCodingToken token) {
if (token == null) {
@@ -138,12 +160,12 @@ public class XmlEmmetParser extends EmmetParser {
return null;
}
final List<Couple<String>> attrList = parseSelectors();
if (mustHaveSelector && attrList.size() == 0) {
final Map<String, String> attributes = parseSelectors();
if (mustHaveSelector && attributes.isEmpty()) {
return null;
}
final TemplateToken templateToken = new TemplateToken(templateKey, attrList);
final TemplateToken templateToken = new TemplateToken(templateKey, attributes);
if (!setTemplate(templateToken, template)) {
return null;
}
@@ -203,16 +225,16 @@ public class XmlEmmetParser extends EmmetParser {
loremWordsCount = group == null || group.isEmpty() ? DEFAULT_LOREM_LENGTH : Integer.parseInt(group);
}
final List<Couple<String>> attrList = parseSelectors();
final Map<String, String> attributes = parseSelectors();
ZenCodingToken token = getToken();
boolean isRepeating = token instanceof OperationToken && ((OperationToken)token).getSign() == '*';
if (!attrList.isEmpty() || isRepeating) {
if (!attributes.isEmpty() || isRepeating) {
String wrapTag = suggestTagName();
TemplateImpl template = myCallback.findApplicableTemplate(templateKey);
if (template == null && !ZenCodingUtil.isXML11ValidQName(templateKey)) {
return null;
}
final TemplateToken templateToken = new TemplateToken(wrapTag, attrList);
final TemplateToken templateToken = new TemplateToken(wrapTag, attributes);
if (!setTemplate(templateToken, template)) {
return null;
}
@@ -246,55 +268,25 @@ public class XmlEmmetParser extends EmmetParser {
}
}
@SuppressWarnings("unchecked")
@NotNull
private List<Couple<String>> parseSelectors() {
final List<Couple<String>> result = new ArrayList<Couple<String>>();
int classAttrPosition = -1;
int idAttrPosition = -1;
final StringBuilder classAttrBuilder = new StringBuilder();
final StringBuilder idAttrBuilder = new StringBuilder();
while (true) {
final List<Couple<String>> attrList = parseSelector();
if (attrList == null) {
if (classAttrPosition != -1) {
result.set(classAttrPosition, Couple.of(getClassAttributeName(), classAttrBuilder.toString()));
}
if (idAttrPosition != -1) {
result.set(idAttrPosition, Couple.of(HtmlUtil.ID_ATTRIBUTE_NAME, idAttrBuilder.toString()));
}
return result;
}
private Map<String, String> parseSelectors() {
final Map<String, String> result = ContainerUtil.newLinkedHashMap();
List<Couple<String>> attrList = parseSelector();
while (attrList != null) {
for (Couple<String> attr : attrList) {
if (getClassAttributeName().equals(attr.first)) {
if (classAttrBuilder.length() > 0) {
classAttrBuilder.append(' ');
}
classAttrBuilder.append(attr.second);
if (classAttrPosition == -1) {
classAttrPosition = result.size();
result.add(attr);
}
result.put(getClassAttributeName(), (StringUtil.notNullize(result.get(getClassAttributeName())) + " " + attr.second).trim());
}
else if (HtmlUtil.ID_ATTRIBUTE_NAME.equals(attr.first)) {
if (idAttrBuilder.length() > 0) {
idAttrBuilder.append(' ');
}
idAttrBuilder.append(attr.second);
if (idAttrPosition == -1) {
idAttrPosition = result.size();
result.add(attr);
}
result.put(HtmlUtil.ID_ATTRIBUTE_NAME, (StringUtil.notNullize(result.get(HtmlUtil.ID_ATTRIBUTE_NAME)) + " " + attr.second).trim());
}
else {
result.add(attr);
result.put(attr.first, attr.second);
}
}
attrList = parseSelector();
}
return result;
}
@Nullable
@@ -349,46 +341,57 @@ public class XmlEmmetParser extends EmmetParser {
@Nullable
private Couple<String> parseAttribute() {
ZenCodingToken token = getToken();
if (!(token instanceof IdentifierToken)) {
return null;
String attributeName = getAttributeNameByToken(getToken());
if (attributeName != null && !attributeName.isEmpty()) {
if (nextToken(1) == ZenCodingTokens.DOT) {
if (isEndOfAttribute(nextToken(2))) {
// boolean attribute
advance(); //id
advance(); // dot
return Couple.of(attributeName, BOOLEAN_ATTRIBUTE_VALUE);
}
}
else {
// attribute with value
advance();
if (getToken() == ZenCodingTokens.EQ) {
advance();
return Couple.of(attributeName, parseAttributeValue());
}
else {
return Couple.of(attributeName, "");
}
}
}
String name = ((IdentifierToken)token).getText();
if (name.isEmpty()) {
return null;
final String impliedValue = parseAttributeValue();
if (!impliedValue.isEmpty()) {
// implied attribute
return Couple.of(DEFAULT_ATTRIBUTE_NAME, impliedValue);
}
return null;
}
final XmlTag tag = XmlElementFactory.getInstance(myCallback.getProject()).createTagFromText("<tag " + name + "=''/>");
XmlAttribute[] attributes = tag.getAttributes();
if (attributes.length == 1) {
name = attributes[0].getName();
}
else {
return null;
}
advance();
token = getToken();
if (token != ZenCodingTokens.EQ) {
return Couple.of(name, "");
}
advance();
@NotNull
private String parseAttributeValue() {
ZenCodingToken token;
final StringBuilder attrValueBuilder = new StringBuilder();
String value;
do {
token = getToken();
value = getAttributeValueByToken(token);
attrValueBuilder.append(value);
if (token != null && token != ZenCodingTokens.CLOSING_SQ_BRACKET
&& token != ZenCodingTokens.SPACE && token != ZenCodingTokens.COMMA) {
if (!isEndOfAttribute(token)) {
advance();
}
}
while (token != null && token != ZenCodingTokens.CLOSING_SQ_BRACKET
&& token != ZenCodingTokens.SPACE && token != ZenCodingTokens.COMMA);
return Couple.of(name, attrValueBuilder.toString());
while (!isEndOfAttribute(token));
return attrValueBuilder.toString();
}
private static boolean isEndOfAttribute(@Nullable ZenCodingToken nextToken) {
return nextToken == null || nextToken == ZenCodingTokens.SPACE || nextToken == ZenCodingTokens.CLOSING_SQ_BRACKET
|| nextToken == ZenCodingTokens.COMMA;
}
}
@@ -41,7 +41,6 @@ import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.JBPopupListener;
import com.intellij.openapi.ui.popup.LightweightWindowEvent;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.registry.Registry;
@@ -67,9 +66,6 @@ import java.util.*;
import java.util.List;
/**
* @author Eugene.Kudelevsky
*/
public class ZenCodingTemplate extends CustomLiveTemplateBase {
public static final char MARKER = '\0';
private static final String EMMET_RECENT_WRAP_ABBREVIATIONS_KEY = "emmet.recent.wrap.abbreviations";
@@ -302,11 +298,9 @@ public class ZenCodingTemplate extends CustomLiveTemplateBase {
if (node instanceof TemplateNode) {
final TemplateToken token = ((TemplateNode)node).getTemplateToken();
if (token != null) {
final List<Couple<String>> attributes = token.getAttribute2Value();
final Couple<String> singleAttribute = ContainerUtil.getFirstItem(attributes);
if (singleAttribute == null || HtmlUtil.CLASS_ATTRIBUTE_NAME.equalsIgnoreCase(singleAttribute.first) && StringUtil.isEmpty(singleAttribute.second)) {
return true;
}
final Map<String, String> attributes = token.getAttributes();
return attributes.isEmpty() ||
attributes.containsKey(HtmlUtil.CLASS_ATTRIBUTE_NAME) && StringUtil.isEmpty(attributes.get(HtmlUtil.CLASS_ATTRIBUTE_NAME));
}
}
return false;
@@ -22,7 +22,6 @@ import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet;
import com.intellij.codeInsight.template.emmet.nodes.GenerationNode;
import com.intellij.lang.xml.XMLLanguage;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement;
@@ -30,10 +29,7 @@ import com.intellij.xml.util.HtmlUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.*;
import java.util.regex.Pattern;
import static com.google.common.base.Strings.isNullOrEmpty;
@@ -43,9 +39,6 @@ import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Lists.newLinkedList;
/**
* User: zolotov
* Date: 2/4/13
* <p/>
* Bem filter for emmet support.
* See the original source code here: https://github.com/emmetio/emmet/blob/master/javascript/filters/bem.js
* And documentation here: http://docs.emmet.io/filters/bem/
@@ -115,10 +108,10 @@ public class BemEmmetFilter extends ZenCodingFilter {
@NotNull
@Override
public GenerationNode filterNode(@NotNull final GenerationNode node) {
final List<Couple<String>> attribute2Value = node.getTemplateToken().getAttribute2Value();
Couple<String> classNamePair = getClassPair(attribute2Value);
if (classNamePair != null) {
Iterable<String> classNames = extractClasses(classNamePair.second);
final Map<String, String> attributes = node.getTemplateToken().getAttributes();
String classValue = attributes.get(HtmlUtil.CLASS_ATTRIBUTE_NAME);
if (classValue != null) {
Iterable<String> classNames = extractClasses(classValue);
BEM_STATE.set(node, new BemState(suggestBlockName(classNames), null, null));
final Set<String> newClassNames = ImmutableSet.copyOf(concat(transform(classNames, new Function<String, Iterable<String>>() {
@Override
@@ -126,7 +119,7 @@ public class BemEmmetFilter extends ZenCodingFilter {
return processClassName(className, node);
}
})));
attribute2Value.add(Couple.of(HtmlUtil.CLASS_ATTRIBUTE_NAME, CLASS_NAME_JOINER.join(newClassNames)));
attributes.put(HtmlUtil.CLASS_ATTRIBUTE_NAME, CLASS_NAME_JOINER.join(newClassNames));
}
return node;
}
@@ -278,23 +271,6 @@ public class BemEmmetFilter extends ZenCodingFilter {
return find(classNames, BLOCK_NAME_PREDICATE, find(classNames, STARTS_WITH_LETTER, ""));
}
/**
* Retrieve pair "class" => classAttributeValue from node attributeList
*
* @param attribute2Value node's attributes
* @return pointer to pair
*/
@Nullable
private static Couple<String> getClassPair(@NotNull List<Couple<String>> attribute2Value) {
for (int i = 0; i < attribute2Value.size(); i++) {
Couple<String> pair = attribute2Value.get(i);
if (HtmlUtil.CLASS_ATTRIBUTE_NAME.equals(pair.first) && !isNullOrEmpty(pair.second)) {
return attribute2Value.remove(i);
}
}
return null;
}
private static class BemState {
@Nullable private String block;
@Nullable private String element;
@@ -21,7 +21,6 @@ import com.intellij.codeInsight.template.emmet.generators.XmlZenCodingGeneratorI
import com.intellij.codeInsight.template.emmet.nodes.GenerationNode;
import com.intellij.codeInsight.template.emmet.tokens.TemplateToken;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.Couple;
import com.intellij.psi.PsiElement;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlDocument;
@@ -29,9 +28,6 @@ import com.intellij.psi.xml.XmlTag;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
/**
* @author Eugene.Kudelevsky
*/
public class XslZenCodingFilter extends ZenCodingFilter {
private final XmlZenCodingGenerator myDelegate = new XmlZenCodingGeneratorImpl();
@NonNls private static final String SELECT_ATTR_NAME = "select";
@@ -45,10 +41,8 @@ public class XslZenCodingFilter extends ZenCodingFilter {
if (document != null) {
final XmlTag tag = document.getRootTag();
if (tag != null) {
for (Couple<String> pair : token.getAttribute2Value()) {
if (SELECT_ATTR_NAME.equals(pair.first)) {
return node;
}
if (token.getAttributes().containsKey(SELECT_ATTR_NAME)) {
return node;
}
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
@@ -22,7 +22,6 @@ import com.intellij.codeInsight.template.impl.TemplateImpl;
import com.intellij.diagnostic.AttachmentFactory;
import com.intellij.openapi.diagnostic.Logger;
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;
@@ -36,12 +35,8 @@ import com.intellij.xml.util.HtmlUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
/**
* @author Eugene.Kudelevsky
*/
public abstract class XmlZenCodingGenerator extends ZenCodingGenerator {
@Override
public TemplateImpl generateTemplate(@NotNull TemplateToken token, boolean hasChildren, @NotNull PsiElement context) {
@@ -70,19 +65,19 @@ public abstract class XmlZenCodingGenerator extends ZenCodingGenerator {
if (document != null) {
XmlTag tag = document.getRootTag();
if (tag != null) {
return toString(tag, token.getAttribute2Value(), hasChildren, context);
return toString(tag, token.getAttributes(), hasChildren, context);
}
}
return file.getText();
}
public abstract String toString(@NotNull XmlTag tag,
@NotNull List<Couple<String>> attribute2Value,
@NotNull Map<String, String> attributes,
boolean hasChildren,
@NotNull PsiElement context);
@NotNull
public abstract String buildAttributesString(@NotNull List<Couple<String>> attribute2value,
public abstract String buildAttributesString(@NotNull Map<String, String> attribute2value,
boolean hasChildren,
int numberInIteration,
int totalIterations, @Nullable String surroundedText);
@@ -111,12 +106,13 @@ public abstract class XmlZenCodingGenerator extends ZenCodingGenerator {
break;
}
}
prevVisibleLeaf = PsiTreeUtil.prevVisibleLeaf(prevVisibleLeaf);
prevVisibleLeaf = PsiTreeUtil.prevVisibleLeaf(prevVisibleLeaf);
}
if (startOffset < 0 || currentOffset > documentText.length() || currentOffset < startOffset) {
Logger.getInstance(getClass()).error("Error while calculating emmet abbreviation. Offset: " + currentOffset + "; Start: " + startOffset,
AttachmentFactory.createAttachment(editor.getDocument()));
Logger.getInstance(getClass())
.error("Error while calculating emmet abbreviation. Offset: " + currentOffset + "; Start: " + startOffset,
AttachmentFactory.createAttachment(editor.getDocument()));
return null;
}
String key = computeKey(documentText.subSequence(startOffset, currentOffset));
@@ -29,7 +29,6 @@ import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.options.UnnamedConfigurable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.xml.XmlChildRole;
@@ -38,8 +37,8 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* @author Eugene.Kudelevsky
@@ -54,7 +53,7 @@ public class XmlZenCodingGeneratorImpl extends XmlZenCodingGenerator {
@Override
@NotNull
public String toString(@NotNull XmlTag tag,
@NotNull List<Couple<String>> attribute2Value,
@NotNull Map<String, String> attributes,
boolean hasChildren,
@NotNull PsiElement context) {
FileType fileType = context.getContainingFile().getFileType();
@@ -66,21 +65,18 @@ public class XmlZenCodingGeneratorImpl extends XmlZenCodingGenerator {
@Override
@NotNull
public String buildAttributesString(@NotNull List<Couple<String>> attribute2value,
public String buildAttributesString(@NotNull Map<String, String> attributes,
boolean hasChildren,
int numberInIteration,
int totalIterations, @Nullable String surroundedText) {
StringBuilder result = new StringBuilder();
for (Iterator<Couple<String>> it = attribute2value.iterator(); it.hasNext();) {
Couple<String> pair = it.next();
String name = pair.first;
String value = ZenCodingUtil.getValue(pair.second, numberInIteration, totalIterations, surroundedText);
for (Map.Entry<String, String> entry : attributes.entrySet()) {
String name = entry.getKey();
String value = ZenCodingUtil.getValue(entry.getValue(), numberInIteration, totalIterations, surroundedText);
result.append(getAttributeString(name, value));
if (it.hasNext()) {
result.append(' ');
}
result.append(' ');
}
return result.toString();
return result.toString().trim();
}
@Override
@@ -16,8 +16,10 @@
package com.intellij.codeInsight.template.emmet.nodes;
import com.google.common.base.Strings;
import com.intellij.application.options.emmet.EmmetOptions;
import com.intellij.codeInsight.template.CustomTemplateCallback;
import com.intellij.codeInsight.template.LiveTemplateBuilder;
import com.intellij.codeInsight.template.emmet.XmlEmmetParser;
import com.intellij.codeInsight.template.emmet.ZenCodingUtil;
import com.intellij.codeInsight.template.emmet.filters.SingleLineEmmetFilter;
import com.intellij.codeInsight.template.emmet.filters.ZenCodingFilter;
@@ -33,7 +35,7 @@ import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
@@ -46,6 +48,7 @@ import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.*;
import com.intellij.util.LocalTimeCounter;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.HashSet;
import com.intellij.xml.util.HtmlUtil;
@@ -187,9 +190,9 @@ public class GenerationNode extends UserDataHolderBase {
Map<String, String> predefinedValues;
if (myTemplateToken instanceof TemplateToken && generator instanceof XmlZenCodingGenerator) {
TemplateToken xmlTemplateToken = myTemplateToken;
List<Couple<String>> attr2value = new ArrayList<Couple<String>>(xmlTemplateToken.getAttribute2Value());
parentTemplate = invokeXmlTemplate(xmlTemplateToken, callback, generator, hasChildren, attr2value);
predefinedValues = buildPredefinedValues(attr2value, (XmlZenCodingGenerator)generator, hasChildren);
Map<String, String> attributes = xmlTemplateToken.getAttributes();
parentTemplate = invokeXmlTemplate(xmlTemplateToken, callback, generator, hasChildren, attributes);
predefinedValues = buildPredefinedValues(attributes, (XmlZenCodingGenerator)generator, hasChildren);
}
else {
parentTemplate = invokeTemplate(myTemplateToken, hasChildren, callback, generator);
@@ -265,7 +268,7 @@ public class GenerationNode extends UserDataHolderBase {
CustomTemplateCallback callback,
@Nullable ZenCodingGenerator generator,
final boolean hasChildren,
final List<Couple<String>> attr2value) {
final Map<String, String> attributes) {
/*assert generator == null || generator instanceof XmlZenCodingGenerator :
"The generator cannot process TemplateToken because it doesn't inherit XmlZenCodingGenerator";*/
@@ -274,17 +277,17 @@ public class GenerationNode extends UserDataHolderBase {
final XmlFile xmlFile = token.getFile();
PsiFileFactory fileFactory = PsiFileFactory.getInstance(xmlFile.getProject());
XmlFile dummyFile = (XmlFile)fileFactory.createFileFromText("dummy.xml", StdFileTypes.XML, xmlFile.getText());
XmlFile dummyFile = (XmlFile)fileFactory.createFileFromText("dummy.html", StdFileTypes.HTML, xmlFile.getText());
final XmlTag tag = dummyFile.getRootTag();
if (tag != null) {
for (Couple<String> pair : attr2value) {
if (Strings.isNullOrEmpty(pair.second)) {
template.addVariable(prepareVariableName(pair.first), "", "", true);
for (Map.Entry<String, String> attribute : attributes.entrySet()) {
if (Strings.isNullOrEmpty(attribute.getValue())) {
template.addVariable(prepareVariableName(attribute.getKey()), "", "", true);
}
}
XmlTag tag1 = hasChildren ? expandEmptyTagIfNecessary(tag) : tag;
setAttributeValues(tag1, attr2value);
XmlFile physicalFile = (XmlFile)fileFactory.createFileFromText("dummy.xml", StdFileTypes.XML, tag1.getContainingFile().getText(),
setAttributeValues(tag1, attributes, callback);
XmlFile physicalFile = (XmlFile)fileFactory.createFileFromText("dummy.html", StdFileTypes.HTML, tag1.getContainingFile().getText(),
LocalTimeCounter.currentTime(), true);
VirtualFile vFile = physicalFile.getVirtualFile();
if (vFile != null) {
@@ -381,44 +384,153 @@ public class GenerationNode extends UserDataHolderBase {
}
@Nullable
private Map<String, String> buildPredefinedValues(List<Couple<String>> attribute2value,
private Map<String, String> buildPredefinedValues(@NotNull Map<String, String> attributes,
@Nullable XmlZenCodingGenerator generator,
boolean hasChildren) {
if (generator == null) {
return Collections.emptyMap();
}
for (Couple<String> pair : attribute2value) {
if (ZenCodingUtil.containsSurroundedTextMarker(pair.second)) {
for (String value : attributes.values()) {
if (ZenCodingUtil.containsSurroundedTextMarker(value)) {
myContainsSurroundedTextMarker = true;
break;
}
}
String attributes = generator.buildAttributesString(attribute2value, hasChildren, myNumberInIteration, myTotalIterations, mySurroundedText);
attributes = attributes.length() > 0 ? ' ' + attributes : null;
String attributesString = generator.buildAttributesString(attributes, hasChildren, myNumberInIteration, myTotalIterations, mySurroundedText);
attributesString = attributesString.length() > 0 ? ' ' + attributesString : null;
Map<String, String> predefinedValues = null;
if (attributes != null) {
if (attributesString != null) {
predefinedValues = new HashMap<String, String>();
predefinedValues.put(TemplateToken.ATTRS, attributes);
predefinedValues.put(TemplateToken.ATTRS, attributesString);
}
return predefinedValues;
}
private void setAttributeValues(XmlTag tag, List<Couple<String>> attr2value) {
for (Iterator<Couple<String>> iterator = attr2value.iterator(); iterator.hasNext();) {
Couple<String> pair = iterator.next();
if (tag.getAttribute(pair.first) != null) {
if (ZenCodingUtil.containsSurroundedTextMarker(pair.second)) {
myContainsSurroundedTextMarker = true;
private void setAttributeValues(@NotNull XmlTag tag, @NotNull final Map<String, String> attributes, CustomTemplateCallback callback) {
final String defaultAttributeValue = attributes.get(XmlEmmetParser.DEFAULT_ATTRIBUTE_NAME);
if (defaultAttributeValue != null) {
attributes.remove(XmlEmmetParser.DEFAULT_ATTRIBUTE_NAME);
// exclude user defined attributes
final List<XmlAttribute> xmlAttributes = ContainerUtil.filter(tag.getAttributes(), new Condition<XmlAttribute>() {
@Override
public boolean value(XmlAttribute attribute) {
return !attributes.containsKey(attribute.getLocalName());
}
});
XmlAttribute defaultAttribute = findDefaultAttribute(xmlAttributes);
if (defaultAttribute == null) {
defaultAttribute = findImpliedAttribute(xmlAttributes);
}
if (defaultAttribute == null) {
defaultAttribute = findEmptyAttribute(xmlAttributes);
}
if (defaultAttribute != null) {
String attributeName = defaultAttribute.getName();
if (attributeName.length() > 1) {
if (isImpliedAttribute(attributeName) || isDefaultAttribute(attributeName)) {
defaultAttribute.setName(attributeName.substring(1));
}
final String oldValue = defaultAttribute.getValue();
if (oldValue != null && StringUtil.containsChar(oldValue, '|')) {
defaultAttribute.setValue(StringUtil.replace(oldValue, "|", defaultAttributeValue));
}
else {
defaultAttribute.setValue(defaultAttributeValue);
}
}
tag.setAttribute(pair.first,
Strings.isNullOrEmpty(pair.second)
? "$" + prepareVariableName(pair.first) + "$"
: ZenCodingUtil.getValue(pair.second, myNumberInIteration, myTotalIterations, mySurroundedText));
iterator.remove();
}
}
for (Map.Entry<String, String> attribute : attributes.entrySet()) {
final String attributeName = attribute.getKey();
final XmlAttribute xmlAttribute = tag.getAttribute(attributeName);
if (xmlAttribute != null) {
final String attributeValue = attribute.getValue();
if (ZenCodingUtil.containsSurroundedTextMarker(attributeValue)) {
myContainsSurroundedTextMarker = true;
}
final EmmetOptions emmetOptions = EmmetOptions.getInstance();
if (isBooleanAttribute(attributeName, attributeValue)) {
if (emmetOptions.isCompactBooleanAllowed() && ZenCodingUtil.isHtml(callback)) {
final XmlAttributeValue valueElement = xmlAttribute.getValueElement();
if (valueElement != null) {
final PsiElement prevSibling = valueElement.getPrevSibling();
if (prevSibling != null && prevSibling.textMatches("=")) {
xmlAttribute.deleteChildRange(prevSibling, valueElement);
}
}
}
else {
tag.setAttribute(attributeName, attributeName);
}
}
else {
tag.setAttribute(attributeName,
Strings.isNullOrEmpty(attributeValue)
? "$" + prepareVariableName(attributeName) + "$"
: ZenCodingUtil.getValue(attributeValue, myNumberInIteration, myTotalIterations, mySurroundedText));
}
}
}
// remove all implicit and default attributes
for (XmlAttribute xmlAttribute : tag.getAttributes()) {
final String xmlAttributeLocalName = xmlAttribute.getLocalName();
if (isImpliedAttribute(xmlAttributeLocalName) || isDefaultAttribute(xmlAttributeLocalName)) {
xmlAttribute.delete();
}
}
}
private static boolean isBooleanAttribute(String name, String value) {
if (value.equals(XmlEmmetParser.BOOLEAN_ATTRIBUTE_VALUE)) {
return true;
}
return Strings.isNullOrEmpty(value) && EmmetOptions.getInstance().getBooleanAttributes().contains(name.toLowerCase(Locale.US));
}
private static boolean isDefaultAttribute(String xmlAttributeLocalName) {
return StringUtil.startsWithChar(xmlAttributeLocalName, '@');
}
private static boolean isImpliedAttribute(String xmlAttributeLocalName) {
return StringUtil.startsWithChar(xmlAttributeLocalName, '!');
}
@Nullable
private static XmlAttribute findDefaultAttribute(@NotNull List<XmlAttribute> attributes) {
for (XmlAttribute attribute : attributes) {
if (isDefaultAttribute(attribute.getLocalName())) {
return attribute;
}
}
return null;
}
@Nullable
private static XmlAttribute findImpliedAttribute(@NotNull List<XmlAttribute> attributes) {
for (XmlAttribute attribute : attributes) {
if (isImpliedAttribute(attribute.getLocalName())) {
return attribute;
}
}
return null;
}
@Nullable
private static XmlAttribute findEmptyAttribute(@NotNull List<XmlAttribute> attributes) {
for (XmlAttribute attribute : attributes) {
final String attributeValue = attribute.getValue();
if (StringUtil.isEmpty(attributeValue) || StringUtil.startsWithChar(attributeValue, '$') && StringUtil.endsWithChar(attributeValue, '$')) {
return attribute;
}
}
return null;
}
private static boolean isNewLineBefore(CharSequence text, int offset) {
@@ -19,16 +19,12 @@ import com.intellij.codeInsight.template.CustomTemplateCallback;
import com.intellij.codeInsight.template.emmet.generators.LoremGenerator;
import com.intellij.codeInsight.template.emmet.tokens.TemplateToken;
import com.intellij.codeInsight.template.impl.TemplateImpl;
import com.intellij.openapi.util.Couple;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
/**
* @author Eugene.Kudelevsky
*/
public class LoremNode extends ZenCodingNode {
private final int myWordsCount;
private final LoremGenerator myLoremGenerator;
@@ -46,7 +42,7 @@ public class LoremNode extends ZenCodingNode {
boolean insertSurroundedTextAtTheEnd, GenerationNode parent) {
final TemplateToken templateToken = new TemplateToken("", Collections.<Couple<String>>emptyList());
final TemplateToken templateToken = new TemplateToken("");
final TemplateImpl template = new TemplateImpl("", myLoremGenerator.generate(myWordsCount, numberInIteration <= 0), "");
templateToken.setTemplate(template, callback);
final GenerationNode node = new GenerationNode(templateToken, numberInIteration,
@@ -15,25 +15,21 @@
*/
package com.intellij.codeInsight.template.emmet.nodes;
import com.google.common.base.Joiner;
import com.intellij.codeInsight.template.CustomTemplateCallback;
import com.intellij.codeInsight.template.emmet.ZenCodingUtil;
import com.intellij.codeInsight.template.emmet.generators.ZenCodingGenerator;
import com.intellij.codeInsight.template.emmet.tokens.TemplateToken;
import com.intellij.codeInsight.template.impl.TemplateImpl;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* @author Eugene.Kudelevsky
*/
public class TemplateNode extends ZenCodingNode {
private static final Joiner JOINER = Joiner.on(",");
private final TemplateToken myTemplateToken;
@Nullable private final ZenCodingGenerator myGenerator;
@@ -60,7 +56,7 @@ public class TemplateNode extends ZenCodingNode {
String templateKey = templateToken.getKey();
if (myGenerator != null && StringUtil.containsChar(templateKey, '$') && callback.findApplicableTemplate(templateKey) == null) {
String newTemplateKey = ZenCodingUtil.replaceMarkers(templateKey, numberInIteration, totalIterations, surroundedText);
TemplateToken newTemplateToken = new TemplateToken(newTemplateKey, templateToken.getAttribute2Value());
TemplateToken newTemplateToken = new TemplateToken(newTemplateKey, templateToken.getAttributes());
TemplateImpl template = myGenerator.createTemplateByKey(newTemplateKey);
if (template != null) {
template.setDeactivated(true);
@@ -71,15 +67,15 @@ public class TemplateNode extends ZenCodingNode {
GenerationNode node = new GenerationNode(templateToken, numberInIteration, totalIterations,
surroundedText, insertSurroundedTextAtTheEnd, parent);
return Arrays.asList(node);
return Collections.singletonList(node);
}
@Override
public String toString() {
String result = myTemplateToken.getKey();
List<Couple<String>> attributes = myTemplateToken.getAttribute2Value();
Map<String, String> attributes = myTemplateToken.getAttributes();
if (!attributes.isEmpty()) {
result += "[" + JOINER.join(myTemplateToken.getAttribute2Value()) + "]";
result += ContainerUtil.toString(attributes);
}
return "Template(" + result + ")";
}
@@ -89,8 +85,8 @@ public class TemplateNode extends ZenCodingNode {
TemplateImpl template = myTemplateToken.getTemplate();
if (template != null) {
int result = template.getTemplateText().length();
for (Couple<String> attribute : myTemplateToken.getAttribute2Value()) {
result += attribute.first.length() + attribute.second.length() + 4; //plus space, eq, quotes
for (Map.Entry<String, String> attribute : myTemplateToken.getAttributes().entrySet()) {
result += attribute.getKey().length() + attribute.getValue().length() + 4; //plus space, eq, quotes
}
return result;
}
@@ -16,10 +16,10 @@
package com.intellij.codeInsight.template.emmet.tokens;
import com.intellij.codeInsight.template.CustomTemplateCallback;
import com.intellij.codeInsight.template.emmet.XmlEmmetParser;
import com.intellij.codeInsight.template.impl.TemplateImpl;
import com.intellij.openapi.command.undo.UndoConstants;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFileFactory;
import com.intellij.psi.XmlElementFactory;
@@ -28,36 +28,34 @@ import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.ArrayUtil;
import com.intellij.util.LocalTimeCounter;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Collections;
import java.util.Map;
/**
* @author Eugene.Kudelevsky
*/
public class TemplateToken extends ZenCodingToken {
public static final String ATTRS = "ATTRS";
public final static TemplateToken EMPTY_TEMPLATE_TOKEN = new TemplateToken("", new ArrayList<Couple<String>>());
public final static TemplateToken EMPTY_TEMPLATE_TOKEN = new TemplateToken("", Collections.<String, String>emptyMap());
private final String myKey;
@NotNull private final String myKey;
private TemplateImpl myTemplate;
private final List<Couple<String>> myAttribute2Value;
@NotNull private final Map<String, String> myAttributes;
private XmlFile myFile;
public TemplateToken(String key) {
this(key, new ArrayList<Couple<String>>());
public TemplateToken(@NotNull String key) {
this(key, Collections.<String, String>emptyMap());
}
public TemplateToken(String key, List<Couple<String>> attribute2value) {
public TemplateToken(@NotNull String key, @NotNull Map<String, String> attribute2value) {
myKey = key;
myAttribute2Value = attribute2value;
myAttributes = attribute2value;
}
public List<Couple<String>> getAttribute2Value() {
return myAttribute2Value;
@NotNull
public Map<String, String> getAttributes() {
return myAttributes;
}
public XmlFile getFile() {
@@ -68,23 +66,24 @@ public class TemplateToken extends ZenCodingToken {
myFile = file;
}
@NotNull
public String getKey() {
return myKey;
}
public boolean setTemplate(TemplateImpl template, CustomTemplateCallback callback) {
public boolean setTemplate(@NotNull TemplateImpl template, @NotNull CustomTemplateCallback callback) {
myTemplate = template;
final XmlFile xmlFile = parseXmlFileInTemplate(template, callback, getAttribute2Value());
final XmlFile xmlFile = parseXmlFileInTemplate(template, callback, getAttributes());
setFile(xmlFile);
final XmlTag tag = xmlFile.getRootTag();
if (getAttribute2Value().size() > 0 && tag == null) {
if (!getAttributes().isEmpty() && tag == null) {
return false;
}
return true;
}
private static boolean containsAttrsVar(TemplateImpl template) {
private static boolean containsAttrsVar(@NotNull TemplateImpl template) {
for (int i = 0; i < template.getVariableCount(); i++) {
String varName = template.getVariableNameAt(i);
if (ATTRS.equals(varName)) {
@@ -95,9 +94,8 @@ public class TemplateToken extends ZenCodingToken {
}
@NotNull
private static XmlFile parseXmlFileInTemplate(TemplateImpl template,
CustomTemplateCallback callback,
List<Couple<String>> attributes) {
private static XmlFile parseXmlFileInTemplate(@NotNull TemplateImpl template, @NotNull CustomTemplateCallback callback,
@NotNull Map<String, String> attributes) {
XmlTag dummyRootTag = null;
String templateString = template.getString();
final PsiFileFactory psiFileFactory = PsiFileFactory.getInstance(callback.getProject());
@@ -110,7 +108,8 @@ public class TemplateToken extends ZenCodingToken {
}
templateString = dummyRootTag != null ? dummyRootTag.getContainingFile().getText() : templateString;
XmlFile xmlFile = (XmlFile)psiFileFactory.createFileFromText("dummy.xml", StdFileTypes.XML, templateString, LocalTimeCounter.currentTime(), true);
XmlFile xmlFile =
(XmlFile)psiFileFactory.createFileFromText("dummy.xml", StdFileTypes.XML, templateString, LocalTimeCounter.currentTime(), true);
VirtualFile vFile = xmlFile.getVirtualFile();
if (vFile != null) {
vFile.putUserData(UndoConstants.DONT_RECORD_UNDO, Boolean.TRUE);
@@ -119,22 +118,22 @@ public class TemplateToken extends ZenCodingToken {
}
private static void addMissingAttributes(XmlTag tag, List<Couple<String>> value) {
List<Couple<String>> attr2value = new ArrayList<Couple<String>>(value);
for (Iterator<Couple<String>> iterator = attr2value.iterator(); iterator.hasNext(); ) {
Couple<String> pair = iterator.next();
if (tag.getAttribute(pair.first) != null) {
iterator.remove();
private static void addMissingAttributes(@NotNull XmlTag tag, @NotNull Map<String, String> attributes) {
Map<String, String> missingAttributes = ContainerUtil.newLinkedHashMap();
for (Map.Entry<String, String> attribute : attributes.entrySet()) {
if (!XmlEmmetParser.DEFAULT_ATTRIBUTE_NAME.equals(attribute.getKey()) && tag.getAttribute(attribute.getKey()) == null) {
missingAttributes.put(attribute.getKey(), attribute.getValue());
}
}
addAttributesBefore(tag, attr2value);
addAttributesBefore(tag, missingAttributes);
}
private static void addAttributesBefore(XmlTag tag, List<Couple<String>> attr2value) {
private static void addAttributesBefore(@NotNull XmlTag tag, @NotNull Map<String, String> attributes) {
XmlAttribute firstAttribute = ArrayUtil.getFirstElement(tag.getAttributes());
XmlElementFactory factory = XmlElementFactory.getInstance(tag.getProject());
for (Couple<String> pair : attr2value) {
XmlAttribute xmlAttribute = factory.createXmlAttribute(pair.first, "");
for (String name : attributes.keySet()) {
XmlAttribute xmlAttribute = factory.createXmlAttribute(name, "");
if (firstAttribute != null) {
tag.addBefore(xmlAttribute, firstAttribute);
}
@@ -149,6 +148,7 @@ public class TemplateToken extends ZenCodingToken {
return myTemplate;
}
@NotNull
public String toString() {
return "TEMPLATE";
}