ansi colors: extend ConsoleViewContentType instead of TextAttributes (http://crucible...
[idea/community.git] / platform / platform-api / src / com / intellij / execution / process / ColoredOutputTypeRegistry.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.execution.process;
17
18 import com.intellij.execution.ui.ConsoleViewContentType;
19 import com.intellij.openapi.components.ServiceManager;
20 import com.intellij.openapi.editor.colors.EditorColorsManager;
21 import com.intellij.openapi.editor.colors.EditorColorsScheme;
22 import com.intellij.openapi.editor.colors.TextAttributesKey;
23 import com.intellij.openapi.editor.markup.EffectType;
24 import com.intellij.openapi.editor.markup.TextAttributes;
25 import com.intellij.openapi.util.Key;
26 import com.intellij.openapi.util.text.StringUtil;
27 import com.intellij.util.containers.ContainerUtil;
28 import org.jetbrains.annotations.NonNls;
29 import org.jetbrains.annotations.NotNull;
30 import org.jetbrains.annotations.Nullable;
31
32 import java.awt.*;
33 import java.util.Map;
34
35 /**
36  * @author yole
37  */
38 public class ColoredOutputTypeRegistry {
39   public static ColoredOutputTypeRegistry getInstance() {
40     return ServiceManager.getService(ColoredOutputTypeRegistry.class);
41   }
42
43   private final Map<String, Key> myRegisteredKeys = ContainerUtil.newConcurrentMap();
44
45   private static final TextAttributesKey[] myAnsiColorKeys = new TextAttributesKey[]{
46     ConsoleHighlighter.BLACK,
47     ConsoleHighlighter.RED,
48     ConsoleHighlighter.GREEN,
49     ConsoleHighlighter.YELLOW,
50     ConsoleHighlighter.BLUE,
51     ConsoleHighlighter.MAGENTA,
52     ConsoleHighlighter.CYAN,
53     ConsoleHighlighter.GRAY,
54
55     ConsoleHighlighter.DARKGRAY,
56     ConsoleHighlighter.RED_BRIGHT,
57     ConsoleHighlighter.GREEN_BRIGHT,
58     ConsoleHighlighter.YELLOW_BRIGHT,
59     ConsoleHighlighter.BLUE_BRIGHT,
60     ConsoleHighlighter.MAGENTA_BRIGHT,
61     ConsoleHighlighter.CYAN_BRIGHT,
62     ConsoleHighlighter.WHITE,
63   };
64
65   /*
66     Description
67      0  Cancel all attributes except foreground/background color
68      1  Bright (bold)
69      2  Normal (not bold)
70      4  Underline
71      5  Blink
72      7  Reverse video
73      8  Concealed (don't display characters)
74      30 Make foreground (the characters) black
75      31 Make foreground red
76      32 Make foreground green
77      33 Make foreground yellow
78      34 Make foreground blue
79      35 Make foreground magenta
80      36 Make foreground cyan
81      37 Make foreground white
82
83      40 Make background (around the characters) black
84      41 Make background red
85      42 Make background green
86      43 Make background yellow
87      44 Make background blue
88      45 Make background magenta
89      46 Make background cyan
90      47 Make background white (you may need 0 instead, or in addition)
91
92      see full doc at http://en.wikipedia.org/wiki/ANSI_escape_code
93   */
94
95   @NotNull
96   public Key getOutputKey(@NonNls String attribute) {
97     final Key key = myRegisteredKeys.get(attribute);
98     if (key != null) {
99       return key;
100     }
101     final String completeAttribute = attribute;
102     if (attribute.startsWith("\u001B[")) {
103       attribute = attribute.substring(2);
104     }
105     else {
106       attribute = StringUtil.trimStart(attribute, "[");
107     }
108     attribute = StringUtil.trimEnd(attribute, "m");
109     if (attribute.equals("0")) {
110       return ProcessOutputTypes.STDOUT;
111     }
112     Key newKey = new Key(completeAttribute);
113     AnsiConsoleViewContentType contentType = createAnsiConsoleViewContentType(attribute);
114     ConsoleViewContentType.registerNewConsoleViewType(newKey, contentType);
115     myRegisteredKeys.put(completeAttribute, newKey);
116     return newKey;
117   }
118
119   private static Color getAnsiColor(final int value) {
120     return getColorByKey(getAnsiColorKey(value));
121   }
122
123   private static Color getColorByKey(TextAttributesKey colorKey) {
124     return EditorColorsManager.getInstance().getGlobalScheme().getAttributes(colorKey).getForegroundColor();
125   }
126
127   @NotNull
128   private static Color getDefaultForegroundColor() {
129     EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
130     TextAttributes attr = scheme.getAttributes(ConsoleViewContentType.NORMAL_OUTPUT_KEY);
131     Color color = attr != null ? attr.getForegroundColor() : null;
132     if (color == null) {
133       color = scheme.getDefaultForeground();
134     }
135     return color;
136   }
137
138   @NotNull
139   private static Color getDefaultBackgroundColor() {
140     EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
141     Color color = scheme.getColor(ConsoleViewContentType.CONSOLE_BACKGROUND_KEY);
142     if (color == null) {
143       color = scheme.getDefaultBackground();
144     }
145     return color;
146   }
147
148   public static TextAttributesKey getAnsiColorKey(int value) {
149     if (value >= 16) {
150       return ConsoleViewContentType.NORMAL_OUTPUT_KEY;
151     }
152     return myAnsiColorKeys[value];
153   }
154
155   @SuppressWarnings("ConstantConditions")
156   @NotNull
157   private static AnsiConsoleViewContentType createAnsiConsoleViewContentType(@NotNull String attribute) {
158     int foregroundColor = -1;
159     int backgroundColor = -1;
160     boolean inverse = false;
161     EffectType effectType = null;
162     int fontType = -1;
163     final String[] strings = attribute.split(";");
164     for (String string : strings) {
165       int value;
166       try {
167         value = Integer.parseInt(string);
168       }
169       catch (NumberFormatException e) {
170         continue;
171       }
172       if (value == 1) {
173         fontType = Font.BOLD;
174       }
175       else if (value == 4) {
176         effectType = EffectType.LINE_UNDERSCORE;
177       }
178       else if (value == 7) {
179         inverse = true;
180       }
181       else if (value == 22) {
182         fontType = Font.PLAIN;
183       }
184       else if (value == 24) {  //not underlined
185         effectType = null;
186       }
187       else if (value >= 30 && value <= 37) {
188         foregroundColor = value - 30;
189       }
190       else if (value == 38) {
191         //TODO: 256 colors foreground
192       }
193       else if (value == 39) {
194         foregroundColor = -1;
195       }
196       else if (value >= 40 && value <= 47) {
197         backgroundColor = value - 40;
198       }
199       else if (value == 48) {
200         //TODO: 256 colors background
201       }
202       else if (value == 49) {
203         backgroundColor = -1;
204       }
205       else if (value >= 90 && value <= 97) {
206         foregroundColor = value - 82;
207       }
208       else if (value >= 100 && value <= 107) {
209         backgroundColor = value - 92;
210       }
211     }
212     return new AnsiConsoleViewContentType(attribute, backgroundColor, foregroundColor, inverse, effectType, fontType);
213   }
214
215   private static class AnsiConsoleViewContentType extends ConsoleViewContentType {
216     private final int myBackgroundColor;
217     private final int myForegroundColor;
218     private final boolean myInverse;
219     private final EffectType myEffectType;
220     private final int myFontType;
221
222     private AnsiConsoleViewContentType(@NotNull String attribute,
223                                        int backgroundColor,
224                                        int foregroundColor,
225                                        boolean inverse,
226                                        @Nullable EffectType effectType,
227                                        int fontType) {
228       super(attribute, ConsoleViewContentType.NORMAL_OUTPUT_KEY);
229       myBackgroundColor = backgroundColor;
230       myForegroundColor = foregroundColor;
231       myInverse = inverse;
232       myEffectType = effectType;
233       myFontType = fontType;
234     }
235
236     @Override
237     public TextAttributes getAttributes() {
238       TextAttributes attrs = new TextAttributes();
239       attrs.setEffectType(myEffectType);
240       if (myFontType != -1) {
241         attrs.setFontType(myFontType);
242       }
243       Color foregroundColor = getForegroundColor();
244       Color backgroundColor = getBackgroundColor();
245       if (myInverse) {
246         attrs.setForegroundColor(backgroundColor);
247         attrs.setEffectColor(backgroundColor);
248         attrs.setBackgroundColor(foregroundColor);
249       }
250       else {
251         attrs.setForegroundColor(foregroundColor);
252         attrs.setEffectColor(foregroundColor);
253         attrs.setBackgroundColor(backgroundColor);
254       }
255       return attrs;
256     }
257
258     private Color getForegroundColor() {
259       return myForegroundColor != -1 ? getAnsiColor(myForegroundColor) : getDefaultForegroundColor();
260     }
261
262     private Color getBackgroundColor() {
263       return myBackgroundColor != -1 ? getAnsiColor(myBackgroundColor) : getDefaultBackgroundColor();
264     }
265   }
266 }