Merge remote-tracking branch 'origin/master'
@@ -93,6 +93,7 @@ import io.netty.handler.codec.protobuf.ProtobufDecoder;
|
||||
import io.netty.handler.codec.protobuf.ProtobufEncoder;
|
||||
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
|
||||
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
|
||||
import io.netty.util.internal.ThreadLocalRandom;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.ide.PooledThreadExecutor;
|
||||
@@ -999,6 +1000,9 @@ public class BuildManager implements ApplicationComponent{
|
||||
cmdLine.addParameter("-Didea.case.sensitive.fs=" + isFSCaseSensitive);
|
||||
}
|
||||
|
||||
// this will make netty initialization faster on some systems
|
||||
cmdLine.addParameter("-Dio.netty.initialSeedUniquifier=" + ThreadLocalRandom.getInitialSeedUniquifier());
|
||||
|
||||
boolean isProfilingMode = false;
|
||||
final String additionalOptions = config.COMPILER_PROCESS_ADDITIONAL_VM_OPTIONS;
|
||||
if (!StringUtil.isEmpty(additionalOptions)) {
|
||||
|
||||
@@ -106,7 +106,7 @@ public class CodeInsightUtil {
|
||||
if (parent == null) return PsiElement.EMPTY_ARRAY;
|
||||
while (true) {
|
||||
if (parent instanceof PsiStatement) {
|
||||
if (!(element1 instanceof PsiComment || element2 instanceof PsiComment)) {
|
||||
if (!(element1 instanceof PsiComment)) {
|
||||
parent = parent.getParent();
|
||||
}
|
||||
break;
|
||||
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 313 B |
|
After Width: | Height: | Size: 233 B |
|
After Width: | Height: | Size: 356 B |
|
After Width: | Height: | Size: 347 B |
|
After Width: | Height: | Size: 134 B |
|
After Width: | Height: | Size: 122 B |
|
After Width: | Height: | Size: 122 B |
|
After Width: | Height: | Size: 242 B |
|
After Width: | Height: | Size: 242 B |
|
After Width: | Height: | Size: 110 B |
|
After Width: | Height: | Size: 245 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 175 B |
@@ -107,7 +107,7 @@
|
||||
<text value="Scope:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="1fda7" class="javax.swing.JLabel">
|
||||
<component id="1fda7" class="javax.swing.JLabel" binding="myOptionalLabel">
|
||||
<constraints>
|
||||
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
|
||||
@@ -51,6 +51,7 @@ public class LayoutCodeDialog extends DialogWrapper {
|
||||
|
||||
private JPanel myActionsPanel;
|
||||
private JPanel myScopePanel;
|
||||
private JLabel myOptionalLabel;
|
||||
|
||||
private LayoutCodeOptions myRunOptions;
|
||||
|
||||
@@ -127,6 +128,8 @@ public class LayoutCodeDialog extends DialogWrapper {
|
||||
if (canRearrangeCode) {
|
||||
myRearrangeCodeCb.setSelected(myLastRunOptions.isRearrangeCode(myFile.getLanguage()));
|
||||
}
|
||||
|
||||
myOptionalLabel.setVisible(canOptimizeImports || canRearrangeCode);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
* Copyright 2000-2015 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.
|
||||
@@ -15,11 +15,8 @@
|
||||
*/
|
||||
package com.intellij.ide.ui.laf;
|
||||
|
||||
import com.intellij.ide.ui.LafManager;
|
||||
import com.intellij.ide.ui.laf.darcula.DarculaLaf;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.plaf.metal.DefaultMetalTheme;
|
||||
|
||||
/**
|
||||
@@ -36,19 +33,6 @@ public class IntelliJLaf extends DarculaLaf {
|
||||
return "intellijlaf";
|
||||
}
|
||||
|
||||
@Override
|
||||
public UIDefaults getDefaults() {
|
||||
UIDefaults defaults = super.getDefaults();
|
||||
if (SystemInfo.isLinux) {
|
||||
try {
|
||||
LafManagerImpl.initFontDefaults(defaults, "Dialog", 12);
|
||||
}
|
||||
catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
return defaults;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DefaultMetalTheme createMetalTheme() {
|
||||
return new IdeaBlueMetalTheme();
|
||||
|
||||
@@ -560,6 +560,7 @@ public final class LafManagerImpl extends LafManager implements ApplicationCompo
|
||||
|
||||
List<String> myIntKeys = Arrays.asList("Tree.leftChildIndent",
|
||||
"Tree.rightChildIndent");
|
||||
List<String> patched = new ArrayList<String>();
|
||||
for (Map.Entry<Object, Object> entry : defaults.entrySet()) {
|
||||
Object value = entry.getValue();
|
||||
String key = entry.getKey().toString();
|
||||
@@ -569,10 +570,16 @@ public final class LafManagerImpl extends LafManager implements ApplicationCompo
|
||||
entry.setValue(JBUI.insets(((InsetsUIResource)value)).asUIResource());
|
||||
} else if (value instanceof Integer) {
|
||||
if (key.endsWith(".maxGutterIconWidth") || myIntKeys.contains(key)) {
|
||||
entry.setValue(Integer.valueOf(JBUI.scale((Integer)value)));
|
||||
if (!"true".equals(defaults.get(key +".hidpi.patched"))) {
|
||||
entry.setValue(Integer.valueOf(JBUI.scale((Integer)value)));
|
||||
patched.add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (String key : patched) {
|
||||
defaults.put(key + ".hidpi.patched", "true");
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateToolWindows() {
|
||||
|
||||
@@ -42,10 +42,8 @@ import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author Konstantin Bulenkov
|
||||
@@ -82,7 +80,7 @@ public class DarculaLaf extends BasicLookAndFeel {
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
private static void log(Exception e) {
|
||||
// everything is gonna be alright
|
||||
// e.printStackTrace();
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -91,6 +89,7 @@ public class DarculaLaf extends BasicLookAndFeel {
|
||||
final Method superMethod = BasicLookAndFeel.class.getDeclaredMethod("getDefaults");
|
||||
superMethod.setAccessible(true);
|
||||
final UIDefaults metalDefaults = (UIDefaults)superMethod.invoke(new MetalLookAndFeel());
|
||||
|
||||
final UIDefaults defaults = (UIDefaults)superMethod.invoke(base);
|
||||
if (SystemInfo.isLinux) {
|
||||
if (!Registry.is("darcula.use.native.fonts.on.linux")) {
|
||||
@@ -124,6 +123,9 @@ public class DarculaLaf extends BasicLookAndFeel {
|
||||
if (SystemInfo.isWindows) {
|
||||
//JFrame.setDefaultLookAndFeelDecorated(true);
|
||||
}
|
||||
if (SystemInfo.isLinux && JBUI.isHiDPI()) {
|
||||
applySystemFonts(defaults);
|
||||
}
|
||||
defaults.put("EditorPane.font", defaults.getFont("TextField.font"));
|
||||
return defaults;
|
||||
}
|
||||
@@ -133,6 +135,23 @@ public class DarculaLaf extends BasicLookAndFeel {
|
||||
return super.getDefaults();
|
||||
}
|
||||
|
||||
private static void applySystemFonts(UIDefaults defaults) {
|
||||
try {
|
||||
String fqn = UIManager.getSystemLookAndFeelClassName();
|
||||
Object systemLookAndFeel = Class.forName(fqn).newInstance();
|
||||
final Method superMethod = BasicLookAndFeel.class.getDeclaredMethod("getDefaults");
|
||||
superMethod.setAccessible(true);
|
||||
final UIDefaults systemDefaults = (UIDefaults)superMethod.invoke(systemLookAndFeel);
|
||||
for (Map.Entry<Object, Object> entry : systemDefaults.entrySet()) {
|
||||
if (entry.getValue() instanceof Font) {
|
||||
defaults.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected DefaultMetalTheme createMetalTheme() {
|
||||
return new DarculaMetalTheme();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
* Copyright 2000-2015 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.
|
||||
@@ -47,7 +47,7 @@ public class Splash extends JDialog implements StartupProgress {
|
||||
@Nullable public static Rectangle BOUNDS;
|
||||
|
||||
private final Icon myImage;
|
||||
private int myProgressHeight = 2;
|
||||
private int myProgressHeight = JBUI.scale(2);
|
||||
private Color myProgressColor = null;
|
||||
private int myProgressY;
|
||||
private float myProgress;
|
||||
@@ -102,7 +102,7 @@ public class Splash extends JDialog implements StartupProgress {
|
||||
this(info.getSplashImageUrl(), info.getSplashTextColor());
|
||||
if (info instanceof ApplicationInfoImpl) {
|
||||
final ApplicationInfoImpl appInfo = (ApplicationInfoImpl)info;
|
||||
myProgressHeight = 2;
|
||||
myProgressHeight = JBUI.scale(2);
|
||||
myProgressColor = appInfo.getProgressColor();
|
||||
myProgressY = appInfo.getProgressY();
|
||||
myProgressTail = appInfo.getProgressTailIcon();
|
||||
@@ -143,8 +143,8 @@ public class Splash extends JDialog implements StartupProgress {
|
||||
g.setColor(color);
|
||||
g.fillRect(1, getProgressY(), width, getProgressHeight());
|
||||
if (myProgressTail != null) {
|
||||
myProgressTail.paintIcon(this, g, width - (myProgressTail.getIconWidth()/2),
|
||||
getProgressY() - (myProgressTail.getIconHeight() - getProgressHeight())/2);
|
||||
myProgressTail.paintIcon(this, g, width - (myProgressTail.getIconWidth() / JBUI.scale(1) / 2 * JBUI.scale(1)),
|
||||
getProgressY() - (myProgressTail.getIconHeight() - getProgressHeight()) / JBUI.scale(1) / 2 * JBUI.scale(1)); //I'll buy you a beer if you understand this line without playing with it
|
||||
}
|
||||
myProgressLastPosition = progressWidth;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
* Copyright 2000-2015 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.
|
||||
@@ -30,6 +30,8 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
@@ -63,7 +65,14 @@ public class ImageLoader implements Serializable {
|
||||
public static Image loadFromUrl(@NotNull URL url) {
|
||||
for (Pair<String, Integer> each : getFileNames(url.toString())) {
|
||||
try {
|
||||
return loadFromStream(URLUtil.openStream(new URL(each.first)), each.second);
|
||||
Image image = loadFromStream(URLUtil.openStream(new URL(each.first)), each.second);
|
||||
|
||||
//we can't check all 3rd party plugins and convince the authors to add @2x icons.
|
||||
// isHiDPI() != isRetina() => we should scale images manually
|
||||
if (image != null && JBUI.isHiDPI() && !each.first.contains("@2x")) {
|
||||
image = upscale(image);
|
||||
}
|
||||
return image;
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
}
|
||||
@@ -71,6 +80,19 @@ public class ImageLoader implements Serializable {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Image upscale(Image image) {
|
||||
float scale = JBUI.scale(1f);
|
||||
int width = (int)(scale * image.getWidth(null));
|
||||
int height = (int)(scale * image.getHeight(null));
|
||||
@SuppressWarnings("UndesirableClassUsage")
|
||||
BufferedImage tmp = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D g = tmp.createGraphics();
|
||||
g.drawImage(image, AffineTransform.getScaleInstance(scale, scale), null);
|
||||
image = tmp;
|
||||
return image;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Image loadFromUrl(URL url, boolean dark, boolean retina) {
|
||||
for (Pair<String, Integer> each : getFileNames(url.toString(), dark, retina || JBUI.isHiDPI())) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2012 JetBrains s.r.o.
|
||||
* Copyright 2000-2015 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.
|
||||
@@ -135,4 +135,12 @@ public class SystemProperties {
|
||||
public static String getJavaVendor() {
|
||||
return System.getProperty("java.vendor");
|
||||
}
|
||||
|
||||
public static boolean is(String key) {
|
||||
return getBooleanProperty(key, false);
|
||||
}
|
||||
|
||||
public static boolean has(String key) {
|
||||
return System.getProperty(key) != null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.intellij.util.ui;
|
||||
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.ui.border.CustomLineBorder;
|
||||
import com.intellij.util.SystemProperties;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
@@ -26,7 +27,27 @@ import java.awt.*;
|
||||
* @author Konstantin Bulenkov
|
||||
*/
|
||||
public class JBUI {
|
||||
private static boolean IS_HIDPI = !SystemInfo.isMac && ("true".equals(System.getProperty("hidpi")) || "true".equals(System.getProperty("is.hidpi")));
|
||||
private static boolean IS_HIDPI = calculateHiDPI();
|
||||
|
||||
private static boolean calculateHiDPI() {
|
||||
if (SystemInfo.isMac) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SystemProperties.is("hidpi")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SystemProperties.has("hidpi") && !SystemProperties.is("hidpi")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SystemInfo.isWindows && getSystemDPI() > 144) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int getSystemDPI() {
|
||||
try {
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package com.siyeh.ig.style;
|
||||
|
||||
import com.intellij.codeInsight.javadoc.JavaDocUtil;
|
||||
import com.intellij.codeInspection.CleanupLocalInspectionTool;
|
||||
import com.intellij.codeInspection.ProblemDescriptor;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
@@ -26,6 +25,7 @@ import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleSettings;
|
||||
import com.intellij.psi.impl.source.codeStyle.ImportHelper;
|
||||
import com.intellij.psi.javadoc.PsiDocComment;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
@@ -206,11 +206,6 @@ public class UnnecessaryFullyQualifiedNameInspection extends BaseInspection impl
|
||||
if (element != null) {
|
||||
return;
|
||||
}
|
||||
final CodeStyleSettings styleSettings = CodeStyleSettingsManager.getSettings(reference.getProject());
|
||||
PsiDocComment containingComment = PsiTreeUtil.getParentOfType(reference, PsiDocComment.class);
|
||||
if (containingComment != null && acceptFullyQualifiedNamesInJavadoc(containingComment, styleSettings)) {
|
||||
return;
|
||||
}
|
||||
final PsiFile containingFile = reference.getContainingFile();
|
||||
if (!(containingFile instanceof PsiJavaFile)) {
|
||||
return;
|
||||
@@ -219,6 +214,11 @@ public class UnnecessaryFullyQualifiedNameInspection extends BaseInspection impl
|
||||
if (!(target instanceof PsiClass)) {
|
||||
return;
|
||||
}
|
||||
final CodeStyleSettings styleSettings = CodeStyleSettingsManager.getSettings(reference.getProject());
|
||||
PsiDocComment containingComment = PsiTreeUtil.getParentOfType(reference, PsiDocComment.class);
|
||||
if (containingComment != null && acceptFqnInJavadoc((PsiJavaFile)containingFile, reference.getQualifiedName(), styleSettings)) {
|
||||
return;
|
||||
}
|
||||
final PsiJavaCodeReferenceElement qualifierReference = (PsiJavaCodeReferenceElement)qualifier;
|
||||
final PsiElement qualifierTarget = qualifierReference.resolve();
|
||||
if (!(qualifierTarget instanceof PsiPackage)) {
|
||||
@@ -262,11 +262,14 @@ public class UnnecessaryFullyQualifiedNameInspection extends BaseInspection impl
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean acceptFullyQualifiedNamesInJavadoc(PsiDocComment comment, CodeStyleSettings styleSettings) {
|
||||
if (JavaDocUtil.isInsidePackageInfo(comment)) {
|
||||
private static boolean acceptFqnInJavadoc(PsiJavaFile javaFile, String fullyQualifiedName, CodeStyleSettings styleSettings) {
|
||||
if ("package-info.java".equals(javaFile.getName())) {
|
||||
return true;
|
||||
}
|
||||
JavaCodeStyleSettings javaSettings = styleSettings.getCustomSettings(JavaCodeStyleSettings.class);
|
||||
if (javaSettings.CLASS_NAMES_IN_JAVADOC == JavaCodeStyleSettings.FULLY_QUALIFY_NAMES_IF_NOT_IMPORTED) {
|
||||
return !ImportHelper.isAlreadyImported(javaFile, fullyQualifiedName);
|
||||
}
|
||||
return javaSettings.useFqNamesInJavadocAlways();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* {@link java.util.HashMap}
|
||||
*/
|
||||
public class DoubleQualify {
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* {@link java.util.HashMap}
|
||||
*/
|
||||
public class Single {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<problems>
|
||||
|
||||
<problem>
|
||||
<file>DoubleQualify.java</file>
|
||||
<line>4</line>
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Unnecessary fully qualified name</problem_class>
|
||||
<description>Fully qualified name <code>java.util.HashMap</code> is unnecessary, and can be replaced with an import #loc</description>
|
||||
</problem>
|
||||
|
||||
</problems>
|
||||
@@ -10,7 +10,7 @@ public class UnnecessaryFullyQualifiedNameInspectionTest extends IGInspectionTes
|
||||
private static final String BASE_DIR = "com/siyeh/igtest/style/";
|
||||
|
||||
public void testFqnInJavadoc_Unnecessary_WhenFullyQualifyIfNotImported() throws Exception {
|
||||
doTestWithFqnInJavadocSetting(BASE_DIR + "unnecessary_fully_qualified_name/", JavaCodeStyleSettings.FULLY_QUALIFY_NAMES_IF_NOT_IMPORTED);
|
||||
doTestWithFqnInJavadocSetting(BASE_DIR + "unnecessary_fqn_javadoc_fully_qualify_if_not_imported", JavaCodeStyleSettings.FULLY_QUALIFY_NAMES_IF_NOT_IMPORTED);
|
||||
}
|
||||
|
||||
public void testFqnInJavadoc_Unnecessary_WhenShortNamesAlways() {
|
||||
|
||||
@@ -54,6 +54,12 @@
|
||||
enabledByDefault="true"
|
||||
level="WARNING"
|
||||
implementationClass="com.intellij.codeInspection.capitalization.TitleCapitalizationInspection"/>
|
||||
<localInspection language="Properties" shortName="SuspiciousLocalesLanguages"
|
||||
groupBundle="messages.InspectionsBundle" groupKey="group.names.internationalization.issues"
|
||||
displayName="Suspicious resource bundle locale languages"
|
||||
enabledByDefault="true"
|
||||
level="WARNING"
|
||||
implementationClass="com.intellij.codeInspection.SuspiciousLocalesLanguagesInspection"/>
|
||||
|
||||
<intentionAction>
|
||||
<className>com.intellij.codeInspection.capitalization.AnnotateCapitalizationIntention</className>
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.codeInspection;
|
||||
|
||||
import com.intellij.codeInspection.ex.BaseLocalInspectionTool;
|
||||
import com.intellij.lang.properties.*;
|
||||
import com.intellij.lang.properties.ResourceBundle;
|
||||
import com.intellij.lang.properties.customizeActions.DissociateResourceBundleAction;
|
||||
import com.intellij.lang.properties.psi.PropertiesFile;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.InputValidator;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.InvalidDataException;
|
||||
import com.intellij.openapi.util.WriteExternalException;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.reference.SoftLazyValue;
|
||||
import com.intellij.ui.AnActionButton;
|
||||
import com.intellij.ui.AnActionButtonRunnable;
|
||||
import com.intellij.ui.ToolbarDecorator;
|
||||
import com.intellij.ui.components.JBList;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class SuspiciousLocalesLanguagesInspection extends BaseLocalInspectionTool {
|
||||
private static final String ADDITIONAL_LANGUAGES_ATTR_NAME = "additionalLanguages";
|
||||
private final static SoftLazyValue<Set<String>> JAVA_LOCALES = new SoftLazyValue<Set<String>>() {
|
||||
@NotNull
|
||||
@Override
|
||||
protected Set<String> compute() {
|
||||
final Set<String> result = new HashSet<String>();
|
||||
for (Locale locale : Locale.getAvailableLocales()) {
|
||||
result.add(locale.getLanguage());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
private final List<String> myAdditionalLanguages = new ArrayList<String>();
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Suspicious resource bundle locale languages";
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public void setAdditionalLanguages(List<String> additionalLanguages) {
|
||||
myAdditionalLanguages.clear();
|
||||
myAdditionalLanguages.addAll(additionalLanguages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSettings(@NotNull Element node) throws InvalidDataException {
|
||||
final String rawLanguages = node.getAttributeValue(ADDITIONAL_LANGUAGES_ATTR_NAME);
|
||||
if (rawLanguages != null) {
|
||||
myAdditionalLanguages.clear();
|
||||
myAdditionalLanguages.addAll(StringUtil.split(rawLanguages, ","));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSettings(@NotNull Element node) throws WriteExternalException {
|
||||
if (!myAdditionalLanguages.isEmpty()) {
|
||||
final ArrayList<String> uniqueLanguages = ContainerUtil.newArrayList(ContainerUtil.newHashSet(myAdditionalLanguages));
|
||||
Collections.sort(uniqueLanguages);
|
||||
final String locales = StringUtil.join(uniqueLanguages, ",");
|
||||
node.setAttribute(ADDITIONAL_LANGUAGES_ATTR_NAME, locales);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public JComponent createOptionsPanel() {
|
||||
return new MyOptions().getComponent();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
|
||||
final PropertiesFile propertiesFile = PropertiesImplUtil.getPropertiesFile(file);
|
||||
if (propertiesFile == null) {
|
||||
return null;
|
||||
}
|
||||
final ResourceBundle resourceBundle = propertiesFile.getResourceBundle();
|
||||
final List<PropertiesFile> files = resourceBundle.getPropertiesFiles();
|
||||
if (!(resourceBundle instanceof ResourceBundleImpl) || files.size() < 2) {
|
||||
return null;
|
||||
}
|
||||
List<Locale> bundleLocales = ContainerUtil.mapNotNull(files, new Function<PropertiesFile, Locale>() {
|
||||
@Override
|
||||
public Locale fun(PropertiesFile propertiesFile) {
|
||||
final Locale locale = propertiesFile.getLocale();
|
||||
return locale == ResourceBundleManager.DEFAULT_LOCALE ? null : locale;
|
||||
}
|
||||
});
|
||||
bundleLocales = ContainerUtil.filter(bundleLocales, new Condition<Locale>() {
|
||||
@Override
|
||||
public boolean value(Locale locale) {
|
||||
return !JAVA_LOCALES.getValue().contains(locale.getLanguage()) && !myAdditionalLanguages.contains(locale.getLanguage());
|
||||
}
|
||||
});
|
||||
if (bundleLocales.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final ProblemDescriptor descriptor = manager.createProblemDescriptor(file,
|
||||
PropertiesBundle.message(
|
||||
"resource.bundle.contains.locales.with.suspicious.locale.languages.desciptor"),
|
||||
new DissociateResourceBundleQuickFix(resourceBundle),
|
||||
ProblemHighlightType.WEAK_WARNING,
|
||||
true);
|
||||
return new ProblemDescriptor[] {descriptor};
|
||||
}
|
||||
|
||||
private static class DissociateResourceBundleQuickFix implements LocalQuickFix {
|
||||
private final ResourceBundle myResourceBundle;
|
||||
|
||||
private DissociateResourceBundleQuickFix(ResourceBundle bundle) {
|
||||
myResourceBundle = bundle;
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return getFamilyName();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return PropertiesBundle.message("dissociate.resource.bundle.quick.fix.name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
DissociateResourceBundleAction.dissociate(Collections.singleton(myResourceBundle), project);
|
||||
}
|
||||
}
|
||||
|
||||
private class MyOptions {
|
||||
private JBList myAdditionalLocalesList;
|
||||
|
||||
public MyOptions() {
|
||||
myAdditionalLocalesList = new JBList(new MyListModel());
|
||||
myAdditionalLocalesList.setCellRenderer(new DefaultListCellRenderer());
|
||||
}
|
||||
|
||||
public JPanel getComponent() {
|
||||
final JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.add(new JLabel(PropertiesBundle.message("dissociate.resource.bundle.quick.fix.options.label")), BorderLayout.NORTH);
|
||||
panel.add(
|
||||
ToolbarDecorator.createDecorator(myAdditionalLocalesList)
|
||||
.setAddAction(new AnActionButtonRunnable() {
|
||||
@Override
|
||||
public void run(AnActionButton button) {
|
||||
Messages.showInputDialog(panel, PropertiesBundle.message("dissociate.resource.bundle.quick.fix.options.input.text"),
|
||||
PropertiesBundle.message("dissociate.resource.bundle.quick.fix.options.input.title"), null, "", new InputValidator() {
|
||||
@Override
|
||||
public boolean checkInput(String inputString) {
|
||||
return 1 < inputString.length() && inputString.length() < 9 && !myAdditionalLanguages.contains(inputString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canClose(String inputString) {
|
||||
if (inputString != null) {
|
||||
myAdditionalLanguages.add(inputString);
|
||||
((MyListModel)myAdditionalLocalesList.getModel()).fireContentsChanged();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.setRemoveAction(new AnActionButtonRunnable() {
|
||||
@Override
|
||||
public void run(AnActionButton button) {
|
||||
final int index = myAdditionalLocalesList.getSelectedIndex();
|
||||
if (index > -1 && index < myAdditionalLanguages.size()) {
|
||||
myAdditionalLanguages.remove(index);
|
||||
((MyListModel)myAdditionalLocalesList.getModel()).fireContentsChanged();
|
||||
}
|
||||
}
|
||||
})
|
||||
.setPreferredSize(new Dimension(-1, 100))
|
||||
.disableUpDownActions()
|
||||
.createPanel(),
|
||||
BorderLayout.CENTER);
|
||||
return panel;
|
||||
}
|
||||
|
||||
private class MyListModel extends AbstractListModel {
|
||||
@Override
|
||||
public int getSize() {
|
||||
return myAdditionalLanguages.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getElementAt(int index) {
|
||||
return myAdditionalLanguages.get(index);
|
||||
}
|
||||
|
||||
public void fireContentsChanged() {
|
||||
fireContentsChanged(myAdditionalLanguages, -1, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<weak_warning descr="Resource bundle contains locales with suspicious locale languages"></weak_warning>
|
||||
@@ -0,0 +1 @@
|
||||
<weak_warning descr="Resource bundle contains locales with suspicious locale languages"></weak_warning>
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.codeInspection;
|
||||
|
||||
import com.intellij.openapi.application.PluginPathManager;
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class SuspiciousLanguagesInspectionTest extends LightCodeInsightFixtureTestCase {
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return PluginPathManager.getPluginHomePathRelative("java-i18n") + "/testData/inspections/suspiciousLanguages";
|
||||
}
|
||||
|
||||
public void testSimple1() {
|
||||
doTest("p.properties", "p_en.properties");
|
||||
}
|
||||
|
||||
public void testSimple2() {
|
||||
doTest("p.properties", "p_asd.properties");
|
||||
}
|
||||
|
||||
public void testWithAdditionalLocales() {
|
||||
doTest("p.properties", "p_asd.properties", "asd");
|
||||
}
|
||||
|
||||
private void doTest(final String file1, final String file2, final String... additionalLocales) {
|
||||
myFixture.configureByFile(getTestName(true) + "/" + file1);
|
||||
myFixture.configureByFile(getTestName(true) + "/" + file2);
|
||||
final SuspiciousLocalesLanguagesInspection inspection = new SuspiciousLocalesLanguagesInspection();
|
||||
if (additionalLocales.length != 0) {
|
||||
inspection.setAdditionalLanguages(ContainerUtil.newArrayList(additionalLocales));
|
||||
}
|
||||
myFixture.enableInspections(inspection);
|
||||
myFixture.checkHighlighting();
|
||||
}
|
||||
}
|
||||
@@ -60,3 +60,9 @@ inline.property.confirmation=Inline property ''{0}'' with value ''{1}''?
|
||||
|
||||
alpha.unsorted.properties.file.inspection.display.name=Properties File or Resource Bundle is Alphabetically Unsorted
|
||||
trailing.spaces.in.property.inspection.ignore.visible.spaces=Ignore visible whitespaces
|
||||
|
||||
resource.bundle.contains.locales.with.suspicious.locale.languages.desciptor=Resource bundle contains locales with suspicious locale languages
|
||||
dissociate.resource.bundle.quick.fix.name=Dissociate Resource Bundle
|
||||
dissociate.resource.bundle.quick.fix.options.label=Additional language codes\:
|
||||
dissociate.resource.bundle.quick.fix.options.input.text=Inter language code
|
||||
dissociate.resource.bundle.quick.fix.options.input.title=Additional Locale Languages
|
||||
|
||||
@@ -47,7 +47,7 @@ import java.util.regex.Matcher;
|
||||
})
|
||||
public class ResourceBundleManager implements PersistentStateComponent<ResourceBundleManagerState> {
|
||||
private final static Logger LOG = Logger.getInstance(ResourceBundleManager.class);
|
||||
private final static Locale DEFAULT_LOCALE = new Locale("", "", "");
|
||||
public final static Locale DEFAULT_LOCALE = new Locale("", "", "");
|
||||
|
||||
private ResourceBundleManagerState myState = new ResourceBundleManagerState();
|
||||
|
||||
@@ -172,7 +172,11 @@ public class ResourceBundleManager implements PersistentStateComponent<ResourceB
|
||||
name = name.substring(customResourceBundleName.length());
|
||||
}
|
||||
|
||||
final Matcher matcher = PropertiesUtil.LOCALE_PATTERN.matcher(name);
|
||||
return extractLocale(name);
|
||||
}
|
||||
|
||||
public static Locale extractLocale(final String suffix) {
|
||||
final Matcher matcher = PropertiesUtil.LOCALE_PATTERN.matcher(suffix);
|
||||
if (matcher.find()) {
|
||||
final String rawLocale = matcher.group(1);
|
||||
final String[] splittedRawLocale = rawLocale.split("_");
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.intellij.lang.properties.customizeActions;
|
||||
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.ide.projectView.ProjectView;
|
||||
import com.intellij.ide.util.treeView.AbstractTreeBuilder;
|
||||
import com.intellij.lang.properties.PropertiesImplUtil;
|
||||
import com.intellij.lang.properties.ResourceBundle;
|
||||
import com.intellij.lang.properties.ResourceBundleManager;
|
||||
@@ -27,12 +28,15 @@ import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.LangDataKeys;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiDirectory;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiFileSystemItem;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@@ -54,15 +58,7 @@ public class DissociateResourceBundleAction extends AnAction {
|
||||
}
|
||||
final Collection<ResourceBundle> resourceBundles = extractResourceBundles(e);
|
||||
assert resourceBundles.size() > 0;
|
||||
final FileEditorManager fileEditorManager = FileEditorManager.getInstance(project);
|
||||
for (ResourceBundle resourceBundle : resourceBundles) {
|
||||
fileEditorManager.closeFile(new ResourceBundleAsVirtualFile(resourceBundle));
|
||||
for (final PropertiesFile propertiesFile : resourceBundle.getPropertiesFiles()) {
|
||||
fileEditorManager.closeFile(propertiesFile.getVirtualFile());
|
||||
}
|
||||
ResourceBundleManager.getInstance(e.getProject()).dissociateResourceBundle(resourceBundle);
|
||||
}
|
||||
ProjectView.getInstance(project).refresh();
|
||||
dissociate(resourceBundles, project);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,6 +75,26 @@ public class DissociateResourceBundleAction extends AnAction {
|
||||
}
|
||||
}
|
||||
|
||||
public static void dissociate(final Collection<ResourceBundle> resourceBundles, final Project project) {
|
||||
final FileEditorManager fileEditorManager = FileEditorManager.getInstance(project);
|
||||
final Set<PsiFileSystemItem> toUpdateInProjectView = new HashSet<PsiFileSystemItem>();
|
||||
for (ResourceBundle resourceBundle : resourceBundles) {
|
||||
fileEditorManager.closeFile(new ResourceBundleAsVirtualFile(resourceBundle));
|
||||
for (final PropertiesFile propertiesFile : resourceBundle.getPropertiesFiles()) {
|
||||
fileEditorManager.closeFile(propertiesFile.getVirtualFile());
|
||||
PsiDirectory containingDirectory = propertiesFile.getContainingFile().getContainingDirectory();
|
||||
if (containingDirectory != null) {
|
||||
toUpdateInProjectView.add(containingDirectory);
|
||||
}
|
||||
}
|
||||
ResourceBundleManager.getInstance(project).dissociateResourceBundle(resourceBundle);
|
||||
}
|
||||
AbstractTreeBuilder treeBuilder = ProjectView.getInstance(project).getCurrentProjectViewPane().getTreeBuilder();
|
||||
for (PsiFileSystemItem item : toUpdateInProjectView) {
|
||||
treeBuilder.queueUpdateFrom(item, false);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Collection<ResourceBundle> extractResourceBundles(final AnActionEvent event) {
|
||||
final Set<ResourceBundle> targetResourceBundles = new HashSet<ResourceBundle>();
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
||||
import com.intellij.openapi.fileTypes.StdFileTypes;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiManager;
|
||||
@@ -97,4 +98,12 @@ public class ResourceBundleNode extends ProjectViewNode<ResourceBundle>{
|
||||
public Comparable getTypeSortKey() {
|
||||
return new PsiFileNode.ExtensionSortKey(StdFileTypes.PROPERTIES.getDefaultExtension());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate() {
|
||||
if (!super.validate()) {
|
||||
return false;
|
||||
}
|
||||
return Comparing.equal(getValue().getDefaultPropertiesFile().getResourceBundle(), getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -488,7 +488,7 @@ public class RemoteDebugger implements ProcessDebugger {
|
||||
private StringBuilder myTextBuilder = new StringBuilder();
|
||||
|
||||
private DebuggerReader(final InputStream stream) throws IOException {
|
||||
super(stream, CharsetToolkit.UTF8_CHARSET); //TODO: correct encoding?
|
||||
super(stream, CharsetToolkit.UTF8_CHARSET, SleepingPolicy.BLOCKING); //TODO: correct encoding?
|
||||
start();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
Inspection detects locales which contain language codes not supported by Java.
|
||||
</body>
|
||||
</html>
|
||||