public TextManipulator(string text, int position) { _position = position; _text = text.AsMemory(); int parserStart = 0; int parserEnd = 0; // To improve performance parse only last tag if (text.Length > 0) { // Findl last < tag parserStart = position; if (position >= text.Length) { parserStart = text.Length - 1; } parserStart = text.LastIndexOf('<', parserStart); if (parserStart < 0) { parserStart = 0; } if (text.Length > position) { parserEnd = position; } else { parserEnd = text.Length; } } _state = XmlParser.Parse(_text, parserStart, parserEnd); }
public CompletionSet GetCompletions(Metadata metadata, string text, int pos, string currentAssemblyName = null) { _helper.SetMetadata(metadata, text, currentAssemblyName); if (_helper.Metadata == null) { return(null); } if (text.Length == 0 || pos == 0) { return(null); } text = text.Substring(0, pos); var state = XmlParser.Parse(text); var completions = new List <Completion>(); var curStart = state.CurrentValueStart ?? 0; if (state.State == XmlParser.ParserState.StartElement) { var tagName = state.TagName; if (tagName.StartsWith("/")) { if (text.Length < 2) { return(null); } var closingState = XmlParser.Parse(text.Substring(0, text.Length - 2)); var name = closingState.GetParentTagName(0); if (name == null) { return(null); } completions.Add(new Completion("/" + name + ">", CompletionKind.Class)); } else if (tagName.Contains(".")) { var dotPos = tagName.IndexOf("."); var typeName = tagName.Substring(0, dotPos); var compName = tagName.Substring(dotPos + 1); curStart = curStart + dotPos + 1; var sameType = state.GetParentTagName(1) == typeName; completions.AddRange(_helper.FilterPropertyNames(typeName, compName, sameType ? (bool?)null : true) .Select(p => new Completion(p, sameType ? CompletionKind.Property : CompletionKind.AttachedProperty))); } else { completions.AddRange(_helper.FilterTypeNames(tagName).Select(x => new Completion(x, CompletionKind.Class))); } } else if (state.State == XmlParser.ParserState.InsideElement || state.State == XmlParser.ParserState.StartAttribute) { if (state.State == XmlParser.ParserState.InsideElement) { curStart = pos; //Force completion to be started from current cursor position } if (state.AttributeName?.Contains(".") == true) { var dotPos = state.AttributeName.IndexOf('.'); curStart += dotPos + 1; var split = state.AttributeName.Split(new[] { '.' }, 2); completions.AddRange(_helper.FilterPropertyNames(split[0], split[1], true) .Select(x => new Completion(x, x + "=\"\"", x, CompletionKind.AttachedProperty, x.Length + 2))); } else { completions.AddRange(_helper.FilterPropertyNames(state.TagName, state.AttributeName, false) .Select(x => new Completion(x, x + "=\"\"", x, CompletionKind.Property, x.Length + 2))); var targetType = _helper.LookupType(state.TagName); completions.AddRange( _helper.FilterTypes(state.AttributeName, xamlDirectiveOnly: true) .Where(t => t.Value.IsValidForXamlContextFunc?.Invoke(currentAssemblyName, targetType, null) ?? true) .Select(v => new Completion(v.Key, v.Key + "=\"\"", v.Key, CompletionKind.Class, v.Key.Length + 2))); if (targetType?.IsAvaloniaObjectType == true) { completions.AddRange( _helper.FilterTypeNames(state.AttributeName, withAttachedPropertiesOnly: true) .Select(v => new Completion(v, v + ".", v, CompletionKind.Class))); } } } else if (state.State == XmlParser.ParserState.AttributeValue) { MetadataProperty prop; if (state.AttributeName.Contains(".")) { //Attached property var split = state.AttributeName.Split('.'); prop = _helper.LookupProperty(split[0], split[1]); } else { prop = _helper.LookupProperty(state.TagName, state.AttributeName); } //Markup extension, ignore everything else if (state.AttributeValue.StartsWith("{")) { curStart = state.CurrentValueStart.Value + BuildCompletionsForMarkupExtension(prop, completions, text.Substring(state.CurrentValueStart.Value), currentAssemblyName); } else { prop = prop ?? _helper.LookupType(state.AttributeName)?.Properties.FirstOrDefault(p => p.Name == ""); if (prop?.Type?.HasHintValues == true) { var search = text.Substring(state.CurrentValueStart.Value); if (prop.Type.IsCompositeValue) { var last = search.Split(' ', ',').LastOrDefault(); curStart = curStart + search.Length - last?.Length ?? 0; search = last; } completions.AddRange(GetHintCompletions(prop.Type, search, currentAssemblyName)); } else if (prop?.Type?.Name == typeof(Type).FullName) { completions.AddRange(_helper.FilterTypeNames(state.AttributeValue).Select(x => new Completion(x, x, x, CompletionKind.Class))); } else if (state.AttributeName == "xmlns" || state.AttributeName.Contains("xmlns:")) { if (state.AttributeValue.StartsWith("clr-namespace:")) { completions.AddRange( metadata.Namespaces.Keys.Where(v => v.StartsWith(state.AttributeValue)) .Select(v => new Completion(v.Substring("clr-namespace:".Length), v, v, CompletionKind.Namespace))); } else { if ("clr-namespace:".StartsWith(state.AttributeValue)) { completions.Add(new Completion("clr-namespace:", CompletionKind.Namespace)); } completions.AddRange( metadata.Namespaces.Keys.Where( v => v.StartsWith(state.AttributeValue) && !v.StartsWith("clr-namespace")) .Select(v => new Completion(v, CompletionKind.Namespace))); } } else if (state.AttributeName.EndsWith(":Class")) { if (_helper.Aliases.TryGetValue(state.AttributeName.Replace(":Class", ""), out var ns) && ns == Utils.Xaml2006Namespace) { var asmKey = $";assembly={currentAssemblyName}"; var fullClassNames = _helper.Metadata.Namespaces.Where(v => v.Key.EndsWith(asmKey)) .SelectMany(v => v.Value.Values.Where(t => t.IsAvaloniaObjectType)) .Select(v => v.FullName); completions.AddRange( fullClassNames .Where(v => v.StartsWith(state.AttributeValue)) .Select(v => new Completion(v, CompletionKind.Class))); } } } } if (completions.Count != 0) { return new CompletionSet() { Completions = completions, StartPosition = curStart } } ; return(null); }
public CompletionSet GetCompletions(Metadata metadata, string text, int pos, string currentAssemblyName = null) { _helper.SetMetadata(metadata, text, currentAssemblyName); if (_helper.Metadata == null) { return(null); } if (text.Length == 0 || pos == 0) { return(null); } text = text.Substring(0, pos); var state = XmlParser.Parse(text); var completions = new List <Completion>(); var curStart = state.CurrentValueStart ?? 0; if (state.State == XmlParser.ParserState.StartElement) { var tagName = state.TagName; if (tagName.Contains(".")) { var dotPos = tagName.IndexOf("."); var typeName = tagName.Substring(0, dotPos); var compName = tagName.Substring(dotPos + 1); curStart = curStart + dotPos + 1; completions.AddRange(_helper.FilterPropertyNames(typeName, compName).Select(p => new Completion(p, CompletionKind.Enum))); } else { completions.AddRange(_helper.FilterTypeNames(tagName).Select(x => new Completion(x, CompletionKind.Class))); } } else if (state.State == XmlParser.ParserState.InsideElement || state.State == XmlParser.ParserState.StartAttribute) { if (state.State == XmlParser.ParserState.InsideElement) { curStart = pos; //Force completion to be started from current cursor position } if (state.AttributeName?.Contains(".") == true) { var dotPos = state.AttributeName.IndexOf('.'); curStart += dotPos + 1; var split = state.AttributeName.Split(new[] { '.' }, 2); completions.AddRange(_helper.FilterPropertyNames(split[0], split[1], true) .Select(x => new Completion(x, x + "=\"\"", x, CompletionKind.Property, x.Length + 2))); } else { completions.AddRange(_helper.FilterPropertyNames(state.TagName, state.AttributeName) .Select(x => new Completion(x, x + "=\"\"", x, CompletionKind.Property, x.Length + 2))); var targetType = _helper.LookupType(state.TagName); if (targetType?.IsAvaloniaObjectType == true) { completions.AddRange( _helper.FilterTypeNames(state.AttributeName, true) .Select(v => new Completion(v, v + ".", v, CompletionKind.Property))); } } } else if (state.State == XmlParser.ParserState.AttributeValue) { MetadataProperty prop; if (state.AttributeName.Contains(".")) { //Attached property var split = state.AttributeName.Split('.'); prop = _helper.LookupProperty(split[0], split[1]); } else { prop = _helper.LookupProperty(state.TagName, state.AttributeName); } //Markup extension, ignore everything else if (state.AttributeValue.StartsWith("{")) { curStart = state.CurrentValueStart.Value + BuildCompletionsForMarkupExtension(prop, completions, text.Substring(state.CurrentValueStart.Value)); } else { if (prop?.Type?.HasHintValues == true) { var search = text.Substring(state.CurrentValueStart.Value); if (prop.Type.IsCompositeValue) { var last = search.Split(' ', ',').LastOrDefault(); curStart = curStart + search.Length - last?.Length ?? 0; search = last; } completions.AddRange(GetHintCompletions(search, prop.Type.HintValues)); } else if (state.AttributeName == "xmlns" || state.AttributeName.Contains("xmlns:")) { if (state.AttributeValue.StartsWith("clr-namespace:")) { completions.AddRange( metadata.Namespaces.Keys.Where(v => v.StartsWith(state.AttributeValue)) .Select(v => new Completion(v.Substring("clr-namespace:".Length), v, v, CompletionKind.Namespace))); } else { if ("clr-namespace:".StartsWith(state.AttributeValue)) { completions.Add(new Completion("clr-namespace:", CompletionKind.Namespace)); } completions.AddRange( metadata.Namespaces.Keys.Where( v => v.StartsWith(state.AttributeValue) && !v.StartsWith("clr-namespace")) .Select(v => new Completion(v, CompletionKind.Namespace))); } } } } if (completions.Count != 0) { return new CompletionSet() { Completions = completions, StartPosition = curStart } } ; return(null); } List <Completion> GetHintCompletions(string entered, string[] values) { var completions = new List <Completion>(); foreach (var val in values) { if (val.StartsWith(entered, StringComparison.OrdinalIgnoreCase)) { completions.Add(new Completion(val, CompletionKind.Enum)); } } return(completions); } int BuildCompletionsForMarkupExtension(MetadataProperty property, List <Completion> completions, string data) { int?forcedStart = null; var ext = MarkupExtensionParser.Parse(data); var transformedName = (ext.ElementName ?? "").Trim(); if (_helper.LookupType(transformedName)?.IsMarkupExtension != true) { transformedName += "Extension"; } if (ext.State == MarkupExtensionParser.ParserStateType.StartElement) { completions.AddRange(_helper.FilterTypeNames(ext.ElementName, markupExtensionsOnly: true) .Select(t => t.EndsWith("Extension") ? t.Substring(0, t.Length - "Extension".Length) : t) .Select(t => new Completion(t, CompletionKind.MarkupExtension))); } if (ext.State == MarkupExtensionParser.ParserStateType.StartAttribute || ext.State == MarkupExtensionParser.ParserStateType.InsideElement) { if (ext.State == MarkupExtensionParser.ParserStateType.InsideElement) { forcedStart = data.Length; } completions.AddRange(_helper.FilterPropertyNames(transformedName, ext.AttributeName ?? "") .Select(x => new Completion(x, x + "=", x, CompletionKind.MarkupExtension))); var attribName = ext.AttributeName ?? ""; var t = _helper.LookupType(transformedName); bool ctorArgument = ext.AttributesCount == 0; //skip ctor hints when some property is already set if (t != null && t.IsMarkupExtension && t.SupportCtorArgument != MetadataTypeCtorArgument.None && ctorArgument) { if (t.SupportCtorArgument == MetadataTypeCtorArgument.HintValues) { if (t.HasHintValues) { completions.AddRange(GetHintCompletions(attribName, t.HintValues)); } } else if (attribName.Contains(".")) { if (t.SupportCtorArgument != MetadataTypeCtorArgument.Type) { var split = attribName.Split('.'); var type = split[0]; var prop = split[1]; var props = _helper.FilterPropertyNames(type, prop, staticGettersOnly: true, hintValues: true); completions.AddRange(props.Select(x => new Completion(x, $"{type}.{x}", x, CompletionKind.MarkupExtension))); } } else { var types = _helper.FilterTypeNames(attribName, staticGettersOnly: t.SupportCtorArgument == MetadataTypeCtorArgument.Object); completions.AddRange(types.Select(x => new Completion(x, x, x, CompletionKind.Class))); if (property?.Type?.HasHintValues == true) { completions.Add(new Completion(property.Type.Name, property.Type.Name + ".", property.Type.Name, CompletionKind.Class)); } } } } if (ext.State == MarkupExtensionParser.ParserStateType.AttributeValue || ext.State == MarkupExtensionParser.ParserStateType.BeforeAttributeValue) { var prop = _helper.LookupProperty(transformedName, ext.AttributeName); if (prop?.Type?.HasHintValues == true) { var enumStart = data.Substring(ext.CurrentValueStart); var enumCompletions = GetHintCompletions(enumStart, prop.Type.HintValues); completions.AddRange(enumCompletions); } } return(forcedStart ?? ext.CurrentValueStart); }
public CompletionSet GetCompletions(Metadata metadata, string text, int pos) { _helper.SetMetadata(metadata, text); if (_helper.Metadata == null) { return(null); } if (text.Length == 0 || pos == 0) { return(null); } text = text.Substring(0, pos); var state = XmlParser.Parse(text); var completions = new List <Completion>(); var curStart = state.CurrentValueStart ?? 0; if (state.State == XmlParser.ParserState.StartElement) { var tagName = state.TagName; if (tagName.Contains(".")) { var dotPos = tagName.IndexOf("."); var typeName = tagName.Substring(0, dotPos); var compName = tagName.Substring(dotPos + 1); curStart = curStart + dotPos + 1; completions.AddRange(_helper.FilterPropertyNames(typeName, compName).Select(p => new Completion(p))); } else { completions.AddRange(_helper.FilterTypeNames(tagName).Select(x => new Completion(x))); } } else if (state.State == XmlParser.ParserState.InsideElement || state.State == XmlParser.ParserState.StartAttribute) { if (state.State == XmlParser.ParserState.InsideElement) { curStart = pos; //Force completion to be started from current cursor position } if (state.AttributeName?.Contains(".") == true) { var dotPos = state.AttributeName.IndexOf('.'); curStart += dotPos + 1; var split = state.AttributeName.Split(new[] { '.' }, 2); completions.AddRange(_helper.FilterPropertyNames(split[0], split[1], true) .Select(x => new Completion(x, x + "=\"\"", x, x.Length + 2))); } else { completions.AddRange(_helper.FilterPropertyNames(state.TagName, state.AttributeName) .Select(x => new Completion(x, x + "=\"\"", x, x.Length + 2))); completions.AddRange( _helper.FilterTypeNames(state.AttributeName, true) .Select(v => new Completion(v, v + ".", v))); } } else if (state.State == XmlParser.ParserState.AttributeValue) { MetadataProperty prop; if (state.AttributeName.Contains(".")) { //Attached property var split = state.AttributeName.Split('.'); prop = _helper.LookupProperty(split[0], split[1]); } else { prop = _helper.LookupProperty(state.TagName, state.AttributeName); } //Markup extension, ignore everything else if (state.AttributeValue.StartsWith("{")) { curStart = state.CurrentValueStart.Value + BuildCompletionsForMarkupExtension(completions, text.Substring(state.CurrentValueStart.Value)); } else { if (prop?.Type?.IsEnum == true) { completions.AddRange(GetEnumCompletions(text.Substring(state.CurrentValueStart.Value), prop.Type.EnumValues)); } else if (state.AttributeName == "xmlns" || state.AttributeName.Contains("xmlns:")) { if (state.AttributeValue.StartsWith("clr-namespace:")) { completions.AddRange( metadata.Namespaces.Keys.Where(v => v.StartsWith(state.AttributeValue)) .Select(v => new Completion(v.Substring("clr-namespace:".Length), v, v))); } else { completions.Add(new Completion("clr-namespace:")); completions.AddRange( metadata.Namespaces.Keys.Where( v => v.StartsWith(state.AttributeValue) && !"clr-namespace".StartsWith(state.AttributeValue)) .Select(v => new Completion(v))); } } } } if (completions.Count != 0) { return new CompletionSet() { Completions = completions, StartPosition = curStart } } ; return(null); } List <Completion> GetEnumCompletions(string entered, string[] enumValues) { var enumCompletions = new List <Completion>(); foreach (var val in enumValues) { if (val.StartsWith(entered, StringComparison.OrdinalIgnoreCase)) { enumCompletions.Add(new Completion(val)); } } return(enumCompletions); } int BuildCompletionsForMarkupExtension(List <Completion> completions, string data) { int?forcedStart = null; var ext = MarkupExtensionParser.Parse(data); var transformedName = (ext.ElementName ?? "").Trim(); if (_helper.LookupType(transformedName)?.IsMarkupExtension != true) { transformedName += "Extension"; } if (ext.State == MarkupExtensionParser.ParserStateType.StartElement) { completions.AddRange(_helper.FilterTypeNames(ext.ElementName, markupExtensionsOnly: true) .Select(t => t.EndsWith("Extension") ? t.Substring(0, t.Length - "Extension".Length) : t) .Select(t => new Completion(t))); } if (ext.State == MarkupExtensionParser.ParserStateType.StartAttribute || ext.State == MarkupExtensionParser.ParserStateType.InsideElement) { if (ext.State == MarkupExtensionParser.ParserStateType.InsideElement) { forcedStart = data.Length; } completions.AddRange(_helper.FilterPropertyNames(transformedName, ext.AttributeName ?? "") .Select(x => new Completion(x, x + "=", x))); } if (ext.State == MarkupExtensionParser.ParserStateType.AttributeValue || ext.State == MarkupExtensionParser.ParserStateType.BeforeAttributeValue) { var prop = _helper.LookupProperty(transformedName, ext.AttributeName); if (prop?.Type?.IsEnum == true) { var enumStart = data.Substring(ext.CurrentValueStart); var enumCompletions = GetEnumCompletions(enumStart, prop.Type.EnumValues); completions.AddRange(enumCompletions); } } return(forcedStart ?? ext.CurrentValueStart); }