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.*; 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.core.EditorConfig.OutPair; import org.editorconfig.plugincomponents.SettingsProviderComponent; import org.editorconfig.Utils; 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 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 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 String calculateIndentSize(final String tabWidth, final String indentSize) { return indentSize.equals("tab") ? tabWidth : indentSize; } private 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 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 boolean applyTabWidth(final CommonCodeStyleSettings.IndentOptions indentOptions, final String tabWidth) { try { indentOptions.TAB_SIZE = Integer.parseInt(tabWidth); return true; } catch (NumberFormatException e) { return false; } } private 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; } } }