diff options
Diffstat (limited to 'plugins/editorconfig/src/org/editorconfig/configmanagement')
4 files changed, 437 insertions, 0 deletions
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)); + } + } + } +} |