summaryrefslogtreecommitdiff
path: root/plugins/editorconfig
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/editorconfig')
-rw-r--r--plugins/editorconfig/LICENSE.txt21
-rw-r--r--plugins/editorconfig/editorconfig.iml28
-rw-r--r--plugins/editorconfig/lib/editorconfig-core-java.jarbin0 -> 15685 bytes
-rw-r--r--plugins/editorconfig/src/META-INF/plugin.xml30
-rw-r--r--plugins/editorconfig/src/org/editorconfig/Utils.java24
-rw-r--r--plugins/editorconfig/src/org/editorconfig/configmanagement/CodeStyleManager.java184
-rw-r--r--plugins/editorconfig/src/org/editorconfig/configmanagement/EditorSettingsManager.java85
-rw-r--r--plugins/editorconfig/src/org/editorconfig/configmanagement/EncodingManager.java73
-rw-r--r--plugins/editorconfig/src/org/editorconfig/configmanagement/LineEndingsManager.java95
-rw-r--r--plugins/editorconfig/src/org/editorconfig/plugincomponents/ConfigProjectComponent.java61
-rw-r--r--plugins/editorconfig/src/org/editorconfig/plugincomponents/SettingsProviderComponent.java49
11 files changed, 650 insertions, 0 deletions
diff --git a/plugins/editorconfig/LICENSE.txt b/plugins/editorconfig/LICENSE.txt
new file mode 100644
index 000000000000..fff944099438
--- /dev/null
+++ b/plugins/editorconfig/LICENSE.txt
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 Kevin Bell
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. \ No newline at end of file
diff --git a/plugins/editorconfig/editorconfig.iml b/plugins/editorconfig/editorconfig.iml
new file mode 100644
index 000000000000..8d67b1cc8c76
--- /dev/null
+++ b/plugins/editorconfig/editorconfig.iml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+ <component name="DevKit.ModuleBuildProperties" url="file://$MODULE_DIR$/META-INF/plugin.xml" />
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="module-library">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/lib/editorconfig-core-java.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES>
+ <root url="jar://$MODULE_DIR$/lib/editorconfig-core-java.jar!/" />
+ </SOURCES>
+ </library>
+ </orderEntry>
+ <orderEntry type="module" module-name="core-api" />
+ <orderEntry type="module" module-name="platform-api" />
+ <orderEntry type="module" module-name="platform-impl" />
+ <orderEntry type="module" module-name="lang-api" />
+ </component>
+</module>
+
diff --git a/plugins/editorconfig/lib/editorconfig-core-java.jar b/plugins/editorconfig/lib/editorconfig-core-java.jar
new file mode 100644
index 000000000000..f115b7480336
--- /dev/null
+++ b/plugins/editorconfig/lib/editorconfig-core-java.jar
Binary files differ
diff --git a/plugins/editorconfig/src/META-INF/plugin.xml b/plugins/editorconfig/src/META-INF/plugin.xml
new file mode 100644
index 000000000000..2be8eb4f5473
--- /dev/null
+++ b/plugins/editorconfig/src/META-INF/plugin.xml
@@ -0,0 +1,30 @@
+<idea-plugin version="2">
+ <id>org.editorconfig.editorconfigjetbrains</id>
+ <name>EditorConfig</name>
+ <version>1.0</version>
+ <vendor url="http://editorconfig.org">Kevin Bell, JetBrains</vendor>
+
+ <description><![CDATA[
+ A JetBrains IDE plugin supporting the EditorConfig standard
+ ]]></description>
+
+ <idea-version since-build="138.1293"/>
+
+ <depends>com.intellij.modules.lang</depends>
+
+ <application-components>
+ <component>
+ <implementation-class>org.editorconfig.plugincomponents.SettingsProviderComponent</implementation-class>
+ </component>
+ </application-components>
+
+ <project-components>
+ <component>
+ <implementation-class>org.editorconfig.plugincomponents.ConfigProjectComponent</implementation-class>
+ </component>
+ </project-components>
+
+ <extensions defaultExtensionNs="com.intellij">
+ <errorHandler implementation="com.intellij.diagnostic.ITNReporter"/>
+ </extensions>
+</idea-plugin> \ No newline at end of file
diff --git a/plugins/editorconfig/src/org/editorconfig/Utils.java b/plugins/editorconfig/src/org/editorconfig/Utils.java
new file mode 100644
index 000000000000..f64d7be34d85
--- /dev/null
+++ b/plugins/editorconfig/src/org/editorconfig/Utils.java
@@ -0,0 +1,24 @@
+package org.editorconfig;
+
+import org.editorconfig.core.EditorConfig.OutPair;
+
+import java.util.List;
+
+public class Utils {
+ public static String configValueForKey(List<OutPair> outPairs, String key) {
+ for (OutPair outPair : outPairs) {
+ if (outPair.getKey().equals(key)) {
+ return outPair.getVal();
+ }
+ }
+ return "";
+ }
+
+ public static String invalidConfigMessage(String configValue, String configKey, String filePath) {
+ return "\"" + configValue + "\" is not a valid value for " + configKey + " for file " + filePath;
+ }
+
+ public static String appliedConfigMessage(String configValue, String configKey, String filePath) {
+ return "Applied \"" + configValue + "\" as " + configKey + " for file " + filePath;
+ }
+}
diff --git a/plugins/editorconfig/src/org/editorconfig/configmanagement/CodeStyleManager.java b/plugins/editorconfig/src/org/editorconfig/configmanagement/CodeStyleManager.java
new file mode 100644
index 000000000000..406e38456976
--- /dev/null
+++ b/plugins/editorconfig/src/org/editorconfig/configmanagement/CodeStyleManager.java
@@ -0,0 +1,184 @@
+package org.editorconfig.configmanagement;
+
+import com.intellij.lang.Language;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.ex.EditorEx;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.fileEditor.FileEditorManagerAdapter;
+import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.LanguageFileType;
+import com.intellij.openapi.fileTypes.PlainTextLanguage;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.codeStyle.CodeStyleSettings;
+import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
+import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
+import org.editorconfig.Utils;
+import org.editorconfig.core.EditorConfig.OutPair;
+import org.editorconfig.plugincomponents.SettingsProviderComponent;
+import org.jetbrains.annotations.NotNull;
+
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowFocusListener;
+import java.util.List;
+
+public class CodeStyleManager extends FileEditorManagerAdapter implements WindowFocusListener {
+ // Handles the following EditorConfig settings:
+ private static final String indentSizeKey = "indent_size";
+ private static final String tabWidthKey = "tab_width";
+ private static final String indentStyleKey = "indent_style";
+
+ private static final Logger LOG = Logger.getInstance("#org.editorconfig.configmanagement.CodeStyleManager");
+ private final CodeStyleSettingsManager codeStyleSettingsManager;
+ private final Project project;
+
+ public CodeStyleManager(Project project) {
+ codeStyleSettingsManager = CodeStyleSettingsManager.getInstance(project);
+ this.project = project;
+ }
+
+ @Override
+ public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) {
+ applySettings(file);
+ }
+
+ @Override
+ public void selectionChanged(@NotNull FileEditorManagerEvent event) {
+ final VirtualFile file = event.getNewFile();
+ applySettings(file);
+ }
+
+ @Override
+ public void windowGainedFocus(WindowEvent e) {
+ final Editor currentEditor = FileEditorManager.getInstance(project).getSelectedTextEditor();
+ if (currentEditor != null) {
+ final Document currentDocument = currentEditor.getDocument();
+ final VirtualFile currentFile = FileDocumentManager.getInstance().getFile(currentDocument);
+ applySettings(currentFile);
+ }
+ }
+
+ @Override
+ public void windowLostFocus(WindowEvent e) {
+ }
+
+ private void applySettings(final VirtualFile file) {
+ if (file != null && file.isInLocalFileSystem()) {
+ // Always drop any current temporary settings so that the defaults will be applied if
+ // this is a non-editorconfig-managed file
+ codeStyleSettingsManager.dropTemporarySettings();
+ // Prepare a new settings object, which will maintain the standard settings if no
+ // editorconfig settings apply
+ final CodeStyleSettings currentSettings = codeStyleSettingsManager.getCurrentSettings();
+ final CodeStyleSettings newSettings = new CodeStyleSettings();
+ newSettings.copyFrom(currentSettings);
+ // Get editorconfig settings
+ final String filePath = file.getCanonicalPath();
+ final SettingsProviderComponent settingsProvider = SettingsProviderComponent.getInstance();
+ final List<OutPair> outPairs = settingsProvider.getOutPairs(filePath);
+ // Apply editorconfig settings for the current editor
+ applyCodeStyleSettings(outPairs, newSettings, file);
+ codeStyleSettingsManager.setTemporarySettings(newSettings);
+ final EditorEx currentEditor = (EditorEx)FileEditorManager.getInstance(project).getSelectedTextEditor();
+ if (currentEditor != null) {
+ currentEditor.reinitSettings();
+ }
+ }
+ }
+
+ private static void applyCodeStyleSettings(final List<OutPair> outPairs, final CodeStyleSettings codeStyleSettings,
+ final VirtualFile file) {
+ // Apply indent options
+ final String indentSize = Utils.configValueForKey(outPairs, indentSizeKey);
+ final String tabWidth = Utils.configValueForKey(outPairs, tabWidthKey);
+ final String indentStyle = Utils.configValueForKey(outPairs, indentStyleKey);
+ final FileType fileType = file.getFileType();
+ final Language language = fileType instanceof LanguageFileType ? ((LanguageFileType)fileType).getLanguage() :
+ PlainTextLanguage.INSTANCE;
+ final CommonCodeStyleSettings commonSettings = codeStyleSettings.getCommonSettings(language);
+ final CommonCodeStyleSettings.IndentOptions indentOptions = commonSettings.getIndentOptions();
+ applyIndentOptions(indentOptions, indentSize, tabWidth, indentStyle, file.getCanonicalPath());
+ }
+
+ private static void applyIndentOptions(CommonCodeStyleSettings.IndentOptions indentOptions,
+ String indentSize, String tabWidth, String indentStyle, String filePath) {
+ final String calculatedIndentSize = calculateIndentSize(tabWidth, indentSize);
+ final String calculatedTabWidth = calculateTabWidth(tabWidth, indentSize);
+ if (!calculatedIndentSize.isEmpty()) {
+ if (applyIndentSize(indentOptions, calculatedIndentSize)) {
+ LOG.debug(Utils.appliedConfigMessage(calculatedIndentSize, indentSizeKey, filePath));
+ }
+ else {
+ LOG.warn(Utils.invalidConfigMessage(calculatedIndentSize, indentSizeKey, filePath));
+ }
+ }
+ if (!calculatedTabWidth.isEmpty()) {
+ if (applyTabWidth(indentOptions, calculatedTabWidth)) {
+ LOG.debug(Utils.appliedConfigMessage(calculatedTabWidth, tabWidthKey, filePath));
+ }
+ else {
+ LOG.warn(Utils.invalidConfigMessage(calculatedTabWidth, tabWidthKey, filePath));
+ }
+ }
+ if (!indentStyle.isEmpty()) {
+ if (applyIndentStyle(indentOptions, indentStyle)) {
+ LOG.debug(Utils.appliedConfigMessage(indentStyle, indentStyleKey, filePath));
+ }
+ else {
+ LOG.warn(Utils.invalidConfigMessage(indentStyle, indentStyleKey, filePath));
+ }
+ }
+ }
+
+ private static String calculateIndentSize(final String tabWidth, final String indentSize) {
+ return indentSize.equals("tab") ? tabWidth : indentSize;
+ }
+
+ private static String calculateTabWidth(final String tabWidth, final String indentSize) {
+ if (tabWidth.isEmpty() && indentSize.equals("tab")) {
+ return "";
+ }
+ else if (tabWidth.isEmpty()) {
+ return indentSize;
+ }
+ else {
+ return tabWidth;
+ }
+ }
+
+ private static boolean applyIndentSize(final CommonCodeStyleSettings.IndentOptions indentOptions, final String indentSize) {
+ try {
+ int indent = Integer.parseInt(indentSize);
+ indentOptions.INDENT_SIZE = indent;
+ indentOptions.CONTINUATION_INDENT_SIZE = indent;
+ return true;
+ }
+ catch (NumberFormatException e) {
+ return false;
+ }
+ }
+
+ private static boolean applyTabWidth(final CommonCodeStyleSettings.IndentOptions indentOptions, final String tabWidth) {
+ try {
+ indentOptions.TAB_SIZE = Integer.parseInt(tabWidth);
+ return true;
+ }
+ catch (NumberFormatException e) {
+ return false;
+ }
+ }
+
+ private static boolean applyIndentStyle(CommonCodeStyleSettings.IndentOptions indentOptions, String indentStyle) {
+ if (indentStyle.equals("tab") || indentStyle.equals("space")) {
+ indentOptions.USE_TAB_CHARACTER = indentStyle.equals("tab");
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+}
diff --git a/plugins/editorconfig/src/org/editorconfig/configmanagement/EditorSettingsManager.java b/plugins/editorconfig/src/org/editorconfig/configmanagement/EditorSettingsManager.java
new file mode 100644
index 000000000000..0a062d4d6e04
--- /dev/null
+++ b/plugins/editorconfig/src/org/editorconfig/configmanagement/EditorSettingsManager.java
@@ -0,0 +1,85 @@
+package org.editorconfig.configmanagement;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
+import com.intellij.openapi.editor.impl.TrailingSpacesStripper;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.fileEditor.FileDocumentManagerAdapter;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.editorconfig.Utils;
+import org.editorconfig.core.EditorConfig;
+import org.editorconfig.plugincomponents.SettingsProviderComponent;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class EditorSettingsManager extends FileDocumentManagerAdapter {
+ // Handles the following EditorConfig settings:
+ private static final String trimTrailingWhitespaceKey = "trim_trailing_whitespace";
+ private static final String insertFinalNewlineKey = "insert_final_newline";
+ private static final Map<String, String> trimMap;
+
+ static {
+ Map<String, String> map = new HashMap<String, String>();
+ map.put("true", EditorSettingsExternalizable.STRIP_TRAILING_SPACES_WHOLE);
+ map.put("false", EditorSettingsExternalizable.STRIP_TRAILING_SPACES_NONE);
+ trimMap = Collections.unmodifiableMap(map);
+ }
+
+ private static final Map<String, Boolean> newlineMap;
+
+ static {
+ Map<String, Boolean> map = new HashMap<String, Boolean>();
+ map.put("true", Boolean.TRUE);
+ map.put("false", Boolean.FALSE);
+ newlineMap = Collections.unmodifiableMap(map);
+ }
+
+ private static final Logger LOG = Logger.getInstance("#org.editorconfig.configmanagement.EditorSettingsManager");
+
+ @Override
+ public void beforeDocumentSaving(@NotNull Document document) {
+ // This is fired when any document is saved, regardless of whether it is part of a save-all or
+ // a save-one operation
+ final VirtualFile file = FileDocumentManager.getInstance().getFile(document);
+ applySettings(file);
+ }
+
+ private static void applySettings(VirtualFile file) {
+ if (file == null || !file.isInLocalFileSystem()) return;
+ // Get editorconfig settings
+ final String filePath = file.getCanonicalPath();
+ final SettingsProviderComponent settingsProvider = SettingsProviderComponent.getInstance();
+ final List<EditorConfig.OutPair> outPairs = settingsProvider.getOutPairs(filePath);
+ // Apply trailing spaces setting
+ final String trimTrailingWhitespace = Utils.configValueForKey(outPairs, trimTrailingWhitespaceKey);
+ applyConfigValueToUserData(file, TrailingSpacesStripper.OVERRIDE_STRIP_TRAILING_SPACES_KEY,
+ trimTrailingWhitespaceKey, trimTrailingWhitespace, trimMap);
+ // Apply final newline setting
+ final String insertFinalNewline = Utils.configValueForKey(outPairs, insertFinalNewlineKey);
+ applyConfigValueToUserData(file, TrailingSpacesStripper.OVERRIDE_ENSURE_NEWLINE_KEY,
+ insertFinalNewlineKey, insertFinalNewline, newlineMap);
+ }
+
+ private static <T> void applyConfigValueToUserData(VirtualFile file, Key<T> userDataKey, String editorConfigKey,
+ String configValue, Map<String, T> configMap) {
+ if (configValue.isEmpty()) {
+ file.putUserData(userDataKey, null);
+ }
+ else {
+ final T data = configMap.get(configValue);
+ if (data == null) {
+ LOG.warn(Utils.invalidConfigMessage(configValue, editorConfigKey, file.getCanonicalPath()));
+ }
+ else {
+ file.putUserData(userDataKey, data);
+ LOG.debug("Applied " + editorConfigKey + " settings for: " + file.getCanonicalPath());
+ }
+ }
+ }
+}
diff --git a/plugins/editorconfig/src/org/editorconfig/configmanagement/EncodingManager.java b/plugins/editorconfig/src/org/editorconfig/configmanagement/EncodingManager.java
new file mode 100644
index 000000000000..919f0861055f
--- /dev/null
+++ b/plugins/editorconfig/src/org/editorconfig/configmanagement/EncodingManager.java
@@ -0,0 +1,73 @@
+package org.editorconfig.configmanagement;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.fileEditor.FileDocumentManagerAdapter;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.encoding.EncodingProjectManager;
+import org.editorconfig.Utils;
+import org.editorconfig.core.EditorConfig.OutPair;
+import org.editorconfig.plugincomponents.SettingsProviderComponent;
+import org.jetbrains.annotations.NotNull;
+
+import java.nio.charset.Charset;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class EncodingManager extends FileDocumentManagerAdapter {
+ // Handles the following EditorConfig settings:
+ private static final String charsetKey = "charset";
+
+ private final Logger LOG = Logger.getInstance("#org.editorconfig.codestylesettings.EncodingManager");
+ private final Project project;
+
+ private static final Map<String, Charset> encodingMap;
+
+ static {
+ Map<String, Charset> map = new HashMap<String, Charset>();
+ map.put("latin1", Charset.forName("ISO-8859-1"));
+ map.put("utf-8", Charset.forName("UTF-8"));
+ map.put("utf-16be", Charset.forName("UTF-16BE"));
+ map.put("utf-16le", Charset.forName("UTF-16LE"));
+ encodingMap = Collections.unmodifiableMap(map);
+ }
+
+ private boolean isApplyingSettings;
+
+ public EncodingManager(Project project) {
+ this.project = project;
+ isApplyingSettings = false;
+ }
+
+ @Override
+ public void beforeDocumentSaving(@NotNull Document document) {
+ final VirtualFile file = FileDocumentManager.getInstance().getFile(document);
+ if (!isApplyingSettings) {
+ applySettings(file);
+ }
+ }
+
+ private void applySettings(VirtualFile file) {
+ if (file == null || !file.isInLocalFileSystem()) return;
+ // Prevent "setEncoding" calling "saveAll" from causing an endless loop
+ isApplyingSettings = true;
+ final String filePath = file.getCanonicalPath();
+ final List<OutPair> outPairs = SettingsProviderComponent.getInstance().getOutPairs(filePath);
+ final EncodingProjectManager encodingProjectManager = EncodingProjectManager.getInstance(project);
+ final String charset = Utils.configValueForKey(outPairs, charsetKey);
+ if (!charset.isEmpty()) {
+ if (encodingMap.containsKey(charset)) {
+ encodingProjectManager.setEncoding(file, encodingMap.get(charset));
+ LOG.debug(Utils.appliedConfigMessage(charset, charsetKey, filePath));
+ }
+ else {
+ LOG.warn(Utils.invalidConfigMessage(charset, charsetKey, filePath));
+ }
+ }
+ isApplyingSettings = false;
+ }
+}
diff --git a/plugins/editorconfig/src/org/editorconfig/configmanagement/LineEndingsManager.java b/plugins/editorconfig/src/org/editorconfig/configmanagement/LineEndingsManager.java
new file mode 100644
index 000000000000..1291ffd297f0
--- /dev/null
+++ b/plugins/editorconfig/src/org/editorconfig/configmanagement/LineEndingsManager.java
@@ -0,0 +1,95 @@
+package org.editorconfig.configmanagement;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.fileEditor.FileDocumentManagerAdapter;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.wm.IdeFrame;
+import com.intellij.openapi.wm.StatusBar;
+import com.intellij.openapi.wm.StatusBarWidget;
+import com.intellij.openapi.wm.WindowManager;
+import com.intellij.openapi.wm.impl.status.LineSeparatorPanel;
+import com.intellij.util.LineSeparator;
+import org.editorconfig.Utils;
+import org.editorconfig.core.EditorConfig;
+import org.editorconfig.plugincomponents.SettingsProviderComponent;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * @author Dennis.Ushakov
+ */
+public class LineEndingsManager extends FileDocumentManagerAdapter {
+ // Handles the following EditorConfig settings:
+ private static final String lineEndingsKey = "end_of_line";
+
+ private final Logger LOG = Logger.getInstance("#org.editorconfig.codestylesettings.LineEndingsManager");
+ private final Project project;
+ private boolean statusBarUpdated = false;
+
+ public LineEndingsManager(Project project) {
+ this.project = project;
+ }
+
+ @Override
+ public void beforeAllDocumentsSaving() {
+ statusBarUpdated = false;
+ }
+
+ private void updateStatusBar() {
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ IdeFrame frame = WindowManager.getInstance().getIdeFrame(project);
+ StatusBar statusBar = frame.getStatusBar();
+ StatusBarWidget widget = statusBar != null ? statusBar.getWidget("LineSeparator") : null;
+
+ if (widget instanceof LineSeparatorPanel) {
+ FileEditorManagerEvent event = new FileEditorManagerEvent(FileEditorManager.getInstance(project),
+ null, null, null, null);
+ ((LineSeparatorPanel)widget).selectionChanged(event);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void beforeDocumentSaving(@NotNull Document document) {
+ VirtualFile file = FileDocumentManager.getInstance().getFile(document);
+ applySettings(file);
+ }
+
+ private void applySettings(VirtualFile file) {
+ if (file == null || !file.isInLocalFileSystem()) return;
+
+ final String filePath = file.getCanonicalPath();
+ final List<EditorConfig.OutPair> outPairs = SettingsProviderComponent.getInstance().getOutPairs(filePath);
+ final String lineEndings = Utils.configValueForKey(outPairs, lineEndingsKey);
+ if (!lineEndings.isEmpty()) {
+ try {
+ LineSeparator separator = LineSeparator.valueOf(lineEndings.toUpperCase(Locale.US));
+ String oldSeparator = file.getDetectedLineSeparator();
+ String newSeparator = separator.getSeparatorString();
+ if (!StringUtil.equals(oldSeparator, newSeparator)) {
+ file.setDetectedLineSeparator(newSeparator);
+ if (!statusBarUpdated) {
+ statusBarUpdated = true;
+ updateStatusBar();
+ }
+ LOG.debug(Utils.appliedConfigMessage(lineEndings, lineEndingsKey, filePath));
+ }
+ }
+ catch (IllegalArgumentException e) {
+ LOG.warn(Utils.invalidConfigMessage(lineEndings, lineEndingsKey, filePath));
+ }
+ }
+ }
+}
diff --git a/plugins/editorconfig/src/org/editorconfig/plugincomponents/ConfigProjectComponent.java b/plugins/editorconfig/src/org/editorconfig/plugincomponents/ConfigProjectComponent.java
new file mode 100644
index 000000000000..0d31b2bb8b2c
--- /dev/null
+++ b/plugins/editorconfig/src/org/editorconfig/plugincomponents/ConfigProjectComponent.java
@@ -0,0 +1,61 @@
+package org.editorconfig.plugincomponents;
+
+import com.intellij.AppTopics;
+import com.intellij.openapi.components.ProjectComponent;
+import com.intellij.openapi.fileEditor.FileEditorManagerListener;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.wm.IdeFrame;
+import com.intellij.openapi.wm.WindowManager;
+import com.intellij.util.messages.MessageBus;
+import org.editorconfig.configmanagement.CodeStyleManager;
+import org.editorconfig.configmanagement.EditorSettingsManager;
+import org.editorconfig.configmanagement.EncodingManager;
+import org.editorconfig.configmanagement.LineEndingsManager;
+import org.jetbrains.annotations.NotNull;
+
+import java.awt.*;
+
+public class ConfigProjectComponent implements ProjectComponent {
+ private final Project project;
+ private final CodeStyleManager codeStyleManager;
+
+ public ConfigProjectComponent(Project project) {
+ this.project = project;
+
+ // Register project-level config managers
+ MessageBus bus = project.getMessageBus();
+ codeStyleManager = new CodeStyleManager(project);
+ EditorSettingsManager editorSettingsManager = new EditorSettingsManager();
+ EncodingManager encodingManager = new EncodingManager(project);
+ LineEndingsManager lineEndingsManager = new LineEndingsManager(project);
+ bus.connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, codeStyleManager);
+ bus.connect().subscribe(AppTopics.FILE_DOCUMENT_SYNC, encodingManager);
+ bus.connect().subscribe(AppTopics.FILE_DOCUMENT_SYNC, editorSettingsManager);
+ bus.connect().subscribe(AppTopics.FILE_DOCUMENT_SYNC, lineEndingsManager);
+ }
+
+ public void initComponent() {
+ }
+
+ public void disposeComponent() {
+ }
+
+ @NotNull
+ public String getComponentName() {
+ return "ConfigProjectComponent";
+ }
+
+ public void projectOpened() {
+ // called when project is opened
+ IdeFrame frame = WindowManager.getInstance().getIdeFrame(project);
+ final Window window = (Window)frame;
+ window.addWindowFocusListener(codeStyleManager);
+ }
+
+ public void projectClosed() {
+ // called when project is being closed
+ IdeFrame frame = WindowManager.getInstance().getIdeFrame(project);
+ final Window window = (Window)frame;
+ window.removeWindowFocusListener(codeStyleManager);
+ }
+}
diff --git a/plugins/editorconfig/src/org/editorconfig/plugincomponents/SettingsProviderComponent.java b/plugins/editorconfig/src/org/editorconfig/plugincomponents/SettingsProviderComponent.java
new file mode 100644
index 000000000000..edd74ebdb22d
--- /dev/null
+++ b/plugins/editorconfig/src/org/editorconfig/plugincomponents/SettingsProviderComponent.java
@@ -0,0 +1,49 @@
+package org.editorconfig.plugincomponents;
+
+import com.intellij.openapi.components.ApplicationComponent;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diagnostic.Logger;
+import org.editorconfig.core.EditorConfig;
+import org.editorconfig.core.EditorConfig.OutPair;
+import org.editorconfig.core.EditorConfigException;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SettingsProviderComponent implements ApplicationComponent {
+ private static final Logger LOG = Logger.getInstance("#org.editorconfig.plugincomponents.SettingsProviderComponent");
+
+ private EditorConfig editorConfig;
+
+ public SettingsProviderComponent() {
+ editorConfig = new EditorConfig();
+ }
+
+ public static SettingsProviderComponent getInstance() {
+ return ServiceManager.getService(SettingsProviderComponent.class);
+ }
+
+ public List<OutPair> getOutPairs(String filePath) {
+ final List<OutPair> outPairs;
+ try {
+ outPairs = editorConfig.getProperties(filePath);
+ return outPairs;
+ }
+ catch (EditorConfigException error) {
+ LOG.error(error);
+ return new ArrayList<OutPair>();
+ }
+ }
+
+ public void initComponent() {
+ }
+
+ public void disposeComponent() {
+ }
+
+ @NotNull
+ public String getComponentName() {
+ return "SettingsProviderComponent";
+ }
+}