Skip to content

Commit

Permalink
Suggest end elements
Browse files Browse the repository at this point in the history
  • Loading branch information
MarkMpn committed Jul 31, 2021
1 parent c3a89dd commit 8c16c9f
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 46 deletions.
111 changes: 73 additions & 38 deletions MarkMpn.XmlSchemaAutoComplete/Autocomplete.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,20 @@ public AutocompleteSuggestion[] GetSuggestions(string text, out int length)
}
else if (node is PartialXmlEndElement end)
{
if (!elements.TryPop(out var lastElement)
|| lastElement.ElementName != end.Name)
{
if (!elements.TryPop(out var lastElement))
return Array.Empty<AutocompleteSuggestion>();

if (lastElement.ElementName != end.Name)
{
if (parser.State == ReaderState.InEndElement)
{
elements.Push(lastElement);
valid = false;
}
else
{
return Array.Empty<AutocompleteSuggestion>();
}
}
}
else if (node is PartialXmlElement elem)
Expand Down Expand Up @@ -249,59 +259,78 @@ public AutocompleteSuggestion[] GetSuggestions(string text, out int length)
.ToArray<AutocompleteSuggestion>();
}

if (elements.TryPeek(out var currentElement) &&
currentElement.Type is XmlSchemaComplexType complex)
var suggestions = new List<AutocompleteSuggestion>();

if (elements.TryPeek(out var currentElement))
{
if (complex.ContentTypeParticle is XmlSchemaSequence sequence)
{
var suggestions = new List<AutocompleteSuggestion>();
var canClose = String.IsNullOrEmpty(element.Name);

foreach (var child in sequence.Items.Cast<XmlSchemaObject>().Skip(currentElement.NextChildElement))
if (currentElement.Type is XmlSchemaComplexType complex)
{
if (complex.ContentTypeParticle is XmlSchemaSequence sequence)
{
if (child is XmlSchemaChoice choice)
foreach (var child in sequence.Items.Cast<XmlSchemaObject>().Skip(currentElement.NextChildElement))
{
foreach (var choiceItem in choice.Items)
if (child is XmlSchemaChoice choice)
{
foreach (var choiceItem in choice.Items)
{
if (!(choiceItem is XmlSchemaElement childElement))
break;

if (childElement.Name.StartsWith(element.Name))
suggestions.Add(new AutocompleteElementSuggestion(childElement));
}
}
else if (child is XmlSchemaElement childElement)
{
if (!(choiceItem is XmlSchemaElement childElement))
break;

if (childElement.Name.StartsWith(element.Name))
suggestions.Add(new AutocompleteElementSuggestion(childElement));
}
}
else if (child is XmlSchemaElement childElement)
{
if (childElement.Name.StartsWith(element.Name))
suggestions.Add(new AutocompleteElementSuggestion(childElement));

currentElement.ElementCount.TryGetValue(childElement, out var count);
if (childElement.MinOccurs > count)
currentElement.ElementCount.TryGetValue(childElement, out var count);
if (childElement.MinOccurs > count)
break;
}
else
{
break;
}
}
else

if (canClose)
{
break;
// This element can only be closed if all the child elements have reached their minimum number
canClose = sequence.Items
.Cast<XmlSchemaObject>()
.Skip(currentElement.NextChildElement)
.OfType<XmlSchemaParticle>()
.All(particle =>
{
currentElement.ElementCount.TryGetValue(particle, out var count);
return count >= particle.MinOccurs;
});
}
}

return suggestions.ToArray();
}
else if (complex.ContentTypeParticle is XmlSchemaChoice choice)
{
var suggestions = new List<AutocompleteSuggestion>();

foreach (var child in choice.Items)
else if (complex.ContentTypeParticle is XmlSchemaChoice choice)
{
if (!(child is XmlSchemaElement childElement))
break;
foreach (var child in choice.Items)
{
if (!(child is XmlSchemaElement childElement))
break;

if (childElement.Name.StartsWith(element.Name))
suggestions.Add(new AutocompleteElementSuggestion(childElement));
}
if (childElement.Name.StartsWith(element.Name))
suggestions.Add(new AutocompleteElementSuggestion(childElement));
}

return suggestions.ToArray();
canClose = currentElement.RepeatCount >= choice.MinOccurs;
}
}

if (canClose)
suggestions.Add(new AutocompleteEndElementSuggestion { Name = currentElement.ElementName, IncludeSlash = true });
}

return suggestions.ToArray();
}
else if (parser.State == ReaderState.AwaitingAttribute || parser.State == ReaderState.InAttributeName)
{
Expand Down Expand Up @@ -434,6 +463,12 @@ public AutocompleteSuggestion[] GetSuggestions(string text, out int length)
length = txt.Text.Length;
return CompleteTextNode(elements, txt.Text);
}
else if (lastNode is PartialXmlEndElement endElement)
{
if (elements.TryPeek(out var currentElement) &&
currentElement.ElementName.StartsWith(endElement.Name))
return new AutocompleteSuggestion[] { new AutocompleteEndElementSuggestion { Name = currentElement.ElementName } };
}

return Array.Empty<AutocompleteSuggestion>();
}
Expand Down
7 changes: 7 additions & 0 deletions MarkMpn.XmlSchemaAutoComplete/AutocompleteSuggestion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ public AutocompleteElementSuggestion()
public bool HasAttributes { get; set; }
}

public class AutocompleteEndElementSuggestion : AutocompleteSuggestion
{
public string Name { get; set; }

public bool IncludeSlash { get; set; }
}

public class AutocompleteAttributeSuggestion : AutocompleteSuggestion
{
public string Name { get; set; }
Expand Down
30 changes: 22 additions & 8 deletions MarkMpn.XmlSchemaAutocomplete.Tests/Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ public void SuggestsAllPossibleRootElements(string input, params string[] elemen
public void SuggestsAllPossibleChildElements(string input, params string[] elements)
{
var autocomplete = new Autocomplete<Root>();
var suggestions = autocomplete.GetSuggestions(input, out _);
var expected = elements.Select(e => new AutocompleteElementSuggestion { Name = e, HasAttributes = e == "Staff" }).ToArray<AutocompleteSuggestion>();
var suggestions = autocomplete.GetSuggestions(input, out _).OfType<AutocompleteElementSuggestion>().ToArray();
var expected = elements.Select(e => new AutocompleteElementSuggestion { Name = e, HasAttributes = e == "Staff" }).ToArray();
Assert.Equal(expected, suggestions, new PropertyComparer());
}

Expand All @@ -40,8 +40,8 @@ public void SuggestsAllPossibleChildElements(string input, params string[] eleme
public void SuggestsArrayMembers(string input, params string[] elements)
{
var autocomplete = new Autocomplete<Root>();
var suggestions = autocomplete.GetSuggestions(input, out _);
var expected = elements.Select(e => new AutocompleteElementSuggestion { Name = e, HasAttributes = true }).ToArray<AutocompleteSuggestion>();
var suggestions = autocomplete.GetSuggestions(input, out _).OfType<AutocompleteElementSuggestion>().ToArray();
var expected = elements.Select(e => new AutocompleteElementSuggestion { Name = e, HasAttributes = true }).ToArray();
Assert.Equal(expected, suggestions, new PropertyComparer());
}

Expand Down Expand Up @@ -96,8 +96,8 @@ public void SuggestsElementEnumValues(string input, params string[] values)
public void SuggestsRecursiveElements(string input, params string[] elements)
{
var autocomplete = new Autocomplete<Recursive>();
var suggestions = autocomplete.GetSuggestions(input, out _);
var expected = elements.Select(e => new AutocompleteElementSuggestion { Name = e }).ToArray<AutocompleteSuggestion>();
var suggestions = autocomplete.GetSuggestions(input, out _).OfType<AutocompleteElementSuggestion>().ToArray();
var expected = elements.Select(e => new AutocompleteElementSuggestion { Name = e }).ToArray();
Assert.Equal(expected, suggestions, new PropertyComparer());
}

Expand Down Expand Up @@ -160,8 +160,22 @@ public void CallbacksToCompleteValues(string input, string path)
public void SequenceOfChoice(string input, params string[] elements)
{
var autocomplete = new Autocomplete<Fetch>();
var suggestions = autocomplete.GetSuggestions(input, out _);
var expected = elements.Select(e => new AutocompleteElementSuggestion { Name = e, SelfClosing = e != "fetch" }).ToArray<AutocompleteSuggestion>();
var suggestions = autocomplete.GetSuggestions(input, out _).OfType<AutocompleteElementSuggestion>();
var expected = elements.Select(e => new AutocompleteElementSuggestion { Name = e, SelfClosing = e != "fetch" }).ToArray();
Assert.Equal(expected, suggestions, new PropertyComparer());
}

[Theory]
[InlineData("<fetch><", "fetch")]
[InlineData("<fetch></", "fetch")]
public void EndElement(string input, string element)
{
var autocomplete = new Autocomplete<Fetch>();
var suggestions = autocomplete.GetSuggestions(input, out _).OfType<AutocompleteEndElementSuggestion>().ToArray();
var expected = new []
{
new AutocompleteEndElementSuggestion { Name = element, IncludeSlash = !input.EndsWith("/") }
};
Assert.Equal(expected, suggestions, new PropertyComparer());
}
}
Expand Down

0 comments on commit 8c16c9f

Please sign in to comment.