/// <summary>
 /// Load the ruleset file according to the document file path.
 /// </summary>
 /// <param name="document">The specified document file.</param>
 private void LoadRuleset(ITextDocument document)
 {
     try
     {
         for (string path = Path.GetDirectoryName(document.FilePath); path != null; path = Path.GetDirectoryName(path))
         {
             string[] rulesetFiles = Directory.GetFiles(path, "*.ruleset");
             if (rulesetFiles.Length == 1)
             {
                 this.ruleset = DirectiveRuleset.LoadFromRulesetFile(rulesetFiles[0]);
                 this.RulesetFilePath = Path.GetFullPath(rulesetFiles[0]);
                 this.NoRulesetFileReason = null;
             }
             else if (rulesetFiles.Length > 1)
             {
                 this.RulesetFilePath = null;
                 this.NoRulesetFileReason = string.Format("Folder \"{0}\" contains more than one \"*.ruleset\" files", path);
             }
             if (rulesetFiles.Length > 0)
                 break;
         }
         if (this.ruleset == null)
             this.NoRulesetFileReason = "No \"*.ruleset\" file was found in the directory hierarchy";
     }
     catch (Exception ex)
     {
         this.RulesetFilePath = null;
         this.NoRulesetFileReason = ex.ToString();
     }
 }
        ///////////////////////////////////////////////////////////////////////////////////
        //
        // Validate the mardown directive syntax according to the ruleset definitions
        //
        // Copyright (c) 2014 Microsoft Corporation.
        // Author: Junyi Yi ([email protected]) - Initial version
        //
        ///////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Validate the whole document according to the specified ruleset.
        /// </summary>
        /// <param name="snapshot">The whole document snapshot.</param>
        /// <param name="errorTagger">The tagger used to generate error squiggles.</param>
        /// <param name="ruleset">The specified ruleset.</param>
        public static void ValidateDirectiveSyntax(ITextSnapshot snapshot, DirectiveRuleset ruleset, SimpleTagger<ErrorTag> errorTagger)
        {
            // Remove all current error squiggles
            errorTagger.RemoveTagSpans(errorTagSpan => true);

            // Get the full document text and clear all HTML tags
            string text = snapshot.GetText();
            text = MarkdownParser.DestroyHtmlTags(text);

            // Three cases:
            // 0123456789              01234567 8              01234567  8
            // [  WA ab ]              [  WA ab \n             [  WA ab EOT
            // |        |-endIndex=9   |        |-endIndex=8   |         |-endIndex=8
            // |-startIndex=0          |-startIndex=0          |-startIndex=0

            // Greedily search for the pair of '[...]' (supports nested pair '[... [...] ...]')
            // Here 'Greedily' means if we have a string '[...[...]', it would also treat the latter '[...]' as the pair
            for (int startIndex = text.IndexOf('['); startIndex >= 0; startIndex = text.IndexOf('[', startIndex))
            {
                int endIndex = MarkdownParser.FindCorrespondingEndBracket(text, startIndex + 1);

                // Get the directive content string
                ITrackingSpan overallDirective = snapshot.CreateTrackingSpan(startIndex + 1, endIndex - startIndex - 1, SpanTrackingMode.EdgeInclusive);
                string directive = overallDirective.GetText(snapshot);
                var directiveMatches = Regex.Matches(directive, string.Concat(@"^\s*(", ValidationUtilities.DirectiveNameRegularPattern, @")(.*)$"));
                if (directiveMatches.Count != 1 || !directiveMatches[0].Success || directiveMatches[0].Groups.Count != 3 || directiveMatches[0].Value != directive)
                {
                    startIndex++;
                    continue;
                }
                string directiveName = directiveMatches[0].Groups[1].Value;
                string directiveContent = directiveMatches[0].Groups[2].Value;

                var rule = ruleset.TryGetDirectiveRule(directiveName);
                if (rule != null)
                {
                    // Get the preceding and following directive string of the same line
                    ITextSnapshotLine line = snapshot.GetLineFromPosition(startIndex);
                    string precedingText = snapshot.GetText(line.Start, startIndex - line.Start);
                    string followingText = endIndex < line.End ? snapshot.GetText(endIndex + 1, line.End - endIndex - 1) : string.Empty;

                    // If we found a exactly-matched rule, just validate it
                    string message = rule.Validate(directiveContent, precedingText, followingText);
                    if (message != null)
                    {
                        ITrackingSpan squiggleSpan = overallDirective;
                        if (rule.SquiggleWholeLine)
                        {
                            squiggleSpan = snapshot.CreateTrackingSpan(line.Start, line.Length, SpanTrackingMode.EdgeInclusive);
                        }
                        errorTagger.CreateTagSpan(squiggleSpan, new ErrorTag(PredefinedErrorTypeNames.SyntaxError, message));
                    }

                    // If we miss the closing bracket, give out the prompt message
                    if (endIndex >= text.Length || text[endIndex] != ']')
                    {
                        errorTagger.CreateTagSpan(snapshot.CreateTrackingSpan(line.End, 0, SpanTrackingMode.EdgePositive), new ErrorTag(PredefinedErrorTypeNames.CompilerError, "Missing the closing bracket"));
                    }
                }
                else
                {
                    // Otherwise we may take a look at the suspects
                    var suspects = ruleset.GetSuspects(directive);
                    if (suspects.Count() > 0)
                    {
                        StringBuilder suspectPrompt = new StringBuilder();
                        suspectPrompt.AppendLine("Are you trying to enter one of the following directives?");
                        foreach (var suspect in suspects)
                        {
                            suspectPrompt.AppendLine(string.Format("    \u2022 {0} - {1}", suspect.ParentRule.DirectiveName, suspect.SuggestionMessage));
                        }
                        errorTagger.CreateTagSpan(overallDirective, new ErrorTag(PredefinedErrorTypeNames.Warning, suspectPrompt.ToString().TrimEnd()));
                    }
                }

                startIndex = endIndex;
            }
        }