private IEnumerable <DiagnosticRecord> FindHashtableViolations(TokenOperations tokenOps)
        {
            var hashtableAsts = tokenOps.Ast.FindAll(ast => ast is HashtableAst, true);
            var groups        = new List <List <Tuple <IScriptExtent, IScriptExtent> > >();

            if (hashtableAsts != null)
            {
                foreach (var astItem in hashtableAsts)
                {
                    groups.Add(GetExtents(tokenOps, (HashtableAst)astItem));
                }
            }

#if !PSV3
            var configAsts = tokenOps.Ast.FindAll(ast => ast is ConfigurationDefinitionAst, true);
            if (configAsts != null)
            {
                // There are probably parse errors caused by an "Undefined DSC resource"
                // which prevents the parser from detecting the property value pairs as
                // hashtable. Hence, this is a workaround to format configurations which
                // have "Undefined DSC resource" parse errors.

                // find all commandAsts of the form "prop" "=" "val" that have the same parent
                // and format those pairs.
                foreach (var configAst in configAsts)
                {
                    groups.AddRange(GetCommandElementExtentGroups(configAst));
                }
            }
#endif

            // it is probably much easier have a hashtable writer that formats the hashtable and writes it
            // but it makes handling comments hard. So we need to use this approach.

            // This is how the algorithm actually works:
            // if each key value pair are on a separate line
            //   find all the assignment operators
            //   if all the assignment operators are aligned (check the column number of each assignment operator)
            //      skip
            //   else
            //      find the distance between the assignment operators and their corresponding LHS
            //      find the longest left expression
            //      make sure all the assignment operators are in the same column as that of the longest left hand.
            foreach (var extentTuples in groups)
            {
                if (!HasPropertiesOnSeparateLines(extentTuples))
                {
                    continue;
                }

                if (extentTuples == null ||
                    extentTuples.Count == 0 ||
                    !extentTuples.All(t => t.Item1.StartLineNumber == t.Item2.EndLineNumber))
                {
                    continue;
                }

                var widestKeyExtent = extentTuples
                                      .Select(t => t.Item1)
                                      .Aggregate((t1, tAggregate) =>
                {
                    return(TokenOperations.GetExtentWidth(tAggregate) > TokenOperations.GetExtentWidth(t1)
                            ? tAggregate
                            : t1);
                });
                var expectedStartColumnNumber = widestKeyExtent.EndColumnNumber + 1;
                foreach (var extentTuple in extentTuples)
                {
                    if (extentTuple.Item2.StartColumnNumber != expectedStartColumnNumber)
                    {
                        yield return(new DiagnosticRecord(
                                         GetError(),
                                         extentTuple.Item2,
                                         GetName(),
                                         GetDiagnosticSeverity(),
                                         extentTuple.Item1.File,
                                         null,
                                         GetHashtableCorrections(extentTuple, expectedStartColumnNumber).ToList()));
                    }
                }
            }
        }
        private IEnumerable <DiagnosticRecord> FindHashtableViolations(TokenOperations tokenOps)
        {
            var hashtableAsts = tokenOps.Ast.FindAll(ast => ast is HashtableAst, true);

            if (hashtableAsts == null)
            {
                yield break;
            }

            // it is probably much easier have a hashtable writer that formats the hashtable and writes it
            // but it makes handling comments hard. So we need to use this approach.

            // This is how the algorithm actually works:
            // if each key value pair are on a separate line
            //   find all the assignment operators
            //   if all the assignment operators are aligned (check the column number of each assignment operator)
            //      skip
            //   else
            //      find the distance between the assignment operators and their corresponding LHS
            //      find the longest left expression
            //      make sure all the assignment operators are in the same column as that of the longest left hand.

            foreach (var astItem in hashtableAsts)
            {
                var hashtableAst = (HashtableAst)astItem;
                if (!HasKeysOnSeparateLines(hashtableAst))
                {
                    continue;
                }

                var extentTuples = GetExtents(tokenOps, hashtableAst);
                if (extentTuples == null ||
                    extentTuples.Count == 0 ||
                    !extentTuples.All(t => t.Item1.StartLineNumber == t.Item2.EndLineNumber))
                {
                    continue;
                }

                var widestKeyExtent = extentTuples
                                      .Select(t => t.Item1)
                                      .Aggregate((t1, tAggregate) => {
                    return(TokenOperations.GetExtentWidth(tAggregate) > TokenOperations.GetExtentWidth(t1)
                        ? tAggregate
                        : t1);
                });
                var expectedStartColumnNumber = widestKeyExtent.EndColumnNumber + 1;
                foreach (var extentTuple in extentTuples)
                {
                    if (extentTuple.Item2.StartColumnNumber != expectedStartColumnNumber)
                    {
                        yield return(new DiagnosticRecord(
                                         GetError(),
                                         extentTuple.Item2,
                                         GetName(),
                                         GetDiagnosticSeverity(),
                                         extentTuple.Item1.File,
                                         null,
                                         GetHashtableCorrections(extentTuple, expectedStartColumnNumber).ToList()));
                    }
                }
            }
        }