diff --git a/AvalonStudio/AvalonStudio.Controls.Standard.Tests/StaticHighlightingTests.cs b/AvalonStudio/AvalonStudio.Controls.Standard.Tests/StaticHighlightingTests.cs new file mode 100644 index 0000000000..0d09e299e2 --- /dev/null +++ b/AvalonStudio/AvalonStudio.Controls.Standard.Tests/StaticHighlightingTests.cs @@ -0,0 +1,83 @@ +using AvaloniaEdit.Document; +using AvalonStudio.Controls.Standard.CodeEditor.Highlighting; +using AvalonStudio.Controls.Standard.CodeEditor.Highlighting.Resources; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using Xunit; + +namespace AvalonStudio.Controls.Standard.Tests +{ + public class StaticHighlightingTests + { + private static SyntaxHighlightingDefinition LoadDefinition(string resourceName) + { + using (var s = (Resources.OpenStream(resourceName))) + { + if (resourceName.EndsWith(".sublime-syntax")) + { + using (var sr = new StreamReader(s)) + { + return CodeEditor.Highlighting.Sublime3.Sublime3Format.ReadHighlighting(sr); + } + } + else if (resourceName.EndsWith(".tmLanguage")) + { + return CodeEditor.Highlighting.TextMate.TextMateFormat.ReadHighlighting(s); + } + else if (resourceName.EndsWith(".tmLanguage.json")) + { + return CodeEditor.Highlighting.TextMate.TextMateFormat.ReadHighlightingFromJson(s); + } + } + + return null; + } + + + + [Fact] + public void CSharp_Highlighting_Can_Detect_BuiltInTypes() + { + var definition = LoadDefinition("C#.sublime-syntax"); + + string testCode = @"namespace Test +{ + public class TestClass + { + public void Main (int x, int y) + { + } + } +}"; + + var highlightedLines = RunHighlightingTest(definition, testCode); + + Assert.Equal(highlightedLines[4].Segments[3].ColorStyleKey, "keyword.other.void.source.cs"); + } + + internal static List RunHighlightingTest(SyntaxHighlightingDefinition highlighting, string inputText) + { + var result = new List(); + + var document = new TextDocument(inputText); + + highlighting.PrepareMatches(); + + var syntaxHighlighting = new SyntaxHighlighting(highlighting, document); + + foreach (var line in document.Lines) + { + var coloredSegments = syntaxHighlighting.GetHighlightedLineAsync(line, CancellationToken.None).Result; + + result.Add(coloredSegments); + } + + return result; + } + + } +} diff --git a/AvalonStudio/AvalonStudio.Controls.Standard/AvalonStudio.Controls.Standard.csproj b/AvalonStudio/AvalonStudio.Controls.Standard/AvalonStudio.Controls.Standard.csproj index e2556477ec..9e50339e13 100644 --- a/AvalonStudio/AvalonStudio.Controls.Standard/AvalonStudio.Controls.Standard.csproj +++ b/AvalonStudio/AvalonStudio.Controls.Standard/AvalonStudio.Controls.Standard.csproj @@ -5,13 +5,26 @@ NU1701 + + TRACE;DEBUG;NETSTANDARD2_0;DISABLE_CAS_USE + + + + + + + + + + + @@ -20,12 +33,21 @@ + + + + + + + + + diff --git a/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/AvaloniaEditTextDocumentAdaptor.cs b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/AvaloniaEditTextDocumentAdaptor.cs index a2fa086462..ace2ba469c 100644 --- a/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/AvaloniaEditTextDocumentAdaptor.cs +++ b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/AvaloniaEditTextDocumentAdaptor.cs @@ -250,7 +250,7 @@ public DocumentAdaptor(AvaloniaEdit.Document.TextDocument document) private void _document_Changed(object sender, AvaloniaEdit.Document.DocumentChangeEventArgs e) { - Changed?.Invoke(this, new DocumentChangeEventArgs(e.Offset, e.RemovedText.Text, e.InsertedText.Text)); + Changed?.Invoke(this, new DocumentChangeEventArgs(e.Offset, offset => e.GetNewOffset(offset), e.RemovedText.Text, e.InsertedText.Text)); } ~DocumentAdaptor() @@ -292,6 +292,8 @@ public IDisposable RunUpdate() public IDocumentLine GetLineByNumber(int lineNumber) => Lines[lineNumber - 1]; + public IDocumentLine GetLineByOffset(int offset) => Lines[_document.GetLocation(offset).Line - 1]; + public TextLocation GetLocation(int offset) { var loc = _document.GetLocation(offset); diff --git a/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/CodeEditor.cs b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/CodeEditor.cs index 0b7bdbf6ed..a7aacd0427 100644 --- a/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/CodeEditor.cs +++ b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/CodeEditor.cs @@ -10,6 +10,7 @@ using AvaloniaEdit; using AvaloniaEdit.Document; using AvaloniaEdit.Editing; +using AvaloniaEdit.Highlighting; using AvaloniaEdit.Indentation; using AvaloniaEdit.Rendering; using AvaloniaEdit.Snippets; @@ -793,9 +794,7 @@ private void RegisterLanguageService(ISourceFile sourceFile) LanguageService = _shell.LanguageServices.FirstOrDefault( o => o.Metadata.TargetCapabilities.Any( - c => contentTypeService.CapabilityAppliesToContentType(c, sourceFile.ContentType)))?.Value; - - SyntaxHighlighting = CustomHighlightingManager.Instance.GetDefinition(sourceFile.ContentType); + c => contentTypeService.CapabilityAppliesToContentType(c, sourceFile.ContentType)))?.Value; if (LanguageService != null) { @@ -846,6 +845,20 @@ private void RegisterLanguageService(ISourceFile sourceFile) }; } + var definition = CustomHighlightingManager.Instance.GetAvalonStudioDefinition(sourceFile.ContentType); + + if (definition is IHighlightingDefinition avalonEditHighlighting) + { + SyntaxHighlighting = avalonEditHighlighting; + } + else if (definition is SyntaxHighlightingDefinition highlightingDefinition) + { + var colorizer = new StaticHighlightingColorizer(highlightingDefinition); + TextArea.TextView.LineTransformers.Insert(0, colorizer); + + colorizer.OnAddToEditor(this); + } + StartBackgroundWorkers(); Document.TextChanged += TextDocument_TextChanged; diff --git a/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/CustomHightlightingLoader.cs b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/CustomHightlightingLoader.cs index c89f1f6a76..ef759c6768 100644 --- a/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/CustomHightlightingLoader.cs +++ b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/CustomHightlightingLoader.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Text; using System.Xml; @@ -381,7 +382,7 @@ private static HighlightingBrush FixedColorHighlightingBrush(Color? color) public class CustomHighlightingManager : IHighlightingDefinitionReferenceResolver { - private readonly Dictionary _highlightingsByContentTypes = new Dictionary(); + private readonly Dictionary _highlightingsByContentTypes = new Dictionary(); public static CustomHighlightingManager Instance { get; } = new CustomHighlightingManager(); @@ -402,6 +403,14 @@ public IHighlightingDefinition GetDefinition(string contentType) } } + public object GetAvalonStudioDefinition(string contentType) + { + lock (this) + { + return _highlightingsByContentTypes.TryGetValue(contentType, out var rh) ? rh : null; + } + } + internal void RegisterHighlighting(string resourceName) { try @@ -416,7 +425,7 @@ internal void RegisterHighlighting(string resourceName) } } - public void RegisterHighlighting(IEnumerable contentTypes, IHighlightingDefinition highlighting) + public void RegisterHighlighting(IEnumerable contentTypes, object highlighting) { if (highlighting == null) { @@ -432,16 +441,52 @@ public void RegisterHighlighting(IEnumerable contentTypes, IHighlighting } } - public (IEnumerable contentTypes, IHighlightingDefinition definition) Load(string resourceName) + private (IEnumerable contentTypes, object definition) Load(string resourceName) { - ASXshdSyntaxDefinition xshd; + var extension = Path.GetExtension(resourceName); + using (var s = Resources.Resources.OpenStream(resourceName)) - using (var reader = XmlReader.Create(s)) { - // in release builds, skip validating the built-in highlightings - xshd = LoadXshd(reader, true); + switch (extension) + { + case ".xshd": + ASXshdSyntaxDefinition xshd; + using (var reader = XmlReader.Create(s)) + { + // in release builds, skip validating the built-in highlightings + xshd = LoadXshd(reader, true); + } + return (xshd.ContentTypes, HighlightingLoader.Load(xshd, this)); + + case ".tmLanguage": + { + var definition = TextMate.TextMateFormat.ReadHighlighting(s); + definition.PrepareMatches(); + return (new List { definition.Name }, definition); + } + + case ".json": + if(resourceName.EndsWith(".tmLanguage.json")) + { + var definition = TextMate.TextMateFormat.ReadHighlightingFromJson(s); + definition.PrepareMatches(); + return (new List { definition.Name }, definition); + } + break; + + case ".sublime-syntax": + { + using (var sr = new StreamReader(s)) + { + var definition = Sublime3.Sublime3Format.ReadHighlighting(sr); + definition.PrepareMatches(); + return (new List { definition.Name }, definition); + } + } + } } - return (xshd.ContentTypes, HighlightingLoader.Load(xshd, this)); + + return (null, null); } internal static ASXshdSyntaxDefinition LoadXshd(XmlReader reader, bool skipValidation) diff --git a/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/LanguageBundle.cs b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/LanguageBundle.cs new file mode 100644 index 0000000000..b7d8353eb6 --- /dev/null +++ b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/LanguageBundle.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace AvalonStudio.Controls.Standard.CodeEditor.Highlighting +{ + class LanguageBundle + { + List highlightings = new List(); + /*List settings = new List(); + List snippets = new List(); + List editorThemes = new List(); + + public IReadOnlyList EditorThemes + { + get + { + return editorThemes; + } + }*/ + + public IReadOnlyList Highlightings + { + get + { + return highlightings; + } + } + + /*public IReadOnlyList Settings + { + get + { + return settings; + } + } + + public IReadOnlyList Snippets + { + get + { + return snippets; + } + }*/ + + public string Name { get; private set; } + + public string FileName { get; private set; } + + public LanguageBundle(string name, string fileName) + { + Name = name; + FileName = fileName; + } + + /*public void Add(EditorTheme theme) + { + editorThemes.Add(theme); + } + + public void Remove(EditorTheme style) + { + editorThemes.Remove(style); + } + + public void Add(TmSetting setting) + { + settings.Add(setting); + } + + public void Add(TmSnippet snippet) + { + snippets.Add(snippet); + }*/ + + public void Add(SyntaxHighlightingDefinition highlighting) + { + highlightings.Add(highlighting); + } + } + +} diff --git a/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/PObject.cs b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/PObject.cs new file mode 100644 index 0000000000..dfa1f7eeee --- /dev/null +++ b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/PObject.cs @@ -0,0 +1,2312 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Security; +using System.Text; +using System.Threading.Tasks; +using System.Xml; + +namespace AvalonStudio.Controls.Standard.CodeEditor.Highlighting +{ + abstract class PObject + { + public static PObject Create(PObjectType type) + { + switch (type) + { + case PObjectType.Dictionary: + return new PDictionary(); + case PObjectType.Array: + return new PArray(); + case PObjectType.Number: + return new PNumber(0); + case PObjectType.Real: + return new PReal(0); + case PObjectType.Boolean: + return new PBoolean(true); + case PObjectType.Data: + return new PData(new byte[0]); + case PObjectType.String: + return new PString(""); + case PObjectType.Date: + return new PDate(DateTime.Now); + default: + throw new ArgumentOutOfRangeException(); + } + } + + public static IEnumerable> ToEnumerable(PObject obj) + { + if (obj is PDictionary) + return (PDictionary)obj; + + if (obj is PArray) + return ((PArray)obj).Select(k => new KeyValuePair(k is IPValueObject ? ((IPValueObject)k).Value.ToString() : null, k)); + + return Enumerable.Empty>(); + } + + PObjectContainer parent; + public PObjectContainer Parent + { + get { return parent; } + set + { + if (parent != null && value != null) + throw new NotSupportedException("Already parented."); + + parent = value; + } + } + + public abstract PObject Clone(); + + public void Replace(PObject newObject) + { + var p = Parent; + if (p is PDictionary) + { + var dict = (PDictionary)p; + var key = dict.GetKey(this); + if (key == null) + return; + Remove(); + dict[key] = newObject; + } + else if (p is PArray) + { + var arr = (PArray)p; + arr.Replace(this, newObject); + } + } + + public string Key + { + get + { + if (Parent is PDictionary) + { + var dict = (PDictionary)Parent; + return dict.GetKey(this); + } + return null; + } + } + + public void Remove() + { + if (Parent is PDictionary) + { + var dict = (PDictionary)Parent; + dict.Remove(Key); + } + else if (Parent is PArray) + { + var arr = (PArray)Parent; + arr.Remove(this); + } + else + { + if (Parent == null) + throw new InvalidOperationException("Can't remove from null parent"); + throw new InvalidOperationException("Can't remove from parent " + Parent); + } + } + +#if POBJECT_MONOMAC + public abstract NSObject Convert (); +#endif + + public abstract PObjectType Type { get; } + + public static implicit operator PObject(string value) + { + return new PString(value); + } + + public static implicit operator PObject(int value) + { + return new PNumber(value); + } + + public static implicit operator PObject(double value) + { + return new PReal(value); + } + + public static implicit operator PObject(bool value) + { + return new PBoolean(value); + } + + public static implicit operator PObject(DateTime value) + { + return new PDate(value); + } + + public static implicit operator PObject(byte[] value) + { + return new PData(value); + } + + protected virtual void OnChanged(EventArgs e) + { + if (SuppressChangeEvents) + return; + + var handler = Changed; + if (handler != null) + handler(this, e); + + if (Parent != null) + Parent.OnCollectionChanged(Key, this); + } + + protected bool SuppressChangeEvents + { + get; set; + } + + public event EventHandler Changed; + + public byte[] ToByteArray(bool binary) + { + var format = binary ? PropertyListFormat.Binary : PropertyListFormat.Xml; + + using (var stream = new MemoryStream()) + { + using (var context = format.StartWriting(stream)) + context.WriteObject(this); + return stream.ToArray(); + } + } + + public string ToXml() + { + return Encoding.UTF8.GetString(ToByteArray(false)); + } + +#if POBJECT_MONOMAC + static readonly IntPtr selObjCType = Selector.GetHandle ("objCType"); + + public static PObject FromNSObject (NSObject val) + { + if (val == null) + return null; + + var dict = val as NSDictionary; + if (dict != null) { + var result = new PDictionary (); + foreach (var pair in dict) { + string k = pair.Key.ToString (); + result[k] = FromNSObject (pair.Value); + } + return result; + } + + var arr = val as NSArray; + if (arr != null) { + var result = new PArray (); + uint count = arr.Count; + for (uint i = 0; i < count; i++) { + var obj = Runtime.GetNSObject (arr.ValueAt (i)); + if (obj != null) + result.Add (FromNSObject (obj)); + } + return result; + } + + var str = val as NSString; + if (str != null) + return str.ToString (); + + var nr = val as NSNumber; + if (nr != null) { + char t; + unsafe { + t = (char) *((byte*) MonoMac.ObjCRuntime.Messaging.IntPtr_objc_msgSend (val.Handle, selObjCType)); + } + if (t == 'c' || t == 'C' || t == 'B') + return nr.BoolValue; + return nr.Int32Value; + } + + var date = val as NSDate; + if (date != null) + return (DateTime) date; + + var data = val as NSData; + if (data != null) { + var bytes = new byte[data.Length]; + System.Runtime.InteropServices.Marshal.Copy (data.Bytes, bytes, 0, (int)data.Length); + return bytes; + } + + throw new NotSupportedException (val.ToString ()); + } +#endif + + public static PObject FromByteArray(byte[] array, int startIndex, int length, out bool isBinary) + { + var ctx = PropertyListFormat.Binary.StartReading(array, startIndex, length); + + isBinary = true; + + try + { + if (ctx == null) + { + isBinary = false; + ctx = PropertyListFormat.CreateReadContext(array, startIndex, length); + if (ctx == null) + return null; + } + + return ctx.ReadObject(); + } + finally + { + if (ctx != null) + ctx.Dispose(); + } + } + + public static PObject FromByteArray(byte[] array, out bool isBinary) + { + return FromByteArray(array, 0, array.Length, out isBinary); + } + + public static PObject FromString(string str) + { + var ctx = PropertyListFormat.CreateReadContext(Encoding.UTF8.GetBytes(str)); + if (ctx == null) + return null; + return ctx.ReadObject(); + } + + public static PObject FromStream(Stream stream) + { + var ctx = PropertyListFormat.CreateReadContext(stream); + if (ctx == null) + return null; + return ctx.ReadObject(); + } + } + + + abstract class PObjectContainer : PObject + { + public abstract int Count { get; } + + public bool Reload(string fileName) + { + using (var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read)) + { + using (var ctx = PropertyListFormat.CreateReadContext(stream)) + { + if (ctx == null) + return false; + + return Reload(ctx); + } + } + } + + protected abstract bool Reload(PropertyListFormat.ReadWriteContext ctx); + + public Task SaveAsync(string filename, bool atomic = false, bool binary = false) + { + return Task.Factory.StartNew(() => Save(filename, atomic, binary)); + } + + public void Save(string filename, bool atomic = false, bool binary = false) + { + var tempFile = atomic ? GetTempFileName(filename) : filename; + try + { + if (!Directory.Exists(Path.GetDirectoryName(tempFile))) + Directory.CreateDirectory(Path.GetDirectoryName(tempFile)); + + using (var stream = new FileStream(tempFile, FileMode.Create, FileAccess.Write)) + { + using (var ctx = binary ? PropertyListFormat.Binary.StartWriting(stream) : PropertyListFormat.Xml.StartWriting(stream)) + ctx.WriteObject(this); + } + if (atomic) + { + if (File.Exists(filename)) + File.Replace(tempFile, filename, null, true); + else + File.Move(tempFile, filename); + } + } + finally + { + if (atomic) + File.Delete(tempFile); // just in case- no exception is raised if file is not found + } + } + + static string GetTempFileName(string filename) + { + var i = 1; + var tempfile = filename + ".tmp"; + while (File.Exists(tempfile)) + tempfile = filename + ".tmp." + (i++).ToString(); + return tempfile; + } + + protected void OnChildAdded(string key, PObject child) + { + child.Parent = this; + + OnCollectionChanged(PObjectContainerAction.Added, key, null, child); + } + + internal void OnCollectionChanged(string key, PObject child) + { + OnCollectionChanged(PObjectContainerAction.Changed, key, null, child); + } + + protected void OnChildRemoved(string key, PObject child) + { + child.Parent = null; + + OnCollectionChanged(PObjectContainerAction.Removed, key, child, null); + } + + protected void OnChildReplaced(string key, PObject oldChild, PObject newChild) + { + oldChild.Parent = null; + newChild.Parent = this; + + OnCollectionChanged(PObjectContainerAction.Replaced, key, oldChild, newChild); + } + + protected void OnCleared() + { + OnCollectionChanged(PObjectContainerAction.Cleared, null, null, null); + } + + protected void OnCollectionChanged(PObjectContainerAction action, string key, PObject oldChild, PObject newChild) + { + if (SuppressChangeEvents) + return; + + var handler = CollectionChanged; + if (handler != null) + handler(this, new PObjectContainerEventArgs(action, key, oldChild, newChild)); + + OnChanged(EventArgs.Empty); + + if (Parent != null) + Parent.OnCollectionChanged(Key, this); + } + + public event EventHandler CollectionChanged; + } + + + interface IPValueObject + { + object Value { get; set; } + bool TrySetValueFromString(string text, IFormatProvider formatProvider); + } + + + abstract class PValueObject : PObject, IPValueObject + { + T val; + public T Value + { + get + { + return val; + } + set + { + val = value; + OnChanged(EventArgs.Empty); + } + } + + object IPValueObject.Value + { + get { return Value; } + set { Value = (T)value; } + } + + protected PValueObject(T value) + { + Value = value; + } + + protected PValueObject() + { + } + + public static implicit operator T(PValueObject pObj) + { + return pObj != null ? pObj.Value : default(T); + } + + public abstract bool TrySetValueFromString(string text, IFormatProvider formatProvider); + } + + + class PDictionary : PObjectContainer, IEnumerable> + { + static readonly byte[] BeginMarkerBytes = Encoding.ASCII.GetBytes(""); + static readonly byte[] EndMarkerBytes = Encoding.ASCII.GetBytes(""); + + readonly Dictionary dict; + readonly List order; + + public PObject this[string key] + { + get + { + PObject value; + if (dict.TryGetValue(key, out value)) + return value; + return null; + } + set + { + PObject existing; + bool exists = dict.TryGetValue(key, out existing); + if (!exists) + order.Add(key); + + dict[key] = value; + + if (exists) + OnChildReplaced(key, existing, value); + else + OnChildAdded(key, value); + } + } + + public void Add(string key, PObject value) + { + try + { + dict.Add(key, value); + } + catch (Exception e) + { + //LoggingService.LogError("error while adding " + key); + throw e; + } + order.Add(key); + + OnChildAdded(key, value); + } + + public void InsertAfter(string keyBefore, string key, PObject value) + { + dict.Add(key, value); + order.Insert(order.IndexOf(keyBefore) + 1, key); + + OnChildAdded(key, value); + } + + public override int Count + { + get { return dict.Count; } + } + + #region IEnumerable[KeyValuePair[System.String,PObject]] implementation + public IEnumerator> GetEnumerator() + { + foreach (var key in order) + yield return new KeyValuePair(key, dict[key]); + } + #endregion + + #region IEnumerable implementation + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + #endregion + + public PDictionary() + { + dict = new Dictionary(); + order = new List(); + } + + public override PObject Clone() + { + var dict = new PDictionary(); + foreach (var kv in this) + dict.Add(kv.Key, kv.Value.Clone()); + return dict; + } + + public bool ContainsKey(string name) + { + return dict.ContainsKey(name); + } + + public bool Remove(string key) + { + PObject obj; + if (dict.TryGetValue(key, out obj)) + { + dict.Remove(key); + order.Remove(key); + OnChildRemoved(key, obj); + return true; + } + return false; + } + + public void Clear() + { + dict.Clear(); + order.Clear(); + OnCleared(); + } + + public bool ChangeKey(PObject obj, string newKey) + { + return ChangeKey(obj, newKey, null); + } + + public bool ChangeKey(PObject obj, string newKey, PObject newValue) + { + var oldkey = GetKey(obj); + if (oldkey == null || dict.ContainsKey(newKey)) + return false; + + dict.Remove(oldkey); + dict.Add(newKey, newValue ?? obj); + order[order.IndexOf(oldkey)] = newKey; + if (newValue != null) + { + OnChildRemoved(oldkey, obj); + OnChildAdded(newKey, newValue); + } + else + { + OnChildRemoved(oldkey, obj); + OnChildAdded(newKey, obj); + } + return true; + } + + public string GetKey(PObject obj) + { + foreach (var pair in dict) + { + if (pair.Value == obj) + return pair.Key; + } + return null; + } + + public T Get(string key) where T : PObject + { + PObject obj; + + if (!dict.TryGetValue(key, out obj)) + return null; + + return obj as T; + } + + public bool TryGetValue(string key, out T value) where T : PObject + { + PObject obj; + + if (!dict.TryGetValue(key, out obj)) + { + value = default(T); + return false; + } + + value = obj as T; + + return value != null; + } + + static int IndexOf(byte[] haystack, int startIndex, byte[] needle) + { + int maxLength = haystack.Length - needle.Length; + int n; + + for (int i = startIndex; i < maxLength; i++) + { + for (n = 0; n < needle.Length; n++) + { + if (haystack[i + n] != needle[n]) + break; + } + + if (n == needle.Length) + return i; + } + + return -1; + } + + public static new PDictionary FromByteArray(byte[] array, int startIndex, int length, out bool isBinary) + { + return (PDictionary)PObject.FromByteArray(array, startIndex, length, out isBinary); + } + + public static new PDictionary FromByteArray(byte[] array, out bool isBinary) + { + return (PDictionary)PObject.FromByteArray(array, out isBinary); + } + + public static PDictionary FromBinaryXml(byte[] array) + { + //find the raw plist within the .mobileprovision file + int start = IndexOf(array, 0, BeginMarkerBytes); + bool binary; + int length; + + if (start < 0 || (length = (IndexOf(array, start, EndMarkerBytes) - start)) < 1) + throw new Exception("Did not find XML plist in buffer."); + + length += EndMarkerBytes.Length; + + return PDictionary.FromByteArray(array, start, length, out binary); + } + + public static PDictionary FromFile(string fileName) + { + bool isBinary; + return FromFile(fileName, out isBinary); + } + + public static Task FromFileAsync(string fileName) + { + return Task.Factory.StartNew(() => { + bool isBinary; + return FromFile(fileName, out isBinary); + }); + } + + public static PDictionary FromFile(string fileName, out bool isBinary) + { + using (var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read)) + { + return FromStream(stream, out isBinary); + } + } + + new public static PDictionary FromStream(Stream stream) + { + bool isBinary; + return FromStream(stream, out isBinary); + } + + public static PDictionary FromStream(Stream stream, out bool isBinary) + { + isBinary = true; + var ctx = PropertyListFormat.Binary.StartReading(stream); + try + { + if (ctx == null) + { + isBinary = false; + ctx = PropertyListFormat.CreateReadContext(stream); + if (ctx == null) + throw new FormatException("Unrecognized property list format."); + } + return (PDictionary)ctx.ReadObject(); + } + finally + { + if (ctx != null) + ctx.Dispose(); + } + } + + + public static PDictionary FromBinaryXml(string fileName) + { + return FromBinaryXml(File.ReadAllBytes(fileName)); + } + + protected override bool Reload(PropertyListFormat.ReadWriteContext ctx) + { + SuppressChangeEvents = true; + var result = ctx.ReadDict(this); + SuppressChangeEvents = false; + if (result) + OnChanged(EventArgs.Empty); + return result; + } + + public override string ToString() + { + return string.Format("[PDictionary: Items={0}]", dict.Count); + } + + public void SetString(string key, string value) + { + var result = Get(key); + + if (result == null) + this[key] = new PString(value); + else + result.Value = value; + } + + public PString GetString(string key) + { + var result = Get(key); + + if (result == null) + this[key] = result = new PString(""); + + return result; + } + + public PArray GetArray(string key) + { + var result = Get(key); + + if (result == null) + this[key] = result = new PArray(); + + return result; + } + + public override PObjectType Type + { + get { return PObjectType.Dictionary; } + } + } + + + class PArray : PObjectContainer, IEnumerable + { + List list; + + public override int Count + { + get { return list.Count; } + } + + public PObject this[int i] + { + get + { + return list[i]; + } + set + { + if (i < 0 || i >= Count) + throw new ArgumentOutOfRangeException(); + var existing = list[i]; + list[i] = value; + + OnChildReplaced(null, existing, value); + } + } + + public PArray() + { + list = new List(); + } + + public PArray(List list) + { + this.list = list; + } + + public override PObject Clone() + { + var array = new PArray(); + foreach (var item in this) + array.Add(item.Clone()); + return array; + } + + protected override bool Reload(PropertyListFormat.ReadWriteContext ctx) + { + SuppressChangeEvents = true; + var result = ctx.ReadArray(this); + SuppressChangeEvents = false; + if (result) + OnChanged(EventArgs.Empty); + return result; + } + + public void Add(PObject obj) + { + list.Add(obj); + OnChildAdded(null, obj); + } + + public void Insert(int index, PObject obj) + { + list.Insert(index, obj); + OnChildAdded(null, obj); + } + + public void Replace(PObject oldObj, PObject newObject) + { + for (int i = 0; i < Count; i++) + { + if (list[i] == oldObj) + { + list[i] = newObject; + OnChildReplaced(null, oldObj, newObject); + break; + } + } + } + + public void Remove(PObject obj) + { + if (list.Remove(obj)) + OnChildRemoved(null, obj); + } + + public void Clear() + { + list.Clear(); + OnCleared(); + } + + public override string ToString() + { + return string.Format("[PArray: Items={0}]", Count); + } + + public void AssignStringList(string strList) + { + SuppressChangeEvents = true; + try + { + Clear(); + foreach (var item in strList.Split(',', ' ')) + { + if (string.IsNullOrEmpty(item)) + continue; + Add(new PString(item)); + } + } + finally + { + SuppressChangeEvents = false; + OnChanged(EventArgs.Empty); + } + } + + public string[] ToStringArray() + { + var strlist = new List(); + + foreach (PString str in list.OfType()) + strlist.Add(str.Value); + + return strlist.ToArray(); + } + + public string ToStringList() + { + var sb = new StringBuilder(); + foreach (PString str in list.OfType()) + { + if (sb.Length > 0) + sb.Append(", "); + sb.Append(str); + } + return sb.ToString(); + } + + public IEnumerator GetEnumerator() + { + return list.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return list.GetEnumerator(); + } + + public override PObjectType Type + { + get { return PObjectType.Array; } + } + } + + + class PBoolean : PValueObject + { + public PBoolean(bool value) : base(value) + { + } + + public override PObject Clone() + { + return new PBoolean(Value); + } + + public override PObjectType Type + { + get { return PObjectType.Boolean; } + } + + public override bool TrySetValueFromString(string text, IFormatProvider formatProvider) + { + const StringComparison ic = StringComparison.OrdinalIgnoreCase; + + if ("true".Equals(text, ic) || "yes".Equals(text, ic)) + { + Value = true; + return true; + } + + if ("false".Equals(text, ic) || "no".Equals(text, ic)) + { + Value = false; + return true; + } + + return false; + } + } + + + class PData : PValueObject + { + static readonly byte[] Empty = new byte[0]; + +#if POBJECT_MONOMAC + public override NSObject Convert () + { + // Work around a bug in NSData.FromArray as it cannot (currently) handle + // zero length arrays + if (Value.Length == 0) + return new NSData (); + else + return NSData.FromArray (Value); + } +#endif + + public PData(byte[] value) : base(value ?? Empty) + { + } + + public override PObject Clone() + { + return new PData(Value); + } + + public override PObjectType Type + { + get { return PObjectType.Data; } + } + + public override bool TrySetValueFromString(string text, IFormatProvider formatProvider) + { + return false; + } + } + + + class PDate : PValueObject + { + public PDate(DateTime value) : base(value) + { + } + + public override PObject Clone() + { + return new PDate(Value); + } + + public override PObjectType Type + { + get { return PObjectType.Date; } + } + + public override bool TrySetValueFromString(string text, IFormatProvider formatProvider) + { + DateTime result; + if (DateTime.TryParse(text, formatProvider, DateTimeStyles.None, out result)) + { + Value = result; + return true; + } + return false; + } + } + + + class PNumber : PValueObject + { + public PNumber(int value) : base(value) + { + } + + public override PObject Clone() + { + return new PNumber(Value); + } + +#if POBJECT_MONOMAC + public override NSObject Convert () + { + return NSNumber.FromInt32 (Value); + } +#endif + + public override PObjectType Type + { + get { return PObjectType.Number; } + } + + public override bool TrySetValueFromString(string text, IFormatProvider formatProvider) + { + int result; + if (int.TryParse(text, NumberStyles.Integer, formatProvider, out result)) + { + Value = result; + return true; + } + return false; + } + } + + + class PReal : PValueObject + { + public PReal(double value) : base(value) + { + } + + public override PObject Clone() + { + return new PReal(Value); + } + + public override PObjectType Type + { + get { return PObjectType.Real; } + } + + public override bool TrySetValueFromString(string text, IFormatProvider formatProvider) + { + double result; + if (double.TryParse(text, NumberStyles.AllowDecimalPoint, formatProvider, out result)) + { + Value = result; + return true; + } + return false; + } + } + + + class PString : PValueObject + { + public PString(string value) : base(value) + { + if (value == null) + throw new ArgumentNullException("value"); + } + + public override PObject Clone() + { + return new PString(Value); + } + +#if POBJECT_MONOMAC + public override NSObject Convert () + { + return new NSString (Value); + } +#endif + + public override PObjectType Type + { + get { return PObjectType.String; } + } + + public override bool TrySetValueFromString(string text, IFormatProvider formatProvider) + { + Value = text; + return true; + } + } + + + abstract class PropertyListFormat + { + public static readonly PropertyListFormat Xml = new XmlFormat(); + public static readonly PropertyListFormat Binary = new BinaryFormat(); + + // Stream must be seekable + public static ReadWriteContext CreateReadContext(Stream input) + { + return Binary.StartReading(input) ?? Xml.StartReading(input); + } + + public static ReadWriteContext CreateReadContext(byte[] array, int startIndex, int length) + { + return CreateReadContext(new MemoryStream(array, startIndex, length)); + } + + public static ReadWriteContext CreateReadContext(byte[] array) + { + return CreateReadContext(new MemoryStream(array, 0, array.Length)); + } + + // returns null if the input is not of the correct format. Stream must be seekable + public abstract ReadWriteContext StartReading(Stream input); + public abstract ReadWriteContext StartWriting(Stream output); + + public ReadWriteContext StartReading(byte[] array, int startIndex, int length) + { + return StartReading(new MemoryStream(array, startIndex, length)); + } + + public ReadWriteContext StartReading(byte[] array) + { + return StartReading(new MemoryStream(array, 0, array.Length)); + } + + class BinaryFormat : PropertyListFormat + { + // magic is bplist + 2 byte version id + static readonly byte[] BPLIST_MAGIC = { 0x62, 0x70, 0x6C, 0x69, 0x73, 0x74 }; // "bplist" + static readonly byte[] BPLIST_VERSION = { 0x30, 0x30 }; // "00" + + public override ReadWriteContext StartReading(Stream input) + { + if (input.Length < BPLIST_MAGIC.Length + 2) + return null; + + input.Seek(0, SeekOrigin.Begin); + for (var i = 0; i < BPLIST_MAGIC.Length; i++) + { + if ((byte)input.ReadByte() != BPLIST_MAGIC[i]) + return null; + } + + // skip past the 2 byte version id for now + // we currently don't bother checking it because it seems different versions of OSX might write different values here? + input.Seek(2, SeekOrigin.Current); + return new Context(input, true); + } + + public override ReadWriteContext StartWriting(Stream output) + { + output.Write(BPLIST_MAGIC, 0, BPLIST_MAGIC.Length); + output.Write(BPLIST_VERSION, 0, BPLIST_VERSION.Length); + + return new Context(output, false); + } + + class Context : ReadWriteContext + { + + static readonly DateTime AppleEpoch = new DateTime(2001, 1, 1, 0, 0, 0, DateTimeKind.Utc); //see CFDateGetAbsoluteTime + + //https://github.com/mono/referencesource/blob/mono/mscorlib/system/datetime.cs + const long TicksPerMillisecond = 10000; + const long TicksPerSecond = TicksPerMillisecond * 1000; + + Stream stream; + int currentLength; + + CFBinaryPlistTrailer trailer; + + //for writing + List objectRefs; + int currentRef; + long[] offsets; + + public Context(Stream stream, bool reading) + { + this.stream = stream; + if (reading) + { + trailer = CFBinaryPlistTrailer.Read(this); + ReadObjectHead(); + } + } + + #region Binary reading members + protected override bool ReadBool() + { + return CurrentType == PlistType.@true; + } + + protected override void ReadObjectHead() + { + var b = stream.ReadByte(); + var len = 0L; + var type = (PlistType)(b & 0xF0); + if (type == PlistType.@null) + { + type = (PlistType)b; + } + else + { + len = b & 0x0F; + if (len == 0xF) + { + ReadObjectHead(); + len = ReadInteger(); + } + } + CurrentType = type; + currentLength = (int)len; + } + + protected override long ReadInteger() + { + switch (CurrentType) + { + case PlistType.integer: + return ReadBigEndianInteger((int)Math.Pow(2, currentLength)); + } + + throw new NotSupportedException("Integer of type: " + CurrentType); + } + + protected override double ReadReal() + { + var bytes = ReadBigEndianBytes((int)Math.Pow(2, currentLength)); + switch (CurrentType) + { + case PlistType.real: + switch (bytes.Length) + { + case 4: + return (double)BitConverter.ToSingle(bytes, 0); + case 8: + return BitConverter.ToDouble(bytes, 0); + } + throw new NotSupportedException(bytes.Length + "-byte real"); + } + + throw new NotSupportedException("Real of type: " + CurrentType); + } + + protected override DateTime ReadDate() + { + var bytes = ReadBigEndianBytes(8); + var seconds = BitConverter.ToDouble(bytes, 0); + // We need to manually convert the seconds to ticks because + // .NET DateTime/TimeSpan methods dealing with (milli)seconds + // round to the nearest millisecond (bxc #29079) + return AppleEpoch.AddTicks((long)(seconds * TicksPerSecond)); + } + + protected override byte[] ReadData() + { + var bytes = new byte[currentLength]; + stream.Read(bytes, 0, currentLength); + return bytes; + } + + protected override string ReadString() + { + byte[] bytes; + switch (CurrentType) + { + case PlistType.@string: // ASCII + bytes = new byte[currentLength]; + stream.Read(bytes, 0, bytes.Length); + return Encoding.ASCII.GetString(bytes); + case PlistType.wideString: //CFBinaryPList.c: Unicode string...big-endian 2-byte uint16_t + bytes = new byte[currentLength * 2]; + stream.Read(bytes, 0, bytes.Length); + return Encoding.BigEndianUnicode.GetString(bytes); + } + + throw new NotSupportedException("String of type: " + CurrentType); + } + + public override bool ReadArray(PArray array) + { + if (CurrentType != PlistType.array) + return false; + + array.Clear(); + + // save currentLength as it will be overwritten by next ReadObjectHead call + var len = currentLength; + for (var i = 0; i < len; i++) + { + var obj = ReadObjectByRef(); + if (obj != null) + array.Add(obj); + } + + return true; + } + + public override bool ReadDict(PDictionary dict) + { + if (CurrentType != PlistType.dict) + return false; + + dict.Clear(); + + // save currentLength as it will be overwritten by next ReadObjectHead call + var len = currentLength; + var keys = new string[len]; + for (var i = 0; i < len; i++) + keys[i] = ((PString)ReadObjectByRef()).Value; + for (var i = 0; i < len; i++) + dict.Add(keys[i], ReadObjectByRef()); + + return true; + } + + PObject ReadObjectByRef() + { + // read index into offset table + var objRef = (long)ReadBigEndianUInteger(trailer.ObjectRefSize); + + // read offset in file from table + var lastPos = stream.Position; + stream.Seek(trailer.OffsetTableOffset + objRef * trailer.OffsetEntrySize, SeekOrigin.Begin); + stream.Seek((long)ReadBigEndianUInteger(trailer.OffsetEntrySize), SeekOrigin.Begin); + + ReadObjectHead(); + var obj = ReadObject(); + + // restore original position + stream.Seek(lastPos, SeekOrigin.Begin); + return obj; + } + + byte[] ReadBigEndianBytes(int count) + { + var bytes = new byte[count]; + stream.Read(bytes, 0, count); + if (BitConverter.IsLittleEndian) + Array.Reverse(bytes); + return bytes; + } + + long ReadBigEndianInteger(int numBytes) + { + var bytes = ReadBigEndianBytes(numBytes); + switch (numBytes) + { + case 1: + return (long)bytes[0]; + case 2: + return (long)BitConverter.ToInt16(bytes, 0); + case 4: + return (long)BitConverter.ToInt32(bytes, 0); + case 8: + return BitConverter.ToInt64(bytes, 0); + } + throw new NotSupportedException(bytes.Length + "-byte integer"); + } + + ulong ReadBigEndianUInteger(int numBytes) + { + var bytes = ReadBigEndianBytes(numBytes); + switch (numBytes) + { + case 1: + return (ulong)bytes[0]; + case 2: + return (ulong)BitConverter.ToUInt16(bytes, 0); + case 4: + return (ulong)BitConverter.ToUInt32(bytes, 0); + case 8: + return BitConverter.ToUInt64(bytes, 0); + } + throw new NotSupportedException(bytes.Length + "-byte integer"); + } + + ulong ReadBigEndianUInt64() + { + var bytes = ReadBigEndianBytes(8); + return BitConverter.ToUInt64(bytes, 0); + } + #endregion + + #region Binary writing members + public override void WriteObject(PObject value) + { + if (offsets == null) + InitOffsetTable(value); + base.WriteObject(value); + } + + protected override void Write(PBoolean boolean) + { + WriteObjectHead(boolean, boolean ? PlistType.@true : PlistType.@false); + } + + protected override void Write(PNumber number) + { + if (WriteObjectHead(number, PlistType.integer)) + Write(number.Value); + } + + protected override void Write(PReal real) + { + if (WriteObjectHead(real, PlistType.real)) + Write(real.Value); + } + + protected override void Write(PDate date) + { + if (WriteObjectHead(date, PlistType.date)) + { + var bytes = MakeBigEndian(BitConverter.GetBytes(date.Value.Subtract(AppleEpoch).TotalSeconds)); + stream.Write(bytes, 0, bytes.Length); + } + } + + protected override void Write(PData data) + { + var bytes = data.Value; + if (WriteObjectHead(data, PlistType.data, bytes.Length)) + stream.Write(bytes, 0, bytes.Length); + } + + protected override void Write(PString str) + { + var type = PlistType.@string; + byte[] bytes; + + if (str.Value.Any(c => c > 127)) + { + type = PlistType.wideString; + bytes = Encoding.BigEndianUnicode.GetBytes(str.Value); + } + else + { + bytes = Encoding.ASCII.GetBytes(str.Value); + } + + if (WriteObjectHead(str, type, str.Value.Length)) + stream.Write(bytes, 0, bytes.Length); + } + + protected override void Write(PArray array) + { + if (!WriteObjectHead(array, PlistType.array, array.Count)) + return; + + var curRef = currentRef; + + foreach (var item in array) + Write(GetObjRef(item), trailer.ObjectRefSize); + + currentRef = curRef; + + foreach (var item in array) + WriteObject(item); + } + + protected override void Write(PDictionary dict) + { + if (!WriteObjectHead(dict, PlistType.dict, dict.Count)) + return; + + // it sucks we have to loop so many times, but we gotta do it + // if we want to lay things out the same way apple does + + var curRef = currentRef; + + //write key refs + foreach (var item in dict) + Write(GetObjRef(item.Key), trailer.ObjectRefSize); + + //write value refs + foreach (var item in dict) + Write(GetObjRef(item.Value), trailer.ObjectRefSize); + + currentRef = curRef; + + //write keys and values + foreach (var item in dict) + WriteObject(item.Key); + foreach (var item in dict) + WriteObject(item.Value); + } + + bool WriteObjectHead(PObject obj, PlistType type, int size = 0) + { + var id = GetObjRef(obj); + if (offsets[id] != 0) // if we've already been written, don't write us again + return false; + offsets[id] = stream.Position; + switch (type) + { + case PlistType.@null: + case PlistType.@false: + case PlistType.@true: + case PlistType.fill: + stream.WriteByte((byte)type); + break; + case PlistType.date: + stream.WriteByte(0x33); + break; + case PlistType.integer: + case PlistType.real: + break; + default: + if (size < 15) + { + stream.WriteByte((byte)((byte)type | size)); + } + else + { + stream.WriteByte((byte)((byte)type | 0xF)); + Write(size); + } + break; + } + return true; + } + + void Write(double value) + { + if (value >= float.MinValue && value <= float.MaxValue) + { + stream.WriteByte((byte)PlistType.real | 0x2); + var bytes = MakeBigEndian(BitConverter.GetBytes((float)value)); + stream.Write(bytes, 0, bytes.Length); + } + else + { + stream.WriteByte((byte)PlistType.real | 0x3); + var bytes = MakeBigEndian(BitConverter.GetBytes(value)); + stream.Write(bytes, 0, bytes.Length); + } + } + + void Write(int value) + { + if (value < 0) + { //they always write negative numbers with 8 bytes + stream.WriteByte((byte)PlistType.integer | 0x3); + var bytes = MakeBigEndian(BitConverter.GetBytes((long)value)); + stream.Write(bytes, 0, bytes.Length); + } + else if (value >= 0 && value < byte.MaxValue) + { + stream.WriteByte((byte)PlistType.integer); + stream.WriteByte((byte)value); + } + else if (value >= short.MinValue && value < short.MaxValue) + { + stream.WriteByte((byte)PlistType.integer | 0x1); + var bytes = MakeBigEndian(BitConverter.GetBytes((short)value)); + stream.Write(bytes, 0, bytes.Length); + } + else + { + stream.WriteByte((byte)PlistType.integer | 0x2); + var bytes = MakeBigEndian(BitConverter.GetBytes(value)); + stream.Write(bytes, 0, bytes.Length); + } + } + + void Write(long value, int byteCount) + { + byte[] bytes; + switch (byteCount) + { + case 1: + stream.WriteByte((byte)value); + break; + case 2: + bytes = MakeBigEndian(BitConverter.GetBytes((short)value)); + stream.Write(bytes, 0, bytes.Length); + break; + case 4: + bytes = MakeBigEndian(BitConverter.GetBytes((int)value)); + stream.Write(bytes, 0, bytes.Length); + break; + case 8: + bytes = MakeBigEndian(BitConverter.GetBytes(value)); + stream.Write(bytes, 0, bytes.Length); + break; + default: + throw new NotSupportedException(byteCount.ToString() + "-byte integer"); + } + } + + void InitOffsetTable(PObject topLevel) + { + objectRefs = new List(); + + var count = 0; + MakeObjectRefs(topLevel, ref count); + trailer.ObjectRefSize = GetMinByteLength(count); + offsets = new long[count]; + } + + void MakeObjectRefs(object obj, ref int count) + { + if (obj == null) + return; + + if (ShouldDuplicate(obj) || !objectRefs.Any(val => PObjectEqualityComparer.Instance.Equals(val, obj))) + { + objectRefs.Add(obj); + count++; + } + + // for containers, also count their contents + var pobj = obj as PObject; + if (pobj != null) + { + switch (pobj.Type) + { + + case PObjectType.Array: + foreach (var child in (PArray)obj) + MakeObjectRefs(child, ref count); + break; + case PObjectType.Dictionary: + foreach (var child in (PDictionary)obj) + MakeObjectRefs(child.Key, ref count); + foreach (var child in (PDictionary)obj) + MakeObjectRefs(child.Value, ref count); + break; + } + } + } + + static bool ShouldDuplicate(object obj) + { + var pobj = obj as PObject; + if (pobj == null) + return false; + + return pobj.Type == PObjectType.Boolean || pobj.Type == PObjectType.Array || pobj.Type == PObjectType.Dictionary || + (pobj.Type == PObjectType.String && ((PString)pobj).Value.Any(c => c > 255)); //LAMESPEC: this is weird. Some things are duplicated + } + + int GetObjRef(object obj) + { + if (currentRef < objectRefs.Count && PObjectEqualityComparer.Instance.Equals(objectRefs[currentRef], obj)) + return currentRef++; + + return objectRefs.FindIndex(val => PObjectEqualityComparer.Instance.Equals(val, obj)); + } + + static int GetMinByteLength(long value) + { + if (value >= 0 && value < byte.MaxValue) + return 1; + if (value >= short.MinValue && value < short.MaxValue) + return 2; + if (value >= int.MinValue && value < int.MaxValue) + return 4; + return 8; + } + + static byte[] MakeBigEndian(byte[] bytes) + { + if (BitConverter.IsLittleEndian) + Array.Reverse(bytes); + return bytes; + } + #endregion + + public override void Dispose() + { + if (offsets != null) + { + trailer.OffsetTableOffset = stream.Position; + trailer.OffsetEntrySize = GetMinByteLength(trailer.OffsetTableOffset); + foreach (var offset in offsets) + Write(offset, trailer.OffsetEntrySize); + + //LAMESPEC: seems like they always add 6 extra bytes here. not sure why + for (var i = 0; i < 6; i++) + stream.WriteByte((byte)0); + + trailer.Write(this); + } + } + + class PObjectEqualityComparer : IEqualityComparer + { + public static readonly PObjectEqualityComparer Instance = new PObjectEqualityComparer(); + + PObjectEqualityComparer() + { + } + + public new bool Equals(object x, object y) + { + var vx = x as IPValueObject; + var vy = y as IPValueObject; + + if (vx == null && vy == null) + return EqualityComparer.Default.Equals(x, y); + + if (vx == null && x != null && vy.Value != null) + return vy.Value.Equals(x); + + if (vy == null && y != null && vx.Value != null) + return vx.Value.Equals(y); + + if (vx == null || vy == null) + return false; + + return vx.Value.Equals(vy.Value); + } + + public int GetHashCode(object obj) + { + var valueObj = obj as IPValueObject; + if (valueObj != null) + return valueObj.Value.GetHashCode(); + return obj.GetHashCode(); + } + } + + struct CFBinaryPlistTrailer + { + const int TRAILER_SIZE = 26; + + public int OffsetEntrySize; + public int ObjectRefSize; + public long ObjectCount; + public long TopLevelRef; + public long OffsetTableOffset; + + public static CFBinaryPlistTrailer Read(Context ctx) + { + var pos = ctx.stream.Position; + ctx.stream.Seek(-TRAILER_SIZE, SeekOrigin.End); + var result = new CFBinaryPlistTrailer + { + OffsetEntrySize = ctx.stream.ReadByte(), + ObjectRefSize = ctx.stream.ReadByte(), + ObjectCount = (long)ctx.ReadBigEndianUInt64(), + TopLevelRef = (long)ctx.ReadBigEndianUInt64(), + OffsetTableOffset = (long)ctx.ReadBigEndianUInt64() + }; + ctx.stream.Seek(pos, SeekOrigin.Begin); + return result; + } + + public void Write(Context ctx) + { + byte[] bytes; + ctx.stream.WriteByte((byte)OffsetEntrySize); + ctx.stream.WriteByte((byte)ObjectRefSize); + //LAMESPEC: apple's comments say this is the number of entries in the offset table, but this really *is* number of objects??!?! + bytes = MakeBigEndian(BitConverter.GetBytes((long)ctx.objectRefs.Count)); + ctx.stream.Write(bytes, 0, bytes.Length); + bytes = new byte[8]; //top level always at offset 0 + ctx.stream.Write(bytes, 0, bytes.Length); + bytes = MakeBigEndian(BitConverter.GetBytes(OffsetTableOffset)); + ctx.stream.Write(bytes, 0, bytes.Length); + } + } + } + } + + // Adapted from: + //https://github.com/mono/monodevelop/blob/07d9e6c07e5be8fe1d8d6f4272d3969bb087a287/main/src/addins/MonoDevelop.MacDev/MonoDevelop.MacDev.Plist/PlistDocument.cs + class XmlFormat : PropertyListFormat + { + const string PLIST_HEADER = @" + + +"; + static readonly Encoding outputEncoding = new UTF8Encoding(false, false); + + public override ReadWriteContext StartReading(Stream input) + { + //allow DTD but not try to resolve it from web + var settings = new XmlReaderSettings() + { + CloseInput = true, + DtdProcessing = DtdProcessing.Ignore, + XmlResolver = null, + }; + + XmlReader reader = null; + input.Seek(0, SeekOrigin.Begin); + try + { + reader = XmlReader.Create(input, settings); + reader.ReadToDescendant("plist"); + while (reader.Read() && reader.NodeType != XmlNodeType.Element) + ; + } + catch (Exception ex) + { + System.Console.WriteLine("Exception: {0}", ex); + } + + if (reader == null || reader.EOF) + return null; + + return new Context(reader); + } + + public override ReadWriteContext StartWriting(Stream output) + { + var writer = new StreamWriter(output, outputEncoding); + writer.Write(PLIST_HEADER); + + return new Context(writer); + } + + class Context : ReadWriteContext + { + const string DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssK"; + + XmlReader reader; + TextWriter writer; + + int indentLevel; + string indentString; + + public Context(XmlReader reader) + { + this.reader = reader; + ReadObjectHead(); + } + public Context(TextWriter writer) + { + this.writer = writer; + indentString = ""; + } + + #region XML reading members + protected override void ReadObjectHead() + { + try + { + CurrentType = (PlistType)Enum.Parse(typeof(PlistType), reader.LocalName); + } + catch (Exception ex) + { + throw new ArgumentException(string.Format("Failed to parse PList data type: {0}", reader.LocalName), ex); + } + } + + protected override bool ReadBool() + { + // Create the PBoolean object, then move to the xml reader to next node + // so we are ready to parse the next object. 'bool' types don't have + // content so we have to move the reader manually, unlike integers which + // implicitly move to the next node because we parse the content. + var result = CurrentType == PlistType.@true; + reader.Read(); + return result; + } + + protected override long ReadInteger() + { + return reader.ReadElementContentAsLong(); + } + + protected override double ReadReal() + { + return reader.ReadElementContentAsDouble(); + } + + protected override DateTime ReadDate() + { + return DateTime.ParseExact(reader.ReadElementContentAsString(), DATETIME_FORMAT, CultureInfo.InvariantCulture).ToUniversalTime(); + } + + protected override byte[] ReadData() + { + return Convert.FromBase64String(reader.ReadElementContentAsString()); + } + + protected override string ReadString() + { + return reader.ReadElementContentAsString(); + } + + public override bool ReadArray(PArray array) + { + if (CurrentType != PlistType.array) + return false; + + array.Clear(); + + if (reader.IsEmptyElement) + { + reader.Read(); + return true; + } + + // advance to first node + reader.ReadStartElement(); + while (!reader.EOF && reader.NodeType != XmlNodeType.Element && reader.NodeType != XmlNodeType.EndElement) + { + if (!reader.Read()) + break; + } + + while (!reader.EOF && reader.NodeType != XmlNodeType.EndElement) + { + if (reader.NodeType == XmlNodeType.Element) + { + ReadObjectHead(); + + var val = ReadObject(); + if (val != null) + array.Add(val); + } + else if (!reader.Read()) + { + break; + } + } + + if (!reader.EOF && reader.NodeType == XmlNodeType.EndElement && reader.Name == "array") + { + reader.ReadEndElement(); + return true; + } + + return false; + } + + public override bool ReadDict(PDictionary dict) + { + if (CurrentType != PlistType.dict) + return false; + + dict.Clear(); + + if (reader.IsEmptyElement) + { + reader.Read(); + return true; + } + + reader.ReadToDescendant("key"); + + while (!reader.EOF && reader.NodeType == XmlNodeType.Element) + { + var key = reader.ReadElementString(); + + while (!reader.EOF && reader.NodeType != XmlNodeType.Element && reader.Read()) + { + if (reader.NodeType == XmlNodeType.EndElement) + throw new FormatException(string.Format("No value found for key {0}", key)); + } + + ReadObjectHead(); + var result = ReadObject(); + if (result != null) + dict.Add(key, result); + + do + { + if (reader.NodeType == XmlNodeType.Element && reader.Name == "key") + break; + + if (reader.NodeType == XmlNodeType.EndElement) + break; + } while (reader.Read()); + } + + if (!reader.EOF && reader.NodeType == XmlNodeType.EndElement && reader.Name == "dict") + { + reader.ReadEndElement(); + return true; + } + + return false; + } + #endregion + + #region XML writing members + protected override void Write(PBoolean boolean) + { + WriteLine(boolean.Value ? "" : ""); + } + + protected override void Write(PNumber number) + { + WriteLine("" + SecurityElement.Escape(number.Value.ToString(CultureInfo.InvariantCulture)) + ""); + } + + protected override void Write(PReal real) + { + WriteLine("" + SecurityElement.Escape(real.Value.ToString(CultureInfo.InvariantCulture)) + ""); + } + + protected override void Write(PDate date) + { + WriteLine("" + SecurityElement.Escape(date.Value.ToString(DATETIME_FORMAT, CultureInfo.InvariantCulture)) + ""); + } + + protected override void Write(PData data) + { + WriteLine("" + SecurityElement.Escape(Convert.ToBase64String(data.Value)) + ""); + } + + protected override void Write(PString str) + { + WriteLine("" + SecurityElement.Escape(str.Value) + ""); + } + + protected override void Write(PArray array) + { + if (array.Count == 0) + { + WriteLine(""); + return; + } + + WriteLine(""); + IncreaseIndent(); + + foreach (var item in array) + WriteObject(item); + + DecreaseIndent(); + WriteLine(""); + } + + protected override void Write(PDictionary dict) + { + if (dict.Count == 0) + { + WriteLine(""); + return; + } + + WriteLine(""); + IncreaseIndent(); + + foreach (var kv in dict) + { + WriteLine("" + SecurityElement.Escape(kv.Key) + ""); + WriteObject(kv.Value); + } + + DecreaseIndent(); + WriteLine(""); + } + + void WriteLine(string value) + { + writer.Write(indentString); + writer.Write(value); + writer.Write('\n'); + } + + void IncreaseIndent() + { + indentString = new string('\t', ++indentLevel); + } + + void DecreaseIndent() + { + indentString = new string('\t', --indentLevel); + } + #endregion + + public override void Dispose() + { + if (writer != null) + { + writer.Write("\n"); + writer.Flush(); + writer.Dispose(); + } + } + } + } + + public abstract class ReadWriteContext : IDisposable + { + // Binary: The type is encoded in the 4 high bits; the low bits are data (except: null, true, false) + // Xml: The enum value name == element tag name (this actually reads a superset of the format, since null, fill and wideString are not plist xml elements afaik) + protected enum PlistType : byte + { + @null = 0x00, + @false = 0x08, + @true = 0x09, + fill = 0x0F, + integer = 0x10, + real = 0x20, + date = 0x30, + data = 0x40, + @string = 0x50, + wideString = 0x60, + array = 0xA0, + dict = 0xD0, + } + + #region Reading members + public PObject ReadObject() + { + switch (CurrentType) + { + case PlistType.@true: + case PlistType.@false: + return new PBoolean(ReadBool()); + case PlistType.fill: + ReadObjectHead(); + return ReadObject(); + + case PlistType.integer: + return new PNumber((int)ReadInteger()); //FIXME: should PNumber handle 64-bit values? ReadInteger can if necessary + case PlistType.real: + return new PReal(ReadReal()); //FIXME: we should probably make PNumber take floating point as well as ints + + case PlistType.date: + return new PDate(ReadDate()); + case PlistType.data: + return new PData(ReadData()); + + case PlistType.@string: + case PlistType.wideString: + return new PString(ReadString()); + + case PlistType.array: + var array = new PArray(); + ReadArray(array); + return array; + + case PlistType.dict: + var dict = new PDictionary(); + ReadDict(dict); + return dict; + } + return null; + } + + protected abstract void ReadObjectHead(); + protected PlistType CurrentType { get; set; } + + protected abstract bool ReadBool(); + protected abstract long ReadInteger(); + protected abstract double ReadReal(); + protected abstract DateTime ReadDate(); + protected abstract byte[] ReadData(); + protected abstract string ReadString(); + + public abstract bool ReadArray(PArray array); + public abstract bool ReadDict(PDictionary dict); + #endregion + + #region Writing members + public virtual void WriteObject(PObject value) + { + switch (value.Type) + { + case PObjectType.Boolean: + Write((PBoolean)value); + return; + case PObjectType.Number: + Write((PNumber)value); + return; + case PObjectType.Real: + Write((PReal)value); + return; + case PObjectType.Date: + Write((PDate)value); + return; + case PObjectType.Data: + Write((PData)value); + return; + case PObjectType.String: + Write((PString)value); + return; + case PObjectType.Array: + Write((PArray)value); + return; + case PObjectType.Dictionary: + Write((PDictionary)value); + return; + } + throw new NotSupportedException(value.Type.ToString()); + } + + protected abstract void Write(PBoolean boolean); + protected abstract void Write(PNumber number); + protected abstract void Write(PReal real); + protected abstract void Write(PDate date); + protected abstract void Write(PData data); + protected abstract void Write(PString str); + protected abstract void Write(PArray array); + protected abstract void Write(PDictionary dict); + #endregion + + public abstract void Dispose(); + } + } + + + enum PObjectContainerAction + { + Added, + Changed, + Removed, + Replaced, + Cleared + } + + + sealed class PObjectContainerEventArgs : EventArgs + { + internal PObjectContainerEventArgs(PObjectContainerAction action, string key, PObject oldItem, PObject newItem) + { + Action = action; + Key = key; + OldItem = oldItem; + NewItem = newItem; + } + + public PObjectContainerAction Action + { + get; private set; + } + + public string Key + { + get; private set; + } + + public PObject OldItem + { + get; private set; + } + + public PObject NewItem + { + get; private set; + } + } + + + enum PObjectType + { + Dictionary, + Array, + Real, + Number, + Boolean, + Data, + String, + Date + } + +} diff --git a/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/Resources/C#.sublime-syntax b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/Resources/C#.sublime-syntax new file mode 100644 index 0000000000..7ec0cfc9b4 --- /dev/null +++ b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/Resources/C#.sublime-syntax @@ -0,0 +1,207 @@ +%YAML 1.2 +--- +# C#.sublime-syntax +# +# Author: +# Mike Krüger +# +# Copyright (c) 2016 Microsoft Corporation +# Copyright (c) 2007 Novell, Inc (http://www.novell.com) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +name: "C#" +file_extensions: + - cs + - csx +scope: source.cs +contexts: + main: + - include: comments + - include: keywords + - include: numbers + - include: strings + - include: preprocessor + comments: + - match: '///' + scope: comment.line.documentation.source.cs + push: + - match: '$\n?' + pop: true + - include: xmldoc + - match: '//' + scope: comment.line.source.cs + push: + - include: commenttags + - match: $\n? + pop: true + - match: '/\*' + scope: comment.block.source.cs + push: + - include: commenttags + - match: '\*/\n?' + pop: true + commenttags: + - match: '\b((?i:todo)|(?i:fixme)|(?i:hack)|(?i:undone))\b' + scope: markup.other.source.cs + xmldoc: + - match: '(?x)()|(?=($\n?)))' + captures: + 1: punctuation.definition.tag.cs + pop: true + - match: "'" + scope: string.quoted.single.source.cs + push: + - match: "('|(?=($\n?)))" + pop: true + - match: '"' + scope: string.quoted.double.source.cs + push: + - match: '("|(?=($\n?)))' + pop: true + - match: $\n? + pop: true + keywords: + - match: '@\w+\b' + - match: '\b(this|base)\b' + scope: keyword.other.access.source.cs + - match: '\b(as|is|new|sizeof|typeof|stackalloc)\b' + scope: keyword.operator.source.cs + - match: '\b(else|if|switch|case|default)\b' + scope: keyword.other.selection.source.cs + - match: '\b(do|for|foreach|in|while)\b' + scope: keyword.other.iteration.source.cs + - match: '\b(break|continue|goto|return)\b' + scope: keyword.other.jump.source.cs + - match: '\b(yield|partial|global|where|__arglist|__makeref|__reftype|__refvalue|by|descending|from|group|into|orderby|select|let|ascending|join|on|equals)\b' + scope: keyword.other.context.source.cs + - match: '\b(try|throw|catch|finally)\b' + scope: keyword.other.exception.source.cs + - match: '\b(abstract|async|await|const|event|extern|override|readonly|sealed|static|virtual|volatile|public|protected|private|internal)\b' + scope: keyword.other.modifiers.source.cs + - match: '\b(bool|byte|char|decimal|double|float|int|long|sbyte|short|uint|ushort|ulong|object|string|var|dynamic)\b' + scope: keyword.other.type.source.cs + - match: '\b(void)\b' + scope: keyword.other.void.source.cs + - match: '\b(namespace|using)\b' + scope: keyword.other.namespace.source.cs + - match: '\b(get|set|add|remove|value)\b' + scope: keyword.other.property.source.cs + - match: '\b(class|interface|delegate|enum|struct)\b' + scope: keyword.other.declaration.source.cs + - match: '\b(params|ref|out)\b' + scope: keyword.other.parameter.source.cs + - match: '\b(explicit|implicit|operator)\b' + scope: keyword.operator.declaration.source.cs + - match: '\b(checked|unchecked|fixed|unsafe|lock)\b' + scope: keyword.other.source.cs + - match: '\b(true|false|null)\b' + scope: constant.language.source.cs + numbers: + - match: '((\b\d+\.?\d+)|(\.\d+))([eE][+-]?\d*)?(F|f|D|d|M|m)?\b' + scope: constant.numeric.float.source.cs + - match: '\b\d+(([eE][+-]?\d*)?(F|f|D|d|M|m)|([eE][+-]?\d*))\b' + scope: constant.numeric.float.source.cs + - match: '\b0[bB][01_]+(U|u|L|l|UL|Ul|uL|ul|LU|Lu|lU|lu)?\b' + scope: constant.numeric.binary.source.cs + - match: '\b(0[xX][0-9a-fA-F]+?|\d+)(U|u|L|l|UL|Ul|uL|ul|LU|Lu|lU|lu)?\b' + scope: constant.numeric.source.cs + strings: + - match: "'" + scope: string.quoted.single.source.cs + push: + - match: '\\.' + scope: constant.character.escape.source.cs + - match: "('|$\n?)" + pop: true + - match: '@"' + scope: string.quoted.other.verbatim.source.cs + push: + - match: '""' + scope: constant.character.verbatim.escape.source.cs + - match: '"' + pop: true + - match: '\$@"' + scope: string.quoted.other.interpolated.verbatim.source.cs + push: + - match: '""' + scope: constant.character.interpolated.verbatim.escape.source.cs + - match: '"' + pop: true + - match: '\{\{' + - match: '({)' + captures: + 1: string.quoted.other.interpolated.verbatim.source.cs + push: + - meta_content_scope: source.cs + - match: '(})' + captures: + 1: string.quoted.other.interpolated.verbatim.source.cs + pop: true + - include: main + - match: '\$"' + scope: string.quoted.other.interpolated.source.cs + push: + - match: '\\.' + scope: constant.character.escape.source.cs + - match: '("|$\n?)' + pop: true + - match: '\{\{' + - match: '({)' + captures: + 1: string.quoted.other.interpolated.source.cs + push: + - meta_content_scope: source.cs + - match: '(})' + captures: + 1: string.quoted.other.interpolated.source.cs + pop: true + - include: main + - match: '"' + scope: string.quoted.double.source.cs + push: + - match: '\\.' + scope: constant.character.escape.source.cs + - match: '("|$\n?)' + pop: true + preprocessor: + - match: '^\s*#(if|else|elif|endif|define|undef|warning|error|line|endregion|pragma)\b' + scope: meta.preprocessor.source.cs + push: + - match: '\n?' + pop: true + - match: '^\s*(#region)(.*)$\n?' + captures: + 1: meta.preprocessor.source.cs + 2: meta.preprocessor.region.name.source.cs diff --git a/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/Resources/Resources.cs b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/Resources/Resources.cs index f80e59ba55..812839af8c 100644 --- a/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/Resources/Resources.cs +++ b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/Resources/Resources.cs @@ -3,7 +3,7 @@ namespace AvalonStudio.Controls.Standard.CodeEditor.Highlighting.Resources { - internal static class Resources + public static class Resources { private const string Prefix = "AvalonStudio.Controls.Standard.CodeEditor.Highlighting.Resources."; @@ -18,6 +18,9 @@ public static Stream OpenStream(string name) internal static void RegisterBuiltInHighlightings(CustomHighlightingManager hlm) { hlm.RegisterHighlighting("XML-Mode.xshd"); + hlm.RegisterHighlighting("c.tmLanguage.json"); + hlm.RegisterHighlighting("cpp.tmLanguage.json"); + hlm.RegisterHighlighting("csharp.tmLanguage"); } } } diff --git a/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/Resources/c.tmLanguage.json b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/Resources/c.tmLanguage.json new file mode 100644 index 0000000000..bcd5470a64 --- /dev/null +++ b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/Resources/c.tmLanguage.json @@ -0,0 +1,1955 @@ +{ + "information_for_contributors": [ + "This file has been converted from https://github.com/atom/language-c/blob/master/grammars/c.cson", + "If you want to provide a fix or improvement, please create a pull request against the original repository.", + "Once accepted there, we are happy to receive an update request." + ], + "version": "https://github.com/atom/language-c/commit/9c0c5f202741a5647025db8d5df5fefba47b036c", + "name": "C", + "scopeName": "source.c", + "patterns": [ + { + "include": "#preprocessor-rule-enabled" + }, + { + "include": "#preprocessor-rule-disabled" + }, + { + "include": "#preprocessor-rule-conditional" + }, + { + "include": "#comments" + }, + { + "match": "\\b(break|case|continue|default|do|else|for|goto|if|_Pragma|return|switch|while)\\b", + "name": "keyword.control.c" + }, + { + "include": "#storage_types" + }, + { + "match": "\\b(const|extern|register|restrict|static|volatile|inline)\\b", + "name": "storage.modifier.c" + }, + { + "match": "\\bk[A-Z]\\w*\\b", + "name": "constant.other.variable.mac-classic.c" + }, + { + "match": "\\bg[A-Z]\\w*\\b", + "name": "variable.other.readwrite.global.mac-classic.c" + }, + { + "match": "\\bs[A-Z]\\w*\\b", + "name": "variable.other.readwrite.static.mac-classic.c" + }, + { + "match": "\\b(NULL|true|false|TRUE|FALSE)\\b", + "name": "constant.language.c" + }, + { + "include": "#operators" + }, + { + "include": "#numbers" + }, + { + "include": "#strings" + }, + { + "begin": "(?x)\n^\\s* ((\\#)\\s*define) \\s+ # define\n((?[a-zA-Z_$][\\w$]*)) # macro name\n(?:\n (\\()\n (\n \\s* \\g \\s* # first argument\n ((,) \\s* \\g \\s*)* # additional arguments\n (?:\\.\\.\\.)? # varargs ellipsis?\n )\n (\\))\n)?", + "beginCaptures": { + "1": { + "name": "keyword.control.directive.define.c" + }, + "2": { + "name": "punctuation.definition.directive.c" + }, + "3": { + "name": "entity.name.function.preprocessor.c" + }, + "5": { + "name": "punctuation.definition.parameters.begin.c" + }, + "6": { + "name": "variable.parameter.preprocessor.c" + }, + "8": { + "name": "punctuation.separator.parameters.c" + }, + "9": { + "name": "punctuation.definition.parameters.end.c" + } + }, + "end": "(?=(?://|/\\*))|(?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.c" + } + }, + "name": "string.quoted.other.lt-gt.include.c" + } + ] + }, + { + "include": "#pragma-mark" + }, + { + "begin": "^\\s*((#)\\s*line)\\b", + "beginCaptures": { + "1": { + "name": "keyword.control.directive.line.c" + }, + "2": { + "name": "punctuation.definition.directive.c" + } + }, + "end": "(?=(?://|/\\*))|(?=+!]+|\\(\\)|\\[\\]))\\s*\\(\n)", + "end": "(?<=\\))(?!\\w)", + "name": "meta.function.c", + "patterns": [ + { + "include": "#function-innards" + } + ] + }, + { + "include": "#line_continuation_character" + }, + { + "match": "(\\[)|(\\])", + "captures": { + "1": { + "name": "punctuation.definition.begin.bracket.square.c" + }, + "2": { + "name": "punctuation.definition.end.bracket.square.c" + } + } + }, + { + "match": ";", + "name": "punctuation.terminator.statement.c" + }, + { + "match": ",", + "name": "punctuation.separator.delimiter.c" + } + ], + "repository": { + "access": { + "captures": { + "2": { + "name": "punctuation.separator.dot-access.c" + }, + "3": { + "name": "punctuation.separator.pointer-access.c" + }, + "4": { + "name": "variable.other.member.c" + } + }, + "match": "((\\.)|(->))\\s*(([a-zA-Z_][a-zA-Z_0-9]*)\\b(?!\\s*\\())?" + }, + "block": { + "patterns": [ + { + "begin": "{", + "beginCaptures": { + "0": { + "name": "punctuation.section.block.begin.bracket.curly.c" + } + }, + "end": "}|(?=\\s*#\\s*(?:elif|else|endif)\\b)", + "endCaptures": { + "0": { + "name": "punctuation.section.block.end.bracket.curly.c" + } + }, + "name": "meta.block.c", + "patterns": [ + { + "include": "#block_innards" + } + ] + } + ] + }, + "block_innards": { + "patterns": [ + { + "include": "#preprocessor-rule-enabled-block" + }, + { + "include": "#preprocessor-rule-disabled-block" + }, + { + "include": "#preprocessor-rule-conditional-block" + }, + { + "include": "#access" + }, + { + "include": "#libc" + }, + { + "include": "#c_function_call" + }, + { + "captures": { + "1": { + "name": "variable.other.c" + }, + "2": { + "name": "punctuation.definition.parameters.c" + } + }, + "match": "(?x)\n(?:\n (?:\n (?=\\s)(?=+!]+ | \\(\\) | \\[\\]))\n)\n\\s*(\\() # opening bracket", + "name": "meta.initialization.c" + }, + { + "begin": "{", + "beginCaptures": { + "0": { + "name": "punctuation.section.block.begin.bracket.curly.c" + } + }, + "end": "}|(?=\\s*#\\s*(?:elif|else|endif)\\b)", + "endCaptures": { + "0": { + "name": "punctuation.section.block.end.bracket.curly.c" + } + }, + "patterns": [ + { + "include": "#block_innards" + } + ] + }, + { + "include": "#parens-block" + }, + { + "include": "$base" + } + ] + }, + "c_function_call": { + "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|enumerate|return|typeid|alignof|alignas|sizeof|[cr]?iterate)\\s*\\()\n(?=\n(?:[A-Za-z_][A-Za-z0-9_]*+|::)++\\s*\\( # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\\s*\\(\n)", + "end": "(?<=\\))(?!\\w)", + "name": "meta.function-call.c", + "patterns": [ + { + "include": "#function-call-innards" + } + ] + }, + "comments": { + "patterns": [ + { + "captures": { + "1": { + "name": "meta.toc-list.banner.block.c" + } + }, + "match": "^/\\* =(\\s*.*?)\\s*= \\*/$\\n?", + "name": "comment.block.c" + }, + { + "begin": "/\\*", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.begin.c" + } + }, + "end": "\\*/", + "endCaptures": { + "0": { + "name": "punctuation.definition.comment.end.c" + } + }, + "name": "comment.block.c" + }, + { + "match": "\\*/.*\\n", + "name": "invalid.illegal.stray-comment-end.c" + }, + { + "captures": { + "1": { + "name": "meta.toc-list.banner.line.c" + } + }, + "match": "^// =(\\s*.*?)\\s*=\\s*$\\n?", + "name": "comment.line.banner.cpp" + }, + { + "begin": "(^[ \\t]+)?(?=//)", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.comment.leading.cpp" + } + }, + "end": "(?!\\G)", + "patterns": [ + { + "begin": "//", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.cpp" + } + }, + "end": "(?=\\n)", + "name": "comment.line.double-slash.cpp", + "patterns": [ + { + "include": "#line_continuation_character" + } + ] + } + ] + } + ] + }, + "disabled": { + "begin": "^\\s*#\\s*if(n?def)?\\b.*$", + "end": "^\\s*#\\s*endif\\b", + "patterns": [ + { + "include": "#disabled" + }, + { + "include": "#pragma-mark" + } + ] + }, + "libc": { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading.c" + }, + "2": { + "name": "support.function.C99.c" + } + }, + "match": "(?x) (\\s*) \\b\n(_Exit|(?:nearbyint|nextafter|nexttoward|netoward|nan)[fl]?|a(?:cos|sin)h?[fl]?|abort|abs|asctime|assert\n|atan(?:[h2]?[fl]?)?|atexit|ato[ifl]|atoll|bsearch|btowc|cabs[fl]?|cacos|cacos[fl]|cacosh[fl]?\n|calloc|carg[fl]?|casinh?[fl]?|catanh?[fl]?|cbrt[fl]?|ccosh?[fl]?|ceil[fl]?|cexp[fl]?|cimag[fl]?\n|clearerr|clock|clog[fl]?|conj[fl]?|copysign[fl]?|cosh?[fl]?|cpow[fl]?|cproj[fl]?|creal[fl]?\n|csinh?[fl]?|csqrt[fl]?|ctanh?[fl]?|ctime|difftime|div|erfc?[fl]?|exit|fabs[fl]?\n|exp(?:2[fl]?|[fl]|m1[fl]?)?|fclose|fdim[fl]?|fe[gs]et(?:env|exceptflag|round)|feclearexcept\n|feholdexcept|feof|feraiseexcept|ferror|fetestexcept|feupdateenv|fflush|fgetpos|fgetw?[sc]\n|floor[fl]?|fmax?[fl]?|fmin[fl]?|fmod[fl]?|fopen|fpclassify|fprintf|fputw?[sc]|fread|free|freopen\n|frexp[fl]?|fscanf|fseek|fsetpos|ftell|fwide|fwprintf|fwrite|fwscanf|genv|get[sc]|getchar|gmtime\n|gwc|gwchar|hypot[fl]?|ilogb[fl]?|imaxabs|imaxdiv|isalnum|isalpha|isblank|iscntrl|isdigit|isfinite\n|isgraph|isgreater|isgreaterequal|isinf|isless(?:equal|greater)?|isw?lower|isnan|isnormal|isw?print\n|isw?punct|isw?space|isunordered|isw?upper|iswalnum|iswalpha|iswblank|iswcntrl|iswctype|iswdigit|iswgraph\n|isw?xdigit|labs|ldexp[fl]?|ldiv|lgamma[fl]?|llabs|lldiv|llrint[fl]?|llround[fl]?|localeconv|localtime\n|log[2b]?[fl]?|log1[p0][fl]?|longjmp|lrint[fl]?|lround[fl]?|malloc|mbr?len|mbr?towc|mbsinit|mbsrtowcs\n|mbstowcs|memchr|memcmp|memcpy|memmove|memset|mktime|modf[fl]?|perror|pow[fl]?|printf|puts|putw?c(?:har)?\n|qsort|raise|rand|remainder[fl]?|realloc|remove|remquo[fl]?|rename|rewind|rint[fl]?|round[fl]?|scalbl?n[fl]?\n|scanf|setbuf|setjmp|setlocale|setvbuf|signal|signbit|sinh?[fl]?|snprintf|sprintf|sqrt[fl]?|srand|sscanf\n|strcat|strchr|strcmp|strcoll|strcpy|strcspn|strerror|strftime|strlen|strncat|strncmp|strncpy|strpbrk\n|strrchr|strspn|strstr|strto[kdf]|strtoimax|strtol[dl]?|strtoull?|strtoumax|strxfrm|swprintf|swscanf\n|system|tan|tan[fl]|tanh[fl]?|tgamma[fl]?|time|tmpfile|tmpnam|tolower|toupper|trunc[fl]?|ungetw?c|va_arg\n|va_copy|va_end|va_start|vfw?printf|vfw?scanf|vprintf|vscanf|vsnprintf|vsprintf|vsscanf|vswprintf|vswscanf\n|vwprintf|vwscanf|wcrtomb|wcscat|wcschr|wcscmp|wcscoll|wcscpy|wcscspn|wcsftime|wcslen|wcsncat|wcsncmp|wcsncpy\n|wcspbrk|wcsrchr|wcsrtombs|wcsspn|wcsstr|wcsto[dkf]|wcstoimax|wcstol[dl]?|wcstombs|wcstoull?|wcstoumax|wcsxfrm\n|wctom?b|wmem(?:set|chr|cpy|cmp|move)|wprintf|wscanf)\\b" + }, + "line_continuation_character": { + "patterns": [ + { + "match": "(\\\\)\\n", + "captures": { + "1": { + "name": "constant.character.escape.line-continuation.c" + } + } + } + ] + }, + "numbers": { + "patterns": [ + { + "match": "\\b((0(x|X)[0-9a-fA-F]([0-9a-fA-F']*[0-9a-fA-F])?)|(0(b|B)[01]([01']*[01])?)|(([0-9]([0-9']*[0-9])?\\.?[0-9]*([0-9']*[0-9])?)|(\\.[0-9]([0-9']*[0-9])?))((e|E)(\\+|-)?[0-9]([0-9']*[0-9])?)?)(L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\\b", + "name": "constant.numeric.c" + } + ] + }, + "parens": { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.section.parens.begin.bracket.round.c" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.parens.end.bracket.round.c" + } + }, + "patterns": [ + { + "include": "$base" + } + ] + }, + "parens-block": { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.section.parens.begin.bracket.round.c" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.parens.end.bracket.round.c" + } + }, + "patterns": [ + { + "include": "#block_innards" + } + ] + }, + "pragma-mark": { + "captures": { + "1": { + "name": "meta.preprocessor.pragma.c" + }, + "2": { + "name": "keyword.control.directive.pragma.pragma-mark.c" + }, + "3": { + "name": "punctuation.definition.directive.c" + }, + "4": { + "name": "entity.name.tag.pragma-mark.c" + } + }, + "match": "^\\s*(((#)\\s*pragma\\s+mark)\\s+(.*))", + "name": "meta.section" + }, + "operators": { + "patterns": [ + { + "match": "(?>=|\\|=", + "name": "keyword.operator.assignment.compound.bitwise.c" + }, + { + "match": "<<|>>", + "name": "keyword.operator.bitwise.shift.c" + }, + { + "match": "!=|<=|>=|==|<|>", + "name": "keyword.operator.comparison.c" + }, + { + "match": "&&|!|\\|\\|", + "name": "keyword.operator.logical.c" + }, + { + "match": "&|\\||\\^|~", + "name": "keyword.operator.c" + }, + { + "match": "=", + "name": "keyword.operator.assignment.c" + }, + { + "match": "%|\\*|/|-|\\+", + "name": "keyword.operator.c" + }, + { + "begin": "\\?", + "beginCaptures": { + "0": { + "name": "keyword.operator.ternary.c" + } + }, + "end": ":", + "applyEndPatternLast": true, + "endCaptures": { + "0": { + "name": "keyword.operator.ternary.c" + } + }, + "patterns": [ + { + "include": "#access" + }, + { + "include": "#libc" + }, + { + "include": "#c_function_call" + }, + { + "include": "$base" + } + ] + } + ] + }, + "strings": { + "patterns": [ + { + "begin": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.c" + } + }, + "end": "\"", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.c" + } + }, + "name": "string.quoted.double.c", + "patterns": [ + { + "include": "#string_escaped_char" + }, + { + "include": "#string_placeholder" + }, + { + "include": "#line_continuation_character" + } + ] + }, + { + "begin": "'", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.c" + } + }, + "end": "'", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.c" + } + }, + "name": "string.quoted.single.c", + "patterns": [ + { + "include": "#string_escaped_char" + }, + { + "include": "#line_continuation_character" + } + ] + } + ] + }, + "string_escaped_char": { + "patterns": [ + { + "match": "(?x)\\\\ (\n\\\\ |\n[abefnprtv'\"?] |\n[0-3]\\d{,2} |\n[4-7]\\d? |\nx[a-fA-F0-9]{,2} |\nu[a-fA-F0-9]{,4} |\nU[a-fA-F0-9]{,8} )", + "name": "constant.character.escape.c" + }, + { + "match": "\\\\.", + "name": "invalid.illegal.unknown-escape.c" + } + ] + }, + "string_placeholder": { + "patterns": [ + { + "match": "(?x) %\n(\\d+\\$)? # field (argument #)\n[#0\\- +']* # flags\n[,;:_]? # separator character (AltiVec)\n((-?\\d+)|\\*(-?\\d+\\$)?)? # minimum field width\n(\\.((-?\\d+)|\\*(-?\\d+\\$)?)?)? # precision\n(hh|h|ll|l|j|t|z|q|L|vh|vl|v|hv|hl)? # length modifier\n[diouxXDOUeEfFgGaACcSspn%] # conversion type", + "name": "constant.other.placeholder.c" + }, + { + "match": "(%)(?!\"\\s*(PRI|SCN))", + "captures": { + "1": { + "name": "invalid.illegal.placeholder.c" + } + } + } + ] + }, + "storage_types": { + "patterns": [ + { + "match": "\\b(asm|__asm__|auto|bool|_Bool|char|_Complex|double|enum|float|_Imaginary|int|long|short|signed|struct|typedef|union|unsigned|void)\\b", + "name": "storage.type.c" + } + ] + }, + "vararg_ellipses": { + "match": "(?=+!]+|\\(\\)|\\[\\]))\\s*\\(\n)", + "end": "(?<=\\))(?!\\w)|(?=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", + "beginCaptures": { + "1": { + "name": "entity.name.function.c" + }, + "2": { + "name": "punctuation.section.arguments.begin.bracket.round.c" + } + }, + "end": "(\\))|(?=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", + "beginCaptures": { + "1": { + "name": "entity.name.function.c" + }, + "2": { + "name": "punctuation.section.parameters.begin.bracket.round.c" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.parameters.end.bracket.round.c" + } + }, + "patterns": [ + { + "include": "#function-innards" + } + ] + }, + { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.section.parens.begin.bracket.round.c" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.parens.end.bracket.round.c" + } + }, + "patterns": [ + { + "include": "#function-innards" + } + ] + }, + { + "include": "$base" + } + ] + }, + "function-call-innards": { + "patterns": [ + { + "include": "#comments" + }, + { + "include": "#storage_types" + }, + { + "include": "#access" + }, + { + "include": "#operators" + }, + { + "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|enumerate|return|typeid|alignof|alignas|sizeof|[cr]?iterate)\\s*\\()\n(\n(?:[A-Za-z_][A-Za-z0-9_]*+|::)++ # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", + "beginCaptures": { + "1": { + "name": "entity.name.function.c" + }, + "2": { + "name": "punctuation.section.arguments.begin.bracket.round.c" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.arguments.end.bracket.round.c" + } + }, + "patterns": [ + { + "include": "#function-call-innards" + } + ] + }, + { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.section.parens.begin.bracket.round.c" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.parens.end.bracket.round.c" + } + }, + "patterns": [ + { + "include": "#function-call-innards" + } + ] + }, + { + "include": "#block_innards" + } + ] + } + } +} \ No newline at end of file diff --git a/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/Resources/cpp.tmLanguage.json b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/Resources/cpp.tmLanguage.json new file mode 100644 index 0000000000..9be454afa5 --- /dev/null +++ b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/Resources/cpp.tmLanguage.json @@ -0,0 +1,447 @@ +{ + "information_for_contributors": [ + "This file has been converted from https://github.com/atom/language-c/blob/master/grammars/c%2B%2B.cson", + "If you want to provide a fix or improvement, please create a pull request against the original repository.", + "Once accepted there, we are happy to receive an update request." + ], + "version": "https://github.com/atom/language-c/commit/3a269f88b12e512fb9495dc006a1dabf325d3d7f", + "name": "C++", + "scopeName": "source.cpp", + "patterns": [ + { + "include": "#special_block" + }, + { + "include": "#strings" + }, + { + "match": "\\b(friend|explicit|virtual|override|final|noexcept)\\b", + "name": "storage.modifier.cpp" + }, + { + "match": "\\b(private:|protected:|public:)", + "name": "storage.modifier.cpp" + }, + { + "match": "\\b(catch|operator|try|throw|using)\\b", + "name": "keyword.control.cpp" + }, + { + "match": "\\bdelete\\b(\\s*\\[\\])?|\\bnew\\b(?!])", + "name": "keyword.control.cpp" + }, + { + "match": "\\b(f|m)[A-Z]\\w*\\b", + "name": "variable.other.readwrite.member.cpp" + }, + { + "match": "\\bthis\\b", + "name": "variable.language.this.cpp" + }, + { + "match": "\\bnullptr\\b", + "name": "constant.language.cpp" + }, + { + "match": "\\btemplate\\b\\s*", + "name": "storage.type.template.cpp" + }, + { + "match": "\\b(const_cast|dynamic_cast|reinterpret_cast|static_cast)\\b\\s*", + "name": "keyword.operator.cast.cpp" + }, + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp" + }, + { + "match": "\\b(and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eq|alignof|alignas)\\b", + "name": "keyword.operator.cpp" + }, + { + "match": "\\b(class|decltype|wchar_t|char16_t|char32_t)\\b", + "name": "storage.type.cpp" + }, + { + "match": "\\b(constexpr|export|mutable|typename|thread_local)\\b", + "name": "storage.modifier.cpp" + }, + { + "begin": "(?x)\n(?:\n ^ | # beginning of line\n (?:(?", + "name": "meta.angle-brackets.cpp", + "patterns": [ + { + "include": "#angle_brackets" + }, + { + "include": "$base" + } + ] + }, + "block": { + "begin": "\\{", + "beginCaptures": { + "0": { + "name": "punctuation.section.block.begin.bracket.curly.c" + } + }, + "end": "\\}", + "endCaptures": { + "0": { + "name": "punctuation.section.block.end.bracket.curly.c" + } + }, + "name": "meta.block.cpp", + "patterns": [ + { + "captures": { + "1": { + "name": "support.function.any-method.c" + }, + "2": { + "name": "punctuation.definition.parameters.c" + } + }, + "match": "(?x)\n(\n (?!while|for|do|if|else|switch|catch|enumerate|return|r?iterate)\n (?:\\b[A-Za-z_][A-Za-z0-9_]*+\\b|::)*+ # actual name\n)\n\\s*(\\() # opening bracket", + "name": "meta.function-call.c" + }, + { + "include": "$base" + } + ] + }, + "constructor": { + "patterns": [ + { + "begin": "(?x)\n(?:^\\s*) # beginning of line\n((?!while|for|do|if|else|switch|catch|enumerate|r?iterate)[A-Za-z_][A-Za-z0-9_:]*) # actual name\n\\s*(\\() # opening bracket", + "beginCaptures": { + "1": { + "name": "entity.name.function.cpp" + }, + "2": { + "name": "punctuation.definition.parameters.begin.c" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.parameters.end.c" + } + }, + "name": "meta.function.constructor.cpp", + "patterns": [ + { + "include": "$base" + } + ] + }, + { + "begin": "(?x)\n(:)\n(\n (?=\n \\s*[A-Za-z_][A-Za-z0-9_:]* # actual name\n \\s* (\\() # opening bracket\n )\n)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.parameters.c" + } + }, + "end": "(?=\\{)", + "name": "meta.function.constructor.initializer-list.cpp", + "patterns": [ + { + "include": "$base" + } + ] + } + ] + }, + "special_block": { + "patterns": [ + { + "begin": "\\b(using)\\b\\s*(namespace)\\b\\s*((?:[_A-Za-z][_A-Za-z0-9]*\\b(::)?)*)", + "beginCaptures": { + "1": { + "name": "keyword.control.cpp" + }, + "2": { + "name": "storage.type.cpp" + }, + "3": { + "name": "entity.name.type.cpp" + } + }, + "end": "(;)", + "name": "meta.using-namespace-declaration.cpp" + }, + { + "begin": "\\b(namespace)\\b\\s*([_A-Za-z][_A-Za-z0-9]*\\b)?+", + "beginCaptures": { + "1": { + "name": "storage.type.cpp" + }, + "2": { + "name": "entity.name.type.cpp" + } + }, + "captures": { + "1": { + "name": "keyword.control.namespace.$2" + } + }, + "end": "(?<=\\})|(?=(;|,|\\(|\\)|>|\\[|\\]|=))", + "name": "meta.namespace-block.cpp", + "patterns": [ + { + "begin": "\\{", + "beginCaptures": { + "0": { + "name": "punctuation.definition.scope.cpp" + } + }, + "end": "\\}", + "endCaptures": { + "0": { + "name": "punctuation.definition.scope.cpp" + } + }, + "patterns": [ + { + "include": "#special_block" + }, + { + "include": "#constructor" + }, + { + "include": "$base" + } + ] + }, + { + "include": "$base" + } + ] + }, + { + "begin": "\\b(class|struct)\\b\\s*([_A-Za-z][_A-Za-z0-9]*\\b)?+(\\s*:\\s*(public|protected|private)\\s*([_A-Za-z][_A-Za-z0-9]*\\b)((\\s*,\\s*(public|protected|private)\\s*[_A-Za-z][_A-Za-z0-9]*\\b)*))?", + "beginCaptures": { + "1": { + "name": "storage.type.cpp" + }, + "2": { + "name": "entity.name.type.cpp" + }, + "4": { + "name": "storage.type.modifier.cpp" + }, + "5": { + "name": "entity.name.type.inherited.cpp" + }, + "6": { + "patterns": [ + { + "match": "(public|protected|private)", + "name": "storage.type.modifier.cpp" + }, + { + "match": "[_A-Za-z][_A-Za-z0-9]*", + "name": "entity.name.type.inherited.cpp" + } + ] + } + }, + "end": "(?<=\\})|(?=(;|\\(|\\)|>|\\[|\\]|=))", + "name": "meta.class-struct-block.cpp", + "patterns": [ + { + "include": "#angle_brackets" + }, + { + "begin": "\\{", + "beginCaptures": { + "0": { + "name": "punctuation.section.block.begin.bracket.curly.cpp" + } + }, + "end": "(\\})(\\s*\\n)?", + "endCaptures": { + "1": { + "name": "punctuation.section.block.end.bracket.curly.cpp" + }, + "2": { + "name": "invalid.illegal.you-forgot-semicolon.cpp" + } + }, + "patterns": [ + { + "include": "#special_block" + }, + { + "include": "#constructor" + }, + { + "include": "$base" + } + ] + }, + { + "include": "$base" + } + ] + }, + { + "begin": "\\b(extern)(?=\\s*\")", + "beginCaptures": { + "1": { + "name": "storage.modifier.cpp" + } + }, + "end": "(?<=\\})|(?=\\w)|(?=\\s*#\\s*endif\\b)", + "name": "meta.extern-block.cpp", + "patterns": [ + { + "begin": "\\{", + "beginCaptures": { + "0": { + "name": "punctuation.section.block.begin.bracket.curly.c" + } + }, + "end": "\\}|(?=\\s*#\\s*endif\\b)", + "endCaptures": { + "0": { + "name": "punctuation.section.block.end.bracket.curly.c" + } + }, + "patterns": [ + { + "include": "#special_block" + }, + { + "include": "$base" + } + ] + }, + { + "include": "$base" + } + ] + } + ] + }, + "strings": { + "patterns": [ + { + "begin": "(u|u8|U|L)?\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.cpp" + }, + "1": { + "name": "meta.encoding.cpp" + } + }, + "end": "\"", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.cpp" + } + }, + "name": "string.quoted.double.cpp", + "patterns": [ + { + "match": "\\\\u\\h{4}|\\\\U\\h{8}", + "name": "constant.character.escape.cpp" + }, + { + "match": "\\\\['\"?\\\\abfnrtv]", + "name": "constant.character.escape.cpp" + }, + { + "match": "\\\\[0-7]{1,3}", + "name": "constant.character.escape.cpp" + }, + { + "match": "\\\\x\\h+", + "name": "constant.character.escape.cpp" + }, + { + "include": "source.c#string_placeholder" + } + ] + }, + { + "begin": "(u|u8|U|L)?R\"(?:([^ ()\\\\\\t]{0,16})|([^ ()\\\\\\t]*))\\(", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.cpp" + }, + "1": { + "name": "meta.encoding.cpp" + }, + "3": { + "name": "invalid.illegal.delimiter-too-long.cpp" + } + }, + "end": "\\)\\2(\\3)\"", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.cpp" + }, + "1": { + "name": "invalid.illegal.delimiter-too-long.cpp" + } + }, + "name": "string.quoted.double.raw.cpp" + } + ] + } + } +} \ No newline at end of file diff --git a/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/Resources/csharp.tmLanguage b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/Resources/csharp.tmLanguage new file mode 100644 index 0000000000..8fd33d70ad --- /dev/null +++ b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/Resources/csharp.tmLanguage @@ -0,0 +1,6603 @@ + + + + + name + C# + scopeName + source.cs + fileTypes + + cs + + uuid + f7de61e2-bdde-4e2a-a139-8221b179584e + patterns + + + include + #preprocessor + + + include + #comment + + + include + #directives + + + include + #declarations + + + include + #script-top-level + + + repository + + directives + + patterns + + + include + #extern-alias-directive + + + include + #using-directive + + + include + #attribute-section + + + include + #punctuation-semicolon + + + + declarations + + patterns + + + include + #namespace-declaration + + + include + #type-declarations + + + include + #punctuation-semicolon + + + + script-top-level + + patterns + + + include + #method-declaration + + + include + #statement + + + include + #punctuation-semicolon + + + + type-declarations + + patterns + + + include + #preprocessor + + + include + #comment + + + include + #storage-modifier + + + include + #class-declaration + + + include + #delegate-declaration + + + include + #enum-declaration + + + include + #interface-declaration + + + include + #struct-declaration + + + include + #attribute-section + + + include + #punctuation-semicolon + + + + class-members + + patterns + + + include + #preprocessor + + + include + #comment + + + include + #storage-modifier + + + include + #type-declarations + + + include + #event-declaration + + + include + #property-declaration + + + include + #indexer-declaration + + + include + #field-declaration + + + include + #variable-initializer + + + include + #constructor-declaration + + + include + #destructor-declaration + + + include + #operator-declaration + + + include + #conversion-operator-declaration + + + include + #method-declaration + + + include + #attribute-section + + + include + #punctuation-semicolon + + + + struct-members + + patterns + + + include + #preprocessor + + + include + #comment + + + include + #storage-modifier + + + include + #type-declarations + + + include + #event-declaration + + + include + #property-declaration + + + include + #indexer-declaration + + + include + #field-declaration + + + include + #variable-initializer + + + include + #constructor-declaration + + + include + #destructor-declaration + + + include + #operator-declaration + + + include + #conversion-operator-declaration + + + include + #method-declaration + + + include + #attribute-section + + + include + #punctuation-semicolon + + + + interface-members + + patterns + + + include + #preprocessor + + + include + #comment + + + include + #event-declaration + + + include + #property-declaration + + + include + #indexer-declaration + + + include + #method-declaration + + + include + #attribute-section + + + include + #punctuation-semicolon + + + + statement + + patterns + + + include + #preprocessor + + + include + #comment + + + include + #while-statement + + + include + #do-statement + + + include + #for-statement + + + include + #foreach-statement + + + include + #if-statement + + + include + #else-part + + + include + #switch-statement + + + include + #goto-statement + + + include + #return-statement + + + include + #break-or-continue-statement + + + include + #throw-statement + + + include + #yield-statement + + + include + #try-statement + + + include + #checked-unchecked-statement + + + include + #lock-statement + + + include + #using-statement + + + include + #labeled-statement + + + include + #local-declaration + + + include + #block + + + include + #expression + + + include + #punctuation-semicolon + + + + expression + + patterns + + + include + #preprocessor + + + include + #comment + + + include + #checked-unchecked-expression + + + include + #typeof-or-default-expression + + + include + #nameof-expression + + + include + #interpolated-string + + + include + #verbatim-interpolated-string + + + include + #literal + + + include + #this-or-base-expression + + + include + #conditional-operator + + + include + #expression-operators + + + include + #await-expression + + + include + #query-expression + + + include + #as-expression + + + include + #is-expression + + + include + #anonymous-method-expression + + + include + #object-creation-expression + + + include + #array-creation-expression + + + include + #anonymous-object-creation-expression + + + include + #member-access-expression + + + include + #invocation-expression + + + include + #element-access-expression + + + include + #cast-expression + + + include + #parenthesized-expression + + + include + #initializer-expression + + + include + #identifier + + + + extern-alias-directive + + begin + \s*(extern)\b\s*(alias)\b\s*([_[:alpha:]][_[:alnum:]]*) + beginCaptures + + 1 + + name + keyword.other.extern.cs + + 2 + + name + keyword.other.alias.cs + + 3 + + name + variable.other.alias.cs + + + end + (?=;) + + using-directive + + patterns + + + begin + \b(using)\b\s+(static)\s+ + beginCaptures + + 1 + + name + keyword.other.using.cs + + 2 + + name + keyword.other.static.cs + + + end + (?=;) + patterns + + + include + #type + + + + + begin + \b(using)\s+(?=([_[:alpha:]][_[:alnum:]]*)\s*=) + beginCaptures + + 1 + + name + keyword.other.using.cs + + 2 + + name + entity.name.type.alias.cs + + + end + (?=;) + patterns + + + include + #comment + + + include + #type + + + include + #operator-assignment + + + + + begin + \b(using)\s* + beginCaptures + + 1 + + name + keyword.other.using.cs + + + end + (?=;) + patterns + + + include + #comment + + + name + entity.name.type.namespace.cs + match + [_[:alpha:]][_[:alnum:]]* + + + include + #operator-assignment + + + + + + attribute-section + + begin + (\[)(assembly|module|field|event|method|param|property|return|type)?(\:)? + beginCaptures + + 1 + + name + punctuation.squarebracket.open.cs + + 2 + + name + keyword.other.attribute-specifier.cs + + 3 + + name + punctuation.separator.colon.cs + + + end + (\]) + endCaptures + + 1 + + name + punctuation.squarebracket.close.cs + + + patterns + + + include + #comment + + + include + #attribute + + + include + #punctuation-comma + + + + attribute + + patterns + + + include + #type-name + + + include + #attribute-arguments + + + + attribute-arguments + + begin + (\() + beginCaptures + + 1 + + name + punctuation.parenthesis.open.cs + + + end + (\)) + endCaptures + + 1 + + name + punctuation.parenthesis.close.cs + + + patterns + + + include + #attribute-named-argument + + + include + #expression + + + include + #punctuation-comma + + + + attribute-named-argument + + begin + ([_[:alpha:]][_[:alnum:]]*)\s*(?==) + beginCaptures + + 1 + + name + entity.name.variable.property.cs + + + end + (?=(,|\))) + patterns + + + include + #operator-assignment + + + include + #expression + + + + namespace-declaration + + begin + \b(namespace)\s+ + beginCaptures + + 1 + + name + keyword.other.namespace.cs + + + end + (?<=\}) + patterns + + + include + #comment + + + name + entity.name.type.namespace.cs + match + [_[:alpha:]][_[:alnum:]]* + + + include + #punctuation-accessor + + + begin + \{ + beginCaptures + + 0 + + name + punctuation.curlybrace.open.cs + + + end + \} + endCaptures + + 0 + + name + punctuation.curlybrace.close.cs + + + patterns + + + include + #declarations + + + include + #using-directive + + + include + #punctuation-semicolon + + + + + + storage-modifier + + name + storage.modifier.cs + match + (?<!\.)\b(new|public|protected|internal|private|abstract|virtual|override|sealed|static|partial|readonly|volatile|const|extern|async|unsafe)\b + + class-declaration + + begin + (?=\bclass\b) + end + (?<=\}) + patterns + + + begin + + (?x) + \b(class)\b\s+ + ([_[:alpha:]][_[:alnum:]]*)\s* + + beginCaptures + + 1 + + name + keyword.other.class.cs + + 2 + + name + entity.name.type.class.cs + + + end + (?=\{) + patterns + + + include + #type-parameter-list + + + include + #base-types + + + include + #generic-constraints + + + + + begin + \{ + beginCaptures + + 0 + + name + punctuation.curlybrace.open.cs + + + end + \} + endCaptures + + 0 + + name + punctuation.curlybrace.close.cs + + + patterns + + + include + #class-members + + + + + include + #preprocessor + + + include + #comment + + + + delegate-declaration + + begin + + (?x) + (?:\b(delegate)\b)\s+ + (?<type-name> + (?: + (?:(?<identifier>[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification + (?<name-and-type-args> # identifier + type arguments (if any) + \g<identifier>\s* + (?<type-args>\s*<(?:[^<>]|\g<type-args>)+>\s*)? + ) + (?:\s*\.\s*\g<name-and-type-args>)* # Are there any more names being dotted into? + (?:\s*\*\s*)* # pointer suffix? + (?:\s*\?\s*)? # nullable suffix? + (?:\s*\[(?:\s*,\s*)*\]\s*)* # array suffix? + )| + (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) + )\s+ + (\g<identifier>)\s* + (<([^<>]+)>)?\s* + (?=\() + + beginCaptures + + 1 + + name + keyword.other.delegate.cs + + 2 + + patterns + + + include + #type + + + + 7 + + name + entity.name.type.delegate.cs + + 8 + + patterns + + + include + #type-parameter-list + + + + + end + (?=;) + patterns + + + include + #comment + + + include + #parenthesized-parameter-list + + + include + #generic-constraints + + + + enum-declaration + + begin + (?=\benum\b) + end + (?<=\}) + patterns + + + begin + (?=enum) + end + (?=\{) + patterns + + + match + (enum)\s+([_[:alpha:]][_[:alnum:]]*) + captures + + 1 + + name + keyword.other.enum.cs + + 2 + + name + entity.name.type.enum.cs + + + + + begin + : + beginCaptures + + 0 + + name + punctuation.separator.colon.cs + + + end + (?=\{) + patterns + + + include + #type + + + + + + + begin + \{ + beginCaptures + + 0 + + name + punctuation.curlybrace.open.cs + + + end + \} + endCaptures + + 0 + + name + punctuation.curlybrace.close.cs + + + patterns + + + include + #preprocessor + + + include + #comment + + + include + #attribute-section + + + include + #punctuation-comma + + + begin + [_[:alpha:]][_[:alnum:]]* + beginCaptures + + 0 + + name + entity.name.variable.enum-member.cs + + + end + (?=(,|\})) + patterns + + + include + #comment + + + include + #variable-initializer + + + + + + + include + #preprocessor + + + include + #comment + + + + interface-declaration + + begin + (?=\binterface\b) + end + (?<=\}) + patterns + + + begin + + (?x) + (interface)\b\s+ + ([_[:alpha:]][_[:alnum:]]*) + + beginCaptures + + 1 + + name + keyword.other.interface.cs + + 2 + + name + entity.name.type.interface.cs + + + end + (?=\{) + patterns + + + include + #type-parameter-list + + + include + #base-types + + + include + #generic-constraints + + + + + begin + \{ + beginCaptures + + 0 + + name + punctuation.curlybrace.open.cs + + + end + \} + endCaptures + + 0 + + name + punctuation.curlybrace.close.cs + + + patterns + + + include + #interface-members + + + + + include + #preprocessor + + + include + #comment + + + + struct-declaration + + begin + (?=\bstruct\b) + end + (?<=\}) + patterns + + + begin + + (?x) + (struct)\b\s+ + ([_[:alpha:]][_[:alnum:]]*) + + beginCaptures + + 1 + + name + keyword.other.struct.cs + + 2 + + name + entity.name.type.struct.cs + + + end + (?=\{) + patterns + + + include + #type-parameter-list + + + include + #base-types + + + include + #generic-constraints + + + + + begin + \{ + beginCaptures + + 0 + + name + punctuation.curlybrace.open.cs + + + end + \} + endCaptures + + 0 + + name + punctuation.curlybrace.close.cs + + + patterns + + + include + #struct-members + + + + + include + #preprocessor + + + include + #comment + + + + type-parameter-list + + begin + \< + beginCaptures + + 0 + + name + punctuation.definition.typeparameters.begin.cs + + + end + \> + endCaptures + + 0 + + name + punctuation.definition.typeparameters.end.cs + + + patterns + + + match + \b(in|out)\b + captures + + 1 + + name + storage.modifier.cs + + + + + match + \b([_[:alpha:]][_[:alnum:]]*)\b + captures + + 1 + + name + entity.name.type.type-parameter.cs + + + + + include + #comment + + + include + #punctuation-comma + + + include + #attribute-section + + + + base-types + + begin + : + beginCaptures + + 0 + + name + punctuation.separator.colon.cs + + + end + (?=\{|where) + patterns + + + include + #type + + + include + #punctuation-comma + + + + generic-constraints + + begin + (where)\s+([_[:alpha:]][_[:alnum:]]*)\s*(:) + beginCaptures + + 1 + + name + keyword.other.where.cs + + 2 + + name + storage.type.cs + + 3 + + name + punctuation.separator.colon.cs + + + end + (?=\{|where|;) + patterns + + + name + keyword.other.class.cs + match + \bclass\b + + + name + keyword.other.struct.cs + match + \bstruct\b + + + match + (new)\s*(\()\s*(\)) + captures + + 1 + + name + keyword.other.new.cs + + 2 + + name + punctuation.parenthesis.open.cs + + 3 + + name + punctuation.parenthesis.close.cs + + + + + include + #type + + + include + #punctuation-comma + + + include + #generic-constraints + + + + field-declaration + + begin + + (?x) + (?<type-name> + (?: + (?:(?<identifier>[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification + (?<name-and-type-args> # identifier + type arguments (if any) + \g<identifier>\s* + (?<type-args>\s*<(?:[^<>]|\g<type-args>)+>\s*)? + ) + (?:\s*\.\s*\g<name-and-type-args>)* # Are there any more names being dotted into? + (?:\s*\*\s*)* # pointer suffix? + (?:\s*\?\s*)? # nullable suffix? + (?:\s*\[(?:\s*,\s*)*\]\s*)* # array suffix? + )| + (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) + )\s+ + (\g<identifier>)\s* # first field name + (?!=>|==)(?=,|;|=) + + beginCaptures + + 1 + + patterns + + + include + #type + + + + 6 + + name + entity.name.variable.field.cs + + + end + (?=;) + patterns + + + name + entity.name.variable.field.cs + match + [_[:alpha:]][_[:alnum:]]* + + + include + #punctuation-comma + + + include + #comment + + + include + #variable-initializer + + + + property-declaration + + begin + + (?x) + (?!.*\b(?:class|interface|struct|enum|event)\b)\s* + (?<return-type> + (?<type-name> + (?: + (?:(?<identifier>[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification + (?<name-and-type-args> # identifier + type arguments (if any) + \g<identifier>\s* + (?<type-args>\s*<(?:[^<>]|\g<type-args>)+>\s*)? + ) + (?:\s*\.\s*\g<name-and-type-args>)* # Are there any more names being dotted into? + (?:\s*\*\s*)* # pointer suffix? + (?:\s*\?\s*)? # nullable suffix? + (?:\s*\[(?:\s*,\s*)*\]\s*)* # array suffix? + )| + (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) + )\s+ + ) + (?<interface-name>\g<type-name>\s*\.\s*)? + (?<property-name>\g<identifier>)\s* + (?=\{|=>|$) + + beginCaptures + + 1 + + patterns + + + include + #type + + + + 7 + + patterns + + + include + #type + + + include + #punctuation-accessor + + + + 8 + + name + entity.name.variable.property.cs + + + end + (?<=\})|(?=;) + patterns + + + include + #comment + + + include + #property-accessors + + + include + #expression-body + + + include + #variable-initializer + + + + indexer-declaration + + begin + + (?x) + (?<return-type> + (?<type-name> + (?: + (?:(?<identifier>[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification + (?<name-and-type-args> # identifier + type arguments (if any) + \g<identifier>\s* + (?<type-args>\s*<(?:[^<>]|\g<type-args>)+>\s*)? + ) + (?:\s*\.\s*\g<name-and-type-args>)* # Are there any more names being dotted into? + (?:\s*\*\s*)* # pointer suffix? + (?:\s*\?\s*)? # nullable suffix? + (?:\s*\[(?:\s*,\s*)*\]\s*)* # array suffix? + )| + (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) + )\s+ + ) + (?<interface-name>\g<type-name>\s*\.\s*)? + (?<indexer-name>this)\s* + (?=\[) + + beginCaptures + + 1 + + patterns + + + include + #type + + + + 7 + + patterns + + + include + #type + + + include + #punctuation-accessor + + + + 8 + + name + keyword.other.this.cs + + + end + (?<=\})|(?=;) + patterns + + + include + #comment + + + include + #bracketed-parameter-list + + + include + #property-accessors + + + include + #expression-body + + + include + #variable-initializer + + + + event-declaration + + begin + + (?x) + \b(event)\b\s* + (?<return-type> + (?<type-name> + (?: + (?:(?<identifier>[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification + (?<name-and-type-args> # identifier + type arguments (if any) + \g<identifier>\s* + (?<type-args>\s*<(?:[^<>]|\g<type-args>)+>\s*)? + ) + (?:\s*\.\s*\g<name-and-type-args>)* # Are there any more names being dotted into? + (?:\s*\*\s*)* # pointer suffix? + (?:\s*\?\s*)? # nullable suffix? + (?:\s*\[(?:\s*,\s*)*\]\s*)* # array suffix? + )| + (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) + )\s+ + ) + (?<interface-name>\g<type-name>\s*\.\s*)? + (?<event-names>\g<identifier>(?:\s*,\s*\g<identifier>)*)\s* + (?=\{|;|$) + + beginCaptures + + 1 + + name + keyword.other.event.cs + + 2 + + patterns + + + include + #type + + + + 8 + + patterns + + + include + #type + + + include + #punctuation-accessor + + + + 9 + + patterns + + + name + entity.name.variable.event.cs + match + [_[:alpha:]][_[:alnum:]]* + + + include + #punctuation-comma + + + + + end + (?<=\})|(?=;) + patterns + + + include + #comment + + + include + #event-accessors + + + include + #punctuation-comma + + + + property-accessors + + begin + \{ + beginCaptures + + 0 + + name + punctuation.curlybrace.open.cs + + + end + \} + endCaptures + + 0 + + name + punctuation.curlybrace.close.cs + + + patterns + + + name + storage.modifier.cs + match + \b(private|protected|internal)\b + + + name + keyword.other.get.cs + match + \b(get)\b + + + name + keyword.other.set.cs + match + \b(set)\b + + + include + #attribute-section + + + include + #block + + + include + #punctuation-semicolon + + + + event-accessors + + begin + \{ + beginCaptures + + 0 + + name + punctuation.curlybrace.open.cs + + + end + \} + endCaptures + + 0 + + name + punctuation.curlybrace.close.cs + + + patterns + + + name + keyword.other.add.cs + match + \b(add)\b + + + name + keyword.other.remove.cs + match + \b(remove)\b + + + include + #attribute-section + + + include + #block + + + include + #punctuation-semicolon + + + + method-declaration + + begin + + (?x) + (?<return-type> + (?<type-name> + (?: + (?:(?<identifier>[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification + (?<name-and-type-args> # identifier + type arguments (if any) + \g<identifier>\s* + (?<type-args>\s*<(?:[^<>]|\g<type-args>)+>\s*)? + ) + (?:\s*\.\s*\g<name-and-type-args>)* # Are there any more names being dotted into? + (?:\s*\*\s*)* # pointer suffix? + (?:\s*\?\s*)? # nullable suffix? + (?:\s*\[(?:\s*,\s*)*\]\s*)* # array suffix? + )| + (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) + )\s+ + ) + (?<interface-name>\g<type-name>\s*\.\s*)? + (\g<identifier>)\s* + (<([^<>]+)>)?\s* + (?=\() + + beginCaptures + + 1 + + patterns + + + include + #type + + + + 7 + + patterns + + + include + #type + + + include + #punctuation-accessor + + + + 8 + + name + entity.name.function.cs + + 9 + + patterns + + + include + #type-parameter-list + + + + + end + (?<=\})|(?=;) + patterns + + + include + #comment + + + include + #parenthesized-parameter-list + + + include + #generic-constraints + + + include + #expression-body + + + include + #block + + + + constructor-declaration + + begin + (?=[_[:alpha:]][_[:alnum:]]*\s*\() + end + (?<=\})|(?=;) + patterns + + + match + \b([_[:alpha:]][_[:alnum:]]*)\b + captures + + 1 + + name + entity.name.function.cs + + + + + begin + (:) + beginCaptures + + 1 + + name + punctuation.separator.colon.cs + + + end + (?=\{|=>) + patterns + + + include + #constructor-initializer + + + + + include + #parenthesized-parameter-list + + + include + #preprocessor + + + include + #comment + + + include + #expression-body + + + include + #block + + + + constructor-initializer + + begin + \b(?:(base)|(this))\b\s*(?=\() + beginCaptures + + 1 + + name + keyword.other.base.cs + + 2 + + name + keyword.other.this.cs + + + end + (?<=\)) + patterns + + + include + #argument-list + + + + destructor-declaration + + begin + (~)([_[:alpha:]][_[:alnum:]]*)\s*(?=\() + beginCaptures + + 1 + + name + punctuation.tilde.cs + + 2 + + name + entity.name.function.cs + + + end + (?<=\})|(?=;) + patterns + + + include + #comment + + + include + #parenthesized-parameter-list + + + include + #expression-body + + + include + #block + + + + operator-declaration + + begin + + (?x) + (?<type-name> + (?: + (?:(?<identifier>[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification + (?<name-and-type-args> # identifier + type arguments (if any) + \g<identifier>\s* + (?<type-args>\s*<(?:[^<>]|\g<type-args>)+>\s*)? + ) + (?:\s*\.\s*\g<name-and-type-args>)* # Are there any more names being dotted into? + (?:\s*\*\s*)* # pointer suffix? + (?:\s*\?\s*)? # nullable suffix? + (?:\s*\[(?:\s*,\s*)*\]\s*)* # array suffix? + )| + (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) + )\s* + (?<operator-keyword>(?:\b(?:operator)))\s* + (?<operator>(?:\+|-|\*|/|%|&|\||\^|\<\<|\>\>|==|!=|\>|\<|\>=|\<=|!|~|\+\+|--|true|false))\s* + (?=\() + + beginCaptures + + 1 + + patterns + + + include + #type + + + + 6 + + name + keyword.other.operator-decl.cs + + 7 + + name + entity.name.function.cs + + + end + (?<=\})|(?=;) + patterns + + + include + #comment + + + include + #parenthesized-parameter-list + + + include + #expression-body + + + include + #block + + + + conversion-operator-declaration + + begin + + (?x) + (?<explicit-or-implicit-keyword>(?:\b(?:explicit|implicit)))\s* + (?<operator-keyword>(?:\b(?:operator)))\s* + (?<type-name> + (?: + (?:(?<identifier>[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification + (?<name-and-type-args> # identifier + type arguments (if any) + \g<identifier>\s* + (?<type-args>\s*<(?:[^<>]|\g<type-args>)+>\s*)? + ) + (?:\s*\.\s*\g<name-and-type-args>)* # Are there any more names being dotted into? + (?:\s*\*\s*)* # pointer suffix? + (?:\s*\?\s*)? # nullable suffix? + (?:\s*\[(?:\s*,\s*)*\]\s*)* # array suffix? + )| + (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) + )\s* + (?=\() + + beginCaptures + + 1 + + patterns + + + match + \b(explicit)\b + captures + + 1 + + name + keyword.other.explicit.cs + + + + + match + \b(implicit)\b + captures + + 1 + + name + keyword.other.implicit.cs + + + + + + 2 + + name + keyword.other.operator-decl.cs + + 3 + + patterns + + + include + #type + + + + + end + (?<=\})|(?=;) + patterns + + + include + #comment + + + include + #parenthesized-parameter-list + + + include + #expression-body + + + include + #block + + + + block + + begin + \{ + beginCaptures + + 0 + + name + punctuation.curlybrace.open.cs + + + end + \} + endCaptures + + 0 + + name + punctuation.curlybrace.close.cs + + + patterns + + + include + #statement + + + + variable-initializer + + begin + (?<!=|!)(=)(?!=|>) + beginCaptures + + 1 + + name + keyword.operator.assignment.cs + + + end + (?=[,\);}]) + patterns + + + include + #expression + + + + expression-body + + begin + => + beginCaptures + + 0 + + name + keyword.operator.arrow.cs + + + end + (?=[,\);}]) + patterns + + + include + #expression + + + + goto-statement + + begin + (?<!\.)\b(goto)\b + beginCaptures + + 1 + + name + keyword.control.goto.cs + + + end + (?=;) + patterns + + + begin + \b(case)\b + beginCaptures + + 1 + + name + keyword.control.case.cs + + + end + (?=;) + patterns + + + include + #expression + + + + + match + \b(default)\b + captures + + 1 + + name + keyword.control.default.cs + + + + + name + entity.name.label.cs + match + [_[:alpha:]][_[:alnum:]]* + + + + return-statement + + begin + (?<!\.)\b(return)\b + beginCaptures + + 1 + + name + keyword.control.flow.return.cs + + + end + (?=;) + patterns + + + include + #expression + + + + break-or-continue-statement + + match + (?<!\.)\b(?:(break)|(continue))\b + captures + + 1 + + name + keyword.control.flow.break.cs + + 2 + + name + keyword.control.flow.continue.cs + + + + throw-statement + + begin + (?<!\.)\b(throw)\b + beginCaptures + + 1 + + name + keyword.control.flow.throw.cs + + + end + (?=;) + patterns + + + include + #expression + + + + yield-statement + + patterns + + + include + #yield-return-statement + + + include + #yield-break-statement + + + + yield-return-statement + + begin + (?<!\.)\b(yield)\b\s*\b(return)\b + beginCaptures + + 1 + + name + keyword.control.flow.yield.cs + + 2 + + name + keyword.control.flow.return.cs + + + end + (?=;) + patterns + + + include + #expression + + + + yield-break-statement + + match + (?<!\.)\b(yield)\b\s*\b(break)\b + captures + + 1 + + name + keyword.control.flow.yield.cs + + 2 + + name + keyword.control.flow.break.cs + + + + if-statement + + begin + (?<!\.)\b(if)\b\s*(?=\() + beginCaptures + + 1 + + name + keyword.control.conditional.if.cs + + + end + (?<=\})|(?=;) + patterns + + + begin + \( + beginCaptures + + 0 + + name + punctuation.parenthesis.open.cs + + + end + \) + endCaptures + + 0 + + name + punctuation.parenthesis.close.cs + + + patterns + + + include + #expression + + + + + include + #statement + + + + else-part + + begin + (?<!\.)\b(else)\b + beginCaptures + + 1 + + name + keyword.control.conditional.else.cs + + + end + (?<=\})|(?=;) + patterns + + + include + #statement + + + + switch-statement + + begin + (?<!\.)\b(switch)\b\s*(?=\() + beginCaptures + + 1 + + name + keyword.control.switch.cs + + + end + (?<=\}) + patterns + + + begin + \( + beginCaptures + + 0 + + name + punctuation.parenthesis.open.cs + + + end + \) + endCaptures + + 0 + + name + punctuation.parenthesis.close.cs + + + patterns + + + include + #expression + + + + + begin + \{ + beginCaptures + + 0 + + name + punctuation.curlybrace.open.cs + + + end + \} + endCaptures + + 0 + + name + punctuation.curlybrace.close.cs + + + patterns + + + include + #switch-label + + + include + #statement + + + + + + switch-label + + patterns + + + begin + (?<!\.)\b(case)\b\s+ + beginCaptures + + 1 + + name + keyword.control.case.cs + + + end + : + endCaptures + + 0 + + name + punctuation.separator.colon.cs + + + patterns + + + include + #expression + + + + + match + (?<!\.)\b(default)\b\s*(:) + captures + + 1 + + name + keyword.control.default.cs + + 2 + + name + punctuation.separator.colon.cs + + + + + + do-statement + + begin + (?<!\.)\b(do)\b + beginCaptures + + 1 + + name + keyword.control.loop.do.cs + + + end + (?=;|}) + patterns + + + include + #statement + + + + while-statement + + begin + (?<!\.)\b(while)\b\s*(?=\() + beginCaptures + + 1 + + name + keyword.control.loop.while.cs + + + end + (?<=\})|(?=;) + patterns + + + begin + \( + beginCaptures + + 0 + + name + punctuation.parenthesis.open.cs + + + end + \) + endCaptures + + 0 + + name + punctuation.parenthesis.close.cs + + + patterns + + + include + #expression + + + + + include + #statement + + + + for-statement + + begin + (?<!\.)\b(for)\b\s*(?=\() + beginCaptures + + 1 + + name + keyword.control.loop.for.cs + + + end + (?<=\})|(?=;) + patterns + + + begin + \( + beginCaptures + + 0 + + name + punctuation.parenthesis.open.cs + + + end + \) + endCaptures + + 0 + + name + punctuation.parenthesis.close.cs + + + patterns + + + include + #local-variable-declaration + + + include + #expression + + + include + #punctuation-comma + + + include + #punctuation-semicolon + + + + + include + #statement + + + + foreach-statement + + begin + (?<!\.)\b(foreach)\b\s*(?=\() + beginCaptures + + 1 + + name + keyword.control.loop.foreach.cs + + + end + (?<=\})|(?=;) + patterns + + + begin + \( + beginCaptures + + 0 + + name + punctuation.parenthesis.open.cs + + + end + \) + endCaptures + + 0 + + name + punctuation.parenthesis.close.cs + + + patterns + + + match + + (?x) + (?: + (\bvar\b)| + (?<type-name> + (?: + (?:(?<identifier>[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification + (?<name-and-type-args> # identifier + type arguments (if any) + \g<identifier>\s* + (?<type-args>\s*<(?:[^<>]|\g<type-args>)+>\s*)? + ) + (?:\s*\.\s*\g<name-and-type-args>)* # Are there any more names being dotted into? + (?:\s*\*\s*)* # pointer suffix? + (?:\s*\?\s*)? # nullable suffix? + (?:\s*\[(?:\s*,\s*)*\]\s*)* # array suffix? + )| + (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) + ) + )\s+ + (\g<identifier>)\s+ + \b(in)\b + + captures + + 1 + + name + keyword.other.var.cs + + 2 + + patterns + + + include + #type + + + + 7 + + name + entity.name.variable.local.cs + + 8 + + name + keyword.control.loop.in.cs + + + + + include + #expression + + + + + include + #statement + + + + try-statement + + patterns + + + include + #try-block + + + include + #catch-clause + + + include + #finally-clause + + + + try-block + + begin + (?<!\.)\b(try)\b + beginCaptures + + 1 + + name + keyword.control.try.cs + + + end + (?<=\}) + patterns + + + include + #block + + + + finally-clause + + begin + (?<!\.)\b(finally)\b + beginCaptures + + 1 + + name + keyword.control.try.finally.cs + + + end + (?<=\}) + patterns + + + include + #block + + + + catch-clause + + begin + (?<!\.)\b(catch)\b + beginCaptures + + 1 + + name + keyword.control.try.catch.cs + + + end + (?<=\}) + patterns + + + begin + \( + beginCaptures + + 0 + + name + punctuation.parenthesis.open.cs + + + end + \) + endCaptures + + 0 + + name + punctuation.parenthesis.close.cs + + + patterns + + + match + + (?x) + (?<type-name> + (?: + (?:(?<identifier>[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification + (?<name-and-type-args> # identifier + type arguments (if any) + \g<identifier>\s* + (?<type-args>\s*<(?:[^<>]|\g<type-args>)+>\s*)? + ) + (?:\s*\.\s*\g<name-and-type-args>)* # Are there any more names being dotted into? + (?:\s*\*\s*)* # pointer suffix? + (?:\s*\?\s*)? # nullable suffix? + (?:\s*\[(?:\s*,\s*)*\]\s*)* # array suffix? + )| + (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) + )\s* + (?:\b(\g<identifier>)\b)? + + captures + + 1 + + patterns + + + include + #type + + + + 6 + + name + entity.name.variable.local.cs + + + + + + + include + #when-clause + + + include + #block + + + + when-clause + + begin + (?<!\.)\b(when)\b\s*(\() + beginCaptures + + 1 + + name + keyword.control.try.when.cs + + 2 + + name + punctuation.parenthesis.open.cs + + + end + \) + endCaptures + + 0 + + name + punctuation.parenthesis.close.cs + + + patterns + + + include + #expression + + + + checked-unchecked-statement + + begin + (?<!\.)\b(?:(checked)|(unchecked))\b\s*(?!\() + beginCaptures + + 1 + + name + keyword.other.checked.cs + + 2 + + name + keyword.other.unchecked.cs + + + end + (?<=\}) + patterns + + + include + #block + + + + lock-statement + + begin + (?<!\.)\b(lock)\b\s*(?=\() + beginCaptures + + 1 + + name + keyword.other.lock.cs + + + end + (?<=\})|(?=;) + patterns + + + begin + \( + beginCaptures + + 0 + + name + punctuation.parenthesis.open.cs + + + end + \) + endCaptures + + 0 + + name + punctuation.parenthesis.close.cs + + + patterns + + + include + #expression + + + + + include + #statement + + + + using-statement + + begin + (?<!\.)\b(using)\b\s*(?=\() + beginCaptures + + 1 + + name + keyword.other.using.cs + + + end + (?=\;|}) + patterns + + + begin + \( + beginCaptures + + 0 + + name + punctuation.parenthesis.open.cs + + + end + \) + endCaptures + + 0 + + name + punctuation.parenthesis.close.cs + + + patterns + + + include + #local-variable-declaration + + + include + #expression + + + + + include + #statement + + + + labeled-statement + + match + ([_[:alpha:]][_[:alnum:]]*)\s*(:) + captures + + 1 + + name + entity.name.label.cs + + 2 + + name + punctuation.separator.colon.cs + + + + local-declaration + + patterns + + + include + #local-constant-declaration + + + include + #local-variable-declaration + + + + local-variable-declaration + + begin + + (?x) + (?: + (\bvar\b)| + (?<type-name> + (?: + (?:(?<identifier>[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification + (?<name-and-type-args> # identifier + type arguments (if any) + \g<identifier>\s* + (?<type-args>\s*<(?:[^<>]|\g<type-args>)+>\s*)? + ) + (?:\s*\.\s*\g<name-and-type-args>)* # Are there any more names being dotted into? + (?:\s*\*\s*)* # pointer suffix? + (?:\s*\?\s*)? # nullable suffix? + (?:\s*\[(?:\s*,\s*)*\]\s*)* # array suffix? + )| + (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) + ) + )\s+ + (\g<identifier>)\s* + (?=,|;|=|\)) + + beginCaptures + + 1 + + name + keyword.other.var.cs + + 2 + + patterns + + + include + #type + + + + 7 + + name + entity.name.variable.local.cs + + + end + (?=;|\)) + patterns + + + name + entity.name.variable.local.cs + match + [_[:alpha:]][_[:alnum:]]* + + + include + #punctuation-comma + + + include + #comment + + + include + #variable-initializer + + + + local-constant-declaration + + begin + + (?x) + (?<const-keyword>\b(?:const)\b)\s* + (?<type-name> + (?: + (?:(?<identifier>[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification + (?<name-and-type-args> # identifier + type arguments (if any) + \g<identifier>\s* + (?<type-args>\s*<(?:[^<>]|\g<type-args>)+>\s*)? + ) + (?:\s*\.\s*\g<name-and-type-args>)* # Are there any more names being dotted into? + (?:\s*\*\s*)* # pointer suffix? + (?:\s*\?\s*)? # nullable suffix? + (?:\s*\[(?:\s*,\s*)*\]\s*)* # array suffix? + )| + (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) + )\s+ + (\g<identifier>)\s* + (?=,|;|=) + + beginCaptures + + 1 + + name + storage.modifier.cs + + 2 + + patterns + + + include + #type + + + + 7 + + name + entity.name.variable.local.cs + + + end + (?=;) + patterns + + + name + entity.name.variable.local.cs + match + [_[:alpha:]][_[:alnum:]]* + + + include + #punctuation-comma + + + include + #comment + + + include + #variable-initializer + + + + checked-unchecked-expression + + begin + (?<!\.)\b(?:(checked)|(unchecked))\b\s*(\() + beginCaptures + + 1 + + name + keyword.other.checked.cs + + 2 + + name + keyword.other.unchecked.cs + + 3 + + name + punctuation.parenthesis.open.cs + + + end + \) + endCaptures + + 0 + + name + punctuation.parenthesis.close.cs + + + patterns + + + include + #expression + + + + typeof-or-default-expression + + begin + (?<!\.)\b(?:(typeof)|(default))\b\s*(\() + beginCaptures + + 1 + + name + keyword.other.typeof.cs + + 2 + + name + keyword.other.default.cs + + 3 + + name + punctuation.parenthesis.open.cs + + + end + \) + endCaptures + + 0 + + name + punctuation.parenthesis.close.cs + + + patterns + + + include + #type + + + + nameof-expression + + begin + (?<!\.)\b(nameof)\b\s*(\() + beginCaptures + + 1 + + name + keyword.other.nameof.cs + + 2 + + name + punctuation.parenthesis.open.cs + + + end + \) + endCaptures + + 0 + + name + punctuation.parenthesis.close.cs + + + patterns + + + include + #expression + + + + interpolated-string + + name + string.quoted.double.cs + begin + \$" + beginCaptures + + 0 + + name + punctuation.definition.string.begin.cs + + + end + (")|((?:[^\\\n])$) + endCaptures + + 1 + + name + punctuation.definition.string.end.cs + + 2 + + name + invalid.illegal.newline.cs + + + patterns + + + include + #string-character-escape + + + include + #interpolation + + + + verbatim-interpolated-string + + name + string.quoted.double.cs + begin + \$@" + beginCaptures + + 0 + + name + punctuation.definition.string.begin.cs + + + end + "(?=[^"]) + endCaptures + + 0 + + name + punctuation.definition.string.end.cs + + + patterns + + + include + #verbatim-string-character-escape + + + include + #interpolation + + + + interpolation + + name + meta.interpolation.cs + begin + (?<=[^\{])((?:\{\{)*)(\{)(?=[^\{]) + beginCaptures + + 1 + + name + string.quoted.double.cs + + 2 + + name + punctuation.definition.interpolation.begin.cs + + + end + \} + endCaptures + + 0 + + name + punctuation.definition.interpolation.end.cs + + + patterns + + + include + #expression + + + + literal + + patterns + + + include + #boolean-literal + + + include + #null-literal + + + include + #numeric-literal + + + include + #char-literal + + + include + #string-literal + + + include + #verbatim-string-literal + + + + boolean-literal + + patterns + + + name + constant.language.boolean.true.cs + match + (?<!\.)\btrue\b + + + name + constant.language.boolean.false.cs + match + (?<!\.)\bfalse\b + + + + null-literal + + name + constant.language.null.cs + match + (?<!\.)\bnull\b + + numeric-literal + + patterns + + + name + constant.numeric.hex.cs + match + \b0(x|X)[0-9a-fA-F_]+(U|u|L|l|UL|Ul|uL|ul|LU|Lu|lU|lu)?\b + + + name + constant.numeric.binary.cs + match + \b0(b|B)[01_]+(U|u|L|l|UL|Ul|uL|ul|LU|Lu|lU|lu)?\b + + + name + constant.numeric.decimal.cs + match + \b([0-9_]+)?\.[0-9_]+((e|E)[0-9]+)?(F|f|D|d|M|m)?\b + + + name + constant.numeric.decimal.cs + match + \b[0-9_]+(e|E)[0-9_]+(F|f|D|d|M|m)?\b + + + name + constant.numeric.decimal.cs + match + \b[0-9_]+(F|f|D|d|M|m)\b + + + name + constant.numeric.decimal.cs + match + \b[0-9_]+(U|u|L|l|UL|Ul|uL|ul|LU|Lu|lU|lu)?\b + + + + char-literal + + name + string.quoted.single.cs + begin + ' + beginCaptures + + 0 + + name + punctuation.definition.char.begin.cs + + + end + (\')|((?:[^\\\n])$) + endCaptures + + 1 + + name + punctuation.definition.char.end.cs + + 2 + + name + invalid.illegal.newline.cs + + + patterns + + + include + #string-character-escape + + + + string-literal + + name + string.quoted.double.cs + begin + (?<!@)" + beginCaptures + + 0 + + name + punctuation.definition.string.begin.cs + + + end + (")|((?:[^\\\n])$) + endCaptures + + 1 + + name + punctuation.definition.string.end.cs + + 2 + + name + invalid.illegal.newline.cs + + + patterns + + + include + #string-character-escape + + + + string-character-escape + + name + constant.character.escape.cs + match + \\. + + verbatim-string-literal + + name + string.quoted.double.cs + begin + @" + beginCaptures + + 0 + + name + punctuation.definition.string.begin.cs + + + end + "(?=[^"]) + endCaptures + + 0 + + name + punctuation.definition.string.end.cs + + + patterns + + + include + #verbatim-string-character-escape + + + + verbatim-string-character-escape + + name + constant.character.escape.cs + match + "" + + expression-operators + + patterns + + + name + keyword.operator.assignment.compound.cs + match + \*=|/=|%=|\+=|-= + + + name + keyword.operator.assignment.compound.bitwise.cs + match + \&=|\^=|<<=|>>=|\|= + + + name + keyword.operator.bitwise.shift.cs + match + <<|>> + + + name + keyword.operator.comparison.cs + match + ==|!= + + + name + keyword.operator.relational.cs + match + <=|>=|<|> + + + name + keyword.operator.logical.cs + match + \!|&&|\|\| + + + name + keyword.operator.bitwise.cs + match + \&|~|\^|\| + + + name + keyword.operator.assignment.cs + match + \= + + + name + keyword.operator.decrement.cs + match + -- + + + name + keyword.operator.increment.cs + match + \+\+ + + + name + keyword.operator.arithmetic.cs + match + %|\*|/|-|\+ + + + name + keyword.operator.null-coalescing.cs + match + \?\? + + + + conditional-operator + + begin + (?<!\?)\?(?!\?|\.|\[) + beginCaptures + + 0 + + name + keyword.operator.conditional.question-mark.cs + + + end + : + endCaptures + + 0 + + name + keyword.operator.conditional.colon.cs + + + patterns + + + include + #expression + + + + await-expression + + name + keyword.other.await.cs + match + (?!\.)\b(await)\b + + parenthesized-expression + + begin + \( + beginCaptures + + 0 + + name + punctuation.parenthesis.open.cs + + + end + \) + endCaptures + + 0 + + name + punctuation.parenthesis.close.cs + + + patterns + + + include + #expression + + + + initializer-expression + + begin + \{ + beginCaptures + + 0 + + name + punctuation.curlybrace.open.cs + + + end + \} + endCaptures + + 0 + + name + punctuation.curlybrace.close.cs + + + patterns + + + include + #expression + + + include + #punctuation-comma + + + + identifier + + name + variable.other.readwrite.cs + match + [_[:alpha:]][_[:alnum:]]* + + cast-expression + + match + + (?x) + (\()\s* + (?<type-name> + (?: + (?:(?<identifier>[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification + (?<name-and-type-args> # identifier + type arguments (if any) + \g<identifier>\s* + (?<type-args>\s*<(?:[^<>]|\g<type-args>)+>\s*)? + ) + (?:\s*\.\s*\g<name-and-type-args>)* # Are there any more names being dotted into? + (?:\s*\*\s*)* # pointer suffix? + (?:\s*\?\s*)? # nullable suffix? + (?:\s*\[(?:\s*,\s*)*\]\s*)* # array suffix? + )| + (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) + )\s* + (\))(?=\s*[_[:alnum:]\(]) + + captures + + 1 + + name + punctuation.parenthesis.open.cs + + 2 + + patterns + + + include + #type + + + + 7 + + name + punctuation.parenthesis.close.cs + + + + as-expression + + match + + (?x) + (?<!\.)\b(as)\b\s* + (?<type-name> + (?: + (?:(?<identifier>[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification + (?<name-and-type-args> # identifier + type arguments (if any) + \g<identifier>\s* + (?<type-args>\s*<(?:[^<>]|\g<type-args>)+>\s*)? + ) + (?:\s*\.\s*\g<name-and-type-args>)* # Are there any more names being dotted into? + (?:\s*\*\s*)* # pointer suffix? + (?:\s*\?\s*)? # nullable suffix? + (?:\s*\[(?:\s*,\s*)*\]\s*)* # array suffix? + )| + (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) + )? + + captures + + 1 + + name + keyword.other.as.cs + + 2 + + patterns + + + include + #type + + + + + + is-expression + + match + + (?x) + (?<!\.)\b(is)\b\s* + (?<type-name> + (?: + (?:(?<identifier>[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification + (?<name-and-type-args> # identifier + type arguments (if any) + \g<identifier>\s* + (?<type-args>\s*<(?:[^<>]|\g<type-args>)+>\s*)? + ) + (?:\s*\.\s*\g<name-and-type-args>)* # Are there any more names being dotted into? + (?:\s*\*\s*)* # pointer suffix? + (?:\s*\?\s*)? # nullable suffix? + (?:\s*\[(?:\s*,\s*)*\]\s*)* # array suffix? + )| + (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) + )? + + captures + + 1 + + name + keyword.other.is.cs + + 2 + + patterns + + + include + #type + + + + + + this-or-base-expression + + match + \b(?:(base)|(this))\b + captures + + 1 + + name + keyword.other.base.cs + + 2 + + name + keyword.other.this.cs + + + + invocation-expression + + begin + + (?x) + (?:(\?)\s*)? # preceding null-conditional operator? + (?:(\.)\s*)? # preceding dot? + ([_[:alpha:]][_[:alnum:]]*)\s* # method name + (?<type-args>\s*<([^<>]|\g<type-args>)+>\s*)?\s* # type arguments + (?=\() # open paren of argument list + + beginCaptures + + 1 + + name + keyword.operator.null-conditional.cs + + 2 + + name + punctuation.accessor.cs + + 3 + + name + entity.name.function.cs + + 4 + + patterns + + + include + #type-arguments + + + + + end + (?<=\)) + patterns + + + include + #argument-list + + + + element-access-expression + + begin + + (?x) + (?:(\?)\s*)? # preceding null-conditional operator? + (?:(\.)\s*)? # preceding dot? + ([_[:alpha:]][_[:alnum:]]*)\s* # property name + (?:(\?)\s*)? # null-conditional operator? + (?=\[) # open bracket of argument list + + beginCaptures + + 1 + + name + keyword.operator.null-conditional.cs + + 2 + + name + punctuation.accessor.cs + + 3 + + name + variable.other.object.property.cs + + 4 + + name + keyword.operator.null-conditional.cs + + + end + (?<=\]) + patterns + + + include + #bracketed-argument-list + + + + member-access-expression + + patterns + + + match + + (?x) + (?:(\?)\s*)? # preceding null-conditional operator? + (\.)\s* # preceding dot + ([_[:alpha:]][_[:alnum:]]*)\s* # property name + (?![_[:alnum:]]|\(|(\?)?\[|<) # next character is not alpha-numeric, nor a (, [, or <. Also, test for ?[ + + captures + + 1 + + name + keyword.operator.null-conditional.cs + + 2 + + name + punctuation.accessor.cs + + 3 + + name + variable.other.object.property.cs + + + + + match + + (?x) + (\.)?\s* + ([_[:alpha:]][_[:alnum:]]*) + (?<type-params>\s*<([^<>]|\g<type-params>)+>\s*) + (?= + (\s*\?)? + \s*\.\s*[_[:alpha:]][_[:alnum:]]* + ) + + captures + + 1 + + name + punctuation.accessor.cs + + 2 + + name + variable.other.object.cs + + 3 + + patterns + + + include + #type-arguments + + + + + + + match + + (?x) + ([_[:alpha:]][_[:alnum:]]*) + (?= + (\s*\?)? + \s*\.\s*[_[:alpha:]][_[:alnum:]]* + ) + + captures + + 1 + + name + variable.other.object.cs + + + + + + object-creation-expression + + patterns + + + include + #object-creation-expression-with-parameters + + + include + #object-creation-expression-with-no-parameters + + + + object-creation-expression-with-parameters + + begin + + (?x) + (new)\s+ + (?<type-name> + (?: + (?:(?<identifier>[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification + (?<name-and-type-args> # identifier + type arguments (if any) + \g<identifier>\s* + (?<type-args>\s*<(?:[^<>]|\g<type-args>)+>\s*)? + ) + (?:\s*\.\s*\g<name-and-type-args>)* # Are there any more names being dotted into? + (?:\s*\*\s*)* # pointer suffix? + (?:\s*\?\s*)? # nullable suffix? + (?:\s*\[(?:\s*,\s*)*\]\s*)* # array suffix? + )| + (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) + )\s* + (?=\() + + beginCaptures + + 1 + + name + keyword.other.new.cs + + 2 + + patterns + + + include + #type + + + + + end + (?<=\)) + patterns + + + include + #argument-list + + + + object-creation-expression-with-no-parameters + + match + + (?x) + (new)\s+ + (?<type-name> + (?: + (?:(?<identifier>[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification + (?<name-and-type-args> # identifier + type arguments (if any) + \g<identifier>\s* + (?<type-args>\s*<(?:[^<>]|\g<type-args>)+>\s*)? + ) + (?:\s*\.\s*\g<name-and-type-args>)* # Are there any more names being dotted into? + (?:\s*\*\s*)* # pointer suffix? + (?:\s*\?\s*)? # nullable suffix? + (?:\s*\[(?:\s*,\s*)*\]\s*)* # array suffix? + )| + (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) + )\s* + (?=\{|$) + + captures + + 1 + + name + keyword.other.new.cs + + 2 + + patterns + + + include + #type + + + + + + array-creation-expression + + begin + + (?x) + \b(new)\b\s* + (?<type-name> + (?: + (?:(?<identifier>[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification + (?<name-and-type-args> # identifier + type arguments (if any) + \g<identifier>\s* + (?<type-args>\s*<(?:[^<>]|\g<type-args>)+>\s*)? + ) + (?:\s*\.\s*\g<name-and-type-args>)* # Are there any more names being dotted into? + (?:\s*\*\s*)* # pointer suffix? + (?:\s*\?\s*)? # nullable suffix? + (?:\s*\[(?:\s*,\s*)*\]\s*)* # array suffix? + )| + (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) + )?\s* + (?=\[) + + beginCaptures + + 1 + + name + keyword.other.new.cs + + 2 + + patterns + + + include + #type + + + + + end + (?<=\]) + patterns + + + include + #bracketed-argument-list + + + + anonymous-object-creation-expression + + begin + \b(new)\b\s*(?=\{|$) + beginCaptures + + 1 + + name + keyword.other.new.cs + + + end + (?=;|\)) + patterns + + + include + #initializer-expression + + + + bracketed-parameter-list + + begin + (?=(\[)) + beginCaptures + + 1 + + name + punctuation.squarebracket.open.cs + + + end + (?=(\])) + endCaptures + + 1 + + name + punctuation.squarebracket.close.cs + + + patterns + + + begin + (?<=\[) + end + (?=\]) + patterns + + + include + #comment + + + include + #attribute-section + + + name + storage.modifier.cs + match + \b(ref|params|out)\b + + + match + \s+([_[:alpha:]][_[:alnum:]]*)\s*(?=[,\]]) + captures + + 1 + + name + entity.name.variable.parameter.cs + + + + + include + #variable-initializer + + + include + #type + + + include + #punctuation-comma + + + + + + parenthesized-parameter-list + + begin + (\() + beginCaptures + + 0 + + name + punctuation.parenthesis.open.cs + + + end + (\)) + endCaptures + + 0 + + name + punctuation.parenthesis.close.cs + + + patterns + + + include + #comment + + + include + #attribute-section + + + name + storage.modifier.cs + match + \b(ref|params|out|this)\b + + + match + \s+([_[:alpha:]][_[:alnum:]]*)\s*(?=[,)]) + captures + + 1 + + name + entity.name.variable.parameter.cs + + + + + include + #variable-initializer + + + include + #type + + + include + #punctuation-comma + + + + argument-list + + begin + \( + beginCaptures + + 0 + + name + punctuation.parenthesis.open.cs + + + end + \) + endCaptures + + 0 + + name + punctuation.parenthesis.close.cs + + + patterns + + + include + #named-argument + + + include + #argument + + + include + #punctuation-comma + + + + bracketed-argument-list + + begin + \[ + beginCaptures + + 0 + + name + punctuation.squarebracket.open.cs + + + end + \] + endCaptures + + 0 + + name + punctuation.squarebracket.close.cs + + + patterns + + + include + #named-argument + + + include + #argument + + + include + #punctuation-comma + + + + named-argument + + begin + ([_[:alpha:]][_[:alnum:]]*)\s*(:) + beginCaptures + + 1 + + name + entity.name.variable.parameter.cs + + 2 + + name + punctuation.separator.colon.cs + + + end + (?=(,|\)|\])) + patterns + + + include + #expression + + + + argument + + patterns + + + name + storage.modifier.cs + match + \b(ref|out)\b + + + include + #expression + + + + query-expression + + begin + + (?x) + \b(from)\b\s* + (?<type-name> + (?: + (?:(?<identifier>[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification + (?<name-and-type-args> # identifier + type arguments (if any) + \g<identifier>\s* + (?<type-args>\s*<(?:[^<>]|\g<type-args>)+>\s*)? + ) + (?:\s*\.\s*\g<name-and-type-args>)* # Are there any more names being dotted into? + (?:\s*\*\s*)* # pointer suffix? + (?:\s*\?\s*)? # nullable suffix? + (?:\s*\[(?:\s*,\s*)*\]\s*)* # array suffix? + )| + (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) + )? + \b(\g<identifier>)\b\s* + \b(in)\b\s* + + beginCaptures + + 1 + + name + keyword.query.from.cs + + 2 + + patterns + + + include + #type + + + + 7 + + name + entity.name.variable.range-variable.cs + + 8 + + name + keyword.query.in.cs + + + end + (?=;|\)) + patterns + + + include + #query-body + + + include + #expression + + + + query-body + + patterns + + + include + #let-clause + + + include + #where-clause + + + include + #join-clause + + + include + #orderby-clause + + + include + #select-clause + + + include + #group-clause + + + + let-clause + + begin + + (?x) + \b(let)\b\s* + \b([_[:alpha:]][_[:alnum:]]*)\b\s* + (=)\s* + + beginCaptures + + 1 + + name + keyword.query.let.cs + + 2 + + name + entity.name.variable.range-variable.cs + + 3 + + name + keyword.operator.assignment.cs + + + end + (?=;|\)) + patterns + + + include + #query-body + + + include + #expression + + + + where-clause + + begin + + (?x) + \b(where)\b\s* + + beginCaptures + + 1 + + name + keyword.query.where.cs + + + end + (?=;|\)) + patterns + + + include + #query-body + + + include + #expression + + + + join-clause + + begin + + (?x) + \b(join)\b\s* + (?<type-name> + (?: + (?:(?<identifier>[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification + (?<name-and-type-args> # identifier + type arguments (if any) + \g<identifier>\s* + (?<type-args>\s*<(?:[^<>]|\g<type-args>)+>\s*)? + ) + (?:\s*\.\s*\g<name-and-type-args>)* # Are there any more names being dotted into? + (?:\s*\*\s*)* # pointer suffix? + (?:\s*\?\s*)? # nullable suffix? + (?:\s*\[(?:\s*,\s*)*\]\s*)* # array suffix? + )| + (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) + )? + \b(\g<identifier>)\b\s* + \b(in)\b\s* + + beginCaptures + + 1 + + name + keyword.query.join.cs + + 2 + + patterns + + + include + #type + + + + 7 + + name + entity.name.variable.range-variable.cs + + 8 + + name + keyword.query.in.cs + + + end + (?=;|\)) + patterns + + + include + #join-on + + + include + #join-equals + + + include + #join-into + + + include + #query-body + + + include + #expression + + + + join-on + + match + \b(on)\b\s* + captures + + 1 + + name + keyword.query.on.cs + + + + join-equals + + match + \b(equals)\b\s* + captures + + 1 + + name + keyword.query.equals.cs + + + + join-into + + match + + (?x) + \b(into)\b\s* + \b([_[:alpha:]][_[:alnum:]]*)\b\s* + + captures + + 1 + + name + keyword.query.into.cs + + 2 + + name + entity.name.variable.range-variable.cs + + + + orderby-clause + + begin + \b(orderby)\b\s* + beginCaptures + + 1 + + name + keyword.query.orderby.cs + + + end + (?=;|\)) + patterns + + + include + #ordering-direction + + + include + #query-body + + + include + #expression + + + include + #punctuation-comma + + + + ordering-direction + + match + \b(?:(ascending)|(descending))\b + captures + + 1 + + name + keyword.query.ascending.cs + + 2 + + name + keyword.query.descending.cs + + + + select-clause + + begin + \b(select)\b\s* + beginCaptures + + 1 + + name + keyword.query.select.cs + + + end + (?=;|\)) + patterns + + + include + #query-body + + + include + #expression + + + + group-clause + + begin + \b(group)\b\s* + beginCaptures + + 1 + + name + keyword.query.group.cs + + + end + (?=;|\)) + patterns + + + include + #group-by + + + include + #group-into + + + include + #query-body + + + include + #expression + + + + group-by + + match + \b(by)\b\s* + captures + + 1 + + name + keyword.query.by.cs + + + + group-into + + match + + (?x) + \b(into)\b\s* + \b([_[:alpha:]][_[:alnum:]]*)\b\s* + + captures + + 1 + + name + keyword.query.into.cs + + 2 + + name + entity.name.variable.range-variable.cs + + + + anonymous-method-expression + + patterns + + + begin + + (?x) + (?:\b(async)\b\s*)? + \b([_[:alpha:]][_[:alnum:]]*)\b\s* + (=>) + + beginCaptures + + 1 + + name + storage.modifier.cs + + 2 + + name + entity.name.variable.parameter.cs + + 3 + + name + keyword.operator.arrow.cs + + + end + (?=\)|;) + patterns + + + include + #block + + + include + #expression + + + + + begin + + (?x) + (?:\b(async)\b\s*)? + (\(.*\))\s* + (=>) + + beginCaptures + + 1 + + name + storage.modifier.cs + + 2 + + patterns + + + include + #lambda-parameter-list + + + + 3 + + name + keyword.operator.arrow.cs + + + end + (?=\)|;) + patterns + + + include + #block + + + include + #expression + + + + + begin + + (?x) + (?:\b(async)\b\s*)? + (?:\b(delegate)\b\s*) + + beginCaptures + + 1 + + name + storage.modifier.cs + + 2 + + name + keyword.other.delegate.cs + + + end + (?=\)|;) + patterns + + + include + #parenthesized-parameter-list + + + include + #block + + + include + #expression + + + + + + lambda-parameter-list + + begin + (\() + beginCaptures + + 0 + + name + punctuation.parenthesis.open.cs + + + end + (\)) + endCaptures + + 0 + + name + punctuation.parenthesis.close.cs + + + patterns + + + include + #comment + + + include + #attribute-section + + + include + #lambda-parameter + + + include + #punctuation-comma + + + + lambda-parameter + + match + + (?x) + (ref|out)?\s* + (?<type-name> + (?: + (?:(?<identifier>[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification + (?<name-and-type-args> # identifier + type arguments (if any) + \g<identifier>\s* + (?<type-args>\s*<(?:[^<>]|\g<type-args>)+>\s*)? + ) + (?:\s*\.\s*\g<name-and-type-args>)* # Are there any more names being dotted into? + (?:\s*\*\s*)* # pointer suffix? + (?:\s*\?\s*)? # nullable suffix? + (?:\s*\[(?:\s*,\s*)*\]\s*)* # array suffix? + )| + (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) + )? + \b(\g<identifier>)\b\s* + (?=[,)]) + + captures + + 1 + + name + storage.modifier.cs + + 2 + + patterns + + + include + #type + + + + 7 + + name + entity.name.variable.parameter.cs + + + + type + + name + meta.type.cs + patterns + + + include + #comment + + + include + #tuple-type + + + include + #type-builtin + + + include + #type-name + + + include + #type-arguments + + + include + #type-array-suffix + + + include + #type-nullable-suffix + + + + tuple-type + + begin + \( + beginCaptures + + 0 + + name + punctuation.parenthesis.open.cs + + + end + \) + endCaptures + + 0 + + name + punctuation.parenthesis.close.cs + + + patterns + + + include + #tuple-element + + + include + #punctuation-comma + + + + tuple-element + + match + + (?x) + (?<type-name> + (?: + (?:(?<identifier>[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification + (?<name-and-type-args> # identifier + type arguments (if any) + \g<identifier>\s* + (?<type-args>\s*<(?:[^<>]|\g<type-args>)+>\s*)? + ) + (?:\s*\.\s*\g<name-and-type-args>)* # Are there any more names being dotted into? + (?:\s*\*\s*)* # pointer suffix? + (?:\s*\?\s*)? # nullable suffix? + (?:\s*\[(?:\s*,\s*)*\]\s*)* # array suffix? + )| + (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) + ) + (?:\b(?<tuple-name>\g<identifier>)\b)? + + captures + + 1 + + patterns + + + include + #type + + + + 6 + + name + entity.name.variable.tuple-element.cs + + + + type-builtin + + match + \b(bool|byte|char|decimal|double|float|int|long|object|sbyte|short|string|uint|ulong|ushort|void)\b + captures + + 1 + + name + keyword.type.cs + + + + type-name + + patterns + + + match + ([_[:alpha:]][_[:alnum:]]*)\s*(\:\:) + captures + + 1 + + name + entity.name.type.alias.cs + + 2 + + name + punctuation.separator.coloncolon.cs + + + + + match + ([_[:alpha:]][_[:alnum:]]*)\s*(\.) + captures + + 1 + + name + storage.type.cs + + 2 + + name + punctuation.accessor.cs + + + + + match + (\.)\s*([_[:alpha:]][_[:alnum:]]*) + captures + + 1 + + name + punctuation.accessor.cs + + 2 + + name + storage.type.cs + + + + + name + storage.type.cs + match + [_[:alpha:]][_[:alnum:]]* + + + + type-arguments + + begin + < + beginCaptures + + 0 + + name + punctuation.definition.typeparameters.begin.cs + + + end + > + endCaptures + + 0 + + name + punctuation.definition.typeparameters.end.cs + + + patterns + + + include + #comment + + + include + #type + + + include + #punctuation-comma + + + + type-array-suffix + + begin + \[ + beginCaptures + + 0 + + name + punctuation.squarebracket.open.cs + + + end + \] + endCaptures + + 0 + + name + punctuation.squarebracket.close.cs + + + patterns + + + include + #punctuation-comma + + + + type-nullable-suffix + + match + \? + captures + + 0 + + name + punctuation.separator.question-mark.cs + + + + operator-assignment + + name + keyword.operator.assignment.cs + match + (?<!=|!)(=)(?!=) + + punctuation-comma + + name + punctuation.separator.comma.cs + match + , + + punctuation-semicolon + + name + punctuation.terminator.statement.cs + match + ; + + punctuation-accessor + + name + punctuation.accessor.cs + match + \. + + preprocessor + + name + meta.preprocessor.cs + begin + ^\s*(\#)\s* + beginCaptures + + 1 + + name + punctuation.separator.hash.cs + + + end + (?<=$) + patterns + + + include + #comment + + + include + #preprocessor-define-or-undef + + + include + #preprocessor-if-or-elif + + + include + #preprocessor-else-or-endif + + + include + #preprocessor-warning-or-error + + + include + #preprocessor-region + + + include + #preprocessor-endregion + + + include + #preprocessor-line + + + include + #preprocessor-pragma-warning + + + include + #preprocessor-pragma-checksum + + + + preprocessor-define-or-undef + + match + \b(?:(define)|(undef))\b\s*\b([_[:alpha:]][_[:alnum:]]*)\b + captures + + 1 + + name + keyword.preprocessor.define.cs + + 2 + + name + keyword.preprocessor.undef.cs + + 3 + + name + entity.name.variable.preprocessor.symbol.cs + + + + preprocessor-if-or-elif + + begin + \b(?:(if)|(elif))\b + beginCaptures + + 1 + + name + keyword.preprocessor.if.cs + + 2 + + name + keyword.preprocessor.elif.cs + + + end + (?=$) + patterns + + + include + #comment + + + include + #preprocessor-expression + + + + preprocessor-else-or-endif + + match + \b(?:(else)|(endif))\b + captures + + 1 + + name + keyword.preprocessor.else.cs + + 2 + + name + keyword.preprocessor.endif.cs + + + + preprocessor-warning-or-error + + match + \b(?:(warning)|(error))\b\s*(.*)(?=$) + captures + + 1 + + name + keyword.preprocessor.warning.cs + + 2 + + name + keyword.preprocessor.error.cs + + 3 + + name + string.unquoted.preprocessor.message.cs + + + + preprocessor-region + + match + \b(region)\b\s*(.*)(?=$) + captures + + 1 + + name + keyword.preprocessor.region.cs + + 2 + + name + string.unquoted.preprocessor.message.cs + + + + preprocessor-endregion + + match + \b(endregion)\b + captures + + 1 + + name + keyword.preprocessor.endregion.cs + + + + preprocessor-line + + begin + \b(line)\b + beginCaptures + + 1 + + name + keyword.preprocessor.line.cs + + + end + (?=$) + patterns + + + match + \b(?:(default|hidden)) + captures + + 1 + + name + keyword.preprocessor.default.cs + + 2 + + name + keyword.preprocessor.hidden.cs + + + + + match + [0-9]+ + captures + + 0 + + name + constant.numeric.decimal.cs + + + + + match + \"[^"]*\" + captures + + 0 + + name + string.quoted.double.cs + + + + + + preprocessor-pragma-warning + + match + \b(pragma)\b\s*\b(warning)\b\s*\b(?:(disable)|(restore))\b(\s*[0-9]+(?:\s*,\s*[0-9]+)?)? + captures + + 1 + + name + keyword.preprocessor.pragma.cs + + 2 + + name + keyword.preprocessor.warning.cs + + 3 + + name + keyword.preprocessor.disable.cs + + 4 + + name + keyword.preprocessor.restore.cs + + 5 + + patterns + + + match + [0-9]+ + captures + + 0 + + name + constant.numeric.decimal.cs + + + + + include + #punctuation-comma + + + + + + preprocessor-pragma-checksum + + match + \b(pragma)\b\s*\b(checksum)\b\s*(\"[^"]*\")\s*(\"[^"]*\")\s*(\"[^"]*\") + captures + + 1 + + name + keyword.preprocessor.pragma.cs + + 2 + + name + keyword.preprocessor.checksum.cs + + 3 + + name + string.quoted.double.cs + + 4 + + name + string.quoted.double.cs + + 5 + + name + string.quoted.double.cs + + + + preprocessor-expression + + patterns + + + begin + \( + beginCaptures + + 0 + + name + punctuation.parenthesis.open.cs + + + end + \) + endCaptures + + 0 + + name + punctuation.parenthesis.close.cs + + + patterns + + + include + #preprocessor-expression + + + + + match + \b(?:(true)|(false)|([_[:alpha:]][_[:alnum:]]*))\b + captures + + 1 + + name + constant.language.boolean.true.cs + + 2 + + name + constant.language.boolean.false.cs + + 3 + + name + entity.name.variable.preprocessor.symbol.cs + + + + + match + (==|!=)|(\!|&&|\|\|) + captures + + 1 + + name + keyword.operator.comparison.cs + + 2 + + name + keyword.operator.logical.cs + + + + + + comment + + patterns + + + name + comment.block.cs + begin + /\* + beginCaptures + + 0 + + name + punctuation.definition.comment.cs + + + end + \*/ + endCaptures + + 0 + + name + punctuation.definition.comment.cs + + + + + begin + (^\s+)?(?=//) + beginCaptures + + 1 + + name + punctuation.whitespace.comment.leading.cs + + + end + (?=$) + patterns + + + name + comment.block.documentation.cs + begin + (?<!/)///(?!/) + beginCaptures + + 0 + + name + punctuation.definition.comment.cs + + + end + (?=$) + patterns + + + include + #xml-doc-comment + + + + + name + comment.line.double-slash.cs + begin + (?<!/)//(?!/) + beginCaptures + + 0 + + name + punctuation.definition.comment.cs + + + end + (?=$) + + + + + + xml-doc-comment + + patterns + + + include + #xml-comment + + + include + #xml-character-entity + + + include + #xml-cdata + + + include + #xml-tag + + + + xml-tag + + name + meta.tag.cs + begin + + (?x) + (</?) + ( + (?: + ([-_[:alnum:]]+) + (:) + )? + ([-_[:alnum:]]+) + ) + + beginCaptures + + 1 + + name + punctuation.definition.tag.cs + + 2 + + name + entity.name.tag.cs + + 3 + + name + entity.name.tag.namespace.cs + + 4 + + name + punctuation.separator.colon.cs + + 5 + + name + entity.name.tag.localname.cs + + + end + (/?>) + endCaptures + + 1 + + name + punctuation.definition.tag.cs + + + patterns + + + include + #xml-attribute + + + + xml-attribute + + patterns + + + match + + (?x) + (?:^|\s+) + ( + (?: + ([-_[:alnum:]]+) + (:) + )? + ([-_[:alnum:]]+) + ) + (=) + + captures + + 1 + + name + entity.other.attribute-name.cs + + 2 + + name + entity.other.attribute-name.namespace.cs + + 3 + + name + punctuation.separator.colon.cs + + 4 + + name + entity.other.attribute-name.localname.cs + + 5 + + name + punctuation.separator.equals.cs + + + + + include + #xml-string + + + + xml-cdata + + name + string.unquoted.cdata.cs + begin + <!\[CDATA\[ + beginCaptures + + 0 + + name + punctuation.definition.string.begin.cs + + + end + \]\]> + endCaptures + + 0 + + name + punctuation.definition.string.end.cs + + + + xml-string + + patterns + + + name + string.quoted.single.cs + begin + \' + beginCaptures + + 0 + + name + punctuation.definition.string.begin.cs + + + end + \' + endCaptures + + 0 + + name + punctuation.definition.string.end.cs + + + patterns + + + include + #xml-character-entity + + + + + name + string.quoted.double.cs + begin + \" + beginCaptures + + 0 + + name + punctuation.definition.string.begin.cs + + + end + \" + endCaptures + + 0 + + name + punctuation.definition.string.end.cs + + + patterns + + + include + #xml-character-entity + + + + + + xml-character-entity + + patterns + + + name + constant.character.entity.cs + match + + (?x) + (&) + ( + (?:[[:alpha:]:_][[:alnum:]:_.-]*)| + (?:\#[[:digit:]]+)| + (?:\#x[[:xdigit:]]+) + ) + (;) + + captures + + 1 + + name + punctuation.definition.constant.cs + + 3 + + name + punctuation.definition.constant.cs + + + + + name + invalid.illegal.bad-ampersand.cs + match + & + + + + xml-comment + + name + comment.block.cs + begin + <!-- + beginCaptures + + 0 + + name + punctuation.definition.comment.cs + + + end + --> + endCaptures + + 0 + + name + punctuation.definition.comment.cs + + + + + + \ No newline at end of file diff --git a/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/Resources/csharp.tmLanguage.json b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/Resources/csharp.tmLanguage.json new file mode 100644 index 0000000000..d151ec8a6d --- /dev/null +++ b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/Resources/csharp.tmLanguage.json @@ -0,0 +1,4319 @@ +{ + "information_for_contributors": [ + "This file has been converted from https://github.com/dotnet/csharp-tmLanguage/blob/master/grammars/csharp.tmLanguage", + "If you want to provide a fix or improvement, please create a pull request against the original repository.", + "Once accepted there, we are happy to receive an update request." + ], + "version": "https://github.com/dotnet/csharp-tmLanguage/commit/7689494edad006eafb9025aa6d72f8a634011a00", + "name": "C#", + "scopeName": "source.cs", + "patterns": [ + { + "include": "#preprocessor" + }, + { + "include": "#comment" + }, + { + "include": "#directives" + }, + { + "include": "#declarations" + }, + { + "include": "#script-top-level" + } + ], + "repository": { + "directives": { + "patterns": [ + { + "include": "#extern-alias-directive" + }, + { + "include": "#using-directive" + }, + { + "include": "#attribute-section" + }, + { + "include": "#punctuation-semicolon" + } + ] + }, + "declarations": { + "patterns": [ + { + "include": "#namespace-declaration" + }, + { + "include": "#type-declarations" + }, + { + "include": "#punctuation-semicolon" + } + ] + }, + "script-top-level": { + "patterns": [ + { + "include": "#method-declaration" + }, + { + "include": "#statement" + }, + { + "include": "#punctuation-semicolon" + } + ] + }, + "type-declarations": { + "patterns": [ + { + "include": "#preprocessor" + }, + { + "include": "#comment" + }, + { + "include": "#storage-modifier" + }, + { + "include": "#class-declaration" + }, + { + "include": "#delegate-declaration" + }, + { + "include": "#enum-declaration" + }, + { + "include": "#interface-declaration" + }, + { + "include": "#struct-declaration" + }, + { + "include": "#attribute-section" + }, + { + "include": "#punctuation-semicolon" + } + ] + }, + "class-or-struct-members": { + "patterns": [ + { + "include": "#preprocessor" + }, + { + "include": "#comment" + }, + { + "include": "#storage-modifier" + }, + { + "include": "#type-declarations" + }, + { + "include": "#field-declaration" + }, + { + "include": "#event-declaration" + }, + { + "include": "#property-declaration" + }, + { + "include": "#indexer-declaration" + }, + { + "include": "#variable-initializer" + }, + { + "include": "#constructor-declaration" + }, + { + "include": "#destructor-declaration" + }, + { + "include": "#operator-declaration" + }, + { + "include": "#conversion-operator-declaration" + }, + { + "include": "#method-declaration" + }, + { + "include": "#attribute-section" + }, + { + "include": "#punctuation-semicolon" + } + ] + }, + "interface-members": { + "patterns": [ + { + "include": "#preprocessor" + }, + { + "include": "#comment" + }, + { + "include": "#event-declaration" + }, + { + "include": "#property-declaration" + }, + { + "include": "#indexer-declaration" + }, + { + "include": "#method-declaration" + }, + { + "include": "#attribute-section" + }, + { + "include": "#punctuation-semicolon" + } + ] + }, + "statement": { + "patterns": [ + { + "include": "#preprocessor" + }, + { + "include": "#comment" + }, + { + "include": "#while-statement" + }, + { + "include": "#do-statement" + }, + { + "include": "#for-statement" + }, + { + "include": "#foreach-statement" + }, + { + "include": "#if-statement" + }, + { + "include": "#else-part" + }, + { + "include": "#switch-statement" + }, + { + "include": "#goto-statement" + }, + { + "include": "#return-statement" + }, + { + "include": "#break-or-continue-statement" + }, + { + "include": "#throw-statement" + }, + { + "include": "#yield-statement" + }, + { + "include": "#await-statement" + }, + { + "include": "#try-statement" + }, + { + "include": "#checked-unchecked-statement" + }, + { + "include": "#lock-statement" + }, + { + "include": "#using-statement" + }, + { + "include": "#labeled-statement" + }, + { + "include": "#local-declaration" + }, + { + "include": "#block" + }, + { + "include": "#expression" + }, + { + "include": "#punctuation-semicolon" + } + ] + }, + "expression": { + "patterns": [ + { + "include": "#preprocessor" + }, + { + "include": "#comment" + }, + { + "include": "#checked-unchecked-expression" + }, + { + "include": "#typeof-or-default-expression" + }, + { + "include": "#nameof-expression" + }, + { + "include": "#throw-expression" + }, + { + "include": "#interpolated-string" + }, + { + "include": "#verbatim-interpolated-string" + }, + { + "include": "#this-or-base-expression" + }, + { + "include": "#conditional-operator" + }, + { + "include": "#expression-operators" + }, + { + "include": "#await-expression" + }, + { + "include": "#query-expression" + }, + { + "include": "#as-expression" + }, + { + "include": "#is-expression" + }, + { + "include": "#anonymous-method-expression" + }, + { + "include": "#object-creation-expression" + }, + { + "include": "#array-creation-expression" + }, + { + "include": "#anonymous-object-creation-expression" + }, + { + "include": "#invocation-expression" + }, + { + "include": "#member-access-expression" + }, + { + "include": "#element-access-expression" + }, + { + "include": "#cast-expression" + }, + { + "include": "#literal" + }, + { + "include": "#parenthesized-expression" + }, + { + "include": "#tuple-deconstruction-assignment" + }, + { + "include": "#initializer-expression" + }, + { + "include": "#identifier" + } + ] + }, + "extern-alias-directive": { + "begin": "\\s*(extern)\\b\\s*(alias)\\b\\s*([_[:alpha:]][_[:alnum:]]*)", + "beginCaptures": { + "1": { + "name": "keyword.other.extern.cs" + }, + "2": { + "name": "keyword.other.alias.cs" + }, + "3": { + "name": "variable.other.alias.cs" + } + }, + "end": "(?=;)" + }, + "using-directive": { + "patterns": [ + { + "begin": "\\b(using)\\b\\s+(static)\\s+", + "beginCaptures": { + "1": { + "name": "keyword.other.using.cs" + }, + "2": { + "name": "keyword.other.static.cs" + } + }, + "end": "(?=;)", + "patterns": [ + { + "include": "#type" + } + ] + }, + { + "begin": "\\b(using)\\s+(?=([_[:alpha:]][_[:alnum:]]*)\\s*=)", + "beginCaptures": { + "1": { + "name": "keyword.other.using.cs" + }, + "2": { + "name": "entity.name.type.alias.cs" + } + }, + "end": "(?=;)", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#type" + }, + { + "include": "#operator-assignment" + } + ] + }, + { + "begin": "\\b(using)\\s*", + "beginCaptures": { + "1": { + "name": "keyword.other.using.cs" + } + }, + "end": "(?=;)", + "patterns": [ + { + "include": "#comment" + }, + { + "name": "entity.name.type.namespace.cs", + "match": "[_[:alpha:]][_[:alnum:]]*" + }, + { + "include": "#operator-assignment" + } + ] + } + ] + }, + "attribute-section": { + "begin": "(\\[)(assembly|module|field|event|method|param|property|return|type)?(\\:)?", + "beginCaptures": { + "1": { + "name": "punctuation.squarebracket.open.cs" + }, + "2": { + "name": "keyword.other.attribute-specifier.cs" + }, + "3": { + "name": "punctuation.separator.colon.cs" + } + }, + "end": "(\\])", + "endCaptures": { + "1": { + "name": "punctuation.squarebracket.close.cs" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#attribute" + }, + { + "include": "#punctuation-comma" + } + ] + }, + "attribute": { + "patterns": [ + { + "include": "#type-name" + }, + { + "include": "#attribute-arguments" + } + ] + }, + "attribute-arguments": { + "begin": "(\\()", + "beginCaptures": { + "1": { + "name": "punctuation.parenthesis.open.cs" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.parenthesis.close.cs" + } + }, + "patterns": [ + { + "include": "#attribute-named-argument" + }, + { + "include": "#expression" + }, + { + "include": "#punctuation-comma" + } + ] + }, + "attribute-named-argument": { + "begin": "([_[:alpha:]][_[:alnum:]]*)\\s*(?==)", + "beginCaptures": { + "1": { + "name": "entity.name.variable.property.cs" + } + }, + "end": "(?=(,|\\)))", + "patterns": [ + { + "include": "#operator-assignment" + }, + { + "include": "#expression" + } + ] + }, + "namespace-declaration": { + "begin": "\\b(namespace)\\s+", + "beginCaptures": { + "1": { + "name": "keyword.other.namespace.cs" + } + }, + "end": "(?<=\\})", + "patterns": [ + { + "include": "#comment" + }, + { + "name": "entity.name.type.namespace.cs", + "match": "[_[:alpha:]][_[:alnum:]]*" + }, + { + "include": "#punctuation-accessor" + }, + { + "begin": "\\{", + "beginCaptures": { + "0": { + "name": "punctuation.curlybrace.open.cs" + } + }, + "end": "\\}", + "endCaptures": { + "0": { + "name": "punctuation.curlybrace.close.cs" + } + }, + "patterns": [ + { + "include": "#declarations" + }, + { + "include": "#using-directive" + }, + { + "include": "#punctuation-semicolon" + } + ] + } + ] + }, + "storage-modifier": { + "name": "storage.modifier.cs", + "match": "(?\n (?:\n (?:ref\\s+)? # ref return\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n)\\s+\n(\\g)\\s*\n(<([^<>]+)>)?\\s*\n(?=\\()", + "beginCaptures": { + "1": { + "name": "keyword.other.delegate.cs" + }, + "2": { + "patterns": [ + { + "include": "#type" + } + ] + }, + "7": { + "name": "entity.name.type.delegate.cs" + }, + "8": { + "patterns": [ + { + "include": "#type-parameter-list" + } + ] + } + }, + "end": "(?=;)", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#parenthesized-parameter-list" + }, + { + "include": "#generic-constraints" + } + ] + }, + "enum-declaration": { + "begin": "(?=\\benum\\b)", + "end": "(?<=\\})", + "patterns": [ + { + "begin": "(?=enum)", + "end": "(?=\\{)", + "patterns": [ + { + "include": "#comment" + }, + { + "match": "(enum)\\s+([_[:alpha:]][_[:alnum:]]*)", + "captures": { + "1": { + "name": "keyword.other.enum.cs" + }, + "2": { + "name": "entity.name.type.enum.cs" + } + } + }, + { + "begin": ":", + "beginCaptures": { + "0": { + "name": "punctuation.separator.colon.cs" + } + }, + "end": "(?=\\{)", + "patterns": [ + { + "include": "#type" + } + ] + } + ] + }, + { + "begin": "\\{", + "beginCaptures": { + "0": { + "name": "punctuation.curlybrace.open.cs" + } + }, + "end": "\\}", + "endCaptures": { + "0": { + "name": "punctuation.curlybrace.close.cs" + } + }, + "patterns": [ + { + "include": "#preprocessor" + }, + { + "include": "#comment" + }, + { + "include": "#attribute-section" + }, + { + "include": "#punctuation-comma" + }, + { + "begin": "[_[:alpha:]][_[:alnum:]]*", + "beginCaptures": { + "0": { + "name": "entity.name.variable.enum-member.cs" + } + }, + "end": "(?=(,|\\}))", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#variable-initializer" + } + ] + } + ] + }, + { + "include": "#preprocessor" + }, + { + "include": "#comment" + } + ] + }, + "interface-declaration": { + "begin": "(?=\\binterface\\b)", + "end": "(?<=\\})", + "patterns": [ + { + "begin": "(?x)\n(interface)\\b\\s+\n([_[:alpha:]][_[:alnum:]]*)", + "beginCaptures": { + "1": { + "name": "keyword.other.interface.cs" + }, + "2": { + "name": "entity.name.type.interface.cs" + } + }, + "end": "(?=\\{)", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#type-parameter-list" + }, + { + "include": "#base-types" + }, + { + "include": "#generic-constraints" + } + ] + }, + { + "begin": "\\{", + "beginCaptures": { + "0": { + "name": "punctuation.curlybrace.open.cs" + } + }, + "end": "\\}", + "endCaptures": { + "0": { + "name": "punctuation.curlybrace.close.cs" + } + }, + "patterns": [ + { + "include": "#interface-members" + } + ] + }, + { + "include": "#preprocessor" + }, + { + "include": "#comment" + } + ] + }, + "struct-declaration": { + "begin": "(?=\\bstruct\\b)", + "end": "(?<=\\})", + "patterns": [ + { + "begin": "(?x)\n(struct)\\b\\s+\n([_[:alpha:]][_[:alnum:]]*)", + "beginCaptures": { + "1": { + "name": "keyword.other.struct.cs" + }, + "2": { + "name": "entity.name.type.struct.cs" + } + }, + "end": "(?=\\{)", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#type-parameter-list" + }, + { + "include": "#base-types" + }, + { + "include": "#generic-constraints" + } + ] + }, + { + "begin": "\\{", + "beginCaptures": { + "0": { + "name": "punctuation.curlybrace.open.cs" + } + }, + "end": "\\}", + "endCaptures": { + "0": { + "name": "punctuation.curlybrace.close.cs" + } + }, + "patterns": [ + { + "include": "#class-or-struct-members" + } + ] + }, + { + "include": "#preprocessor" + }, + { + "include": "#comment" + } + ] + }, + "type-parameter-list": { + "begin": "\\<", + "beginCaptures": { + "0": { + "name": "punctuation.definition.typeparameters.begin.cs" + } + }, + "end": "\\>", + "endCaptures": { + "0": { + "name": "punctuation.definition.typeparameters.end.cs" + } + }, + "patterns": [ + { + "match": "\\b(in|out)\\b", + "captures": { + "1": { + "name": "storage.modifier.cs" + } + } + }, + { + "match": "\\b([_[:alpha:]][_[:alnum:]]*)\\b", + "captures": { + "1": { + "name": "entity.name.type.type-parameter.cs" + } + } + }, + { + "include": "#comment" + }, + { + "include": "#punctuation-comma" + }, + { + "include": "#attribute-section" + } + ] + }, + "base-types": { + "begin": ":", + "beginCaptures": { + "0": { + "name": "punctuation.separator.colon.cs" + } + }, + "end": "(?=\\{|where)", + "patterns": [ + { + "include": "#type" + }, + { + "include": "#punctuation-comma" + }, + { + "include": "#preprocessor" + } + ] + }, + "generic-constraints": { + "begin": "(where)\\s+([_[:alpha:]][_[:alnum:]]*)\\s*(:)", + "beginCaptures": { + "1": { + "name": "keyword.other.where.cs" + }, + "2": { + "name": "storage.type.cs" + }, + "3": { + "name": "punctuation.separator.colon.cs" + } + }, + "end": "(?=\\{|where|;|=>)", + "patterns": [ + { + "name": "keyword.other.class.cs", + "match": "\\bclass\\b" + }, + { + "name": "keyword.other.struct.cs", + "match": "\\bstruct\\b" + }, + { + "match": "(new)\\s*(\\()\\s*(\\))", + "captures": { + "1": { + "name": "keyword.other.new.cs" + }, + "2": { + "name": "punctuation.parenthesis.open.cs" + }, + "3": { + "name": "punctuation.parenthesis.close.cs" + } + } + }, + { + "include": "#type" + }, + { + "include": "#punctuation-comma" + }, + { + "include": "#generic-constraints" + } + ] + }, + "field-declaration": { + "begin": "(?x)\n(?\n (?:\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n)\\s+\n(\\g)\\s* # first field name\n(?!=>|==)(?=,|;|=|$)", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#type" + } + ] + }, + "6": { + "name": "entity.name.variable.field.cs" + } + }, + "end": "(?=;)", + "patterns": [ + { + "name": "entity.name.variable.field.cs", + "match": "[_[:alpha:]][_[:alnum:]]*" + }, + { + "include": "#punctuation-comma" + }, + { + "include": "#comment" + }, + { + "include": "#variable-initializer" + }, + { + "include": "#class-or-struct-members" + } + ] + }, + "property-declaration": { + "begin": "(?x)\n(?!.*\\b(?:class|interface|struct|enum|event)\\b)\\s*\n(?\n (?\n (?:\n (?:ref\\s+)? # ref return\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n )\\s+\n)\n(?\\g\\s*\\.\\s*)?\n(?\\g)\\s*\n(?=\\{|=>|$)", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#type" + } + ] + }, + "7": { + "patterns": [ + { + "include": "#type" + }, + { + "include": "#punctuation-accessor" + } + ] + }, + "8": { + "name": "entity.name.variable.property.cs" + } + }, + "end": "(?<=\\})|(?=;)", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#property-accessors" + }, + { + "include": "#expression-body" + }, + { + "include": "#variable-initializer" + }, + { + "include": "#class-or-struct-members" + } + ] + }, + "indexer-declaration": { + "begin": "(?x)\n(?\n (?\n (?:\n (?:ref\\s+)? # ref return\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n )\\s+\n)\n(?\\g\\s*\\.\\s*)?\n(?this)\\s*\n(?=\\[)", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#type" + } + ] + }, + "7": { + "patterns": [ + { + "include": "#type" + }, + { + "include": "#punctuation-accessor" + } + ] + }, + "8": { + "name": "keyword.other.this.cs" + } + }, + "end": "(?<=\\})|(?=;)", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#bracketed-parameter-list" + }, + { + "include": "#property-accessors" + }, + { + "include": "#expression-body" + }, + { + "include": "#variable-initializer" + } + ] + }, + "event-declaration": { + "begin": "(?x)\n\\b(event)\\b\\s*\n(?\n (?\n (?:\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n )\\s+\n)\n(?\\g\\s*\\.\\s*)?\n(?\\g(?:\\s*,\\s*\\g)*)\\s*\n(?=\\{|;|$)", + "beginCaptures": { + "1": { + "name": "keyword.other.event.cs" + }, + "2": { + "patterns": [ + { + "include": "#type" + } + ] + }, + "8": { + "patterns": [ + { + "include": "#type" + }, + { + "include": "#punctuation-accessor" + } + ] + }, + "9": { + "patterns": [ + { + "name": "entity.name.variable.event.cs", + "match": "[_[:alpha:]][_[:alnum:]]*" + }, + { + "include": "#punctuation-comma" + } + ] + } + }, + "end": "(?<=\\})|(?=;)", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#event-accessors" + }, + { + "include": "#punctuation-comma" + } + ] + }, + "property-accessors": { + "begin": "\\{", + "beginCaptures": { + "0": { + "name": "punctuation.curlybrace.open.cs" + } + }, + "end": "\\}", + "endCaptures": { + "0": { + "name": "punctuation.curlybrace.close.cs" + } + }, + "patterns": [ + { + "name": "storage.modifier.cs", + "match": "\\b(private|protected|internal)\\b" + }, + { + "name": "keyword.other.get.cs", + "match": "\\b(get)\\b" + }, + { + "name": "keyword.other.set.cs", + "match": "\\b(set)\\b" + }, + { + "include": "#comment" + }, + { + "include": "#attribute-section" + }, + { + "include": "#expression-body" + }, + { + "include": "#block" + }, + { + "include": "#punctuation-semicolon" + } + ] + }, + "event-accessors": { + "begin": "\\{", + "beginCaptures": { + "0": { + "name": "punctuation.curlybrace.open.cs" + } + }, + "end": "\\}", + "endCaptures": { + "0": { + "name": "punctuation.curlybrace.close.cs" + } + }, + "patterns": [ + { + "name": "keyword.other.add.cs", + "match": "\\b(add)\\b" + }, + { + "name": "keyword.other.remove.cs", + "match": "\\b(remove)\\b" + }, + { + "include": "#comment" + }, + { + "include": "#attribute-section" + }, + { + "include": "#expression-body" + }, + { + "include": "#block" + }, + { + "include": "#punctuation-semicolon" + } + ] + }, + "method-declaration": { + "begin": "(?x)\n(?\n (?\n (?:\n (?:ref\\s+)? # ref return\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n )\\s+\n)\n(?\\g\\s*\\.\\s*)?\n(\\g)\\s*\n(<([^<>]+)>)?\\s*\n(?=\\()", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#type" + } + ] + }, + "7": { + "patterns": [ + { + "include": "#type" + }, + { + "include": "#punctuation-accessor" + } + ] + }, + "8": { + "name": "entity.name.function.cs" + }, + "9": { + "patterns": [ + { + "include": "#type-parameter-list" + } + ] + } + }, + "end": "(?<=\\})|(?=;)", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#parenthesized-parameter-list" + }, + { + "include": "#generic-constraints" + }, + { + "include": "#expression-body" + }, + { + "include": "#block" + } + ] + }, + "constructor-declaration": { + "begin": "(?=[_[:alpha:]][_[:alnum:]]*\\s*\\()", + "end": "(?<=\\})|(?=;)", + "patterns": [ + { + "match": "\\b([_[:alpha:]][_[:alnum:]]*)\\b", + "captures": { + "1": { + "name": "entity.name.function.cs" + } + } + }, + { + "begin": "(:)", + "beginCaptures": { + "1": { + "name": "punctuation.separator.colon.cs" + } + }, + "end": "(?=\\{|=>)", + "patterns": [ + { + "include": "#constructor-initializer" + } + ] + }, + { + "include": "#parenthesized-parameter-list" + }, + { + "include": "#preprocessor" + }, + { + "include": "#comment" + }, + { + "include": "#expression-body" + }, + { + "include": "#block" + } + ] + }, + "constructor-initializer": { + "begin": "\\b(?:(base)|(this))\\b\\s*(?=\\()", + "beginCaptures": { + "1": { + "name": "keyword.other.base.cs" + }, + "2": { + "name": "keyword.other.this.cs" + } + }, + "end": "(?<=\\))", + "patterns": [ + { + "include": "#argument-list" + } + ] + }, + "destructor-declaration": { + "begin": "(~)([_[:alpha:]][_[:alnum:]]*)\\s*(?=\\()", + "beginCaptures": { + "1": { + "name": "punctuation.tilde.cs" + }, + "2": { + "name": "entity.name.function.cs" + } + }, + "end": "(?<=\\})|(?=;)", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#parenthesized-parameter-list" + }, + { + "include": "#expression-body" + }, + { + "include": "#block" + } + ] + }, + "operator-declaration": { + "begin": "(?x)\n(?\n (?:\n (?:ref\\s+)? # ref return\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n)\\s*\n(?(?:\\b(?:operator)))\\s*\n(?(?:\\+|-|\\*|/|%|&|\\||\\^|\\<\\<|\\>\\>|==|!=|\\>|\\<|\\>=|\\<=|!|~|\\+\\+|--|true|false))\\s*\n(?=\\()", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#type" + } + ] + }, + "6": { + "name": "keyword.other.operator-decl.cs" + }, + "7": { + "name": "entity.name.function.cs" + } + }, + "end": "(?<=\\})|(?=;)", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#parenthesized-parameter-list" + }, + { + "include": "#expression-body" + }, + { + "include": "#block" + } + ] + }, + "conversion-operator-declaration": { + "begin": "(?x)\n(?(?:\\b(?:explicit|implicit)))\\s*\n(?(?:\\b(?:operator)))\\s*\n(?\n (?:\n (?:ref\\s+)? # ref return\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n)\\s*\n(?=\\()", + "beginCaptures": { + "1": { + "patterns": [ + { + "match": "\\b(explicit)\\b", + "captures": { + "1": { + "name": "keyword.other.explicit.cs" + } + } + }, + { + "match": "\\b(implicit)\\b", + "captures": { + "1": { + "name": "keyword.other.implicit.cs" + } + } + } + ] + }, + "2": { + "name": "keyword.other.operator-decl.cs" + }, + "3": { + "patterns": [ + { + "include": "#type" + } + ] + } + }, + "end": "(?<=\\})|(?=;)", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#parenthesized-parameter-list" + }, + { + "include": "#expression-body" + }, + { + "include": "#block" + } + ] + }, + "block": { + "begin": "\\{", + "beginCaptures": { + "0": { + "name": "punctuation.curlybrace.open.cs" + } + }, + "end": "\\}", + "endCaptures": { + "0": { + "name": "punctuation.curlybrace.close.cs" + } + }, + "patterns": [ + { + "include": "#statement" + } + ] + }, + "variable-initializer": { + "begin": "(?)", + "beginCaptures": { + "1": { + "name": "keyword.operator.assignment.cs" + } + }, + "end": "(?=[,\\)\\];}])", + "patterns": [ + { + "include": "#ref-modifier" + }, + { + "include": "#expression" + } + ] + }, + "expression-body": { + "begin": "=>", + "beginCaptures": { + "0": { + "name": "keyword.operator.arrow.cs" + } + }, + "end": "(?=[,\\);}])", + "patterns": [ + { + "include": "#ref-modifier" + }, + { + "include": "#expression" + } + ] + }, + "goto-statement": { + "begin": "(?\n (?:\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n )\n)\\s+\n(\\g)\\s+\n\\b(in)\\b", + "captures": { + "1": { + "name": "keyword.other.var.cs" + }, + "2": { + "patterns": [ + { + "include": "#type" + } + ] + }, + "7": { + "name": "entity.name.variable.local.cs" + }, + "8": { + "name": "keyword.control.loop.in.cs" + } + } + }, + { + "match": "(?x) # match foreach (var (x, y) in ...)\n(?:\\b(var)\\b\\s*)?\n(?\\((?:[^\\(\\)]|\\g)+\\))\\s+\n\\b(in)\\b", + "captures": { + "1": { + "name": "keyword.other.var.cs" + }, + "2": { + "patterns": [ + { + "include": "#tuple-declaration-deconstruction-element-list" + } + ] + }, + "3": { + "name": "keyword.control.loop.in.cs" + } + } + }, + { + "include": "#expression" + } + ] + }, + { + "include": "#statement" + } + ] + }, + "try-statement": { + "patterns": [ + { + "include": "#try-block" + }, + { + "include": "#catch-clause" + }, + { + "include": "#finally-clause" + } + ] + }, + "try-block": { + "begin": "(?\n (?:\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n)\\s*\n(?:\\b(\\g)\\b)?", + "captures": { + "1": { + "patterns": [ + { + "include": "#type" + } + ] + }, + "6": { + "name": "entity.name.variable.local.cs" + } + } + } + ] + }, + { + "include": "#when-clause" + }, + { + "include": "#comment" + }, + { + "include": "#block" + } + ] + }, + "when-clause": { + "begin": "(?\n (?:\n (?:ref\\s+)? # ref local\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n )\n)\\s+\n(\\g)\\s*\n(?=,|;|=|\\))", + "beginCaptures": { + "1": { + "name": "storage.modifier.cs" + }, + "2": { + "name": "keyword.other.var.cs" + }, + "3": { + "patterns": [ + { + "include": "#type" + } + ] + }, + "8": { + "name": "entity.name.variable.local.cs" + } + }, + "end": "(?=;|\\))", + "patterns": [ + { + "name": "entity.name.variable.local.cs", + "match": "[_[:alpha:]][_[:alnum:]]*" + }, + { + "include": "#punctuation-comma" + }, + { + "include": "#comment" + }, + { + "include": "#variable-initializer" + } + ] + }, + "local-constant-declaration": { + "begin": "(?x)\n(?\\b(?:const)\\b)\\s*\n(?\n (?:\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n)\\s+\n(\\g)\\s*\n(?=,|;|=)", + "beginCaptures": { + "1": { + "name": "storage.modifier.cs" + }, + "2": { + "patterns": [ + { + "include": "#type" + } + ] + }, + "7": { + "name": "entity.name.variable.local.cs" + } + }, + "end": "(?=;)", + "patterns": [ + { + "name": "entity.name.variable.local.cs", + "match": "[_[:alpha:]][_[:alnum:]]*" + }, + { + "include": "#punctuation-comma" + }, + { + "include": "#comment" + }, + { + "include": "#variable-initializer" + } + ] + }, + "local-tuple-var-deconstruction": { + "begin": "(?x) # e.g. var (x, y) = GetPoint();\n(?:\\b(var)\\b\\s*)\n(?\\((?:[^\\(\\)]|\\g)+\\))\\s*\n(?=;|=|\\))", + "beginCaptures": { + "1": { + "name": "keyword.other.var.cs" + }, + "2": { + "patterns": [ + { + "include": "#tuple-declaration-deconstruction-element-list" + } + ] + } + }, + "end": "(?=;|\\))", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#variable-initializer" + } + ] + }, + "tuple-deconstruction-assignment": { + "match": "(?x)\n(?\\s*\\((?:[^\\(\\)]|\\g)+\\))\\s*\n(?!=>|==)(?==)", + "captures": { + "1": { + "patterns": [ + { + "include": "#tuple-deconstruction-element-list" + } + ] + } + } + }, + "tuple-declaration-deconstruction-element-list": { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.parenthesis.open.cs" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.parenthesis.close.cs" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#tuple-declaration-deconstruction-element-list" + }, + { + "include": "#declaration-expression-tuple" + }, + { + "include": "#punctuation-comma" + }, + { + "match": "(?x) # e.g. x\n\\b([_[:alpha:]][_[:alnum:]]*)\\b\\s*\n(?=[,)])", + "captures": { + "1": { + "name": "entity.name.variable.tuple-element.cs" + } + } + } + ] + }, + "tuple-deconstruction-element-list": { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.parenthesis.open.cs" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.parenthesis.close.cs" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#tuple-deconstruction-element-list" + }, + { + "include": "#declaration-expression-tuple" + }, + { + "include": "#punctuation-comma" + }, + { + "match": "(?x) # e.g. x\n\\b([_[:alpha:]][_[:alnum:]]*)\\b\\s*\n(?=[,)])", + "captures": { + "1": { + "name": "variable.other.readwrite.cs" + } + } + } + ] + }, + "declaration-expression-local": { + "match": "(?x) # e.g. int x OR var x\n(?:\n \\b(var)\\b|\n (?\n (?:\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n )\n)\\s+\n\\b(\\g)\\b\\s*\n(?=[,)\\]])", + "captures": { + "1": { + "name": "keyword.other.var.cs" + }, + "2": { + "patterns": [ + { + "include": "#type" + } + ] + }, + "7": { + "name": "entity.name.variable.local.cs" + } + } + }, + "declaration-expression-tuple": { + "match": "(?x) # e.g. int x OR var x\n(?:\n \\b(var)\\b|\n (?\n (?:\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n )\n)\\s+\n\\b(\\g)\\b\\s*\n(?=[,)])", + "captures": { + "1": { + "name": "keyword.other.var.cs" + }, + "2": { + "patterns": [ + { + "include": "#type" + } + ] + }, + "7": { + "name": "entity.name.variable.tuple-element.cs" + } + } + }, + "checked-unchecked-expression": { + "begin": "(?>=|\\|=" + }, + { + "name": "keyword.operator.bitwise.shift.cs", + "match": "<<|>>" + }, + { + "name": "keyword.operator.comparison.cs", + "match": "==|!=" + }, + { + "name": "keyword.operator.relational.cs", + "match": "<=|>=|<|>" + }, + { + "name": "keyword.operator.logical.cs", + "match": "\\!|&&|\\|\\|" + }, + { + "name": "keyword.operator.bitwise.cs", + "match": "\\&|~|\\^|\\|" + }, + { + "name": "keyword.operator.assignment.cs", + "match": "\\=" + }, + { + "name": "keyword.operator.decrement.cs", + "match": "--" + }, + { + "name": "keyword.operator.increment.cs", + "match": "\\+\\+" + }, + { + "name": "keyword.operator.arithmetic.cs", + "match": "%|\\*|/|-|\\+" + }, + { + "name": "keyword.operator.null-coalescing.cs", + "match": "\\?\\?" + } + ] + }, + "conditional-operator": { + "begin": "(?\n (?:\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n)\\s*\n(\\))(?=\\s*[_[:alnum:]\\(])", + "captures": { + "1": { + "name": "punctuation.parenthesis.open.cs" + }, + "2": { + "patterns": [ + { + "include": "#type" + } + ] + }, + "7": { + "name": "punctuation.parenthesis.close.cs" + } + } + }, + "as-expression": { + "match": "(?x)\n(?\n (?:\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n)?", + "captures": { + "1": { + "name": "keyword.other.as.cs" + }, + "2": { + "patterns": [ + { + "include": "#type" + } + ] + } + } + }, + "is-expression": { + "match": "(?x)\n(?\n (?:\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n)?", + "captures": { + "1": { + "name": "keyword.other.is.cs" + }, + "2": { + "patterns": [ + { + "include": "#type" + } + ] + } + } + }, + "this-or-base-expression": { + "match": "\\b(?:(base)|(this))\\b", + "captures": { + "1": { + "name": "keyword.other.base.cs" + }, + "2": { + "name": "keyword.other.this.cs" + } + } + }, + "invocation-expression": { + "begin": "(?x)\n(?:(\\?)\\s*)? # preceding null-conditional operator?\n(?:(\\.)\\s*)? # preceding dot?\n([_[:alpha:]][_[:alnum:]]*)\\s* # method name\n(?\\s*<([^<>]|\\g)+>\\s*)?\\s* # type arguments\n(?=\\() # open paren of argument list", + "beginCaptures": { + "1": { + "name": "keyword.operator.null-conditional.cs" + }, + "2": { + "name": "punctuation.accessor.cs" + }, + "3": { + "name": "entity.name.function.cs" + }, + "4": { + "patterns": [ + { + "include": "#type-arguments" + } + ] + } + }, + "end": "(?<=\\))", + "patterns": [ + { + "include": "#argument-list" + } + ] + }, + "element-access-expression": { + "begin": "(?x)\n(?:(\\?)\\s*)? # preceding null-conditional operator?\n(?:(\\.)\\s*)? # preceding dot?\n(?:([_[:alpha:]][_[:alnum:]]*)\\s*)? # property name\n(?:(\\?)\\s*)? # null-conditional operator?\n(?=\\[) # open bracket of argument list", + "beginCaptures": { + "1": { + "name": "keyword.operator.null-conditional.cs" + }, + "2": { + "name": "punctuation.accessor.cs" + }, + "3": { + "name": "variable.other.object.property.cs" + }, + "4": { + "name": "keyword.operator.null-conditional.cs" + } + }, + "end": "(?<=\\])(?!\\s*\\[)", + "patterns": [ + { + "include": "#bracketed-argument-list" + } + ] + }, + "member-access-expression": { + "patterns": [ + { + "match": "(?x)\n(?:(\\?)\\s*)? # preceding null-conditional operator?\n(\\.)\\s* # preceding dot\n([_[:alpha:]][_[:alnum:]]*)\\s* # property name\n(?![_[:alnum:]]|\\(|(\\?)?\\[|<) # next character is not alpha-numeric, nor a (, [, or <. Also, test for ?[", + "captures": { + "1": { + "name": "keyword.operator.null-conditional.cs" + }, + "2": { + "name": "punctuation.accessor.cs" + }, + "3": { + "name": "variable.other.object.property.cs" + } + } + }, + { + "match": "(?x)\n(\\.)?\\s*\n([_[:alpha:]][_[:alnum:]]*)\n(?\\s*<([^<>]|\\g)+>\\s*)\n(?=\n (\\s*\\?)?\n \\s*\\.\\s*[_[:alpha:]][_[:alnum:]]*\n)", + "captures": { + "1": { + "name": "punctuation.accessor.cs" + }, + "2": { + "name": "variable.other.object.cs" + }, + "3": { + "patterns": [ + { + "include": "#type-arguments" + } + ] + } + } + }, + { + "match": "(?x)\n([_[:alpha:]][_[:alnum:]]*)\n(?=\n (\\s*\\?)?\n \\s*\\.\\s*[_[:alpha:]][_[:alnum:]]*\n)", + "captures": { + "1": { + "name": "variable.other.object.cs" + } + } + } + ] + }, + "object-creation-expression": { + "patterns": [ + { + "include": "#object-creation-expression-with-parameters" + }, + { + "include": "#object-creation-expression-with-no-parameters" + } + ] + }, + "object-creation-expression-with-parameters": { + "begin": "(?x)\n(new)\\s+\n(?\n (?:\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n)\\s*\n(?=\\()", + "beginCaptures": { + "1": { + "name": "keyword.other.new.cs" + }, + "2": { + "patterns": [ + { + "include": "#type" + } + ] + } + }, + "end": "(?<=\\))", + "patterns": [ + { + "include": "#argument-list" + } + ] + }, + "object-creation-expression-with-no-parameters": { + "match": "(?x)\n(new)\\s+\n(?\n (?:\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n)\\s*\n(?=\\{|$)", + "captures": { + "1": { + "name": "keyword.other.new.cs" + }, + "2": { + "patterns": [ + { + "include": "#type" + } + ] + } + } + }, + "array-creation-expression": { + "begin": "(?x)\n\\b(new)\\b\\s*\n(?\n (?:\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n)?\\s*\n(?=\\[)", + "beginCaptures": { + "1": { + "name": "keyword.other.new.cs" + }, + "2": { + "patterns": [ + { + "include": "#type" + } + ] + } + }, + "end": "(?<=\\])", + "patterns": [ + { + "include": "#bracketed-argument-list" + } + ] + }, + "anonymous-object-creation-expression": { + "begin": "\\b(new)\\b\\s*(?=\\{|$)", + "beginCaptures": { + "1": { + "name": "keyword.other.new.cs" + } + }, + "end": "(?<=\\})", + "patterns": [ + { + "include": "#initializer-expression" + } + ] + }, + "bracketed-parameter-list": { + "begin": "(?=(\\[))", + "beginCaptures": { + "1": { + "name": "punctuation.squarebracket.open.cs" + } + }, + "end": "(?=(\\]))", + "endCaptures": { + "1": { + "name": "punctuation.squarebracket.close.cs" + } + }, + "patterns": [ + { + "begin": "(?<=\\[)", + "end": "(?=\\])", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#attribute-section" + }, + { + "include": "#parameter" + }, + { + "include": "#punctuation-comma" + }, + { + "include": "#variable-initializer" + } + ] + } + ] + }, + "parenthesized-parameter-list": { + "begin": "(\\()", + "beginCaptures": { + "0": { + "name": "punctuation.parenthesis.open.cs" + } + }, + "end": "(\\))", + "endCaptures": { + "0": { + "name": "punctuation.parenthesis.close.cs" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#attribute-section" + }, + { + "include": "#parameter" + }, + { + "include": "#punctuation-comma" + }, + { + "include": "#variable-initializer" + } + ] + }, + "parameter": { + "match": "(?x)\n(?:(?:\\b(ref|params|out|this)\\b)\\s+)?\n(?\n (?:\n (?:ref\\s+)? # ref return\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n)\\s+\n(\\g)", + "captures": { + "1": { + "name": "storage.modifier.cs" + }, + "2": { + "patterns": [ + { + "include": "#type" + } + ] + }, + "7": { + "name": "entity.name.variable.parameter.cs" + } + } + }, + "argument-list": { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.parenthesis.open.cs" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.parenthesis.close.cs" + } + }, + "patterns": [ + { + "include": "#named-argument" + }, + { + "include": "#argument" + }, + { + "include": "#punctuation-comma" + } + ] + }, + "bracketed-argument-list": { + "begin": "\\[", + "beginCaptures": { + "0": { + "name": "punctuation.squarebracket.open.cs" + } + }, + "end": "\\]", + "endCaptures": { + "0": { + "name": "punctuation.squarebracket.close.cs" + } + }, + "patterns": [ + { + "include": "#named-argument" + }, + { + "include": "#argument" + }, + { + "include": "#punctuation-comma" + } + ] + }, + "named-argument": { + "begin": "([_[:alpha:]][_[:alnum:]]*)\\s*(:)", + "beginCaptures": { + "1": { + "name": "entity.name.variable.parameter.cs" + }, + "2": { + "name": "punctuation.separator.colon.cs" + } + }, + "end": "(?=(,|\\)|\\]))", + "patterns": [ + { + "include": "#argument" + } + ] + }, + "argument": { + "patterns": [ + { + "name": "storage.modifier.cs", + "match": "\\b(ref|out)\\b" + }, + { + "include": "#declaration-expression-local" + }, + { + "include": "#expression" + } + ] + }, + "query-expression": { + "begin": "(?x)\n\\b(from)\\b\\s*\n(?\n (?:\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n)?\n\\b(\\g)\\b\\s*\n\\b(in)\\b\\s*", + "beginCaptures": { + "1": { + "name": "keyword.query.from.cs" + }, + "2": { + "patterns": [ + { + "include": "#type" + } + ] + }, + "7": { + "name": "entity.name.variable.range-variable.cs" + }, + "8": { + "name": "keyword.query.in.cs" + } + }, + "end": "(?=;|\\))", + "patterns": [ + { + "include": "#query-body" + }, + { + "include": "#expression" + } + ] + }, + "query-body": { + "patterns": [ + { + "include": "#let-clause" + }, + { + "include": "#where-clause" + }, + { + "include": "#join-clause" + }, + { + "include": "#orderby-clause" + }, + { + "include": "#select-clause" + }, + { + "include": "#group-clause" + } + ] + }, + "let-clause": { + "begin": "(?x)\n\\b(let)\\b\\s*\n\\b([_[:alpha:]][_[:alnum:]]*)\\b\\s*\n(=)\\s*", + "beginCaptures": { + "1": { + "name": "keyword.query.let.cs" + }, + "2": { + "name": "entity.name.variable.range-variable.cs" + }, + "3": { + "name": "keyword.operator.assignment.cs" + } + }, + "end": "(?=;|\\))", + "patterns": [ + { + "include": "#query-body" + }, + { + "include": "#expression" + } + ] + }, + "where-clause": { + "begin": "(?x)\n\\b(where)\\b\\s*", + "beginCaptures": { + "1": { + "name": "keyword.query.where.cs" + } + }, + "end": "(?=;|\\))", + "patterns": [ + { + "include": "#query-body" + }, + { + "include": "#expression" + } + ] + }, + "join-clause": { + "begin": "(?x)\n\\b(join)\\b\\s*\n(?\n (?:\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n)?\n\\b(\\g)\\b\\s*\n\\b(in)\\b\\s*", + "beginCaptures": { + "1": { + "name": "keyword.query.join.cs" + }, + "2": { + "patterns": [ + { + "include": "#type" + } + ] + }, + "7": { + "name": "entity.name.variable.range-variable.cs" + }, + "8": { + "name": "keyword.query.in.cs" + } + }, + "end": "(?=;|\\))", + "patterns": [ + { + "include": "#join-on" + }, + { + "include": "#join-equals" + }, + { + "include": "#join-into" + }, + { + "include": "#query-body" + }, + { + "include": "#expression" + } + ] + }, + "join-on": { + "match": "\\b(on)\\b\\s*", + "captures": { + "1": { + "name": "keyword.query.on.cs" + } + } + }, + "join-equals": { + "match": "\\b(equals)\\b\\s*", + "captures": { + "1": { + "name": "keyword.query.equals.cs" + } + } + }, + "join-into": { + "match": "(?x)\n\\b(into)\\b\\s*\n\\b([_[:alpha:]][_[:alnum:]]*)\\b\\s*", + "captures": { + "1": { + "name": "keyword.query.into.cs" + }, + "2": { + "name": "entity.name.variable.range-variable.cs" + } + } + }, + "orderby-clause": { + "begin": "\\b(orderby)\\b\\s*", + "beginCaptures": { + "1": { + "name": "keyword.query.orderby.cs" + } + }, + "end": "(?=;|\\))", + "patterns": [ + { + "include": "#ordering-direction" + }, + { + "include": "#query-body" + }, + { + "include": "#expression" + }, + { + "include": "#punctuation-comma" + } + ] + }, + "ordering-direction": { + "match": "\\b(?:(ascending)|(descending))\\b", + "captures": { + "1": { + "name": "keyword.query.ascending.cs" + }, + "2": { + "name": "keyword.query.descending.cs" + } + } + }, + "select-clause": { + "begin": "\\b(select)\\b\\s*", + "beginCaptures": { + "1": { + "name": "keyword.query.select.cs" + } + }, + "end": "(?=;|\\))", + "patterns": [ + { + "include": "#query-body" + }, + { + "include": "#expression" + } + ] + }, + "group-clause": { + "begin": "\\b(group)\\b\\s*", + "beginCaptures": { + "1": { + "name": "keyword.query.group.cs" + } + }, + "end": "(?=;|\\))", + "patterns": [ + { + "include": "#group-by" + }, + { + "include": "#group-into" + }, + { + "include": "#query-body" + }, + { + "include": "#expression" + } + ] + }, + "group-by": { + "match": "\\b(by)\\b\\s*", + "captures": { + "1": { + "name": "keyword.query.by.cs" + } + } + }, + "group-into": { + "match": "(?x)\n\\b(into)\\b\\s*\n\\b([_[:alpha:]][_[:alnum:]]*)\\b\\s*", + "captures": { + "1": { + "name": "keyword.query.into.cs" + }, + "2": { + "name": "entity.name.variable.range-variable.cs" + } + } + }, + "anonymous-method-expression": { + "patterns": [ + { + "begin": "(?x)\n(?:\\b(async)\\b\\s*)?\n\\b([_[:alpha:]][_[:alnum:]]*)\\b\\s*\n(=>)", + "beginCaptures": { + "1": { + "name": "storage.modifier.cs" + }, + "2": { + "name": "entity.name.variable.parameter.cs" + }, + "3": { + "name": "keyword.operator.arrow.cs" + } + }, + "end": "(?=\\)|;|}|,)", + "patterns": [ + { + "include": "#block" + }, + { + "include": "#ref-modifier" + }, + { + "include": "#expression" + } + ] + }, + { + "begin": "(?x)\n(?:\\b(async)\\b\\s*)?\n(\\(.*?\\))\\s*\n(=>)", + "beginCaptures": { + "1": { + "name": "storage.modifier.cs" + }, + "2": { + "patterns": [ + { + "include": "#lambda-parameter-list" + } + ] + }, + "3": { + "name": "keyword.operator.arrow.cs" + } + }, + "end": "(?=\\)|;|}|,)", + "patterns": [ + { + "include": "#block" + }, + { + "include": "#ref-modifier" + }, + { + "include": "#expression" + } + ] + }, + { + "begin": "(?x)\n(?:\\b(async)\\b\\s*)?\n(?:\\b(delegate)\\b\\s*)", + "beginCaptures": { + "1": { + "name": "storage.modifier.cs" + }, + "2": { + "name": "keyword.other.delegate.cs" + } + }, + "end": "(?=\\)|;|}|,)", + "patterns": [ + { + "include": "#parenthesized-parameter-list" + }, + { + "include": "#block" + }, + { + "include": "#expression" + } + ] + } + ] + }, + "lambda-parameter-list": { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.parenthesis.open.cs" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.parenthesis.close.cs" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#attribute-section" + }, + { + "include": "#lambda-parameter" + }, + { + "include": "#punctuation-comma" + } + ] + }, + "lambda-parameter": { + "match": "(?x)\n(ref|out)?\\s*\n(?\n (?:\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n)?\n\\b(\\g)\\b\\s*\n(?=[,)])", + "captures": { + "1": { + "name": "storage.modifier.cs" + }, + "2": { + "patterns": [ + { + "include": "#type" + } + ] + }, + "7": { + "name": "entity.name.variable.parameter.cs" + } + } + }, + "type": { + "name": "meta.type.cs", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#ref-modifier" + }, + { + "include": "#tuple-type" + }, + { + "include": "#type-builtin" + }, + { + "include": "#type-name" + }, + { + "include": "#type-arguments" + }, + { + "include": "#type-array-suffix" + }, + { + "include": "#type-nullable-suffix" + } + ] + }, + "ref-modifier": { + "name": "storage.modifier.cs", + "match": "\\b(ref)\\b" + }, + "tuple-type": { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.parenthesis.open.cs" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.parenthesis.close.cs" + } + }, + "patterns": [ + { + "include": "#tuple-element" + }, + { + "include": "#punctuation-comma" + } + ] + }, + "tuple-element": { + "match": "(?x)\n(?\n (?:\n (?:\n (?:(?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\*\\s*)* # pointer suffix?\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n)\n(?:\\b(?\\g)\\b)?", + "captures": { + "1": { + "patterns": [ + { + "include": "#type" + } + ] + }, + "6": { + "name": "entity.name.variable.tuple-element.cs" + } + } + }, + "type-builtin": { + "match": "\\b(bool|byte|char|decimal|double|float|int|long|object|sbyte|short|string|uint|ulong|ushort|void|dynamic)\\b", + "captures": { + "1": { + "name": "keyword.type.cs" + } + } + }, + "type-name": { + "patterns": [ + { + "match": "([_[:alpha:]][_[:alnum:]]*)\\s*(\\:\\:)", + "captures": { + "1": { + "name": "entity.name.type.alias.cs" + }, + "2": { + "name": "punctuation.separator.coloncolon.cs" + } + } + }, + { + "match": "([_[:alpha:]][_[:alnum:]]*)\\s*(\\.)", + "captures": { + "1": { + "name": "storage.type.cs" + }, + "2": { + "name": "punctuation.accessor.cs" + } + } + }, + { + "match": "(\\.)\\s*([_[:alpha:]][_[:alnum:]]*)", + "captures": { + "1": { + "name": "punctuation.accessor.cs" + }, + "2": { + "name": "storage.type.cs" + } + } + }, + { + "name": "storage.type.cs", + "match": "[_[:alpha:]][_[:alnum:]]*" + } + ] + }, + "type-arguments": { + "begin": "<", + "beginCaptures": { + "0": { + "name": "punctuation.definition.typeparameters.begin.cs" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.typeparameters.end.cs" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#type" + }, + { + "include": "#punctuation-comma" + } + ] + }, + "type-array-suffix": { + "begin": "\\[", + "beginCaptures": { + "0": { + "name": "punctuation.squarebracket.open.cs" + } + }, + "end": "\\]", + "endCaptures": { + "0": { + "name": "punctuation.squarebracket.close.cs" + } + }, + "patterns": [ + { + "include": "#punctuation-comma" + } + ] + }, + "type-nullable-suffix": { + "match": "\\?", + "captures": { + "0": { + "name": "punctuation.separator.question-mark.cs" + } + } + }, + "operator-assignment": { + "name": "keyword.operator.assignment.cs", + "match": "(?)", + "endCaptures": { + "1": { + "name": "punctuation.definition.tag.cs" + } + }, + "patterns": [ + { + "include": "#xml-attribute" + } + ] + }, + "xml-attribute": { + "patterns": [ + { + "match": "(?x)\n(?:^|\\s+)\n(\n (?:\n ([-_[:alnum:]]+)\n (:)\n )?\n ([-_[:alnum:]]+)\n)\n(=)", + "captures": { + "1": { + "name": "entity.other.attribute-name.cs" + }, + "2": { + "name": "entity.other.attribute-name.namespace.cs" + }, + "3": { + "name": "punctuation.separator.colon.cs" + }, + "4": { + "name": "entity.other.attribute-name.localname.cs" + }, + "5": { + "name": "punctuation.separator.equals.cs" + } + } + }, + { + "include": "#xml-string" + } + ] + }, + "xml-cdata": { + "name": "string.unquoted.cdata.cs", + "begin": "", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.cs" + } + } + }, + "xml-string": { + "patterns": [ + { + "name": "string.quoted.single.cs", + "begin": "\\'", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.cs" + } + }, + "end": "\\'", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.cs" + } + }, + "patterns": [ + { + "include": "#xml-character-entity" + } + ] + }, + { + "name": "string.quoted.double.cs", + "begin": "\\\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.cs" + } + }, + "end": "\\\"", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.cs" + } + }, + "patterns": [ + { + "include": "#xml-character-entity" + } + ] + } + ] + }, + "xml-character-entity": { + "patterns": [ + { + "name": "constant.character.entity.cs", + "match": "(?x)\n(&)\n(\n (?:[[:alpha:]:_][[:alnum:]:_.-]*)|\n (?:\\#[[:digit:]]+)|\n (?:\\#x[[:xdigit:]]+)\n)\n(;)", + "captures": { + "1": { + "name": "punctuation.definition.constant.cs" + }, + "3": { + "name": "punctuation.definition.constant.cs" + } + } + }, + { + "name": "invalid.illegal.bad-ampersand.cs", + "match": "&" + } + ] + }, + "xml-comment": { + "name": "comment.block.cs", + "begin": "", + "endCaptures": { + "0": { + "name": "punctuation.definition.comment.cs" + } + } + } + } +} \ No newline at end of file diff --git a/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/StaticHighlightingColorizer.cs b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/StaticHighlightingColorizer.cs new file mode 100644 index 0000000000..88c5fe2b8d --- /dev/null +++ b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/StaticHighlightingColorizer.cs @@ -0,0 +1,431 @@ +using Avalonia.Media; +using Avalonia.Threading; +using AvaloniaEdit.Document; +using AvaloniaEdit.Highlighting; +using AvaloniaEdit.Rendering; +using AvalonStudio.CodeEditor; +using AvalonStudio.Extensibility.Editor; +using AvalonStudio.Languages; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading; + +namespace AvalonStudio.Controls.Standard.CodeEditor.Highlighting +{ + public class StaticHighlightingColorizer : GenericLineTransformer + { + private readonly SyntaxHighlightingDefinition _definition; + private CodeEditor _editor; + private SyntaxHighlighting _highlighter; + private readonly bool _isFixedHighlighter; + + /// + /// Creates a new HighlightingColorizer instance. + /// + /// The highlighting definition. + public StaticHighlightingColorizer(SyntaxHighlightingDefinition definition) + { + _definition = definition ?? throw new ArgumentNullException(nameof(definition)); + } + + /// + /// Creates a new HighlightingColorizer instance that uses a fixed highlighter instance. + /// The colorizer can only be used with text views that show the document for which + /// the highlighter was created. + /// + /// The highlighter to be used. + public StaticHighlightingColorizer(SyntaxHighlighting highlighter) + { + _highlighter = highlighter ?? throw new ArgumentNullException(nameof(highlighter)); + _isFixedHighlighter = true; + } + + /// + /// Creates a new HighlightingColorizer instance. + /// Derived classes using this constructor must override the method. + /// + protected StaticHighlightingColorizer() + { + } + + private void TextView_DocumentChanged(object sender, EventArgs e) + { + var textView = (CodeEditor)sender; + DeregisterServices(textView); + RegisterServices(textView); + } + + /// + /// This method is called when a text view is removed from this HighlightingColorizer, + /// and also when the TextDocument on any associated text view changes. + /// + protected virtual void DeregisterServices(CodeEditor editor) + { + if (_highlighter != null) + { + if (_isInHighlightingGroup) + { + //_highlighter.EndHighlighting(); + _isInHighlightingGroup = false; + } + //_highlighter.HighlightingStateChanged -= OnHighlightStateChanged; + // remove highlighter if it is registered + if (editor.TextArea.TextView.Services.GetService(typeof(IHighlighter)) == _highlighter) + { + editor.TextArea.TextView.Services.RemoveService(typeof(IHighlighter)); + } + if (!_isFixedHighlighter) + { + _highlighter?.Dispose(); + _highlighter = null; + } + } + } + + /// + /// This method is called when a new text view is added to this HighlightingColorizer, + /// and also when the TextDocument on any associated text view changes. + /// + protected virtual void RegisterServices(CodeEditor editor) + { + if (editor.Document != null) + { + if (!_isFixedHighlighter) + _highlighter = editor.DocumentAccessor.Document != null ? CreateHighlighter(editor.TextArea.TextView, editor.Document) : null; + if (_highlighter != null && _highlighter.Document == editor.DocumentAccessor.Document) + { + //add service only if it doesn't already exist + if (editor.TextArea.TextView.Services.GetService(typeof(IHighlighter)) == null) + { + editor.TextArea.TextView.Services.AddService(typeof(IHighlighter), _highlighter); + } + + //_highlighter.HighlightingStateChanged += OnHighlightStateChanged; + } + } + } + + /// + /// Creates the IHighlighter instance for the specified text document. + /// + protected virtual SyntaxHighlighting CreateHighlighter(TextView textView, IDocument document) + { + if (_definition != null) + return new SyntaxHighlighting(_definition, document); + throw new NotSupportedException("Cannot create a highlighter because no IHighlightingDefinition was specified, and the CreateHighlighter() method was not overridden."); + } + + /// + public void OnAddToEditor(CodeEditor editor) + { + if (_editor != null) + { + throw new InvalidOperationException("Cannot use a HighlightingColorizer instance in multiple text views. Please create a separate instance for each text view."); + } + + _editor = editor; + editor.DocumentChanged += TextView_DocumentChanged; + editor.TextArea.TextView.VisualLineConstructionStarting += TextView_VisualLineConstructionStarting; + editor.TextArea.TextView.VisualLinesChanged += TextView_VisualLinesChanged; + RegisterServices(editor); + } + + /// + public void OnRemoveFromEditor(CodeEditor editor) + { + DeregisterServices(editor); + editor.DocumentChanged -= TextView_DocumentChanged; + editor.TextArea.TextView.VisualLineConstructionStarting -= TextView_VisualLineConstructionStarting; + editor.TextArea.TextView.VisualLinesChanged -= TextView_VisualLinesChanged; + _editor = null; + } + + private bool _isInHighlightingGroup; + + private void TextView_VisualLineConstructionStarting(object sender, VisualLineConstructionStartEventArgs e) + { + if (_highlighter != null) + { + // Force update of highlighting state up to the position where we start generating visual lines. + // This is necessary in case the document gets modified above the FirstLineInView so that the highlighting state changes. + // We need to detect this case and issue a redraw (through OnHighlightStateChanged) + // before the visual line construction reuses existing lines that were built using the invalid highlighting state. + _lineNumberBeingColorized = e.FirstLineInView.LineNumber - 1; + if (!_isInHighlightingGroup) + { + // avoid opening group twice if there was an exception during the previous visual line construction + // (not ideal, but better than throwing InvalidOperationException "group already open" + // without any way of recovering) + //_highlighter.BeginHighlighting(); + _isInHighlightingGroup = true; + } + //_highlighter.UpdateHighlightingState(_lineNumberBeingColorized); + _lineNumberBeingColorized = 0; + } + } + + private void TextView_VisualLinesChanged(object sender, EventArgs e) + { + if (_highlighter != null && _isInHighlightingGroup) + { + //_highlighter.EndHighlighting(); + _isInHighlightingGroup = false; + } + } + + private AvaloniaEdit.Document.DocumentLine _lastColorizedLine; + + /// + protected override void Colorize(ITextRunConstructionContext context) + { + _lastColorizedLine = null; + base.Colorize(context); + if (_lastColorizedLine != context.VisualLine.LastDocumentLine) + { + if (_highlighter != null) + { + // In some cases, it is possible that we didn't highlight the last document line within the visual line + // (e.g. when the line ends with a fold marker). + // But even if we didn't highlight it, we'll have to update the highlighting state for it so that the + // proof inside TextViewDocumentHighlighter.OnHighlightStateChanged holds. + _lineNumberBeingColorized = context.VisualLine.LastDocumentLine.LineNumber; + + var highlightedLine = _highlighter.GetHighlightedLineAsync(context.Document.GetLineByNumber(_lineNumberBeingColorized), CancellationToken.None).Result; + + _lineNumberBeingColorized = 0; + } + } + _lastColorizedLine = null; + } + + private int _lineNumberBeingColorized; + + public IBrush GetBrush(ColorScheme ColorScheme, HighlightType type) + { + IBrush result; + + switch (type) + { + case HighlightType.DelegateName: + result = ColorScheme.DelegateName; + break; + + case HighlightType.Comment: + result = ColorScheme.Comment; + break; + + case HighlightType.Identifier: + result = ColorScheme.Identifier; + break; + + case HighlightType.Keyword: + result = ColorScheme.Keyword; + break; + + case HighlightType.Literal: + result = ColorScheme.Literal; + break; + + case HighlightType.NumericLiteral: + result = ColorScheme.NumericLiteral; + break; + + case HighlightType.Punctuation: + result = ColorScheme.Punctuation; + break; + + case HighlightType.InterfaceName: + result = ColorScheme.InterfaceType; + break; + + case HighlightType.ClassName: + result = ColorScheme.Type; + break; + + case HighlightType.CallExpression: + result = ColorScheme.CallExpression; + break; + + case HighlightType.EnumTypeName: + result = ColorScheme.EnumType; + break; + + case HighlightType.Operator: + result = ColorScheme.Operator; + break; + + case HighlightType.StructName: + result = ColorScheme.StructName; + break; + + default: + result = Brushes.Red; + break; + } + + return result; + } + + /// + /// Applies a highlighting color to a visual line element. + /// + protected virtual void ApplyColorToElement(VisualLineElement element, HighlightingColor color) + { + ApplyColorToElement(element, color, CurrentContext); + } + + internal static void ApplyColorToElement(VisualLineElement element, HighlightingColor color, ITextRunConstructionContext context) + { + if (color.Foreground != null) + { + var b = color.Foreground.GetBrush(context); + if (b != null) + element.TextRunProperties.ForegroundBrush = b; + } + if (color.Background != null) + { + var b = color.Background.GetBrush(context); + if (b != null) + element.BackgroundBrush = b; + } + if (color.FontStyle != null || color.FontWeight != null) + { + var tf = element.TextRunProperties.Typeface; + element.TextRunProperties.Typeface = tf; + } + //if(color.Underline ?? false) + // element.TextRunProperties.SetTextDecorations(TextDecorations.Underline); + } + + /// + /// This method is responsible for telling the TextView to redraw lines when the highlighting state has changed. + /// + /// + /// Creation of a VisualLine triggers the syntax highlighter (which works on-demand), so it says: + /// Hey, the user typed "/*". Don't just recreate that line, but also the next one + /// because my highlighting state (at end of line) changed! + /// + private void OnHighlightStateChanged(int fromLineNumber, int toLineNumber) + { + if (_lineNumberBeingColorized != 0) + { + // Ignore notifications for any line except the one we're interested in. + // This improves the performance as Redraw() can take quite some time when called repeatedly + // while scanning the document (above the visible area) for highlighting changes. + if (toLineNumber <= _lineNumberBeingColorized) + { + return; + } + } + + // The user may have inserted "/*" into the current line, and so far only that line got redrawn. + // So when the highlighting state is changed, we issue a redraw for the line immediately below. + // If the highlighting state change applies to the lines below, too, the construction of each line + // will invalidate the next line, and the construction pass will regenerate all lines. + + Debug.WriteLine("OnHighlightStateChanged forces redraw of lines {0} to {1}", fromLineNumber, toLineNumber); + + // If the VisualLine construction is in progress, we have to avoid sending redraw commands for + // anything above the line currently being constructed. + // It takes some explanation to see why this cannot happen. + // VisualLines always get constructed from top to bottom. + // Each VisualLine construction calls into the highlighter and thus forces an update of the + // highlighting state for all lines up to the one being constructed. + + // To guarantee that we don't redraw lines we just constructed, we need to show that when + // a VisualLine is being reused, the highlighting state at that location is still up-to-date. + + // This isn't exactly trivial and the initial implementation was incorrect in the presence of external document changes + // (e.g. split view). + + // For the first line in the view, the TextView.VisualLineConstructionStarting event is used to check that the + // highlighting state is up-to-date. If it isn't, this method will be executed, and it'll mark the first line + // in the view as requiring a redraw. This is safely possible because that event occurs before any lines are reused. + + // Once we take care of the first visual line, we won't get in trouble with other lines due to the top-to-bottom + // construction process. + + // We'll prove that: if line N is being reused, then the highlighting state is up-to-date until (end of) line N-1. + + // Start of induction: the first line in view is reused only if the highlighting state was up-to-date + // until line N-1 (no change detected in VisualLineConstructionStarting event). + + // Induction step: + // If another line N+1 is being reused, then either + // a) the previous line (the visual line containing document line N) was newly constructed + // or b) the previous line was reused + // In case a, the construction updated the highlighting state. This means the stack at end of line N is up-to-date. + // In case b, the highlighting state at N-1 was up-to-date, and the text of line N was not changed. + // (if the text was changed, the line could not have been reused). + // From this follows that the highlighting state at N is still up-to-date. + + // The above proof holds even in the presence of folding: folding only ever hides text in the middle of a visual line. + // Our Colorize-override ensures that the highlighting state is always updated for the LastDocumentLine, + // so it will always invalidate the next visual line when a folded line is constructed + // and the highlighting stack has changed. + + if (fromLineNumber == toLineNumber) + { + _editor.TextArea.TextView.Redraw(_editor.Document.GetLineByNumber(fromLineNumber)); + } + else + { + // If there are multiple lines marked as changed; only the first one really matters + // for the highlighting during rendering. + // However this callback is also called outside of the rendering process, e.g. when a highlighter + // decides to re-highlight some section based on external feedback (e.g. semantic highlighting). + var fromLine = _editor.Document.GetLineByNumber(fromLineNumber); + var toLine = _editor.Document.GetLineByNumber(toLineNumber); + var startOffset = fromLine.Offset; + _editor.TextArea.TextView.Redraw(startOffset, toLine.EndOffset - startOffset); + } + + /* + * Meta-comment: "why does this have to be so complicated?" + * + * The problem is that I want to re-highlight only on-demand and incrementally; + * and at the same time only repaint changed lines. + * So the highlighter and the VisualLine construction both have to run in a single pass. + * The highlighter must take care that it never touches already constructed visual lines; + * if it detects that something must be redrawn because the highlighting state changed, + * it must do so early enough in the construction process. + * But doing it too early means it doesn't have the information necessary to re-highlight and redraw only the desired parts. + */ + } + + protected override void TransformLine(AvaloniaEdit.Document.DocumentLine line, ITextRunConstructionContext context) + { + if (_highlighter != null) + { + var lineText = context.Document.GetText(line); + + _lineNumberBeingColorized = line.LineNumber; + + var hl = _highlighter.GetHighlightedLineAsync(line, CancellationToken.None).Result; + _lineNumberBeingColorized = 0; + + foreach (var section in hl.Segments) + { + var colorKey = section.ColorStyleKey; + + var levels = colorKey.Split('.'); + + var brush = ColorScheme.CurrentColorScheme[colorKey]; + + if (brush != null) + { + SetTextStyle(line, section.Offset, section.Length, brush); + } + else + { + System.Console.WriteLine($"{colorKey}:{lineText}"); + } + } + } + _lastColorizedLine = line; + } + } +} diff --git a/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/Sublime3/Sublime3Format.cs b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/Sublime3/Sublime3Format.cs new file mode 100644 index 0000000000..0a07f7605f --- /dev/null +++ b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/Sublime3/Sublime3Format.cs @@ -0,0 +1,1087 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using YamlDotNet.RepresentationModel; + +namespace AvalonStudio.Controls.Standard.CodeEditor.Highlighting.Sublime3 +{ + public static class Sublime3Format + { + public static SyntaxHighlightingDefinition ReadHighlighting(TextReader input) + { + input.ReadLine(); + input.ReadLine(); + string name = null, scope = null, firstLineMatch = null; + var variables = new Dictionary(); + bool hidden = false; + var extensions = new List(); + var contexts = new List(); + + var yaml = new YamlStream(); + yaml.Load(input); + var mapping = (YamlMappingNode)yaml.Documents[0].RootNode; + + foreach (var entry in mapping.Children) + { + switch (((YamlScalarNode)entry.Key).Value) + { + case "variables": + foreach (var captureEntry in ((YamlMappingNode)entry.Value).Children) + { + variables[((YamlScalarNode)captureEntry.Key).Value] = ((YamlScalarNode)captureEntry.Value).Value; + } + break; + } + } + + foreach (var entry in mapping.Children) + { + switch (((YamlScalarNode)entry.Key).Value) + { + case "name": + name = ((YamlScalarNode)entry.Value).Value; + break; + case "file_extensions": + foreach (var nn in entry.Value.AllNodes.OfType()) + { + extensions.Add("." + nn.Value); + } + break; + case "scope": + scope = ((YamlScalarNode)entry.Value).Value; + break; + case "hidden": + try + { + hidden = bool.Parse(((YamlScalarNode)entry.Value).Value); + } + catch (Exception e) + { + //LoggingService.LogError("Error while parsing hidden flag of " + name, e); + } + break; + case "first_line_match": + firstLineMatch = CompileRegex(((YamlScalarNode)entry.Value).Value); + break; + case "contexts": + foreach (var contextMapping in ((YamlMappingNode)entry.Value).Children) + { + contexts.Add(ReadContext(contextMapping, variables)); + } + break; + } + } + return new SyntaxHighlightingDefinition(name, scope, firstLineMatch, hidden, extensions, contexts); + } + + + internal static SyntaxContext ReadContext(KeyValuePair mapping, Dictionary variables) + { + var result = new SyntaxContext(((YamlScalarNode)mapping.Key).Value); + result.ParseMapping(mapping.Value as YamlSequenceNode, variables); + return result; + } + + internal static SyntaxMatch ReadMatch(YamlMappingNode mapping, Dictionary variables) + { + string match = null; + List scope = new List(); + var captures = new List>(); + + ContextReference push = null, set = null, withPrototype = null; + bool pop = false; + foreach (var entry in mapping.Children) + { + switch (((YamlScalarNode)entry.Key).Value) + { + case "match": + match = CompileRegex(ReplaceVariables(((YamlScalarNode)entry.Value).Value, variables)); + break; + case "scope": + ParseScopes(scope, ((YamlScalarNode)entry.Value).Value); + break; + case "captures": + foreach (var captureEntry in ((YamlMappingNode)entry.Value).Children) + { + captures.Add( + Tuple.Create( + int.Parse(((YamlScalarNode)captureEntry.Key).Value), + ((YamlScalarNode)captureEntry.Value).Value + ) + ); + } + break; + case "push": + push = ReadContextReference(entry.Value, variables); + break; + case "with_prototype": + withPrototype = ReadContextReference(entry.Value, variables); + break; + case "pop": + // according to the spec the only accepted value + pop = true; + break; + case "set": + set = ReadContextReference(entry.Value, variables); + break; + case "syntax": + // ? + break; + } + } + return new SyntaxMatch(match, scope, new Captures(captures), push, pop, set, withPrototype); + } + + internal static void ParseScopes(List scope, string value) + { + if (value == null) + return; + scope.AddRange(value.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries)); + } + + internal static ContextReference ReadContextReference(YamlNode value, Dictionary variables) + { + var seq = value as YamlSequenceNode; + if (seq != null) + { + var l = seq.Children.OfType().Select(s => s.Value).ToList(); + if (l.Count > 0) + { + return new ContextNameListContextReference(l); + } + + return ReadAnonymousMatchContextReference(seq, variables); + } + if (value.NodeType == YamlNodeType.Scalar) + return new ContextNameContextReference(((YamlScalarNode)value).Value); + return null; + } + + internal static ContextReference ReadAnonymousMatchContextReference(YamlSequenceNode seq, Dictionary variables) + { + var ctx = new SyntaxContext("__Anonymous__", new List(), metaIncludePrototype: false); + + foreach (var c in seq.Children.OfType()) + { + ctx.ParseMapping(c, variables); + } + + return new AnonymousMatchContextReference(ctx); + } + + static string ReplaceVariables(string str, Dictionary variables) + { + if (string.IsNullOrEmpty(str)) + return str; + + var result = new StringBuilder(); + var wordBuilder = new StringBuilder(); + bool recordWord = false; + for (int i = 0; i < str.Length - 1; i++) + { + var ch = str[i]; + var next = str[i + 1]; + if (ch == '{' && next == '{') + { + i++; + recordWord = true; + continue; + } + if (ch == '}' && next == '}' && recordWord) + { + i++; + recordWord = false; + string replacement; + if (variables.TryGetValue(wordBuilder.ToString(), out replacement)) + { + result.Append(ReplaceVariables(replacement, variables)); + } + else + { + //LoggingService.LogWarning("Sublime3Format: Can't find variable " + wordBuilder.ToString()); + } + wordBuilder.Length = 0; + if (i >= str.Length - 1) + return result.ToString(); + continue; + } + + if (recordWord) + { + wordBuilder.Append(ch); + } + else + { + result.Append(ch); + } + } + result.Append(str[str.Length - 1]); + return result.ToString(); + } + + + class CharacterClass + { + bool first = true; + bool negativeGroup; + StringBuilder wordBuilder; + StringBuilder unicodeGroupBuilder = new StringBuilder(); + + bool hasLast; + bool escape, range, readUnicodeGroup; + char lastChar = '\0'; + char lastPushedChar; + int[] table = new int[256]; + StringBuilder org = new StringBuilder(); + List unicodeGroups = new List(); + CharacterClass subClass; + + public void Push(char ch) + { + org.Append(ch); + switch (ch) + { + case ':': + if (escape) + goto default; + if (first || lastPushedChar == '[') + { + wordBuilder = new StringBuilder(); + subClass = new CharacterClass(); + subClass.Add(':'); + } + else + { + if (wordBuilder != null) + { + ConvertUnicodeCategory(wordBuilder.ToString()); + wordBuilder = null; + subClass = null; + } + else + goto default; + } + break; + case '^': + if (first) + { + negativeGroup = true; + break; + } + goto default; + case '{': + if (readUnicodeGroup) + return; + goto default; + case '}': + if (readUnicodeGroup) + { + readUnicodeGroup = false; + unicodeGroups.Add(unicodeGroupBuilder.ToString()); + unicodeGroupBuilder.Length = 0; + return; + } + goto default; + case '[': + if (escape) + goto default; + PushLastChar(); + break; + case ']': + if (escape) + goto default; + break; + case '\\': + if (escape || wordBuilder != null) + goto default; + escape = true; + break; + case '-': + if (escape || first || wordBuilder != null) + goto default; + range = true; + break; + default: + if (readUnicodeGroup) + { + unicodeGroupBuilder.Append(ch); + return; + } + if (escape) + { + PushLastChar(); + + switch (ch) + { + case 'w': // A word character ([a - zA - Z0 - 9_]) + AddRange('a', 'z'); + AddRange('A', 'Z'); + AddRange('0', '9'); + table['_'] = negativeGroup ? -1 : 1; + break; + case 'W': // A non-word character ([^a-zA-Z0-9_]). + for (int i = 0; i < table.Length; i++) + { + if ('0' <= i && i <= '9') + continue; + if ('a' <= i && i <= 'z') + continue; + if ('A' <= i && i <= 'Z') + continue; + if (i == '_') + continue; + table[i] = negativeGroup ? -1 : 1; + } + break; + case 'd': // A digit character ([0-9]) + AddRange('0', '9'); + break; + case 'D': // A non-digit character ([^0-9]) + for (int i = 0; i < table.Length; i++) + { + if ('0' <= i && i <= '9') + continue; + table[i] = negativeGroup ? -1 : 1; + } + break; + case 'h': // A hexdigit character ([0-9a-fA-F]) + AddRange('0', '9'); + AddRange('a', 'f'); + AddRange('A', 'F'); + break; + case 'H': // A non-hexdigit character ([^0-9a-fA-F]) + for (int i = 0; i < table.Length; i++) + { + if ('0' <= i && i <= '9') + continue; + if ('a' <= i && i <= 'f') + continue; + if ('A' <= i && i <= 'F') + continue; + table[i] = negativeGroup ? -1 : 1; + } + break; + case 's': // A whitespace character: /[ \t\r\n\f]/ + table[' '] = negativeGroup ? -1 : 1; + table['\t'] = negativeGroup ? -1 : 1; + table['\r'] = negativeGroup ? -1 : 1; + table['\n'] = negativeGroup ? -1 : 1; + table['\f'] = negativeGroup ? -1 : 1; + break; + case 'S': // A non-whitespace character: /[^ \t\r\n\f]/ + for (int i = 0; i < table.Length; i++) + { + if (" \\t\\r\\n\\f".Contains((char)i)) + continue; + table[i] = negativeGroup ? -1 : 1; + } + break; + case 'a': + table['\a'] = negativeGroup ? -1 : 1; + break; + case 'b': + table['\b'] = negativeGroup ? -1 : 1; + break; + case 't': + table['\t'] = negativeGroup ? -1 : 1; + break; + case 'r': + table['\r'] = negativeGroup ? -1 : 1; + break; + case 'v': + table['\v'] = negativeGroup ? -1 : 1; + break; + case 'f': + table['\f'] = negativeGroup ? -1 : 1; + break; + case 'n': + table['\n'] = negativeGroup ? -1 : 1; + break; + case 'p': + readUnicodeGroup = true; + break; + default: + lastChar = ch; + hasLast = true; + PushLastChar(); + break; + } + + escape = false; + break; + } + + if (wordBuilder != null) + { + wordBuilder.Append(ch); + subClass.Push(ch); + break; + } + + if (!hasLast) + { + lastChar = ch; + hasLast = true; + break; + } + if (range) + { + for (int i = (int)lastChar; i <= (int)ch; i++) + { + table[i] = negativeGroup ? -1 : 1; + } + hasLast = false; + } + else + { + PushLastChar(); + lastChar = ch; + hasLast = true; + } + range = false; + break; + } + first = false; + lastPushedChar = ch; + } + + void PushLastChar() + { + if (hasLast) + { + table[(int)lastChar] = negativeGroup ? -1 : 1; + hasLast = false; + } + } + + void AddRange(char fromC, char toC) + { + for (int i = fromC; i <= toC; i++) + { + table[i] = negativeGroup ? -1 : 1; + } + } + + void Add(char ch) + { + table[ch] = negativeGroup ? -1 : 1; + } + + void AddUnicodeCategory(params UnicodeCategory[] categories) + { + for (int i = 0; i < table.Length; i++) + { + var cat = char.GetUnicodeCategory((char)i); + if (categories.Contains(cat)) + { + table[i] = negativeGroup ? -1 : 1; + } + } + } + + bool HasRange(char fromC, char toC) + { + for (int i = fromC; i <= toC; i++) + { + if (table[i] == 0) + return false; + } + return true; + } + + bool Has(string v) + { + foreach (var ch in v) + { + if (table[ch] == 0) + return false; + } + return true; + } + + void RemoveRange(char fromC, char toC) + { + for (int i = fromC; i <= toC; i++) + { + table[i] = 0; + } + } + + + void PrepareGeneration() + { + PushLastChar(); + + if (range) + table['-'] = negativeGroup ? -1 : 1; + if (escape) + table['\\'] = negativeGroup ? -1 : 1; + + } + public string Generate() + { + if (subClass != null) + { + subClass.PrepareGeneration(); + for (int i = 0; i < table.Length; i++) + { + if (subClass.table[i] != 0) + table[i] = subClass.table[i]; + } + } + PrepareGeneration(); + var result = new StringBuilder(); + result.Append('['); + if (negativeGroup) + result.Append('^'); + + bool hasAllPrintable = true; + for (int i = 0; i < table.Length; i++) + { + var ch = (char)i; + if (ch == ' ' || ch == '\t') + continue; + var cat = char.GetUnicodeCategory(ch); + if (cat == UnicodeCategory.Control || cat == UnicodeCategory.LineSeparator) + continue; + if (table[i] == 0) + { + hasAllPrintable = false; + } + } + + if (hasAllPrintable) + { + for (int i = 0; i < table.Length; i++) + { + var ch = (char)i; + if (ch == ' ' || ch == '\t') + continue; + var cat = char.GetUnicodeCategory(ch); + if (cat == UnicodeCategory.Control || cat == UnicodeCategory.LineSeparator) + continue; + table[i] = 0; + } + result.Append("\\S"); + } + + if (HasRange('a', 'z') && HasRange('A', 'Z')) + { + RemoveRange('a', 'z'); + RemoveRange('A', 'Z'); + result.Append("\\w"); + } + + if (HasRange('0', '9')) + { + RemoveRange('0', '9'); + result.Append("\\d"); + } + + if (Has(" \t\r\n")) + { + result.Append("\\s"); + table[' '] = 0; + table['\t'] = 0; + table['\r'] = 0; + table['\n'] = 0; + table['\f'] = 0; + } + + for (int i = 0; i < table.Length; i++) + { + int cur = table[i]; + if (cur != 0) + { + AddChar(result, (char)i); + int j = i + 1; + for (; j < table.Length; j++) + { + if (table[j] == 0) + break; + } + if (j - i > 3) + { + result.Append("-"); + AddChar(result, (char)(j - 1)); + i = j - 1; + } + } + } + foreach (var grp in unicodeGroups) + { + result.Append("\\p{"); + result.Append(grp); + result.Append("}"); + } + result.Append(']'); + return result.ToString(); + } + + void AddChar(StringBuilder result, char ch) + { + if ("[]\\".Contains(ch)) + { + result.Append('\\'); + result.Append(ch); + return; + } + switch (ch) + { + case '\a': + result.Append("\\a"); + break; + case '\b': + result.Append("\\b"); + break; + case '\t': + result.Append("\\t"); + break; + case '\r': + result.Append("\\r"); + break; + case '\v': + result.Append("\\v"); + break; + case '\f': + result.Append("\\f"); + break; + case '\n': + result.Append("\\n"); + break; + default: + result.Append(ch); + break; + } + } + + void ConvertUnicodeCategory(string category) + { + switch (category) + { + case "alnum": // Alphabetic and numeric character + //return inCharacterClass ? "\\w\\d": "[\\w\\d]"; + AddRange('a', 'z'); + AddRange('A', 'Z'); + AddRange('0', '9'); + break; + case "alpha": // Alphabetic character + AddRange('a', 'z'); + AddRange('A', 'Z'); + break; + case "blank": // Space or tab + Add(' '); + Add('\t'); + break; + case "cntrl": // Control character + AddUnicodeCategory(UnicodeCategory.Control); + break; + case "digit": // Digit + AddRange('0', '9'); + break; + case "graph": // Non - blank character (excludes spaces, control characters, and similar) + for (int i = 0; i < table.Length; i++) + { + var ch = (char)i; + if (ch == ' ' || ch == '\t') + continue; + var cat = char.GetUnicodeCategory(ch); + if (cat == UnicodeCategory.Control || cat == UnicodeCategory.LineSeparator) + continue; + table[i] = negativeGroup ? -1 : 1; + } + break; + case "lower": // Lowercase alphabetical character + AddRange('a', 'z'); + break; + case "print": // Like [:graph:], but includes the space character + for (int i = 0; i < table.Length; i++) + { + var ch = (char)i; + var cat = char.GetUnicodeCategory(ch); + if (cat == UnicodeCategory.Control || cat == UnicodeCategory.LineSeparator) + continue; + table[i] = negativeGroup ? -1 : 1; + } + break; + case "punct": // Punctuation character + AddUnicodeCategory(UnicodeCategory.OpenPunctuation, UnicodeCategory.ClosePunctuation, UnicodeCategory.DashPunctuation, + UnicodeCategory.OtherPunctuation, UnicodeCategory.ConnectorPunctuation, UnicodeCategory.FinalQuotePunctuation, UnicodeCategory.InitialQuotePunctuation); + break; + case "space": // Whitespace character ([:blank:], newline, carriage return, etc.) + Add(' '); + Add('\t'); + Add('\r'); + Add('\n'); + break; + case "upper": // Uppercase alphabetical + AddRange('A', 'Z'); + break; + case "xdigit": // Digit allowed in a hexadecimal number (i.e., 0 - 9a - fA - F) + AddRange('a', 'f'); + AddRange('A', 'F'); + AddRange('0', '9'); + break; + default: + //LoggingService.LogWarning("unknown unicode category : " + category); + break; + } + } + } + + class Group + { + public string Id { get; set; } + public StringBuilder groupContent = new StringBuilder(); + public Group(string id) + { + Id = id; + } + } + + // translates ruby regex -> .NET regexes + internal static string CompileRegex(string regex) + { + if (string.IsNullOrEmpty(regex)) + return regex; + regex = StripRegexComments(regex); + var result = new StringBuilder(); + + var charProperty = new StringBuilder(); + + int characterClassLevel = 0; + bool escape = false; + bool readCharacterProperty = false, readCharPropertyIdentifier = false, replaceGroup = false, skipRecordChar = false; + bool readPlusQuantifier = false, readStarQuantifier = false, readGroupName = false, recordGroupName = false; + StringBuilder curGroupName = new StringBuilder(); + var groups = new List(); + var groupStack = new Stack(); + int groupNumber = 1; + CharacterClass curClass = null; + for (int i = 0; i < regex.Length; i++) + { + var ch = regex[i]; + switch (ch) + { + case '+': + if (readPlusQuantifier) + continue; + if (readStarQuantifier) + { + continue; + } + readPlusQuantifier = true; + break; + case '*': + if (readStarQuantifier) + continue; + if (readPlusQuantifier && result.Length > 0) + { + result.Length--; + if (!recordGroupName && groupStack.Count > 0 && groupStack.Peek().groupContent.Length > 0) + { + groupStack.Peek().groupContent.Length--; + } + } + + readStarQuantifier = true; + break; + case '\\': + if (curClass != null) + { + escape = !escape; + goto addChar; + } + if (escape) + break; + if (i + 1 >= regex.Length) + break; + var next = regex[i + 1]; + if (next == 'h') + { + result.Append("[0-9a-fA-F]"); + i++; + continue; + } + if (next == 'H') + { + result.Append("[^0-9a-fA-F]"); + i++; + continue; + } + if (next == 'p') + { + i++; + readCharacterProperty = true; + readCharPropertyIdentifier = false; + continue; + } + if (next == 'g') + { + i++; + replaceGroup = true; + readGroupName = true; + continue; + } + escape = true; + goto addChar; + case '(': + if (escape) + break; + groupStack.Push(new Group(groupNumber.ToString())); + groupNumber++; + skipRecordChar = true; + break; + case '>': + recordGroupName = false; + if (replaceGroup) + { + bool foundGroup = false; + result.Append("\\k<"); + result.Append(curGroupName.ToString()); + result.Append(">"); + + replaceGroup = false; + curGroupName.Length = 0; + continue; + } + if (groupStack.Count > 0) + groupStack.Peek().Id = curGroupName.ToString(); + skipRecordChar = true; + curGroupName.Length = 0; + break; + case '<': + if (readGroupName) + { + recordGroupName = true; + readGroupName = false; + } + break; + case '?': + if (groupStack.Count > 0 && result[result.Length - 1] == '(') + { + readGroupName = true; + groupNumber--; + skipRecordChar = true; + } + break; + case ')': + if (escape) + break; + if (groupStack.Count > 0) + groups.Add(groupStack.Pop()); + break; + case '[': + if (escape) + break; + characterClassLevel++; + if (curClass == null) + { + curClass = new CharacterClass(); + continue; + } + break; + case ']': + if (escape) + break; + if (characterClassLevel > 0) + { + characterClassLevel--; + if (characterClassLevel == 0) + { + var cg = curClass.Generate(); + result.Append(cg); + if (!recordGroupName && groupStack.Count > 0) + groupStack.Peek().groupContent.Append(cg); + curClass = null; + continue; + } + } + break; + } + escape = false; + addChar: + if (recordGroupName) + { + if (ch == '-') + ch = '_'; + if (ch != '<') + { + curGroupName.Append(ch); + } + } + if (replaceGroup) + continue; + if (ch != '?') + readGroupName = false; + if (ch != '+') + readPlusQuantifier = false; + if (ch != '*') + readStarQuantifier = false; + if (readCharacterProperty) + { + if (ch == '}') + { + result.Append(ConvertCharacterProperty(charProperty.ToString())); + charProperty.Length = 0; + readCharacterProperty = false; + } + else if (ch == '{') + { + if (readCharPropertyIdentifier) + { + //LoggingService.LogWarning("invalid regex character property group " + regex); + } + + readCharPropertyIdentifier = true; + } + else + { + if (readCharPropertyIdentifier) + { + charProperty.Append(ch); + } + else + { + //LoggingService.LogWarning("invalid regex character property group " + regex); + } + } + continue; + } + if (curClass != null) + { + curClass.Push(ch); + } + else + { + result.Append(ch); + if (!recordGroupName && groupStack.Count > 0) + { + if (!skipRecordChar) + groupStack.Peek().groupContent.Append(ch); + } + skipRecordChar = false; + } + } + return result.ToString(); + } + + static string StripRegexComments(string regex) + { + var result = new StringBuilder(); + bool inCommment = false; + bool escape = false; + bool wasWhitespace = false; + foreach (var ch in regex) + { + if (inCommment) + { + if (ch == '\n') + inCommment = false; + continue; + } + if (escape) + { + escape = false; + result.Append(ch); + continue; + } + if (ch == '\\') + { + escape = true; + } + if (ch == '#' && wasWhitespace) + { + inCommment = true; + continue; + } + wasWhitespace = ch == ' ' || ch == '\t'; + result.Append(ch); + } + return result.ToString(); + } + + static string ConvertCharacterProperty(string property) + { + switch (property) + { + case "Alnum": + return "[a-zA-Z0-9]"; + case "Alpha": + return "[a-zA-Z]"; + case "Blank": + return "[\\t ]"; + case "Cntrl": + return "\\p{P}"; + case "Digit": + return "\\d"; + case "Graph": + return "\\S"; + case "Lower": + return "[a-z]"; + case "Print": + return "[\\S ]"; + case "Punct": + return "\\p{P}"; + case "Space": + return "\\s"; + case "Upper": + return "[A-Z]"; + case "XDigit": + return "[0-9a-fA-F]"; + case "Word": + return "\\w"; + case "ASCII": + return "[\\x00-\\x7F]"; + case "Any": + return "."; + case "Assigned": // TODO + return "."; + default: + // assume unicode character category (that's supported by C# regexes) + return "\\p{" + property + "}"; + } + } + + + /*internal static TmSnippet ReadSnippet(Stream stream) + { + string name = null; + string content = null; + string tabTrigger = null; + var scopes = new List(); + using (var reader = XmlReader.Create(stream)) + { + while (reader.Read()) + { + if (reader.NodeType != XmlNodeType.Element) + continue; + switch (reader.LocalName) + { + case "content": + if (reader.Read()) + content = reader.Value; + break; + case "tabTrigger": + if (reader.Read()) + tabTrigger = reader.Value; + break; + case "scope": + if (reader.Read()) + scopes.Add(StackMatchExpression.Parse(reader.Value)); + break; + case "description": + if (reader.Read()) + name = reader.Value; + break; + } + } + } + return new TmSnippet(name, scopes, content, tabTrigger); + }*/ + } + +} diff --git a/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/SyntaxHighlighting.cs b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/SyntaxHighlighting.cs new file mode 100644 index 0000000000..c5b49cffd2 --- /dev/null +++ b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/SyntaxHighlighting.cs @@ -0,0 +1,784 @@ +using AvaloniaEdit.Document; +using AvalonStudio.Languages; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; + +namespace AvalonStudio.Controls.Standard.CodeEditor.Highlighting +{ + public class OffsetSyntaxHighlightingData : IComparable, ISegment + { + public OffsetSyntaxHighlightingData() + { + + } + + public OffsetSyntaxHighlightingData(ISegment segment) + { + Offset = segment.Offset; + Length = segment.Length; + } + + public OffsetSyntaxHighlightingData(int offset, int length) + { + Offset = offset; + Length = length; + } + + public int Offset { get; set; } + public int Length { get; set; } + public int EndOffset => Offset + Length; + + public int CompareTo(OffsetSyntaxHighlightingData other) + { + if (Offset > other.Offset) + { + return 1; + } + + if (Offset == other.Offset) + { + return 0; + } + + return -1; + } + } + + public class ColoredSegment : OffsetSyntaxHighlightingData + { + readonly ScopeStack scopeStack; + + internal ScopeStack ScopeStack + { + get + { + return scopeStack; + } + } + + public string ColorStyleKey + { + get + { + return scopeStack.IsEmpty ? "" : scopeStack.Peek(); + } + } + + public ColoredSegment(int offset, int length, ScopeStack scopeStack) : base(offset, length) + { + this.scopeStack = scopeStack; + } + + public ColoredSegment(ISegment segment, ScopeStack scopeStack) : base(segment) + { + this.scopeStack = scopeStack; + } + + public ColoredSegment WithOffsetAndLength(int offset, int length) + { + return new ColoredSegment(offset, length, scopeStack); + } + + public ColoredSegment WithOffset(int offset) + { + return new ColoredSegment(offset, Length, scopeStack); + } + + public ColoredSegment WithLength(int length) + { + return new ColoredSegment(Offset, length, scopeStack); + } + } + + public class LineEventArgs : EventArgs + { + readonly IDocumentLine line; + + public IDocumentLine Line + { + get + { + return line; + } + } + + public LineEventArgs(IDocumentLine line) + { + if (line == null) + throw new ArgumentNullException("line"); + this.line = line; + } + } + + + public sealed class ScopeStack : IEnumerable + { + public static readonly ScopeStack Empty = new ScopeStack(null, ImmutableStack.Empty, 0, null); + + public string FirstElement + { + get; + private set; + } + + public bool IsEmpty + { + get + { + return stack.IsEmpty; + } + } + + public int Count + { + get; + private set; + } + + ImmutableStack stack; + ScopeStack parent; + + public ScopeStack(string first) + { + FirstElement = first; + stack = ImmutableStack.Empty.Push(first); + parent = ScopeStack.Empty; + Count = 1; + } + + // The reasoning for having a constructor which takes a parent is that we want to make allocations + // only onthe less common operation - Push. Having the allocation of the parent ScopeStack happening + // on Pop, rather than retaining the one on Push, would yield an allocation hot loop, given that: + // We already allocate it once on push. + // We pass the same scope stack multiple times. + ScopeStack(string first, ImmutableStack immutableStack, int count, ScopeStack parent) + { + this.FirstElement = first; + this.stack = immutableStack; + this.Count = count; + this.parent = parent; + } + + public ScopeStack Push(string item) + { + return new ScopeStack(FirstElement, stack.Push(item), Count + 1, this); + } + + public ScopeStack Pop() + { + if (parent == null) + throw new InvalidOperationException("ScopeStack is empty"); + return parent; + } + + public string Peek() + { + return stack.Peek(); + } + + public ImmutableStack.Enumerator GetEnumerator() + { + return stack.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)stack).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)stack).GetEnumerator(); + } + } + + + public sealed class HighlightedLine + { + public ISegment TextSegment { get; } + /// + /// The segment offsets are 0 at line start regardless of where the line is inside the document. + /// + public IReadOnlyList Segments { get; private set; } + + bool? isContinuedBeyondLineEnd; + public bool? IsContinuedBeyondLineEnd + { + get + { + if (isContinuedBeyondLineEnd.HasValue) + return isContinuedBeyondLineEnd.Value; + var result = Segments.Count > 0 ? TextSegment.Length < Segments.Last().EndOffset : false; + return isContinuedBeyondLineEnd = result; + } + set + { + isContinuedBeyondLineEnd = value; + } + } + + public HighlightedLine(ISegment textSegment, IReadOnlyList segments) + { + TextSegment = textSegment; + Segments = segments; + } + } + + /// + /// The basic interface for all syntax modes + /// + public interface ISyntaxHighlighting : IDisposable + { + /// + /// Gets colorized segments (aka chunks) from offset to offset + length. + /// + /// + /// The starting line at (offset). This is the same as Document.GetLineByOffset (offset). + /// + Task GetHighlightedLineAsync(IDocumentLine line, CancellationToken cancellationToken); + Task GetScopeStackAsync(int offset, CancellationToken cancellationToken); + + event EventHandler HighlightingStateChanged; + } + + public sealed class DefaultSyntaxHighlighting : ISyntaxHighlighting + { + public static readonly DefaultSyntaxHighlighting Instance = new DefaultSyntaxHighlighting(); + + DefaultSyntaxHighlighting() + { + } + + public Task GetHighlightedLineAsync(IDocumentLine line, CancellationToken cancellationToken) + { + return Task.FromResult(new HighlightedLine(line, new[] { new ColoredSegment(0, line.Length, ScopeStack.Empty) })); + } + + public Task GetScopeStackAsync(int offset, CancellationToken cancellationToken) + { + return Task.FromResult(ScopeStack.Empty); + } + + public event EventHandler HighlightingStateChanged { add { } remove { } } + + public void Dispose() + { + } + } + + + public class SyntaxHighlighting : ISyntaxHighlighting + { + readonly SyntaxHighlightingDefinition definition; + + public IDocument Document + { + get; + set; + } + + internal SyntaxHighlightingDefinition Definition + { + get + { + return definition; + } + } + + public SyntaxHighlighting(SyntaxHighlightingDefinition definition, IDocument document) + { + this.definition = definition; + Document = document; + + document.TextChanged += Handle_TextChanged; + } + + public void Dispose() + { + Document.TextChanged -= Handle_TextChanged; + } + + void Handle_TextChanged(object sender, TextChangeEventArgs e) + { + var newOffset = e.GetNewOffset(e.Offset); + var ln = Document.GetLineByOffset(newOffset).LineNumber; + if (ln < stateCache.Count) + { + var line = Document.GetLineByOffset(newOffset); + var lastState = GetState(line); + + var high = new Highlighter(this, lastState); + high.GetColoredSegments(Document, line.Offset, line.Length + line.DelimiterLength).Wait(); + OnHighlightingStateChanged(new LineEventArgs(line)); + stateCache.RemoveRange(ln - 1, stateCache.Count - ln + 1); + } + } + + public Task GetHighlightedLineAsync(IDocumentLine line, CancellationToken cancellationToken) + { + if (Document == null) + { + return DefaultSyntaxHighlighting.Instance.GetHighlightedLineAsync(line, cancellationToken); + } + var snapshot = Document.CreateSnapshot(); + var high = new Highlighter(this, GetState(line)); + int offset = line.Offset; + int length = line.Length; + //return Task.Run (async delegate { + return high.GetColoredSegments(snapshot, offset, length); + //}); + } + + public async Task GetScopeStackAsync(int offset, CancellationToken cancellationToken) + { + var line = Document.GetLineByOffset(offset); + var state = GetState(line); + var lineOffset = line.Offset; + if (lineOffset == offset) + { + return state.ScopeStack; + } + + var high = new Highlighter(this, state); + foreach (var seg in (await high.GetColoredSegments(Document, lineOffset, line.Length)).Segments) + { + if (seg.Contains(offset - lineOffset)) + return seg.ScopeStack; + } + return high.State.ScopeStack; + } + + List stateCache = new List(); + + HighlightState GetState(IDocumentLine line) + { + var pl = line?.PreviousLine; + if (pl == null) + return HighlightState.CreateNewState(this); + if (stateCache.Count == 0) + stateCache.Add(HighlightState.CreateNewState(this)); + var ln = line.LineNumber; + if (ln <= stateCache.Count) + { + return stateCache[ln - 1].Clone(); + } + var lastState = stateCache[stateCache.Count - 1]; + var cur = Document.GetLineByNumber(stateCache.Count); + if (cur != null && cur.Offset < line.Offset) + { + do + { + var high = new Highlighter(this, lastState.Clone()); + high.GetColoredSegments(Document, cur.Offset, cur.Length + cur.DelimiterLength).Wait(); + stateCache.Add(lastState = high.State); + cur = cur.NextLine; + } while (cur != null && cur.Offset < line.Offset); + } + + return lastState.Clone(); + } + + + class HighlightState : IEquatable + { + public ImmutableStack ContextStack; + public ImmutableStack MatchStack; + public ScopeStack ScopeStack; + + + public static HighlightState CreateNewState(SyntaxHighlighting highlighting) + { + return new HighlightState + { + ContextStack = ImmutableStack.Empty.Push(highlighting.definition.MainContext), + ScopeStack = new ScopeStack(highlighting.definition.Scope), + MatchStack = ImmutableStack.Empty + }; + } + + + internal HighlightState Clone() + { + return new HighlightState + { + ContextStack = this.ContextStack, + ScopeStack = this.ScopeStack, + MatchStack = this.MatchStack + }; + } + + + public bool Equals(HighlightState other) + { + return ContextStack.SequenceEqual(other.ContextStack) && ScopeStack.SequenceEqual(other.ScopeStack) && MatchStack.SequenceEqual(other.MatchStack); + } + } + + class Highlighter + { + HighlightState state; + SyntaxHighlighting highlighting; + ImmutableStack ContextStack { get { return state.ContextStack; } set { state.ContextStack = value; } } + ImmutableStack MatchStack { get { return state.MatchStack; } set { state.MatchStack = value; } } + ScopeStack ScopeStack { get { return state.ScopeStack; } set { state.ScopeStack = value; } } + + public HighlightState State + { + get + { + return state; + } + } + + public Highlighter(SyntaxHighlighting highlighting, HighlightState state) + { + this.highlighting = highlighting; + this.state = state; + } + + static readonly TimeSpan matchTimeout = TimeSpan.FromMilliseconds(500); + + public Task GetColoredSegments(ITextSource text, int startOffset, int length) + { + if (ContextStack.IsEmpty) + return Task.FromResult(new HighlightedLine(new SimpleSegment(startOffset, length), new[] { new ColoredSegment(0, length, ScopeStack.Empty) })); + SyntaxContext currentContext = null; + List lastContexts = new List(); + Match match = null; + SyntaxMatch curMatch = null; + var segments = new List(); + int offset = 0; + int curSegmentOffset = 0; + int endOffset = offset + length; + int lastMatch = -1; + var highlightedSegment = new SimpleSegment(startOffset, length); + string lineText = text.GetText(startOffset, length); + var initialState = state.Clone(); + int timeoutOccursAt; + unchecked + { + timeoutOccursAt = Environment.TickCount + (int)matchTimeout.TotalMilliseconds; + } + restart: + if (lastMatch == offset) + { + if (lastContexts.Contains(currentContext)) + { + offset++; + length--; + } + else + { + lastContexts.Add(currentContext); + } + } + else + { + lastContexts.Clear(); + lastContexts.Add(currentContext); + } + if (offset >= lineText.Length) + goto end; + lastMatch = offset; + currentContext = ContextStack.Peek(); + match = null; + curMatch = null; + foreach (var m in currentContext.Matches) + { + if (m.GotTimeout) + continue; + var r = m.GetRegex(); + if (r == null) + continue; + try + { + Match possibleMatch; + var pattern = r.ToString(); + + if (pattern == "(?<=\\})" && offset > 0) + { // HACK to fix typescript highlighting. + possibleMatch = r.Match(lineText, offset - 1, length); + } + else + { + possibleMatch = r.Match(lineText, offset, length); + } + if (possibleMatch.Success) + { + if (match == null || possibleMatch.Index < match.Index) + { + match = possibleMatch; + curMatch = m; + // Console.WriteLine (match.Index + " possible match : " + m + "/" + possibleMatch.Index + "-" + possibleMatch.Length); + } + else + { + // Console.WriteLine (match.Index + " skip match : " + m + "/" + possibleMatch.Index + "-" + possibleMatch.Length); + } + } + else + { + // Console.WriteLine ("fail match : " + m); + } + } + catch (RegexMatchTimeoutException) + { + //LoggingService.LogWarning("Warning: Regex " + m.Match + " timed out on line:" + text.GetText(offset, length)); + m.GotTimeout = true; + continue; + } + } + if (length <= 0 && curMatch == null) + goto end; + + /*if (Environment.TickCount >= timeoutOccursAt) + { + curMatch.GotTimeout = true; + goto end; + }*/ + + if (match != null) + { + // Console.WriteLine (match.Index + " taken match : " + curMatch + "/" + match.Index + "-" + match.Length); + var matchEndOffset = match.Index + match.Length; + if (curSegmentOffset < match.Index && match.Length > 0) + { + segments.Add(new ColoredSegment(curSegmentOffset, match.Index - curSegmentOffset, ScopeStack)); + curSegmentOffset = match.Index; + } + if (curMatch.Pop) + { + PopMetaContentScopeStack(currentContext, curMatch); + } + + PushScopeStack(curMatch.Scope); + + if (curMatch.Captures.Groups.Count > 0) + { + for (int i = 0; i < curMatch.Captures.Groups.Count; ++i) + { + var capture = curMatch.Captures.Groups[i]; + var grp = match.Groups[capture.Item1]; + if (grp == null || grp.Length == 0) + continue; + if (curSegmentOffset < grp.Index) + { + ReplaceSegment(segments, new ColoredSegment(curSegmentOffset, grp.Index - curSegmentOffset, ScopeStack)); + } + ReplaceSegment(segments, new ColoredSegment(grp.Index, grp.Length, ScopeStack.Push(capture.Item2))); + curSegmentOffset = Math.Max(curSegmentOffset, grp.Index + grp.Length); + } + } + + if (curMatch.Captures.NamedGroups.Count > 0) + { + for (int i = 0; i < curMatch.Captures.NamedGroups.Count; ++i) + { + var capture = curMatch.Captures.NamedGroups[i]; + var grp = match.Groups[capture.Item1]; + if (grp == null || grp.Length == 0) + continue; + if (curSegmentOffset < grp.Index) + { + ReplaceSegment(segments, new ColoredSegment(curSegmentOffset, grp.Index - curSegmentOffset, ScopeStack)); + } + ReplaceSegment(segments, new ColoredSegment(grp.Index, grp.Length, ScopeStack.Push(capture.Item2))); + curSegmentOffset = grp.Index + grp.Length; + } + } + + if (curMatch.Scope.Count > 0 && curSegmentOffset < matchEndOffset && match.Length > 0) + { + segments.Add(new ColoredSegment(curSegmentOffset, matchEndOffset - curSegmentOffset, ScopeStack)); + curSegmentOffset = matchEndOffset; + } + + if (curMatch.Pop) + { + if (matchEndOffset - curSegmentOffset > 0) + segments.Add(new ColoredSegment(curSegmentOffset, matchEndOffset - curSegmentOffset, ScopeStack)); + //if (curMatch.Scope != null) + // scopeStack = scopeStack.Pop (); + PopStack(currentContext, curMatch); + curSegmentOffset = matchEndOffset; + } + else if (curMatch.Set != null) + { + // if (matchEndOffset - curSegmentOffset > 0) + // segments.Add (new ColoredSegment (curSegmentOffset, matchEndOffset - curSegmentOffset, ScopeStack)); + //if (curMatch.Scope != null) + // scopeStack = scopeStack.Pop (); + PopMetaContentScopeStack(currentContext, curMatch); + PopStack(currentContext, curMatch); + //curSegmentOffset = matchEndOffset; + var nextContexts = curMatch.Set.GetContexts(currentContext); + PushStack(curMatch, nextContexts); + goto skip; + } + else if (curMatch.Push != null) + { + var nextContexts = curMatch.Push.GetContexts(currentContext); + PushStack(curMatch, nextContexts); + } + else + { + if (curMatch.Scope.Count > 0) + { + for (int i = 0; i < curMatch.Scope.Count; i++) + ScopeStack = ScopeStack.Pop(); + } + } + + if (curSegmentOffset < matchEndOffset && match.Length > 0) + { + segments.Add(new ColoredSegment(curSegmentOffset, matchEndOffset - curSegmentOffset, ScopeStack)); + curSegmentOffset = matchEndOffset; + } + skip: + length -= curSegmentOffset - offset; + offset = curSegmentOffset; + goto restart; + } + + end: + if (endOffset - curSegmentOffset > 0) + { + segments.Add(new ColoredSegment(curSegmentOffset, endOffset - curSegmentOffset, ScopeStack)); + } + + return Task.FromResult(new HighlightedLine(highlightedSegment, segments) + { + IsContinuedBeyondLineEnd = !initialState.Equals(state) + }); + } + + void PushStack(SyntaxMatch curMatch, IEnumerable nextContexts) + { + if (nextContexts != null) + { + bool first = true; + foreach (var nextContext in nextContexts) + { + var ctx = nextContext; + if (curMatch.WithPrototype != null) + ctx = new SyntaxContextWithPrototype(nextContext, curMatch.WithPrototype); + + if (first) + { + MatchStack = MatchStack.Push(curMatch); + first = false; + } + else + { + MatchStack = MatchStack.Push(null); + } + ContextStack = ContextStack.Push(ctx); + PushScopeStack(ctx.MetaScope); + PushScopeStack(ctx.MetaContentScope); + } + } + } + + void PushScopeStack(IReadOnlyList scopeList) + { + if (scopeList == null) + return; + for (int i = 0; i < scopeList.Count; ++i) + { + var scope = scopeList[i]; + ScopeStack = ScopeStack.Push(scope); + } + } + + void PopScopeStack(IReadOnlyList scopeList) + { + if (scopeList == null) + return; + for (int i = 0; !ScopeStack.IsEmpty && i < scopeList.Count; i++) + ScopeStack = ScopeStack.Pop(); + } + + void PopMetaContentScopeStack(SyntaxContext currentContext, SyntaxMatch curMatch) + { + if (ContextStack.Count() == 1) + { + return; + } + PopScopeStack(currentContext.MetaContentScope); + } + + void PopStack(SyntaxContext currentContext, SyntaxMatch curMatch) + { + if (ContextStack.Count() == 1) + { + MatchStack = MatchStack.Clear(); + ScopeStack = new ScopeStack(highlighting.definition.Scope); + return; + } + ContextStack = ContextStack.Pop(); + if (!MatchStack.IsEmpty) + { + PopScopeStack(MatchStack.Peek()?.Scope); + MatchStack = MatchStack.Pop(); + } + PopScopeStack(currentContext.MetaScope); + + if (curMatch.Scope.Count > 0 && !ScopeStack.IsEmpty) + { + for (int i = 0; i < curMatch.Scope.Count; i++) + ScopeStack = ScopeStack.Pop(); + } + } + } + + internal static void ReplaceSegment(List list, ColoredSegment newSegment) + { + if (list.Count == 0) + { + list.Add(newSegment); + return; + } + int i = list.Count; + while (i > 0 && list[i - 1].EndOffset > newSegment.Offset) + { + i--; + } + if (i >= list.Count) + { + list.Add(newSegment); + return; + } + + int j = i; + while (j + 1 < list.Count && newSegment.EndOffset > list[j + 1].Offset) + { + j++; + } + var startItem = list[i]; + var endItem = list[j]; + list.RemoveRange(i, j - i + 1); + var lengthAfter = endItem.EndOffset - newSegment.EndOffset; + if (lengthAfter > 0) + list.Insert(i, new ColoredSegment(newSegment.EndOffset, lengthAfter, endItem.ScopeStack)); + + list.Insert(i, newSegment); + var lengthBefore = newSegment.Offset - startItem.Offset; + if (lengthBefore > 0) + list.Insert(i, new ColoredSegment(startItem.Offset, lengthBefore, startItem.ScopeStack)); + } + + void OnHighlightingStateChanged(LineEventArgs e) + { + HighlightingStateChanged?.Invoke(this, e); + } + + public event EventHandler HighlightingStateChanged; + } + +} diff --git a/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/SyntaxHighlightingDefinition.cs b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/SyntaxHighlightingDefinition.cs new file mode 100644 index 0000000000..1bad0cee1a --- /dev/null +++ b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/SyntaxHighlightingDefinition.cs @@ -0,0 +1,436 @@ +using AvalonStudio.Controls.Standard.CodeEditor.Highlighting.Sublime3; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using YamlDotNet.RepresentationModel; + +namespace AvalonStudio.Controls.Standard.CodeEditor.Highlighting +{ + public class SyntaxHighlightingDefinition + { + public string Name { get; internal set; } + + readonly List fileTypes; + public IReadOnlyList FileTypes { get { return fileTypes; } } + + public string Scope { get; internal set; } + + public bool Hidden { get; internal set; } + + public string FirstLineMatch { get; internal set; } + + readonly List contexts; + public IReadOnlyList Contexts { get { return contexts; } } + + public SyntaxContext MainContext + { + get + { + return contexts[0]; + } + } + + internal SyntaxHighlightingDefinition(string name, string scope, string firstLineMatch, bool hidden, List fileTypes, List contexts) + { + this.fileTypes = fileTypes; + this.contexts = contexts; + Name = name; + Scope = scope; + FirstLineMatch = firstLineMatch; + Hidden = hidden; + foreach (var ctx in contexts) + { + ctx.SetDefinition(this); + } + } + + public void PrepareMatches() + { + foreach (var ctx in Contexts) + { + ctx.PrepareMatches(); + } + } + } + + + class SyntaxContextWithPrototype : SyntaxContext + { + SyntaxContext ctx; + + public override IReadOnlyList MetaContentScope { get { return ctx.MetaContentScope; } } + public override IReadOnlyList MetaScope { get { return ctx.MetaScope; } } + public override bool MetaIncludePrototype { get { return ctx.MetaIncludePrototype; } } + + public SyntaxContextWithPrototype(SyntaxContext ctx, ContextReference withPrototype) : base(ctx.Name) + { + this.ctx = ctx; + this.definition = ctx.definition; + matches = new List(ctx.Matches); + foreach (var c in withPrototype.GetContexts(ctx)) + { + if (c.Matches == null) + c.PrepareMatches(); + this.matches.AddRange(c.Matches); + } + } + } + + public class SyntaxContext + { + protected List matches; + internal SyntaxHighlightingDefinition definition; + + public string Name { get; private set; } + + protected List metaScope = new List(); + public virtual IReadOnlyList MetaScope { get { return metaScope; } } + + protected List metaContentScope = new List(); + public virtual IReadOnlyList MetaContentScope { get { return metaContentScope; } } + + + public virtual bool MetaIncludePrototype { get; private set; } + + public virtual IEnumerable Matches { get { return matches; } } + + readonly List includesAndMatches; + + internal void ParseMapping(YamlSequenceNode seqNode, Dictionary variables) + { + if (seqNode != null) + { + foreach (var node in seqNode.Children.OfType()) + { + ParseMapping(node, variables); + } + } + + //var scalarNode = mapping.Value as YamlScalarNode; + //if (scalarNode != null) { + // Console.WriteLine (mapping.Key +"/"+scalarNode.Value); + //} + } + + internal void ParseMapping(YamlMappingNode node, Dictionary variables) + { + var children = node.Children; + if (children.ContainsKey(new YamlScalarNode("match"))) + { + includesAndMatches.Add(Sublime3Format.ReadMatch(node, variables)); + return; + } + + YamlNode val; + if (children.TryGetValue(new YamlScalarNode("meta_scope"), out val)) + { + Sublime3Format.ParseScopes(metaScope, ((YamlScalarNode)val).Value); + } + if (children.TryGetValue(new YamlScalarNode("meta_content_scope"), out val)) + { + Sublime3Format.ParseScopes(metaContentScope, ((YamlScalarNode)val).Value); + } + if (children.TryGetValue(new YamlScalarNode("meta_include_prototype"), out val)) + { + MetaIncludePrototype = ((YamlScalarNode)val).Value != "false"; + } + if (children.TryGetValue(new YamlScalarNode("include"), out val)) + { + includesAndMatches.Add(((YamlScalarNode)val).Value); + } + } + + internal SyntaxContext(string name) + { + Name = name; + includesAndMatches = new List(); + MetaIncludePrototype = true; + } + + internal SyntaxContext(string name, List includesAndMatches, IReadOnlyList metaScope = null, IReadOnlyList metaContentScope = null, bool metaIncludePrototype = true) + { + this.includesAndMatches = includesAndMatches; + Name = name; + if (metaScope != null) + this.metaScope.AddRange(metaScope); + if (metaContentScope != null) + this.metaContentScope.AddRange(metaContentScope); + + MetaIncludePrototype = metaIncludePrototype; + } + + internal virtual SyntaxContext GetContext(string name) + { + if (name.StartsWith("scope:", StringComparison.Ordinal)) + { + var splittedNames = name.Substring("scope:".Length).Split(new[] { '#' }, StringSplitOptions.RemoveEmptyEntries); + if (splittedNames.Length == 0) + return null; + foreach (var bundle in SyntaxHighlightingService.AllBundles) + { + foreach (var highlighting in bundle.Highlightings) + { + if (highlighting.Scope == splittedNames[0]) + { + var searchName = splittedNames.Length == 1 ? "main" : splittedNames[1]; + foreach (var ctx in highlighting.Contexts) + { + if (ctx.Name == searchName) + { + return ctx; + } + } + } + } + } + return null; + } + foreach (var ctx in definition.Contexts) + { + if (ctx.Name == name) + return ctx; + } + return null; + } + + IEnumerable GetMatches() + { + return GetMatches(new List()); + } + + IEnumerable GetMatches(List alreadyIncluded) + { + foreach (var o in includesAndMatches) + { + var match = o as SyntaxMatch; + if (match != null) + { + yield return match; + continue; + } + var include = o as string; + var ctx = GetContext(include); + if (ctx == null) + { + // LoggingService.LogWarning ($"highlighting {definition.Name} can't find include {include}."); + continue; + } + if (alreadyIncluded.Contains(include)) + continue; + alreadyIncluded.Add(include); + foreach (var match2 in ctx.GetMatches(alreadyIncluded)) + yield return match2; + } + } + + internal void AddMatch(SyntaxMatch match) + { + this.matches.Add(match); + } + + internal void SetDefinition(SyntaxHighlightingDefinition definition) + { + this.definition = definition; + foreach (var o in includesAndMatches) + { + var match = o as SyntaxMatch; + if (match != null) + { + if (match.Push is AnonymousMatchContextReference) + ((AnonymousMatchContextReference)match.Push).Context.SetDefinition(definition); + if (match.Set is AnonymousMatchContextReference) + ((AnonymousMatchContextReference)match.Set).Context.SetDefinition(definition); + } + } + } + + internal void PrepareMatches() + { + if (this.matches != null) + return; + var preparedMatches = new List(); + IEnumerable list = includesAndMatches; + if (MetaIncludePrototype && Name != "prototype") + { + var prototypeContext = GetContext("prototype"); + if (prototypeContext != null) + list = list.Concat(prototypeContext.GetMatches()); + } + foreach (var o in list) + { + var match = o as SyntaxMatch; + if (match != null) + { + if (match.Push is AnonymousMatchContextReference) + match.Push.GetContexts(this).First().PrepareMatches(); + if (match.Set is AnonymousMatchContextReference) + match.Set.GetContexts(this).First().PrepareMatches(); + preparedMatches.Add(match); + continue; + } + var include = o as string; + var ctx = GetContext(include); + if (ctx == null) + { + // LoggingService.LogWarning ($"highlighting {definition.Name} can't find include {include}."); + continue; + } + preparedMatches.AddRange(ctx.GetMatches()); + } + this.matches = preparedMatches; + } + + public override string ToString() + { + return string.Format("[SyntaxContext: Name={0}, MetaScope={1}, MetaContentScope={2}, MetaIncludePrototype={3}]", Name, MetaScope.Count == 0 ? "empty" : string.Join(", ", MetaScope), MetaContentScope.Count == 0 ? "empty" : string.Join(", ", MetaContentScope), MetaIncludePrototype); + } + + public SyntaxContext Clone() + { + return (SyntaxContext)this.MemberwiseClone(); + } + } + + public class Captures + { + public static readonly Captures Empty = new Captures(new List>(), new List>()); + public IReadOnlyList> Groups { get; private set; } + public IReadOnlyList> NamedGroups { get; private set; } + + public Captures(IReadOnlyList> groups) + { + Groups = groups; + NamedGroups = Captures.Empty.NamedGroups; + } + + public Captures(IReadOnlyList> groups, IReadOnlyList> namedGroups) + { + Groups = groups; + NamedGroups = namedGroups; + } + } + + public class SyntaxMatch + { + public string Match { get; private set; } + public IReadOnlyList Scope { get; private set; } + public Captures Captures { get; private set; } + public ContextReference Push { get; private set; } + public bool Pop { get; private set; } + public ContextReference Set { get; private set; } + public ContextReference WithPrototype { get; private set; } + internal bool GotTimeout { get; set; } + + internal SyntaxMatch(string match, IReadOnlyList scope, Captures captures, ContextReference push, bool pop, ContextReference set, ContextReference withPrototype) + { + Match = match; + Scope = scope; + Captures = captures ?? Captures.Empty; + Push = push; + Pop = pop; + Set = set; + WithPrototype = withPrototype; + } + + public override string ToString() + { + return string.Format("[SyntaxMatch: Match={0}, Scope={1}]", Match, Scope.Count == 0 ? "empty" : string.Join(", ", Scope)); + } + + bool hasRegex; + Regex cachedRegex; + object lockObj = new object(); + + internal Regex GetRegex() + { + if (hasRegex) + return cachedRegex; + + lock (lockObj) + { + if (hasRegex) + return cachedRegex; + hasRegex = true; + try + { + cachedRegex = new Regex(Match); + } + catch (Exception e) + { + //LoggingService.LogWarning("Warning regex : '" + Match + "' can't be parsed.", e); + } + return cachedRegex; + } + } + } + + public abstract class ContextReference + { + public abstract IEnumerable GetContexts(SyntaxContext context); + } + + public class ContextNameContextReference : ContextReference + { + public string Name { get; private set; } + + internal ContextNameContextReference(string value) + { + this.Name = value; + } + + public override IEnumerable GetContexts(SyntaxContext context) + { + var localContext = context.GetContext(Name); + if (localContext != null) + { + yield return localContext; + yield break; + } + + foreach (var bundle in SyntaxHighlightingService.AllBundles) + { + foreach (var highlighting in bundle.Highlightings) + { + if (highlighting.Name == Name) + { + yield return highlighting.MainContext; + } + } + } + } + } + + public class ContextNameListContextReference : ContextReference + { + public ContextNameListContextReference(IReadOnlyList names) + { + this.Names = names; + } + + public IReadOnlyList Names { get; private set; } + + public override IEnumerable GetContexts(SyntaxContext context) + { + foreach (var name in Names) + yield return context.GetContext(name); + } + } + + public class AnonymousMatchContextReference : ContextReference + { + public SyntaxContext Context { get; private set; } + + internal AnonymousMatchContextReference(SyntaxContext context) + { + Context = context; + } + + public override IEnumerable GetContexts(SyntaxContext context) + { + yield return Context; + } + } + +} diff --git a/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/SyntaxHighlightingService.cs b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/SyntaxHighlightingService.cs new file mode 100644 index 0000000000..7efac32f5b --- /dev/null +++ b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/SyntaxHighlightingService.cs @@ -0,0 +1,52 @@ +using AvalonStudio.Controls.Standard.CodeEditor.Highlighting.TextMate; +using System; +using System.Collections.Generic; +using System.IO; + +namespace AvalonStudio.Controls.Standard.CodeEditor.Highlighting +{ + public static class SyntaxHighlightingService + { + static LanguageBundle builtInBundle = new LanguageBundle("default", null); + static List languageBundles = new List(); + + internal static IEnumerable AllBundles + { + get + { + return languageBundles; + } + } + + static object LoadFile(LanguageBundle bundle, string file, Func openStream) + { + if (file.EndsWith(".tmLanguage", StringComparison.OrdinalIgnoreCase)) + { + + using (var stream = openStream()) + { + var highlighting = TextMateFormat.ReadHighlighting(stream); + + if (highlighting != null) + bundle.Add(highlighting); + + return highlighting; + + } + } + + return null; + } + + static void PrepareMatches() + { + foreach (var bundle in languageBundles) + { + foreach (var h in bundle.Highlightings) + h.PrepareMatches(); + } + } + + + } +} diff --git a/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/TextMate/TextMateFormat.cs b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/TextMate/TextMateFormat.cs new file mode 100644 index 0000000000..1c4dc5c09e --- /dev/null +++ b/AvalonStudio/AvalonStudio.Controls.Standard/CodeEditor/Highlighting/TextMate/TextMateFormat.cs @@ -0,0 +1,608 @@ +// Author: +// Mike Krüger +// +// Copyright (c) 2016 Xamarin Inc. (http://xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using AvalonStudio.Controls.Standard.CodeEditor.Highlighting.Sublime3; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml.Linq; +using System.Xml.XPath; + +namespace AvalonStudio.Controls.Standard.CodeEditor.Highlighting.TextMate +{ + public static class TextMateFormat + { + #region Themes + + /*public static EditorTheme LoadEditorTheme(Stream stream) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + var dictionary = PDictionary.FromStream(stream); + var name = (PString)dictionary["name"]; + var contentArray = dictionary["settings"] as PArray; + if (contentArray == null || contentArray.Count == 0) + return new EditorTheme(name); + var settings = new List(); + for (int i = 0; i < contentArray.Count; i++) + { + var dict = contentArray[i] as PDictionary; + if (dict == null) + continue; + var themeSetting = LoadThemeSetting(dict); + if (i == 0) + themeSetting = CalculateMissingDefaultColors(themeSetting); + settings.Add(themeSetting); + } + + var uuid = (PString)dictionary["uuid"]; + + var methodDecl = GetSetting(settings, EditorThemeColors.UserMethodDeclaration); + var methodUsage = GetSetting(settings, EditorThemeColors.UserMethodUsage); + if (methodUsage == null && methodDecl != null) + { + settings.Add(new ThemeSetting( + "User Method(Usage)", + new List { EditorThemeColors.UserMethodUsage }, + settings[0].Settings + )); + } + ConvertSetting(settings, "storage.type", EditorThemeColors.UserTypesInterfaces); + ConvertSetting(settings, "entity.name", EditorThemeColors.UserTypes); + ConvertSetting(settings, "entity.name", EditorThemeColors.UserTypesEnums); + ConvertSetting(settings, "entity.name", EditorThemeColors.UserTypesMutable); + ConvertSetting(settings, "entity.name", EditorThemeColors.UserTypesDelegates); + ConvertSetting(settings, "entity.name", EditorThemeColors.UserTypesValueTypes); + ConvertSetting(settings, "entity.name", EditorThemeColors.UserTypesTypeParameters); + ConvertSetting(settings, "support.constant", "markup.other"); + ConvertSetting(settings, "constant.character", "meta.preprocessor"); + settings.Add(new ThemeSetting("", new List { "meta.preprocessor.region.name" }, settings[0].Settings)); + + // set all remaining semantic colors to default. + var semanticColors = new[] { + EditorThemeColors.UserTypes, + EditorThemeColors.UserTypesValueTypes, + EditorThemeColors.UserTypesInterfaces, + EditorThemeColors.UserTypesEnums, + EditorThemeColors.UserTypesTypeParameters, + EditorThemeColors.UserTypesDelegates, + EditorThemeColors.UserTypesMutable, + EditorThemeColors.UserFieldDeclaration, + EditorThemeColors.UserFieldUsage, + EditorThemeColors.UserPropertyDeclaration, + EditorThemeColors.UserPropertyUsage, + EditorThemeColors.UserEventDeclaration, + EditorThemeColors.UserEventUsage, + EditorThemeColors.UserMethodDeclaration, + EditorThemeColors.UserMethodUsage, + EditorThemeColors.UserParameterDeclaration, + EditorThemeColors.UserParameterUsage, + EditorThemeColors.UserVariableDeclaration, + EditorThemeColors.UserVariableUsage + }; + foreach (var semanticColor in semanticColors) + { + if (GetSetting(settings, semanticColor) == null) + { + settings.Add(new ThemeSetting("", new List { semanticColor }, settings[0].Settings)); + } + } + + return new EditorTheme(name, settings, uuid); + } + + static void ConvertSetting(List settings, string fromSetting, string toSetting) + { + var fs = GetSetting(settings, fromSetting, false); + var ts = GetSetting(settings, toSetting); + if (ts == null && fs != null) + { + settings.Add(new ThemeSetting( + "Copied From (" + fs + ")", + new List { toSetting }, + fs.Settings + )); + } + } + + static ThemeSetting GetSetting(List settings, string scope, bool exact = true) + { + ThemeSetting result = null; + string cs = null; + int d = 0; + var stack = new ScopeStack(scope); + foreach (var s in settings.Skip(1)) + { + if (s.Scopes.Any(a => EditorTheme.IsCompatibleScope(a, stack, ref cs, ref d))) + { + result = s; + } + } + return result; + } + + public static void Save(TextWriter writer, EditorTheme theme) + { + writer.WriteLine(""); + writer.WriteLine(""); + writer.WriteLine(""); + writer.WriteLine(""); + writer.WriteLine("\tname"); + writer.WriteLine("\t" + Ambience.EscapeText(theme.Name) + ""); + writer.WriteLine("\tsettings"); + writer.WriteLine("\t"); + foreach (var setting in theme.Settings) + { + writer.WriteLine("\t\t"); + if (setting.Name != null) + { + writer.WriteLine("\t\t\tname"); + writer.WriteLine("\t\t\t" + Ambience.EscapeText(setting.Name) + ""); + } + if (setting.Scopes.Count > 0) + { + writer.WriteLine("\t\t\tscope"); + writer.WriteLine("\t\t\t" + Ambience.EscapeText(string.Join(", ", setting.Scopes)) + ""); + } + if (setting.Settings.Count > 0) + { + writer.WriteLine("\t\t\tsettings"); + writer.WriteLine("\t\t\t"); + foreach (var kv in setting.Settings) + { + writer.WriteLine("\t\t\t\t" + Ambience.EscapeText(kv.Key) + ""); + writer.WriteLine("\t\t\t\t" + Ambience.EscapeText(kv.Value) + ""); + } + writer.WriteLine("\t\t\t"); + } + writer.WriteLine("\t\t"); + } + writer.WriteLine("\t"); + writer.WriteLine("\tuuid"); + writer.WriteLine("\t" + theme.Uuid + ""); + writer.WriteLine(""); + writer.WriteLine(""); + } + + static ThemeSetting LoadThemeSetting(PDictionary dict) + { + string name = null; + var scopes = new List(); + var settings = new Dictionary(); + + PObject val; + if (dict.TryGetValue("name", out val)) + name = ((PString)val).Value; + if (dict.TryGetValue("scope", out val)) + { + var scope = ((PString)val).Value; + scopes.Add(scope); + } + if (dict.TryGetValue("settings", out val)) + { + var settingsDictionary = val as PDictionary; + foreach (var setting in settingsDictionary) + { + settings.Add(setting.Key, ((PString)setting.Value).Value); + } + } + + return new ThemeSetting(name, scopes, settings); + } + + internal static TmSetting ReadPreferences(Stream stream) + { + var dict = PDictionary.FromStream(stream); + return ReadPreferences(dict); + } + + internal static TmSetting ReadPreferences(PDictionary dict) + { + string name = null; + var scopes = new List(); + var settings = new Dictionary(); + + PObject val; + if (dict.TryGetValue("name", out val)) + name = ((PString)val).Value; + if (dict.TryGetValue("scope", out val)) + { + foreach (var scope in ((PString)val).Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) + { + scopes.Add(StackMatchExpression.Parse(scope)); + } + } + if (dict.TryGetValue("settings", out val)) + { + var settingsDictionary = val as PDictionary; + foreach (var setting in settingsDictionary) + { + settings.Add(setting.Key, setting.Value); + } + } + + return new TmSetting(name, scopes, settings); + } + + internal static TmSnippet ReadSnippet(Stream stream) + { + var dict = PDictionary.FromStream(stream); + + string name = null; + string content = null; + string tabTrigger = null; + var scopes = new List(); + + PObject val; + if (dict.TryGetValue("name", out val)) + name = ((PString)val).Value; + if (dict.TryGetValue("content", out val)) + content = ((PString)val).Value; + if (dict.TryGetValue("tabTrigger", out val)) + tabTrigger = ((PString)val).Value; + if (dict.TryGetValue("scope", out val)) + { + foreach (var scope in ((PString)val).Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) + { + scopes.Add(StackMatchExpression.Parse(scope)); + } + } + + return new TmSnippet(name, scopes, content, tabTrigger); + } + + static ThemeSetting CalculateMissingDefaultColors(ThemeSetting themeSetting) + { + var settings = (Dictionary)themeSetting.Settings; + var bgColor = HslColor.Parse(settings[EditorThemeColors.Background]); + var darkModificator = HslColor.Brightness(bgColor) < 0.5 ? 1 : -1; + + // do some better best-fit calculations + if (!settings.ContainsKey(EditorThemeColors.LineNumbersBackground)) + settings[EditorThemeColors.LineNumbersBackground] = bgColor.AddLight(0.01 * darkModificator).ToPangoString(); + if (!settings.ContainsKey(EditorThemeColors.IndicatorMarginSeparator)) + settings[EditorThemeColors.IndicatorMarginSeparator] = bgColor.AddLight(0.03 * darkModificator).ToPangoString(); + if (!settings.ContainsKey(EditorThemeColors.LineNumbers)) + settings[EditorThemeColors.LineNumbers] = HslColor.Parse(settings[EditorThemeColors.Foreground]).AddLight(-0.1 * darkModificator).ToPangoString(); + if (!settings.ContainsKey(EditorThemeColors.IndicatorMargin)) + settings[EditorThemeColors.IndicatorMargin] = bgColor.AddLight(0.02 * darkModificator).ToPangoString(); + + // copy all missing settings from main template theme + var templateTheme = SyntaxHighlightingService.GetEditorTheme(darkModificator == 1 ? EditorTheme.DefaultDarkThemeName : EditorTheme.DefaultThemeName); + foreach (var kv in templateTheme.Settings[0].Settings) + { + if (settings.ContainsKey(kv.Key)) + continue; + settings.Add(kv.Key, kv.Value); + } + + return new ThemeSetting(themeSetting.Name, themeSetting.Scopes, settings); + }*/ + + #endregion + + #region Syntax highlighting + + public static SyntaxHighlightingDefinition ReadHighlighting(Stream stream) + { + var dictionary = PDictionary.FromStream(stream); + return ReadHighlighting(dictionary); + } + + internal static SyntaxHighlightingDefinition ReadHighlighting(PDictionary dictionary) + { + + string firstLineMatch = null; + var extensions = new List(); + var contexts = new List(); + + var name = (dictionary["name"] as PString)?.Value; + var scope = (dictionary["scopeName"] as PString)?.Value; + var fileTypesArray = dictionary["fileTypes"] as PArray; + if (fileTypesArray != null) + { + foreach (var type in fileTypesArray.OfType()) + { + extensions.Add(type.Value); + } + } + + // Construct main context + var patternsArray = dictionary["patterns"] as PArray; + if (patternsArray != null) + { + var includes = new List(); + ReadPatterns(patternsArray, includes); + contexts.Add(new SyntaxContext("main", includes)); + } + + var repository = dictionary["repository"] as PDictionary; + if (repository != null) + { + ReadRepository(repository, contexts); + } + + // var uuid = (dictionary ["uuid"] as PString)?.Value; + var hideFromUser = (dictionary["hideFromUser"] as PBoolean)?.Value; + return new SyntaxHighlightingDefinition(name, scope, firstLineMatch, hideFromUser == true, extensions, contexts); + } + + private static void ReadRepository(PDictionary repository, List contexts) + { + foreach (var kv in repository) + { + string contextName = kv.Key; + + Debug.WriteLine(kv.Key); + + var includes = new List(); + var contents = kv.Value as PDictionary; + if (contents != null) + { + var newMatch = ReadMatch(contents); + if (newMatch != null) + { + includes.Add(newMatch); + } + else + { + var patternsArray = contents["patterns"] as PArray; + if (patternsArray != null) + { + ReadPatterns(patternsArray, includes); + } + + var repository2 = contents["repository"] as PDictionary; + if (repository2 != null) + ReadRepository(repository2, contexts); + + } + } + + contexts.Add(new SyntaxContext(contextName, includes)); + + + } + } + + static void ReadPatterns(PArray patternsArray, List includesAndMatches) + { + foreach (var type in patternsArray) + { + var dict = type as PDictionary; + if (dict == null) + continue; + var incl = (dict["include"] as PString)?.Value; + if (incl != null) + { + if (incl == "$base") + { + includesAndMatches.Add("main"); + } + else if (incl == "$self") + { + includesAndMatches.Add("main"); + } + else if (incl.StartsWith("#", StringComparison.Ordinal)) + { + includesAndMatches.Add(incl.TrimStart('#')); + } + else + { + includesAndMatches.Add("scope:" + incl); + } + continue; + } + var newMatch = ReadMatch(dict); + if (newMatch != null) + includesAndMatches.Add(newMatch); + + } + } + + static SyntaxMatch ReadMatch(PDictionary dict) + { + List matchScope = new List(); + Sublime3Format.ParseScopes(matchScope, (dict["name"] as PString)?.Value); + + Captures captures = null; + var captureDict = dict["captures"] as PDictionary; + if (captureDict != null) + captures = ReadCaptureDictionary(captureDict); + + ContextReference pushContext = null; + + var begin = (dict["begin"] as PString)?.Value; + if (begin != null) + { + + Captures beginCaptures = null; + captureDict = dict["beginCaptures"] as PDictionary; + + if (captureDict != null) + beginCaptures = ReadCaptureDictionary(captureDict); + + var end = (dict["end"] as PString)?.Value; + Captures endCaptures = null; + List endScope = new List(); + if (end != null) + { + captureDict = dict["endCaptures"] as PDictionary; + if (captureDict != null) + endCaptures = ReadCaptureDictionary(captureDict); + + var list = new List(); + if (end != null) + list.Add(new SyntaxMatch(Sublime3Format.CompileRegex(end), endScope, endCaptures ?? captures, null, true, null, null)); + var patternsArray = dict["patterns"] as PArray; + if (patternsArray != null) + { + ReadPatterns(patternsArray, list); + } + + List metaContent = null; + var contentScope = (dict["contentName"] as PString)?.Value; + if (contentScope != null) + { + metaContent = new List { contentScope }; + } + + var ctx = new SyntaxContext("__generated begin/end capture context", list, metaScope: metaContent); + + pushContext = new AnonymousMatchContextReference(ctx); + + } + + var whileLoop = (dict["while"] as PString)?.Value; + endCaptures = null; + endScope = new List(); + if (whileLoop != null) + { + captureDict = dict["endCaptures"] as PDictionary; + if (captureDict != null) + endCaptures = ReadCaptureDictionary(captureDict); + + var list = new List(); + if (whileLoop != null) + list.Add(new SyntaxMatch("^(?!" + Sublime3Format.CompileRegex(whileLoop) + ")", endScope, endCaptures ?? captures, null, true, null, null)); + var patternsArray = dict["patterns"] as PArray; + if (patternsArray != null) + { + ReadPatterns(patternsArray, list); + } + + List metaContent = null; + var contentScope = (dict["contentName"] as PString)?.Value; + if (contentScope != null) + { + metaContent = new List { contentScope }; + } + + var ctx = new SyntaxContext("__generated begin/while capture context", list, metaScope: metaContent); + + pushContext = new AnonymousMatchContextReference(ctx); + } + + return new SyntaxMatch(Sublime3Format.CompileRegex(begin), matchScope, beginCaptures ?? captures, pushContext, false, null, null); + } + + var match = (dict["match"] as PString)?.Value; + if (match == null) + return null; + + return new SyntaxMatch(Sublime3Format.CompileRegex(match), matchScope, captures, pushContext, false, null, null); + } + + static Captures ReadCaptureDictionary(PDictionary captureDict) + { + var group = new List>(); + var named = new List>(); + foreach (var kv in captureDict) + { + var s = ((kv.Value as PDictionary)["name"] as PString)?.Value; + if (s == null) + continue; + + //Debug.WriteLine(s); + int g; + try + { + g = int.Parse(kv.Key); + } + catch (Exception) + { + named.Add(Tuple.Create(kv.Key, s)); + continue; + } + group.Add(Tuple.Create(g, s)); + } + return new Captures(group, named); + } + #endregion + + #region JSon Format + public static SyntaxHighlightingDefinition ReadHighlightingFromJson(Stream stream) + { + byte[] bytes; + using (var sr = new StreamReader(stream)) + { + bytes = System.Text.Encoding.UTF8.GetBytes(sr.ReadToEnd()); + } + var reader = System.Runtime.Serialization.Json.JsonReaderWriterFactory.CreateJsonReader(bytes, new System.Xml.XmlDictionaryReaderQuotas()); + var root = XElement.Load(reader); + var name = root.XPathSelectElement("name")?.Value; + var scopeName = root.XPathSelectElement("scopeName")?.Value; + if (name == null || scopeName == null) + return null; // no json textmate highlighting file + + var dict = (PDictionary)Convert(root); + return ReadHighlighting(dict); + } + + static PObject Convert(XElement f) + { + var type = f.Attribute("type").Value; + switch (type) + { + case "string": + return new PString(f.Value); + case "boolean": + return new PBoolean(string.Equals("true", f.Value, StringComparison.OrdinalIgnoreCase)); + case "array": + return new PArray(new List(f.Elements().Select(Convert))); + case "object": + var val = new PDictionary(); + foreach (var subElement in f.Elements()) + { + var name = subElement.Name.LocalName; + if (string.IsNullOrEmpty(name)) + continue; + if (name == "item") + name = subElement.Attribute("item").Value; + if (!val.ContainsKey(name)) + { + var converted = Convert(subElement); + if (converted != null) + val.Add(name, converted); + } + else + { + //LoggingService.LogWarning("Warning while converting json highlighting to textmate 'key' " + name + " is duplicated in : " + f); + } + } + return val; + case "number": + return new PNumber(int.Parse(f.Value)); + default: + //LoggingService.LogWarning("Can't convert element of type: " + type + "/" + f); + break; + + } + return null; + } + #endregion + } +} diff --git a/AvalonStudio/AvalonStudio.Extensibility/Documents/ITextDocument.cs b/AvalonStudio/AvalonStudio.Extensibility/Documents/ITextDocument.cs index b05bb14719..62d4f8dfbf 100644 --- a/AvalonStudio/AvalonStudio.Extensibility/Documents/ITextDocument.cs +++ b/AvalonStudio/AvalonStudio.Extensibility/Documents/ITextDocument.cs @@ -71,6 +71,16 @@ public static ISegment GetWhitespaceBefore(this ITextDocument textSource, int of pos++; // go back the one character that isn't whitespace return new SimpleSegment(pos, offset - pos); } + + public static bool Contains(this ISegment segment, int offset) + { + return segment.Contains(offset, 0); + } + + public static bool Contains (this ISegment segment, int offset, int length) + { + return segment.Offset <= offset && offset + length <= segment.EndOffset; + } } /// @@ -271,15 +281,21 @@ public int CompareTo(TextLocation other) public class DocumentChangeEventArgs : EventArgs { - public DocumentChangeEventArgs(int offset, string removedText, string insertedText) + public DocumentChangeEventArgs(int offset, Func getNewOffset, string removedText, string insertedText) { if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset), offset, "offset must not be negative"); Offset = offset; RemovedText = removedText; InsertedText = insertedText; + + _getNewOffset = getNewOffset; } + private Func _getNewOffset; + + public int GetNewOffset(int offset) => _getNewOffset(offset); + public int Offset { get; } /// @@ -319,6 +335,8 @@ public interface ITextDocument IDocumentLine GetLineByNumber(int lineNumber); + IDocumentLine GetLineByOffset(int offset); + int LineCount { get; } string GetText(int offset, int length); diff --git a/AvalonStudio/AvalonStudio.Extensibility/Editor/ColorScheme.cs b/AvalonStudio/AvalonStudio.Extensibility/Editor/ColorScheme.cs index 2f2607c997..2422e5f9bf 100644 --- a/AvalonStudio/AvalonStudio.Extensibility/Editor/ColorScheme.cs +++ b/AvalonStudio/AvalonStudio.Extensibility/Editor/ColorScheme.cs @@ -7,6 +7,7 @@ using Avalonia; using AvalonStudio.Extensibility.Plugin; using System; +using System.Linq; namespace AvalonStudio.Extensibility.Editor { @@ -35,12 +36,31 @@ public class ColorScheme static ColorScheme() { + s_colorAccessors["source"] = () => CurrentColorScheme.Text; + s_colorAccessors["constant.language"] = () => CurrentColorScheme.Keyword; + s_colorAccessors["punctuation"] = () => CurrentColorScheme.Punctuation; + s_colorAccessors["keyword"] = () => CurrentColorScheme.Keyword; + s_colorAccessors["keyword.operator"] = () => CurrentColorScheme.Operator; + s_colorAccessors["storage.type"] = () => CurrentColorScheme.Keyword; + s_colorAccessors["storage.modifier"] = () => CurrentColorScheme.Keyword; + s_colorAccessors["constant.numeric"] = () => CurrentColorScheme.NumericLiteral; + s_colorAccessors["string"] = () => CurrentColorScheme.Literal; + + s_colorAccessors["entity.name.type"] = () => Brushes.Red; + s_colorAccessors["entity.name.type.class"] = () => CurrentColorScheme.Type; + /*s_colorAccessors["storage.type.generic.cs"] = () => CurrentColorScheme.Type; + s_colorAccessors["storage.type.modifier.cs"] = () => CurrentColorScheme.Type; + s_colorAccessors["storage.type.variable.cs"] = () => CurrentColorScheme.Type;*/ + + s_colorAccessors["background"] = () => CurrentColorScheme.Background; s_colorAccessors["background.accented"] = () => CurrentColorScheme.BackgroundAccent; s_colorAccessors["text"] = () => CurrentColorScheme.Text; + s_colorAccessors["comment"] = () => CurrentColorScheme.Comment; s_colorAccessors["delegate.name"] = () => CurrentColorScheme.DelegateName; - s_colorAccessors["keyword"] = () => CurrentColorScheme.Keyword; + + s_colorAccessors["literal"] = () => CurrentColorScheme.Literal; s_colorAccessors["identifier"] = () => CurrentColorScheme.Identifier; s_colorAccessors["callexpression"] = () => CurrentColorScheme.CallExpression; @@ -50,7 +70,7 @@ static ColorScheme() s_colorAccessors["operator"] = () => CurrentColorScheme.Operator; s_colorAccessors["struct.name"] = () => CurrentColorScheme.StructName; s_colorAccessors["interface"] = () => CurrentColorScheme.InterfaceType; - s_colorAccessors["punctuation"] = () => CurrentColorScheme.Punctuation; + s_colorAccessors["type"] = () => CurrentColorScheme.Type; s_colorAccessors["xml.tag"] = () => CurrentColorScheme.XmlTag; s_colorAccessors["xml.property"] = () => CurrentColorScheme.XmlProperty; @@ -222,14 +242,31 @@ public IBrush this[string key] { get { - key = key.ToLower(); + if (key.Contains('.')) + { + var parts = key.Split('.'); - if (s_colorAccessors.ContainsKey(key)) + for(int i = parts.Length - 1; i > 0; i--) + { + var specificKeyword = string.Join(".", parts.Take(i)); + + if (s_colorAccessors.ContainsKey(specificKeyword)) + { + return s_colorAccessors[specificKeyword](); + } + } + } + else { - return s_colorAccessors[key](); + key = key.ToLower(); + + if (s_colorAccessors.ContainsKey(key)) + { + return s_colorAccessors[key](); + } } - return Brushes.Red; + return null; } } diff --git a/AvalonStudio/AvalonStudio.Extensibility/Languages/OffsetSyntaxHighlightingData.cs b/AvalonStudio/AvalonStudio.Extensibility/Languages/OffsetSyntaxHighlightingData.cs index d434c8834d..a9e991cfa5 100644 --- a/AvalonStudio/AvalonStudio.Extensibility/Languages/OffsetSyntaxHighlightingData.cs +++ b/AvalonStudio/AvalonStudio.Extensibility/Languages/OffsetSyntaxHighlightingData.cs @@ -1,21 +1,40 @@ -using System; +using AvalonStudio.Documents; +using System; namespace AvalonStudio.Languages { - public class OffsetSyntaxHighlightingData : IComparable + public class OffsetSyntaxHighlightingData : IComparable, ISegment { + public OffsetSyntaxHighlightingData() + { + + } + + public OffsetSyntaxHighlightingData(ISegment segment) + { + Offset = segment.Offset; + Length = segment.Length; + } + + public OffsetSyntaxHighlightingData (int offset, int length) + { + Offset = offset; + Length = length; + } + public HighlightType Type { get; set; } - public int Start { get; set; } + public int Offset { get; set; } public int Length { get; set; } + public int EndOffset => Offset + Length; public int CompareTo(OffsetSyntaxHighlightingData other) { - if (Start > other.Start) + if (Offset > other.Offset) { return 1; } - if (Start == other.Start) + if (Offset == other.Offset) { return 0; } diff --git a/AvalonStudio/AvalonStudio.Extensibility/Languages/TextColoringTransformer.cs b/AvalonStudio/AvalonStudio.Extensibility/Languages/TextColoringTransformer.cs index 172aab0229..aad7e5e483 100644 --- a/AvalonStudio/AvalonStudio.Extensibility/Languages/TextColoringTransformer.cs +++ b/AvalonStudio/AvalonStudio.Extensibility/Languages/TextColoringTransformer.cs @@ -84,16 +84,16 @@ private TextTransformation GetTextTransformation(object tag, OffsetSyntaxHighlig { return new OpacityTextTransformation( tag, - highlight.Start, - highlight.Start + highlight.Length, + highlight.Offset, + highlight.Offset + highlight.Length, 0.5); } else { return new ForegroundTextTransformation( tag, - highlight.Start, - highlight.Start + highlight.Length, + highlight.Offset, + highlight.Offset + highlight.Length, GetBrush(highlight.Type)); } } diff --git a/AvalonStudio/AvalonStudio.LanguageSupport.TypeScript/LanguageService/TypeScriptLanguageService.cs b/AvalonStudio/AvalonStudio.LanguageSupport.TypeScript/LanguageService/TypeScriptLanguageService.cs index 19c093fd8f..67712d52d5 100644 --- a/AvalonStudio/AvalonStudio.LanguageSupport.TypeScript/LanguageService/TypeScriptLanguageService.cs +++ b/AvalonStudio/AvalonStudio.LanguageSupport.TypeScript/LanguageService/TypeScriptLanguageService.cs @@ -266,7 +266,7 @@ public async Task RunCodeAnalysisAsync(IEditor editor, { result.SyntaxHighlightingData.Add(new OffsetSyntaxHighlightingData { - Start = commentMatch.Index, + Offset = commentMatch.Index, Length = commentMatch.Length, Type = HighlightType.Comment }); @@ -277,7 +277,7 @@ public async Task RunCodeAnalysisAsync(IEditor editor, { result.SyntaxHighlightingData.Add(new OffsetSyntaxHighlightingData { - Start = commentMatch.Index, + Offset = commentMatch.Index, Length = commentMatch.Length, Type = HighlightType.Comment }); @@ -289,7 +289,7 @@ public async Task RunCodeAnalysisAsync(IEditor editor, { result.SyntaxHighlightingData.Add(new OffsetSyntaxHighlightingData { - Start = keywordMatch.Index, + Offset = keywordMatch.Index, Length = keywordMatch.Length, Type = HighlightType.Keyword }); @@ -366,7 +366,7 @@ private void HighlightNode(INode node, CodeAnalysisResults result) endPos = ((FunctionDeclaration)node).Name.End; } - highlightData.Start = startPos; + highlightData.Offset = startPos; highlightData.Length = endPos - startPos; result.SyntaxHighlightingData.Add(highlightData); } diff --git a/AvalonStudio/AvalonStudio.Languages.CPlusPlus/CPlusPlusLanguageService.cs b/AvalonStudio/AvalonStudio.Languages.CPlusPlus/CPlusPlusLanguageService.cs index 317611153a..4bd2ab87f4 100644 --- a/AvalonStudio/AvalonStudio.Languages.CPlusPlus/CPlusPlusLanguageService.cs +++ b/AvalonStudio/AvalonStudio.Languages.CPlusPlus/CPlusPlusLanguageService.cs @@ -384,7 +384,7 @@ private OffsetSyntaxHighlightingData CreateOffsetData(NClang.ClangCursor cursor, { return new OffsetSyntaxHighlightingData() { - Start = cursor.Location.SpellingLocation.Offset, + Offset = cursor.Location.SpellingLocation.Offset, Length = cursor.Spelling.Length - 5, // Because spelling includes keyword "class" Type = highlightKind }; @@ -393,7 +393,7 @@ private OffsetSyntaxHighlightingData CreateOffsetData(NClang.ClangCursor cursor, { return new OffsetSyntaxHighlightingData() { - Start = cursor.Location.SpellingLocation.Offset, + Offset = cursor.Location.SpellingLocation.Offset, Length = cursor.Spelling.Length, // TODO select only the name... Type = highlightKind }; @@ -402,7 +402,7 @@ private OffsetSyntaxHighlightingData CreateOffsetData(NClang.ClangCursor cursor, { return new OffsetSyntaxHighlightingData() { - Start = cursor.Location.SpellingLocation.Offset, + Offset = cursor.Location.SpellingLocation.Offset, Length = cursor.Spelling.Length, Type = highlightKind }; @@ -412,7 +412,7 @@ private OffsetSyntaxHighlightingData CreateOffsetData(NClang.ClangCursor cursor, { return new OffsetSyntaxHighlightingData() { - Start = cursor.CursorExtent.Start.FileLocation.Offset, + Offset = cursor.CursorExtent.Start.FileLocation.Offset, Length = cursor.CursorExtent.End.FileLocation.Offset - cursor.CursorExtent.Start.FileLocation.Offset, Type = highlightKind }; @@ -426,8 +426,8 @@ private void ScanTokens(NClang.ClangTranslationUnit tu, SyntaxHighlightDataList foreach (var token in tokens.Tokens) { var highlightData = new OffsetSyntaxHighlightingData(); - highlightData.Start = token.Extent.Start.FileLocation.Offset; - highlightData.Length = token.Extent.End.FileLocation.Offset - highlightData.Start; + highlightData.Offset = token.Extent.Start.FileLocation.Offset; + highlightData.Length = token.Extent.End.FileLocation.Offset - highlightData.Offset; switch (token.Kind) { diff --git a/AvalonStudio/AvalonStudio.Languages.CSharp/CSharpLanguageService.cs b/AvalonStudio/AvalonStudio.Languages.CSharp/CSharpLanguageService.cs index 9494483067..535c933cb9 100644 --- a/AvalonStudio/AvalonStudio.Languages.CSharp/CSharpLanguageService.cs +++ b/AvalonStudio/AvalonStudio.Languages.CSharp/CSharpLanguageService.cs @@ -721,7 +721,7 @@ public void RegisterSourceFile(IEditor editor) { fadedCode.Add(new OffsetSyntaxHighlightingData { - Start = diagnostic.TextSpan.Start, + Offset = diagnostic.TextSpan.Start, Length = diagnostic.TextSpan.Length, Type = HighlightType.Unnecessary }); @@ -921,11 +921,21 @@ public async Task RunCodeAnalysisAsync(IEditor editor, List try { var highlightData = await Classifier.GetClassifiedSpansAsync(document, new TextSpan(0, textLength)); - var displayParts = await Classifier.GetClassifiedSymbolDisplayPartsAsync(document, new TextSpan(0, textLength)); foreach (var span in highlightData) { - result.SyntaxHighlightingData.Add(new OffsetSyntaxHighlightingData { Start = span.TextSpan.Start, Length = span.TextSpan.Length, Type = FromRoslynType(span.ClassificationType) }); + var type = FromRoslynType(span.ClassificationType); + + switch (type) + { + case HighlightType.DelegateName: + case HighlightType.EnumTypeName: + case HighlightType.StructName: + case HighlightType.InterfaceName: + case HighlightType.ClassName: + result.SyntaxHighlightingData.Add(new OffsetSyntaxHighlightingData { Offset = span.TextSpan.Start, Length = span.TextSpan.Length, Type = type }); + break; + } } } catch (NullReferenceException) diff --git a/AvalonStudio/Avalonia.Ide b/AvalonStudio/Avalonia.Ide index 1b3ce49983..378fc8ac50 160000 --- a/AvalonStudio/Avalonia.Ide +++ b/AvalonStudio/Avalonia.Ide @@ -1 +1 @@ -Subproject commit 1b3ce499835325df24c16b3569525eaebaae1f71 +Subproject commit 378fc8ac5057ff77a82d061130d296b1c1f3e656 diff --git a/AvaloniaEdit b/AvaloniaEdit index 7b9ddfa6d0..b14ac69689 160000 --- a/AvaloniaEdit +++ b/AvaloniaEdit @@ -1 +1 @@ -Subproject commit 7b9ddfa6d08f52bdae9622834c7e18d67eb96ef9 +Subproject commit b14ac6968990b369df3a8fd68a1b6eecbb54eb57