Skip to content

Commit

Permalink
Improved validator
Browse files Browse the repository at this point in the history
[release]
  • Loading branch information
madskristensen committed Dec 20, 2021
1 parent 8470d4f commit 99488e7
Show file tree
Hide file tree
Showing 17 changed files with 130 additions and 75 deletions.
4 changes: 2 additions & 2 deletions src/Commands/CommentCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace PkgdefLanguage
{
[Export(typeof(ICommandHandler))]
[Name(nameof(CommentCommand))]
[ContentType(Language.LanguageName)]
[ContentType(Constants.LanguageName)]
[TextViewRole(PredefinedTextViewRoles.PrimaryDocument)]
public class CommentCommand : ICommandHandler<CommentSelectionCommandArgs>
{
Expand All @@ -25,7 +25,7 @@ public bool ExecuteCommand(CommentSelectionCommandArgs args, CommandExecutionCon

foreach (ITextViewLine line in lines.Reverse())
{
args.TextView.TextBuffer.Insert(line.Start.Position, Constants.CommentChar.ToString());
args.TextView.TextBuffer.Insert(line.Start.Position, Constants.CommentChars[0]);
}

return true;
Expand Down
4 changes: 2 additions & 2 deletions src/Commands/UncommentCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace PkgdefLanguage
{
[Export(typeof(ICommandHandler))]
[Name(nameof(CommentCommand))]
[ContentType(Language.LanguageName)]
[ContentType(Constants.LanguageName)]
[TextViewRole(PredefinedTextViewRoles.PrimaryDocument)]
public class UncommentCommand : ICommandHandler<UncommentSelectionCommandArgs>
{
Expand All @@ -26,7 +26,7 @@ public bool ExecuteCommand(UncommentSelectionCommandArgs args, CommandExecutionC
foreach (ITextViewLine line in lines.Reverse())
{
var span = Span.FromBounds(line.Start, line.End);
var text = args.TextView.TextBuffer.CurrentSnapshot.GetText(span).TrimStart(Constants.CommentChar);
var text = args.TextView.TextBuffer.CurrentSnapshot.GetText(span).TrimStart(Constants.CommentChars[0][0]);
args.TextView.TextBuffer.Replace(span, text);
}

Expand Down
5 changes: 4 additions & 1 deletion src/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
{
public class Constants
{
public const char CommentChar = ';';
public const string LanguageName = "Pkgdef";
public static string[] CommentChars = new[] { ";", "//" };
public const string PkgDefExt = ".pkgdef";
public const string PkgUndefExt = ".pkgundef";
}
}
4 changes: 2 additions & 2 deletions src/Language/BraceCompletion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ namespace PkgdefLanguage
[BracePair('{', '}')]
[BracePair('"', '"')]
[BracePair('$', '$')]
[ContentType(Language.LanguageName)]
[Name(Language.LanguageName)]
[ContentType(Constants.LanguageName)]
[Name(Constants.LanguageName)]
internal sealed class BraceCompletion : BraceCompletionBase
{

Expand Down
4 changes: 2 additions & 2 deletions src/Language/ClassificationTagger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ namespace PkgdefLanguage
{
[Export(typeof(ITaggerProvider))]
[TagType(typeof(IClassificationTag))]
[ContentType(Language.LanguageName)]
[Name(Language.LanguageName)]
[ContentType(Constants.LanguageName)]
[Name(Constants.LanguageName)]
internal class ClassificationTaggerProvider : ITaggerProvider
{
[Import] internal IClassificationTypeRegistryService _classificationRegistry = null;
Expand Down
4 changes: 2 additions & 2 deletions src/Language/CompletionCommitManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
namespace PkgdefLanguage
{
[Export(typeof(IAsyncCompletionCommitManagerProvider))]
[ContentType(Language.LanguageName)]
[Name(Language.LanguageName)]
[ContentType(Constants.LanguageName)]
[Name(Constants.LanguageName)]
internal class CompletionCommitManager : CompletionCommitManagerBase
{
public override IEnumerable<char> CommitChars => new char[] { ' ', '\'', '"', ',', '.', ';', ':' };
Expand Down
13 changes: 8 additions & 5 deletions src/Language/CompletionSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
namespace PkgdefLanguage
{
[Export(typeof(IAsyncCompletionSourceProvider))]
[ContentType(Language.LanguageName)]
[Name(Language.LanguageName)]
[ContentType(Constants.LanguageName)]
[Name(Constants.LanguageName)]
internal class CompletionSourceProvider : IAsyncCompletionSourceProvider
{
[Import] internal ITextStructureNavigatorSelectorService _structureNavigator = null;
Expand Down Expand Up @@ -84,8 +84,8 @@ public CompletionStartData InitializeCompletion(CompletionTrigger trigger, Snaps
// We don't trigger completion when user typed
if (char.IsNumber(trigger.Character) // a number
|| char.IsPunctuation(trigger.Character) // punctuation
|| char.IsSymbol(trigger.Character) // punctuation
|| trigger.Character == '\n' // new line
|| trigger.Character == Constants.CommentChar
|| trigger.Reason == CompletionTriggerReason.Backspace
|| trigger.Reason == CompletionTriggerReason.Deletion)
{
Expand All @@ -107,9 +107,12 @@ public CompletionStartData InitializeCompletion(CompletionTrigger trigger, Snaps

SnapshotSpan tokenSpan = FindTokenSpanAtPosition(triggerLocation);

if (triggerLocation.GetContainingLine().GetText().StartsWith(Constants.CommentChar.ToString(), StringComparison.Ordinal))
foreach (var commentChar in Constants.CommentChars)
{
return CompletionStartData.DoesNotParticipateInCompletion;
if (triggerLocation.GetContainingLine().GetText().StartsWith(commentChar, StringComparison.Ordinal))
{
return CompletionStartData.DoesNotParticipateInCompletion;
}
}

return new CompletionStartData(CompletionParticipation.ProvidesItems, tokenSpan);
Expand Down
4 changes: 2 additions & 2 deletions src/Language/ErrorTagger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ namespace PkgdefLanguage
{
[Export(typeof(ITaggerProvider))]
[TagType(typeof(IErrorTag))]
[ContentType(Language.LanguageName)]
[Name(Language.LanguageName)]
[ContentType(Constants.LanguageName)]
[Name(Constants.LanguageName)]
public class RestErrorTaggerProvider : ITaggerProvider
{
public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag =>
Expand Down
7 changes: 2 additions & 5 deletions src/Language/Language.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,14 @@ namespace PkgdefLanguage
[Guid(PackageGuids.EditorFactoryString)]
public class Language : LanguageBase
{
public const string LanguageName = "Pkgdef";
public const string FileExtension = ".pkgdef";

public Language(object site) : base(site)
{
ThreadHelper.ThrowIfNotOnUIThread();
}

public override string Name => LanguageName;
public override string Name => Constants.LanguageName;

public override string[] FileExtensions => new[] { FileExtension };
public override string[] FileExtensions => new[] { Constants.PkgDefExt, Constants.PkgUndefExt };

public override void SetDefaultPreferences(LanguagePreferences preferences)
{
Expand Down
4 changes: 2 additions & 2 deletions src/Language/QuickInfoSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
namespace PkgdefLanguage
{
[Export(typeof(IAsyncQuickInfoSourceProvider))]
[ContentType(Language.LanguageName)]
[Name(Language.LanguageName)]
[ContentType(Constants.LanguageName)]
[Name(Constants.LanguageName)]
internal sealed class QuickInfoSourceProvider : IAsyncQuickInfoSourceProvider
{
public IAsyncQuickInfoSource TryCreateQuickInfoSource(ITextBuffer buffer) =>
Expand Down
5 changes: 2 additions & 3 deletions src/Language/StructureTagger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Adornments;
Expand All @@ -13,8 +12,8 @@ namespace PkgdefLanguage
{
[Export(typeof(ITaggerProvider))]
[TagType(typeof(IStructureTag))]
[ContentType(Language.LanguageName)]
[Name(Language.LanguageName)]
[ContentType(Constants.LanguageName)]
[Name(Constants.LanguageName)]
public class StructureTaggerProvider : ITaggerProvider
{
public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag =>
Expand Down
48 changes: 5 additions & 43 deletions src/Parser/DocumentParser.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace PkgdefLanguage
{
public partial class Document
{
private static readonly Regex _regexProperty = new(@"^(?<name>(@|"".+""))(\s)*(?<equals>=)\s*(?<value>.+)", RegexOptions.Compiled);
private static readonly Regex _regexProperty = new(@"^(?<name>.+)(\s)*(?<equals>=)\s*(?<value>.+)", RegexOptions.Compiled);
private static readonly Regex _regexRef = new(@"(?<open>\$)(?<value>[\w]+)(?<close>\$)", RegexOptions.Compiled);

public bool IsParsing { get; private set; }
Expand Down Expand Up @@ -51,7 +49,7 @@ private IEnumerable<ParseItem> ParseLine(int start, string line, List<ParseItem>
List<ParseItem> items = new();

// Comment
if (trimmedLine.StartsWith(Constants.CommentChar.ToString()))
if (trimmedLine.StartsWith(Constants.CommentChars[0]) || trimmedLine.StartsWith(Constants.CommentChars[1]))
{
items.Add(ToParseItem(line, start, ItemType.Comment, false));
}
Expand All @@ -65,8 +63,9 @@ private IEnumerable<ParseItem> ParseLine(int start, string line, List<ParseItem>
// Property
else if (tokens.Count > 0 && IsMatch(_regexProperty, trimmedLine, out Match matchHeader))
{
items.Add(ToParseItem(matchHeader, start, "name"));
items.Add(ToParseItem(matchHeader, start, "value", true));
items.Add(ToParseItem(matchHeader, start, "name", false));
items.Add(ToParseItem(matchHeader, start, "equals", ItemType.Operator, false));
items.Add(ToParseItem(matchHeader, start, "value"));
}
// Unknown
else if (trimmedLine.Length > 0)
Expand Down Expand Up @@ -122,43 +121,6 @@ private void AddVariableReferences(ParseItem token)
}
}

private void ValidateDocument()
{
foreach (ParseItem item in Items)
{
// Unknown symbols
if (item.Type == ItemType.Unknown)
{
item.Errors.Add("Unknown token at this location.");
}

// Registry key
if (item.Type == ItemType.RegistryKey)
{
var trimmedText = item.Text.Trim();

if (!trimmedText.EndsWith("]"))
{
item.Errors.Add("Unclosed registry key entry. Add the missing ] character");
}

if (trimmedText.Contains("/") && !trimmedText.Contains("\\/"))
{
item.Errors.Add("Use the backslash character as delimiter instead of forward slash.");
}
}

// Unknown variables
if (item.Type == ItemType.ReferenceName)
{
if (!CompletionCatalog.Variables.Any(v => v.Key.Equals(item.Text, StringComparison.OrdinalIgnoreCase)))
{
item.Errors.Add($"The variable \"{item.Text}\" doens't exist.");
}
}
}
}

private void OrganizeItems()
{
List<Entry> entries = new();
Expand Down
73 changes: 73 additions & 0 deletions src/Parser/DocumentValidator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System;
using System.Linq;

namespace PkgdefLanguage
{
public partial class Document
{
private void ValidateDocument()
{
foreach (ParseItem item in Items)
{
// Unknown symbols
if (item.Type == ItemType.Unknown)
{
item.Errors.Add("Unknown token at this location.");
}

// Registry key
if (item.Type == ItemType.RegistryKey)
{
var trimmedText = item.Text.Trim();

if (!trimmedText.EndsWith("]"))
{
item.Errors.Add("Unclosed registry key entry. Add the missing ] character");
}

if (trimmedText.Contains("/") && !trimmedText.Contains("\\/"))
{
item.Errors.Add("Use the backslash character as delimiter instead of forward slash.");
}
}

// Properties
else if (item.Type == ItemType.Operator)
{
ParseItem name = item.Previous;
ParseItem value = item.Next;

if (name?.Type == ItemType.String)
{
if (name.Text == "\"@\"")
{
name.Errors.Add("To set a registry key's default value, use '@' without quotation marks");
}
}
else if (name?.Type == ItemType.Literal && name?.Text != "@")
{
name.Errors.Add("Value names must be enclosed in quotation marks.");
}
}

// Make sure strings are correctly closed with quotation mark
if (item.Type == ItemType.String)
{
if (!item.Text.EndsWith("\""))
{
item.Errors.Add("Value names must be enclosed in quotation marks.");
}
}

// Unknown references
foreach (Reference reference in item.References)
{
if (!CompletionCatalog.Variables.Any(v => v.Key.Equals(reference.Value.Text, StringComparison.OrdinalIgnoreCase)))
{
reference.Value.Errors.Add($"The variable \"{item.Text}\" doens't exist.");
}
}
}
}
}
}
1 change: 1 addition & 0 deletions src/Parser/ItemType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public enum ItemType
RegistryKey,
String,
Literal,
Operator,
Unknown,
}
}
1 change: 1 addition & 0 deletions src/PkgdefLanguage.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
<Compile Include="Language\StructureTagger.cs" />
<Compile Include="Parser\Document.cs" />
<Compile Include="Parser\DocumentParser.cs" />
<Compile Include="Parser\DocumentValidator.cs" />
<Compile Include="Parser\ItemType.cs" />
<Compile Include="Parser\ParseItem.cs" />
<Compile Include="Parser\Property.cs" />
Expand Down
10 changes: 6 additions & 4 deletions src/PkgdefPackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ namespace PkgdefLanguage
[Guid(PackageGuids.PkgdefLanguageString)]
[ProvideMenuResource("Menus.ctmenu", 1)]

[ProvideLanguageService(typeof(Language), Language.LanguageName, 0, MatchBraces = true, MatchBracesAtCaret = true, EnableAsyncCompletion = true, EnableCommenting = true, ShowCompletion = true, ShowMatchingBrace = true)]
[ProvideLanguageExtension(typeof(Language), Language.FileExtension)]
[ProvideFileIcon(Language.FileExtension, "KnownMonikers.RegistrationScript")]
[ProvideBraceCompletion(Language.LanguageName)]
[ProvideLanguageService(typeof(Language), Constants.LanguageName, 0, MatchBraces = true, MatchBracesAtCaret = true, EnableAsyncCompletion = true, EnableCommenting = true, ShowCompletion = true, ShowMatchingBrace = true)]
[ProvideLanguageExtension(typeof(Language), Constants.PkgDefExt)]
[ProvideLanguageExtension(typeof(Language), Constants.PkgUndefExt)]
[ProvideFileIcon(Constants.PkgDefExt, "KnownMonikers.RegistrationScript")]
[ProvideFileIcon(Constants.PkgUndefExt, "KnownMonikers.RegistrationScript")]
[ProvideBraceCompletion(Constants.LanguageName)]
[ProvideEditorFactory(typeof(Language), 0, CommonPhysicalViewAttributes = (int)__VSPHYSICALVIEWATTRIBUTES.PVA_SupportsPreview, TrustLevel = __VSEDITORTRUSTLEVEL.ETL_AlwaysTrusted)]
[ProvideEditorLogicalView(typeof(Language), VSConstants.LOGVIEWID.TextView_string, IsTrusted = true)]
public sealed class PkgdefPackage : ToolkitPackage
Expand Down
14 changes: 14 additions & 0 deletions test/ParseTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,19 @@ public async Task Comment()
Assert.AreEqual(2, doc.Items.Count);
Assert.AreEqual(2, doc.Items.Where(i => i.Type == ItemType.Comment).Count());
}

[TestMethod]
public async Task InvalidPropertyName()
{
var lines = new[] { "[test]\r\n",
"\"@\"=\"test\"",
};

var doc = Document.FromLines(lines);
await doc.WaitForParsingCompleteAsync();
ParseItem prop = doc.Entries[0].Properties[0].Name;

Assert.IsFalse(prop.IsValid);
}
}
}

0 comments on commit 99488e7

Please sign in to comment.