summaryrefslogtreecommitdiff
path: root/plugins/editorconfig/src/org/editorconfig/configmanagement/CodeStyleManager.java
blob: 406e384569762f8cae08adc1eced84d58574f912 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
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;
    }
  }
}