5
// Mike KrĆ¼ger <mkrueger@novell.com>
7
// Copyright (C) 2009 Novell, Inc (http://www.novell.com)
9
// Permission is hereby granted, free of charge, to any person obtaining
10
// a copy of this software and associated documentation files (the
11
// "Software"), to deal in the Software without restriction, including
12
// without limitation the rights to use, copy, modify, merge, publish,
13
// distribute, sublicense, and/or sell copies of the Software, and to
14
// permit persons to whom the Software is furnished to do so, subject to
15
// the following conditions:
17
// The above copyright notice and this permission notice shall be
18
// included in all copies or substantial portions of the Software.
20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31
using System.Collections.Generic;
32
using Mono.TextEditor.Highlighting;
33
using Mono.TextEditor;
35
using MonoDevelop.Projects;
40
namespace MonoDevelop.CSharpBinding
42
public class CSharpSyntaxMode : Mono.TextEditor.Highlighting.SyntaxMode
44
public CSharpSyntaxMode ()
46
ResourceXmlProvider provider = new ResourceXmlProvider (typeof(IXmlProvider).Assembly, "CSharpSyntaxMode.xml");
47
using (XmlReader reader = provider.Open ()) {
48
SyntaxMode baseMode = SyntaxMode.Read (reader);
49
this.rules = new List<Rule> (baseMode.Rules);
50
this.keywords = new List<Keywords> (baseMode.Keywords);
51
this.spans = baseMode.Spans;
52
this.matches = baseMode.Matches;
53
this.prevMarker = baseMode.PrevMarker;
54
this.SemanticRules = new List<SemanticRule> (baseMode.SemanticRules);
55
this.table = baseMode.Table;
56
this.properties = baseMode.Properties;
59
AddSemanticRule ("Comment", new HighlightUrlSemanticRule ("comment"));
60
AddSemanticRule ("XmlDocumentation", new HighlightUrlSemanticRule ("comment"));
61
AddSemanticRule ("String", new HighlightUrlSemanticRule ("string"));
62
AddSemanticRule (new HighlightCSharpSemanticRule ());
65
public override SpanParser CreateSpanParser (Document doc, SyntaxMode mode, LineSegment line, Stack<Span> spanStack)
67
return new CSharpSpanParser (doc, mode, line, spanStack);
70
class IfBlockSpan : Span
77
public IfBlockSpan (bool isValid)
79
this.IsValid = isValid;
80
TagColor = "text.preprocessor";
82
Color = "comment.block";
90
public override string ToString ()
92
return string.Format("[IfBlockSpan: IsValid={0}, Color={1}, Rule={2}]", IsValid, Color, Rule);
96
class ElseIfBlockSpan : Span
103
public ElseIfBlockSpan (bool isValid)
105
this.IsValid = isValid;
106
TagColor = "text.preprocessor";
108
Color = "comment.block";
116
public override string ToString ()
118
return string.Format("[ElseIfBlockSpan: IsValid={0}, Color={1}, Rule={2}]", IsValid, Color, Rule);
122
class ElseBlockSpan : Span
124
public bool IsValid {
129
public ElseBlockSpan (bool isValid)
131
this.IsValid = isValid;
132
TagColor = "text.preprocessor";
134
Color = "comment.block";
143
public override string ToString ()
145
return string.Format("[ElseBlockSpan: IsValid={0}, Color={1}, Rule={2}]", IsValid, Color, Rule);
149
protected class CSharpSpanParser : SpanParser
151
class ConditinalExpressionEvaluator : ICSharpCode.NRefactory.Visitors.AbstractAstVisitor
153
HashSet<string> symbols = new HashSet<string> ();
154
public ConditinalExpressionEvaluator ()
156
Project project = MonoDevelop.Ide.Gui.IdeApp.ProjectOperations.CurrentSelectedProject;
157
if (project != null) {
158
CSharpCompilerParameters cparams = ((DotNetProjectConfiguration)project.GetActiveConfiguration (project.ParentSolution.DefaultConfigurationId)).CompilationParameters as CSharpCompilerParameters;
159
if (cparams != null) {
160
string[] syms = cparams.DefineSymbols.Split (';', ',');
161
foreach (string s in syms) {
162
string ss = s.Trim ();
163
if (ss.Length > 0 && !symbols.Contains (ss))
170
public override object VisitIdentifierExpression (ICSharpCode.NRefactory.Ast.IdentifierExpression identifierExpression, object data)
172
return symbols.Contains (identifierExpression.Identifier);
175
public override object VisitUnaryOperatorExpression (ICSharpCode.NRefactory.Ast.UnaryOperatorExpression unaryOperatorExpression, object data)
177
bool result = (bool)(unaryOperatorExpression.Expression.AcceptVisitor (this, data) ?? (object)false);
178
if (unaryOperatorExpression.Op == ICSharpCode.NRefactory.Ast.UnaryOperatorType.Not)
183
public override object VisitPrimitiveExpression (ICSharpCode.NRefactory.Ast.PrimitiveExpression primitiveExpression, object data)
185
return (bool)primitiveExpression.Value;
188
public override object VisitBinaryOperatorExpression (ICSharpCode.NRefactory.Ast.BinaryOperatorExpression binaryOperatorExpression, object data)
190
bool left = (bool)(binaryOperatorExpression.Left.AcceptVisitor (this, data) ?? (object)false);
191
bool right = (bool)(binaryOperatorExpression.Right.AcceptVisitor (this, data) ?? (object)false);
193
switch (binaryOperatorExpression.Op) {
194
case ICSharpCode.NRefactory.Ast.BinaryOperatorType.InEquality:
195
return left != right;
196
case ICSharpCode.NRefactory.Ast.BinaryOperatorType.Equality:
197
return left == right;
198
case ICSharpCode.NRefactory.Ast.BinaryOperatorType.LogicalOr:
199
return left || right;
200
case ICSharpCode.NRefactory.Ast.BinaryOperatorType.LogicalAnd:
201
return left && right;
204
Console.WriteLine ("Unknown operator:" + binaryOperatorExpression.Op);
209
protected override void ScanSpan (ref int i)
211
if (i + 5 < doc.Length && doc.GetTextAt (i, 5) == "#else" && spanStack.Any (span => span is IfBlockSpan)) {
212
bool previousResult = false;
213
foreach (Span span in spanStack.ToArray ().Reverse ()) {
214
if (span is IfBlockSpan) {
215
previousResult = ((IfBlockSpan)span).IsValid;
217
if (span is ElseIfBlockSpan) {
218
previousResult |= ((ElseIfBlockSpan)span).IsValid;
222
LineSegment line = doc.GetLineByOffset (i);
223
int length = line.Offset + line.EditableLength - i;
224
while (spanStack.Count > 0 && !(CurSpan is IfBlockSpan)) {
227
// IfBlockSpan ifBlock = (IfBlockSpan)CurSpan;
229
ElseBlockSpan elseBlockSpan = new ElseBlockSpan (!previousResult);
230
OnFoundSpanBegin (elseBlockSpan, i, 0);
232
spanStack.Push (elseBlockSpan);
233
ruleStack.Push (GetRule (elseBlockSpan));
235
// put pre processor eol span on stack, so that '#else' gets the correct highlight
236
OnFoundSpanBegin (preprocessorSpan, i, 1);
237
spanStack.Push (preprocessorSpan);
238
ruleStack.Push (preprocessorRule);
242
if (CurRule.Name == "text.preprocessor" && i >= 3 && doc.GetTextAt (i - 3, 3) == "#if") {
243
LineSegment line = doc.GetLineByOffset (i);
244
int length = line.Offset + line.EditableLength - i;
245
string parameter = doc.GetTextAt (i, length);
246
ICSharpCode.NRefactory.Parser.CSharp.Lexer lexer = new ICSharpCode.NRefactory.Parser.CSharp.Lexer (new System.IO.StringReader (parameter));
247
ICSharpCode.NRefactory.Ast.Expression expr = lexer.PPExpression ();
249
if (expr != null && !expr.IsNull) {
250
object o = expr.AcceptVisitor (new ConditinalExpressionEvaluator (), null);
254
IfBlockSpan ifBlockSpan = new IfBlockSpan (result);
255
OnFoundSpanBegin (ifBlockSpan, i, length);
257
spanStack.Push (ifBlockSpan);
258
ruleStack.Push (GetRule (ifBlockSpan));
261
if (i + 5 < doc.Length && doc.GetTextAt (i, 5) == "#elif" && spanStack.Any (span => span is IfBlockSpan)) {
262
LineSegment line = doc.GetLineByOffset (i);
263
int length = line.Offset + line.EditableLength - i;
264
string parameter = doc.GetTextAt (i + 5, length - 5);
266
ICSharpCode.NRefactory.Parser.CSharp.Lexer lexer = new ICSharpCode.NRefactory.Parser.CSharp.Lexer (new System.IO.StringReader (parameter));
267
ICSharpCode.NRefactory.Ast.Expression expr = lexer.PPExpression ();
269
bool result = !expr.IsNull ? (bool)expr.AcceptVisitor (new ConditinalExpressionEvaluator (), null) : false;
272
bool previousResult = false;
273
foreach (Span span in spanStack.ToArray ().Reverse ()) {
274
if (span is IfBlockSpan) {
275
previousResult = ((IfBlockSpan)span).IsValid;
277
if (span is ElseIfBlockSpan) {
278
previousResult |= ((ElseIfBlockSpan)span).IsValid;
282
result = !previousResult;
285
ElseIfBlockSpan elseIfBlockSpan = new ElseIfBlockSpan (result);
286
OnFoundSpanBegin (elseIfBlockSpan, i, 0);
288
spanStack.Push (elseIfBlockSpan);
289
ruleStack.Push (GetRule (elseIfBlockSpan));
291
// put pre processor eol span on stack, so that '#elif' gets the correct highlight
292
OnFoundSpanBegin (preprocessorSpan, i, 1);
293
spanStack.Push (preprocessorSpan);
294
ruleStack.Push (preprocessorRule);
298
base.ScanSpan (ref i);
301
protected override bool ScanSpanEnd (Mono.TextEditor.Highlighting.Span cur, int i)
303
if (cur is IfBlockSpan || cur is ElseIfBlockSpan || cur is ElseBlockSpan) {
304
bool end = i + 6 < doc.Length && doc.GetTextAt (i, 6) == "#endif";
306
OnFoundSpanEnd (cur, i, 0); // put empty end tag in
307
while (!(spanStack.Peek () is IfBlockSpan)) {
309
if (ruleStack.Count > 1) // rulStack[1] is always syntax mode
313
if (ruleStack.Count > 1) // rulStack[1] is always syntax mode
315
// put pre processor eol span on stack, so that '#endif' gets the correct highlight
316
foreach (Span span in mode.Spans) {
317
if (span.Rule == "text.preprocessor") {
318
OnFoundSpanBegin (span, i, 1);
319
spanStack.Push (span);
320
ruleStack.Push (GetRule (span));
327
return base.ScanSpanEnd (cur, i);
330
Span preprocessorSpan;
331
Rule preprocessorRule;
333
public CSharpSpanParser (Document doc, SyntaxMode mode, LineSegment line, Stack<Span> spanStack) : base (doc, mode, line, spanStack)
335
foreach (Span span in mode.Spans) {
336
if (span.Rule == "text.preprocessor") {
337
preprocessorSpan = span;
338
preprocessorRule = GetRule (span);
b'\\ No newline at end of file'