~halega/+junk/sharpdevelop

« back to all changes in this revision

Viewing changes to src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlColorizer.cs

  • Committer: sk
  • Date: 2011-09-10 05:17:57 UTC
  • Revision ID: halega@halega.com-20110910051757-qfouz1llya9m6boy
4.1.0.7915 Release Candidate 1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
 
2
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
 
3
 
 
4
using ICSharpCode.Core;
 
5
using System;
 
6
using System.Collections.Generic;
 
7
using System.Diagnostics;
 
8
using System.Linq;
 
9
using System.Threading;
 
10
using System.Windows.Threading;
 
11
using ICSharpCode.AvalonEdit.Document;
 
12
using ICSharpCode.AvalonEdit.Rendering;
 
13
using ICSharpCode.AvalonEdit.Xml;
 
14
using ICSharpCode.SharpDevelop;
 
15
using ICSharpCode.SharpDevelop.Dom;
 
16
using ICSharpCode.SharpDevelop.Editor;
 
17
using ICSharpCode.SharpDevelop.Gui;
 
18
using ICSharpCode.XmlEditor;
 
19
 
 
20
namespace ICSharpCode.XamlBinding
 
21
{
 
22
        using Task = System.Threading.Tasks.Task;
 
23
        using Tasks = System.Threading.Tasks;
 
24
        
 
25
        public class XamlColorizer : DocumentColorizingTransformer, ILineTracker, IDisposable
 
26
        {
 
27
                public struct Highlight {
 
28
                        public IMember Member { get; set; }
 
29
                        public HighlightingInfo Info { get; set; }
 
30
                }
 
31
                
 
32
                public sealed class HighlightTask {
 
33
                        // input
 
34
                        readonly string fileName;
 
35
                        readonly string lineText;
 
36
                        readonly int offset;
 
37
                        
 
38
                        TextView textView;
 
39
                        ITextBuffer snapshot;
 
40
                        
 
41
                        public HighlightTask(ITextEditor editor, DocumentLine currentLine, TextView textView)
 
42
                        {
 
43
                                this.fileName = editor.FileName;
 
44
                                this.textView = textView;
 
45
                                this.snapshot = editor.Document.CreateSnapshot();
 
46
                                this.lineText = textView.Document.GetText(currentLine);
 
47
                                this.offset = currentLine.Offset;
 
48
                                this.task = new Task(Process);
 
49
                        }
 
50
                        
 
51
                        public HighlightTask(ITextEditor editor, DocumentLine currentLine, TextView textView, IList<Highlight> oldHighlightData)
 
52
                                : this(editor, currentLine, textView)
 
53
                        {
 
54
                                this.results = oldHighlightData;
 
55
                        }
 
56
                        
 
57
                        IList<Highlight> results;
 
58
                        
 
59
                        // output
 
60
                        public Highlight[] GetResults()
 
61
                        {
 
62
                                lock (this) {
 
63
                                        if (results == null)
 
64
                                                return null;
 
65
                                        return results.ToArray();
 
66
                                }
 
67
                        }
 
68
                        
 
69
                        readonly Task task;
 
70
                        
 
71
                        public void Start()
 
72
                        {
 
73
                                task.Start();
 
74
                        }
 
75
                        
 
76
                        public bool CompletedSuccessfully {
 
77
                                get {
 
78
                                        return task.IsCompleted && task.Status == Tasks.TaskStatus.RanToCompletion;
 
79
                                }
 
80
                        }
 
81
                        
 
82
                        public bool Invalid { get; set; }
 
83
                        
 
84
                        void Process()
 
85
                        {
 
86
                                try {
 
87
                                        List<Highlight> results = new List<Highlight>();
 
88
                                        
 
89
                                        foreach (HighlightingInfo info in GetInfo()) {
 
90
                                                IMember member = null;
 
91
                                                
 
92
                                                // Commented out because task doesn't come with cancellation support in .NET 4.0 Beta 2
 
93
                                                // (use CancellationToken instead)
 
94
                                                // I didn't have to remove any call to task.Cancel(), so apparently this was dead code.
 
95
                                                //if (task.IsCancellationRequested) {
 
96
                                                //      task.AcknowledgeCancellation();
 
97
                                                //      return;
 
98
                                                //}
 
99
                                                // TODO: implement cancellation support
 
100
                                                
 
101
                                                if (!info.Token.StartsWith("xmlns", StringComparison.OrdinalIgnoreCase)) {
 
102
                                                        MemberResolveResult rr = XamlResolver.Resolve(info.Token, info.Context) as MemberResolveResult;
 
103
                                                        member = (rr != null) ? rr.ResolvedMember : null;
 
104
                                                }
 
105
                                                
 
106
                                                results.Add(new Highlight() { Member = member, Info = info });
 
107
                                        }
 
108
                                        
 
109
                                        lock (this)
 
110
                                                this.results = results;
 
111
                                        
 
112
                                        WorkbenchSingleton.SafeThreadAsyncCall(InvokeRedraw);
 
113
                                } catch (Exception e) {
 
114
                                        WorkbenchSingleton.SafeThreadAsyncCall(() => MessageService.ShowException(e));
 
115
                                }
 
116
                        }
 
117
                        
 
118
                        void InvokeRedraw()
 
119
                        {
 
120
                                task.Wait();
 
121
                                textView.Redraw(this.offset, this.lineText.Length, DispatcherPriority.Background);
 
122
                        }
 
123
                        
 
124
                        IEnumerable<HighlightingInfo> GetInfo()
 
125
                        {
 
126
                                int index = -1;
 
127
                                XamlContext context = null;
 
128
                                
 
129
                                do {
 
130
                                        if (index + 1 >= lineText.Length)
 
131
                                                break;
 
132
 
 
133
                                        index = lineText.IndexOfAny(index + 1, '=', '.');
 
134
                                        if (index > -1) {
 
135
                                                context = CompletionDataHelper.ResolveContext(snapshot, fileName, offset + index);
 
136
                                                
 
137
                                                if (context.ActiveElement == null || context.InAttributeValueOrMarkupExtension || context.InCommentOrCData)
 
138
                                                        continue;
 
139
                                                
 
140
                                                string propertyName;
 
141
                                                string token;
 
142
                                                int startIndex;
 
143
                                                
 
144
                                                switch (context.Description) {
 
145
                                                        case XamlContextDescription.AtTag:
 
146
                                                                token = context.ActiveElement.Name;
 
147
                                                                int propertyNameIndex = token.IndexOf('.');
 
148
                                                                
 
149
                                                                if (propertyNameIndex == -1)
 
150
                                                                        continue;
 
151
                                                                
 
152
                                                                propertyName = token.Substring(propertyNameIndex + 1);
 
153
                                                                startIndex = lineText.IndexOf(propertyName, index, StringComparison.Ordinal);
 
154
                                                                break;
 
155
                                                        case XamlContextDescription.InTag:
 
156
                                                                if (lineText[index] == '.' || context.Attribute == null)
 
157
                                                                        continue;
 
158
                                                                
 
159
                                                                token = propertyName = context.Attribute.Name;
 
160
                                                                startIndex = lineText.LastIndexOf(propertyName, index, StringComparison.Ordinal);
 
161
                                                                break;
 
162
                                                        default:
 
163
                                                                continue;
 
164
                                                }
 
165
                                                
 
166
                                                if (startIndex > -1) {
 
167
                                                        yield return new HighlightingInfo(token, startIndex, startIndex + propertyName.Length, offset, context);
 
168
                                                }
 
169
                                        }
 
170
                                } while (index > -1);
 
171
                        }
 
172
                }
 
173
                
 
174
                Dictionary<DocumentLine, HighlightTask> highlightCache = new Dictionary<DocumentLine, HighlightTask>();
 
175
                
 
176
                public ITextEditor Editor { get; set; }
 
177
                
 
178
                public AvalonEdit.Rendering.TextView TextView { get; set;       }
 
179
                
 
180
                public XamlColorizer(ITextEditor editor, TextView textView)
 
181
                {
 
182
                        this.Editor = editor;
 
183
                        this.TextView = textView;
 
184
                        
 
185
                        this.weakLineTracker = WeakLineTracker.Register(this.Editor.Document.GetService(typeof(TextDocument)) as TextDocument, this);
 
186
                        
 
187
                        ParserService.LoadSolutionProjectsThreadEnded += ParserServiceLoadSolutionProjectsThreadEnded;
 
188
                        
 
189
                        colorizers.Add(this);
 
190
                }
 
191
                
 
192
                static IList<XamlColorizer> colorizers = new List<XamlColorizer>();
 
193
                
 
194
                public static void RefreshAll()
 
195
                {
 
196
                        foreach (XamlColorizer colorizer in colorizers) {
 
197
                                colorizer.RebuildDocument();
 
198
                                colorizer.TextView.Redraw();
 
199
                        }
 
200
                }
 
201
 
 
202
                void ParserServiceLoadSolutionProjectsThreadEnded(object sender, EventArgs e)
 
203
                {
 
204
                        WorkbenchSingleton.SafeThreadAsyncCall(
 
205
                                () => {
 
206
                                        highlightCache.Clear();
 
207
                                        TextView.Redraw();
 
208
                                }
 
209
                        );
 
210
                }
 
211
                
 
212
                WeakLineTracker weakLineTracker;
 
213
                bool disposed;
 
214
                
 
215
                public void Dispose()
 
216
                {
 
217
                        if (!disposed) {
 
218
                                ParserService.LoadSolutionProjectsThreadEnded -= ParserServiceLoadSolutionProjectsThreadEnded;
 
219
                                weakLineTracker.Deregister();
 
220
                                colorizers.Remove(this);
 
221
                        }
 
222
                        disposed = true;
 
223
                }
 
224
                
 
225
                protected override void ColorizeLine(DocumentLine line)
 
226
                {
 
227
                        if (!highlightCache.ContainsKey(line)) {
 
228
                                HighlightTask task = new HighlightTask(this.Editor, line, this.TextView);
 
229
                                task.Start();
 
230
                                highlightCache.Add(line, task);
 
231
                        } else {
 
232
                                HighlightTask task = highlightCache[line];
 
233
                                var results = task.GetResults();
 
234
                                if (results != null) {
 
235
                                        foreach (var result in results) {
 
236
                                                ColorizeMember(result.Info, line, result.Member);
 
237
                                        }
 
238
                                }
 
239
                        }
 
240
                        ColorizeInvalidated();
 
241
                }
 
242
                
 
243
                void ColorizeMember(HighlightingInfo info, DocumentLine line, IMember member)
 
244
                {
 
245
                        try {
 
246
                                Action<VisualLineElement> handler = null;
 
247
                                
 
248
                                if (info.Token.StartsWith(info.Context.XamlNamespacePrefix + ":", StringComparison.Ordinal))
 
249
                                        handler = HighlightProperty;
 
250
                                else if (info.Context.IgnoredXmlns.Any(item => info.Token.StartsWith(item + ":", StringComparison.Ordinal)))
 
251
                                        handler = HighlightIgnored;
 
252
                                else if (member != null) {
 
253
                                        if (member is IEvent)
 
254
                                                handler = HighlightEvent;
 
255
                                        else
 
256
                                                handler = HighlightProperty;
 
257
                                } else {
 
258
                                        if (info.Token.StartsWith("xmlns", StringComparison.OrdinalIgnoreCase) || info.Token.StartsWith(Utils.GetNamespacePrefix(CompletionDataHelper.MarkupCompatibilityNamespace, info.Context) + ":", StringComparison.OrdinalIgnoreCase))
 
259
                                                handler = HighlightNamespaceDeclaration;
 
260
                                        else if (info.Token.StartsWith("xml:", StringComparison.OrdinalIgnoreCase))
 
261
                                                handler = HighlightProperty;
 
262
                                        else
 
263
                                                Core.LoggingService.Debug(info.Token + " not highlighted; line " + line.LineNumber);
 
264
                                }
 
265
                                if (handler != null)
 
266
                                        ChangeLinePart(line.Offset + info.StartOffset, line.Offset + info.EndOffset, handler);
 
267
                        } catch (ArgumentOutOfRangeException) {}
 
268
                }
 
269
 
 
270
                void ColorizeInvalidated()
 
271
                {
 
272
                        foreach (var item in highlightCache.ToArray()) {
 
273
                                if (item.Key.IsDeleted) {
 
274
                                        highlightCache.Remove(item.Key);
 
275
                                        continue;
 
276
                                }
 
277
                                if (item.Value.Invalid) {
 
278
                                        var newTask = new HighlightTask(this.Editor, item.Key, this.TextView, item.Value.GetResults());
 
279
                                        newTask.Start();
 
280
                                        highlightCache[item.Key] = newTask;
 
281
                                }
 
282
                        }
 
283
                }
 
284
 
 
285
                void InvalidateLines(DocumentLine line)
 
286
                {
 
287
                        DocumentLine current = line;
 
288
                        while (current != null) {
 
289
                                HighlightTask task;
 
290
                                if (highlightCache.TryGetValue(current, out task))
 
291
                                        task.Invalid = true;
 
292
 
 
293
                                current = current.NextLine;
 
294
                        }
 
295
                }
 
296
 
 
297
                #region highlight helpers
 
298
                void HighlightProperty(VisualLineElement element)
 
299
                {
 
300
                        element.TextRunProperties.SetForegroundBrush(XamlBindingOptions.PropertyForegroundColor.ToBrush());
 
301
                        element.TextRunProperties.SetBackgroundBrush(XamlBindingOptions.PropertyBackgroundColor.ToBrush());
 
302
                }
 
303
 
 
304
                void HighlightEvent(VisualLineElement element)
 
305
                {
 
306
                        element.TextRunProperties.SetForegroundBrush(XamlBindingOptions.EventForegroundColor.ToBrush());
 
307
                        element.TextRunProperties.SetBackgroundBrush(XamlBindingOptions.EventBackgroundColor.ToBrush());
 
308
                }
 
309
 
 
310
                void HighlightNamespaceDeclaration(VisualLineElement element)
 
311
                {
 
312
                        element.TextRunProperties.SetForegroundBrush(XamlBindingOptions.NamespaceDeclarationForegroundColor.ToBrush());
 
313
                        element.TextRunProperties.SetBackgroundBrush(XamlBindingOptions.NamespaceDeclarationBackgroundColor.ToBrush());
 
314
                }
 
315
 
 
316
                void HighlightIgnored(VisualLineElement element)
 
317
                {
 
318
                        element.TextRunProperties.SetForegroundBrush(XamlBindingOptions.IgnoredForegroundColor.ToBrush());
 
319
                        element.TextRunProperties.SetBackgroundBrush(XamlBindingOptions.IgnoredBackgroundColor.ToBrush());
 
320
                }
 
321
                #endregion
 
322
 
 
323
                #region ILineTracker implementation
 
324
                public void BeforeRemoveLine(DocumentLine line)
 
325
                {
 
326
                        InvalidateLines(line.NextLine);
 
327
                }
 
328
 
 
329
                public void SetLineLength(DocumentLine line, int newTotalLength)
 
330
                {
 
331
                        InvalidateLines(line);
 
332
                }
 
333
 
 
334
                public void LineInserted(DocumentLine insertionPos, DocumentLine newLine)
 
335
                {
 
336
                        InvalidateLines(newLine);
 
337
                }
 
338
 
 
339
                public void RebuildDocument()
 
340
                {
 
341
                        highlightCache.Clear();
 
342
                }
 
343
                #endregion
 
344
 
 
345
                public struct HighlightingInfo
 
346
                {
 
347
                        string token;
 
348
                        int startOffset;
 
349
                        int endOffset;
 
350
                        int lineOffset;
 
351
                        XamlContext context;
 
352
                        
 
353
                        public HighlightingInfo(string token, int startOffset, int endOffset, int lineOffset, XamlContext context)
 
354
                        {
 
355
                                if (token == null)
 
356
                                        throw new ArgumentNullException("token");
 
357
                                if (startOffset < 0)
 
358
                                        throw new ArgumentOutOfRangeException("startOffset", startOffset, "Value must be greater 0");
 
359
                                if (endOffset < 0)
 
360
                                        throw new ArgumentOutOfRangeException("endOffset", endOffset, "Value must be greater 0");
 
361
                                if (lineOffset < 0)
 
362
                                        throw new ArgumentOutOfRangeException("lineOffset", lineOffset, "Value must be greater 0");
 
363
                                if (context == null)
 
364
                                        throw new ArgumentNullException("context");
 
365
                                
 
366
                                this.token = token;
 
367
                                this.startOffset = startOffset;
 
368
                                this.endOffset = endOffset;
 
369
                                this.lineOffset = lineOffset;
 
370
                                this.context = context;
 
371
                        }
 
372
                        
 
373
                        public string Token {
 
374
                                get { return token; }
 
375
                        }
 
376
                        
 
377
                        public int StartOffset {
 
378
                                get { return startOffset; }
 
379
                        }
 
380
                        
 
381
                        public int EndOffset {
 
382
                                get { return endOffset; }
 
383
                        }
 
384
                        
 
385
                        public int LineOffset {
 
386
                                get { return lineOffset; }
 
387
                        }
 
388
                        
 
389
                        public XamlContext Context {
 
390
                                get { return context; }
 
391
                        }
 
392
                }
 
393
        }
 
394
}