source: trunk/tools/vslol/CppKeywordClassifier.cs @ 2354

Last change on this file since 2354 was 2213, checked in by sam, 8 years ago

vslol: fix an infinite recursion triggered by IntelliSense.

File size: 11.3 KB
Line 
1//
2// Lol Engine - VsLol add-in for Visual Studio
3//
4// Copyright: (c) 2010-2013 Sam Hocevar <sam@hocevar.net>
5//   This program is free software; you can redistribute it and/or
6//   modify it under the terms of the Do What The Fuck You Want To
7//   Public License, Version 2, as published by Sam Hocevar. See
8//   http://www.wtfpl.net/ for more details.
9//
10
11using System;
12using System.Linq;
13using System.Collections.Generic;
14using System.ComponentModel.Composition;
15using System.Windows.Media;
16using System.Text.RegularExpressions;
17using System.Diagnostics; /* For debugging purposes */
18
19using Microsoft.VisualStudio.Text;
20using Microsoft.VisualStudio.Text.Classification;
21using Microsoft.VisualStudio.Text.Formatting;
22using Microsoft.VisualStudio.Language.StandardClassification;
23using Microsoft.VisualStudio.Utilities;
24
25namespace lol
26{
27
28[Export(typeof(IClassifierProvider))]
29[ContentType("c/c++")]
30[ContentType("csharp")]
31[ContentType("lolfx")]
32internal class LolClassifierProvider : IClassifierProvider
33{
34    [Import]
35    internal IClassificationTypeRegistryService m_type_registry = null;
36    [Import]
37    internal IClassifierAggregatorService m_aggregator = null;
38    [Import]
39    internal IClassificationFormatMapService m_format_map = null;
40
41    internal static bool m_inprogress = false;
42
43    public IClassifier GetClassifier(ITextBuffer buffer)
44    {
45        /* Avoid infinite recursion */
46        if (m_inprogress)
47            return null;
48
49        LolGenericFormat.SetRegistry(m_type_registry, m_format_map);
50
51        try
52        {
53            m_inprogress = true;
54            return buffer.Properties.GetOrCreateSingletonProperty<CppKeywordClassifier>(delegate { return new CppKeywordClassifier(m_type_registry, m_aggregator.GetClassifier(buffer), buffer.ContentType); });
55        }
56        finally { m_inprogress = false; }
57    }
58}
59
60class CppKeywordClassifier : IClassifier
61{
62    private IClassifier m_classifier;
63    private bool m_inprogress;
64
65    private IClassificationType m_types_type, m_constant_type, m_normal_type;
66    private Regex m_types_regex, m_constant_regex, m_normal_regex;
67
68    private static string[] m_all_types =
69    {
70        "void|bool|int|signed|unsigned|char|short|long|float|double",
71        "class|struct|union|template|namespace|typename|typedef",
72        "inline|restrict|export|explicit|mutable",
73        "static|register|auto|volatile|extern|const"
74    };
75
76    private static string[] m_cpp_types =
77    {
78        "u?int(8|16|32|64|ptr|max)_t",
79        "u?int_(least|fast)(8|16|32|64)_t",
80        "(wchar|char16|char32|size|ssize|off|ptrdiff)_t",
81        "(sig_atomic|fpos|clock|time|div|ldiv)_t",
82        "va_list|jmp_buf|FILE|DIR",
83        "__(int(8|16|32|64)|ptr(32|64)|m(64|128|128d|128i))",
84    };
85
86    /* ldouble real half
87       "(f(16|128)||d|[ui](8|16||64)|r)(vec[234]|mat[234]|quat|cmplx)";
88     */
89
90    private static string[] m_csharp_types =
91    {
92        "var|string",
93        "out|ref|internal|sealed|public|private|protected|override"
94    };
95
96    private static string[] m_lolfx_types =
97    {
98        "attribute|varying|uniform|in|out",
99        "int|uint",
100        "(|[dui])(vec|mat)[234]"
101    };
102
103    private static string[] m_all_constants =
104    {
105        "true|false"
106    };
107
108    private static string[] m_cpp_constants =
109    {
110        "NULL|nullptr",
111        "EXIT_SUCCESS|EXIT_FAILURE",
112        "M_(E|LOG(2|10)E|LN2|LN10|PI|PI_2|PI_4|1_PI|2_PI|2_SQRTPI|SQRT(2|1_2))",
113        "SIG(HUP|INT|QUIT|ILL|TRAP|ABRT|FPE|KILL|USR1|SEGV|USR2|PIPE|ALRM)",
114        "SIG(TERM|CHLD|CONT|STOP|TSTP|TTIN|TTOU)"
115    };
116
117    private static string[] m_csharp_constants =
118    {
119        "null",
120    };
121
122    private static string[] m_lolfx_constants =
123    {
124        "gl_Position|gl_FragColor",
125    };
126
127    private static string[] m_all_normal =
128    {
129    };
130
131    private static string[] m_cpp_normal =
132    {
133        "interface|delegate|event|finally",
134        "gcnew|generic|initonly|property|sealed",
135    };
136
137    internal CppKeywordClassifier(IClassificationTypeRegistryService registry,
138                                  IClassifier classifier,
139                                  IContentType type)
140    {
141        m_classifier = classifier;
142        m_inprogress = false;
143
144        m_types_type = registry.GetClassificationType("LolAnyType");
145        m_normal_type = registry.GetClassificationType("LolAnyIdentifier");
146        m_constant_type = registry.GetClassificationType("LolAnyConstant");
147
148        List<string> types_list = m_all_types.ToList();
149        List<string> constants_list = m_all_constants.ToList();
150        List<string> normals_list = m_all_normal.ToList();
151
152        if (type.IsOfType("c/c++"))
153        {
154            types_list = types_list.Concat(m_cpp_types).ToList();
155            constants_list = constants_list.Concat(m_cpp_constants).ToList();
156            normals_list = normals_list.Concat(m_cpp_normal).ToList();
157        }
158
159        if (type.IsOfType("csharp"))
160        {
161            types_list = types_list.Concat(m_csharp_types).ToList();
162            constants_list = constants_list.Concat(m_csharp_constants).ToList();
163        }
164
165        if (type.IsOfType("lolfx"))
166        {
167            types_list = types_list.Concat(m_lolfx_types).ToList();
168            constants_list = constants_list.Concat(m_lolfx_constants).ToList();
169        }
170
171        m_types_regex =
172            new Regex("^(" + String.Join("|", types_list.ToArray()) + ")$");
173        m_constant_regex =
174            new Regex("^(" + String.Join("|", constants_list.ToArray()) + ")$");
175        m_normal_regex =
176            new Regex("^(" + String.Join("|", normals_list.ToArray()) + ")$");
177    }
178
179    public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
180    {
181        List<ClassificationSpan> ret = new List<ClassificationSpan>();
182
183        if (m_inprogress)
184        {
185            /* For some reason we can get called recursively when parsing a
186             * string for the IntelliSense drop-down menu. There is information
187             * on how to deal with it properly on the following SO page:
188             * http://stackoverflow.com/q/3155598/111461
189             * The crash can be reproduced by simply typing "vec2(" and waiting
190             * for IntelliSense to spawn the menu. */
191            ret.Add(new ClassificationSpan(span, m_constant_type));
192            return ret;
193        }
194
195        m_inprogress = true;
196
197        foreach (ClassificationSpan cs in m_classifier.GetClassificationSpans(span))
198        {
199            string cs_class = cs.ClassificationType.Classification.ToLower();
200
201            /* Only apply our rules if we found a keyword or an identifier */
202            if (cs_class == "keyword" || cs_class == "identifier")
203            {
204                if (m_types_regex.IsMatch(cs.Span.GetText()))
205                {
206                    ret.Add(new ClassificationSpan(cs.Span, m_types_type));
207                    continue;
208                }
209
210                if (m_constant_regex.IsMatch(cs.Span.GetText()))
211                {
212                    ret.Add(new ClassificationSpan(cs.Span, m_constant_type));
213                    continue;
214                }
215
216                if (m_normal_regex.IsMatch(cs.Span.GetText()))
217                {
218                    ret.Add(new ClassificationSpan(cs.Span, m_normal_type));
219                    continue;
220                }
221            }
222
223            ret.Add(cs);
224        }
225
226        m_inprogress = false;
227
228        return ret;
229    }
230
231    public event EventHandler<ClassificationChangedEventArgs> ClassificationChanged;
232}
233
234internal class LolGenericFormat : ClassificationFormatDefinition
235{
236    static IClassificationTypeRegistryService m_type_registry;
237    static IClassificationFormatMapService m_format_map;
238
239    public static void SetRegistry(IClassificationTypeRegistryService type_registry,
240                                   IClassificationFormatMapService format_map)
241    {
242        m_type_registry = type_registry;
243        m_format_map = format_map;
244    }
245
246    protected void CopyStyleColor(string category)
247    {
248        if (m_type_registry == null || m_format_map == null)
249            return;
250
251        var map = m_format_map.GetClassificationFormatMap("Text Editor");
252        if (map == null)
253            return;
254
255        //string[] foo = { "Comment", "Keyword", "C/C++ User Keywords", "Call Return", "HTML Comment" , "User Types", "User Types (Type parameters)", "User Types (Value types)"};
256
257        var type = m_type_registry.GetClassificationType(category);
258        if (type == null)
259            return;
260
261        var prop = map.GetExplicitTextProperties(type);
262        if (prop == null)
263            return;
264
265        var c1 = prop.ForegroundBrush as SolidColorBrush;
266        if (c1 != null && c1.Color != Colors.Transparent)
267        {
268            this.ForegroundColor = c1.Color;
269            this.ForegroundOpacity = 1.0;
270        }
271        var c2 = prop.BackgroundBrush as SolidColorBrush;
272        if (c2 != null && c2.Color != Colors.Transparent)
273        {
274            this.BackgroundColor = c2.Color;
275            this.BackgroundOpacity = 1.0;
276        }
277    }
278}
279
280internal static class LolClassifierClassificationDefinition
281{
282    [Export(typeof(ClassificationTypeDefinition))]
283    [Name(LolCppTypeFormat.m_name)]
284    internal static ClassificationTypeDefinition LolCustomClassType = null;
285
286    [Export(typeof(ClassificationTypeDefinition))]
287    [Name(LolCppConstantFormat.m_name)]
288    internal static ClassificationTypeDefinition LolCustomConstantType = null;
289
290    [Export(typeof(ClassificationTypeDefinition))]
291    [Name(LolCppIdentifierFormat.m_name)]
292    internal static ClassificationTypeDefinition LolCustomIdentifierType = null;
293}
294
295[Export(typeof(EditorFormatDefinition))]
296[ClassificationType(ClassificationTypeNames = LolCppTypeFormat.m_name)]
297[Name(LolCppTypeFormat.m_name)]
298[UserVisible(true)]
299[Order(After = Priority.Default)] /* Override the Visual Studio classifiers */
300internal sealed class LolCppTypeFormat : LolGenericFormat
301{
302    public const string m_name = "LolAnyType";
303    public LolCppTypeFormat()
304    {
305        this.DisplayName = "C/C++ Types and Qualifiers (VsLol)";
306        this.ForegroundColor = Colors.Lime;
307        this.ForegroundOpacity = 1.0;
308        this.IsBold = true;
309        //CopyStyleColor("User Types");
310    }
311}
312
313[Export(typeof(EditorFormatDefinition))]
314[ClassificationType(ClassificationTypeNames = LolCppConstantFormat.m_name)]
315[Name(LolCppConstantFormat.m_name)]
316[UserVisible(true)]
317[Order(After = Priority.Default)] /* Override the Visual Studio classifiers */
318internal sealed class LolCppConstantFormat : LolGenericFormat
319{
320    public const string m_name = "LolAnyConstant";
321    public LolCppConstantFormat()
322    {
323        this.DisplayName = "C/C++ Constants (VsLol)";
324        this.ForegroundColor = Colors.Magenta;
325        this.ForegroundOpacity = 1.0;
326        this.IsBold = true;
327        //CopyStyleColor("User Types");
328    }
329}
330
331[Export(typeof(EditorFormatDefinition))]
332[ClassificationType(ClassificationTypeNames = LolCppIdentifierFormat.m_name)]
333[Name(LolCppIdentifierFormat.m_name)]
334[UserVisible(true)]
335[Order(After = Priority.Default)] /* Override the Visual Studio classifiers */
336internal sealed class LolCppIdentifierFormat : LolGenericFormat
337{
338    public const string m_name = "LolAnyIdentifier";
339    public LolCppIdentifierFormat()
340    {
341        this.DisplayName = "C/C++ Identifiers (VsLol)";
342        this.ForegroundColor = Colors.Silver;
343        this.ForegroundOpacity = 1.0;
344        this.IsBold = false;
345        CopyStyleColor(PredefinedClassificationTypeNames.Identifier);
346    }
347}
348
349} /* namespace lol */
Note: See TracBrowser for help on using the repository browser.