示例#1
0
        /// <summary>
        ///  Get the grammatical categories and value being completed at the given query position.
        ///  This is the pure grammar part of IntelliSense determination.
        /// </summary>
        /// <param name="queryBeforeCursor">Query up to where the cursor is placed</param>
        /// <param name="lastTerm">The TermExpression in progress as parsed</param>
        /// <returns>IntelliSenseGuidance showing the token in progress and possible grammar categories for it</returns>
        internal IntelliSenseGuidance GetCurrentTokenOptions(string queryBeforeCursor, out TermExpression lastTerm, out IExpression query)
        {
            lastTerm = null;
            query    = null;
            IntelliSenseGuidance defaultGuidance = new IntelliSenseGuidance(String.Empty, QueryTokenCategory.Term);

            // If the query is empty, return the guidance for the beginning of the first term
            if (String.IsNullOrEmpty(queryBeforeCursor))
            {
                return(defaultGuidance);
            }

            // Parse the query, asking for hint terms
            query = QueryParser.Parse(queryBeforeCursor, true);

            // If the query had parse errors, return empty guidance
            if (query is EmptyExpression)
            {
                return(new IntelliSenseGuidance(String.Empty, QueryTokenCategory.None));
            }

            // Get the last query term to look at the IntelliSense guidance
            lastTerm = query.GetLastTerm();

            // If no last term, return first term guidance (ex: inside new '('
            if (lastTerm == null)
            {
                return(defaultGuidance);
            }

            // Otherwise, grab the last term guidance
            IntelliSenseGuidance guidance = lastTerm.Guidance;

            return(guidance);
        }
示例#2
0
        private static void AddSuggestionsForTerm(IReadOnlyCollection <Table> targetTables, IntelliSenseResult result, TermExpression lastTerm, IntelliSenseGuidance guidance, List <IntelliSenseItem> suggestions)
        {
            if (lastTerm != null && lastTerm.ColumnName == "*")
            {
                object termValue = lastTerm.Value;
                List <Tuple <string, int, int> > columnsForTerm = new List <Tuple <string, int, int> >();

                foreach (Table table in targetTables)
                {
                    DataBlockResult columns = table.Query(new TermInColumnsQuery(termValue.ToString(), GetCompleteQueryPrefix(result)));
                    for (int i = 0; i < columns.Values.RowCount; ++i)
                    {
                        columnsForTerm.Add(new Tuple <string, int, int>((string)columns.Values[i, 0], (int)columns.Values[i, 1], (int)columns.Total));
                    }
                }

                // Sort overall set by frequency descending
                columnsForTerm.Sort((left, right) => ((double)right.Item2 / (double)right.Item3).CompareTo((double)left.Item2 / (double)left.Item3));

                // Add top 10 suggestions
                int countToReturn = Math.Min(10, columnsForTerm.Count);
                for (int i = 0; i < countToReturn; ++i)
                {
                    suggestions.Add(new IntelliSenseItem(QueryTokenCategory.ColumnName, QueryParser.WrapColumnName(columnsForTerm[i].Item1) + " : " + QueryParser.WrapValue(termValue), columnsForTerm[i].Item2, columnsForTerm[i].Item3));
                }
            }
        }
示例#3
0
        private static void AddTopColumnValues(IntelliSenseResult result, TermExpression lastTerm, IntelliSenseGuidance guidance, List <IntelliSenseItem> suggestions, Table singleTable, ColumnDetails singleColumn)
        {
            string completeQuery = GetCompleteQueryPrefix(result);

            // Recommend the top ten values in the column with the prefix typed so far
            DistinctResult topValues = singleTable.Query(new DistinctQueryTop(singleColumn.Name, lastTerm.Value.ToString(), completeQuery, 10));
            int            total     = (int)topValues.Total;

            if (topValues.Total == 0)
            {
                return;
            }

            // Walk values in order for ==, :, ::, backwards with inverse percentages for !=
            bool isNotEquals = lastTerm.Operator == Operator.NotEquals;
            int  start       = isNotEquals ? topValues.Values.RowCount - 1 : 0;
            int  end         = isNotEquals ? -1 : topValues.Values.RowCount;
            int  step        = isNotEquals ? -1 : 1;

            for (int i = start; i != end; i += step)
            {
                string value         = topValues.Values[i, 0].ToString();
                int    countForValue = (int)topValues.Values[i, 1];
                if (isNotEquals)
                {
                    countForValue = (int)topValues.Total - countForValue;
                }

                if ((countForValue > 1 || total <= 10) && value.StartsWith(guidance.Value, StringComparison.OrdinalIgnoreCase) && value.Length < 100)
                {
                    suggestions.Add(new IntelliSenseItem(QueryTokenCategory.Value, QueryParser.WrapValue(topValues.Values[i, 0]), countForValue, topValues.Total));
                }
            }
        }
示例#4
0
        private static void AddValueDistribution(IntelliSenseResult result, TermExpression lastTerm, IntelliSenseGuidance guidance, List <IntelliSenseItem> suggestions, Table singleTable, ColumnDetails singleColumn)
        {
            string completeQuery = GetCompleteQueryPrefix(result);

            bool inclusive = (lastTerm.Operator == Operator.LessThanOrEqual || lastTerm.Operator == Operator.GreaterThan);
            bool reverse   = (lastTerm.Operator == Operator.GreaterThan || lastTerm.Operator == Operator.GreaterThanOrEqual);

            // Recommend the top ten values in the column with the prefix typed so far
            DataBlockResult distribution = singleTable.Query(new DistributionQuery(singleColumn.Name, completeQuery, inclusive));

            if (distribution.Total == 0 || distribution.Details.Succeeded == false)
            {
                return;
            }

            int countSoFar;

            if (reverse)
            {
                countSoFar = (int)distribution.Values[distribution.Values.RowCount - 1, 1];

                for (int i = distribution.Values.RowCount - 2; i >= 0; --i)
                {
                    string value         = distribution.Values[i, 0].ToString();
                    double frequency     = (double)countSoFar / (double)(distribution.Total);
                    int    countForRange = (int)distribution.Values[i, 1];

                    if ((distribution.Values.RowCount == 2 || (int)distribution.Values[i + 1, 1] > 0) && value.StartsWith(guidance.Value, StringComparison.OrdinalIgnoreCase))
                    {
                        suggestions.Add(new IntelliSenseItem(QueryTokenCategory.Value, QueryParser.WrapValue(distribution.Values[i, 0]), countSoFar, distribution.Total));
                    }

                    countSoFar += countForRange;
                }
            }
            else
            {
                countSoFar = 0;

                for (int i = 0; i < distribution.Values.RowCount - 1; ++i)
                {
                    string value         = distribution.Values[i, 0].ToString();
                    int    countForRange = (int)distribution.Values[i, 1];
                    countSoFar += countForRange;

                    double frequency = (double)countSoFar / (double)(distribution.Total);

                    if ((distribution.Values.RowCount == 2 || countForRange > 0) && value.StartsWith(guidance.Value, StringComparison.OrdinalIgnoreCase))
                    {
                        suggestions.Add(new IntelliSenseItem(QueryTokenCategory.Value, QueryParser.WrapValue(distribution.Values[i, 0]), countSoFar, distribution.Total));
                    }
                }
            }
        }
示例#5
0
        private static void AddSuggestionsForValue(IReadOnlyCollection <Table> targetTables, IntelliSenseResult result, TermExpression lastTerm, IntelliSenseGuidance guidance, ref bool spaceIsSafeCompletionCharacter, List <IntelliSenseItem> suggestions)
        {
            Table         singleTable;
            ColumnDetails singleColumn;

            if (!TryFindSingleMatchingColumn(targetTables, lastTerm, out singleTable, out singleColumn))
            {
                // If more than one table has the column, we can't recommend anything
                result.SyntaxHint = Value;
            }
            else
            {
                if (lastTerm.Operator == Operator.Equals || lastTerm.Operator == Operator.NotEquals || lastTerm.Operator == Operator.Matches || lastTerm.Operator == Operator.MatchesExact)
                {
                    AddTopColumnValues(result, lastTerm, guidance, suggestions, singleTable, singleColumn);
                }
                else if (lastTerm.Operator == Operator.LessThan || lastTerm.Operator == Operator.LessThanOrEqual || lastTerm.Operator == Operator.GreaterThan || lastTerm.Operator == Operator.GreaterThanOrEqual)
                {
                    AddValueDistribution(result, lastTerm, guidance, suggestions, singleTable, singleColumn);
                }

                Type columnType = singleTable.GetColumnType(singleColumn.Name);

                if (columnType == typeof(ByteBlock))
                {
                    result.SyntaxHint = StringValue;
                }
                else if (columnType == typeof(bool))
                {
                    if (suggestions.Count == 0)
                    {
                        AddWhenPrefixes(BooleanValues, guidance.Value, suggestions);
                    }
                    spaceIsSafeCompletionCharacter = true;
                }
                else if (columnType == typeof(DateTime))
                {
                    result.SyntaxHint = DateTimeValue;
                }
                else if (columnType == typeof(TimeSpan))
                {
                    result.SyntaxHint = TimeSpanValue;
                }
                else if (columnType == typeof(float) || columnType == typeof(double))
                {
                    result.SyntaxHint = FloatValue;
                }
                else if (columnType == typeof(byte) || columnType == typeof(sbyte) || columnType == typeof(short) || columnType == typeof(ushort) || columnType == typeof(int) || columnType == typeof(uint) || columnType == typeof(long) || columnType == typeof(ulong))
                {
                    result.SyntaxHint = IntegerValue;
                }
                else
                {
                    result.SyntaxHint = String.Format("<{0}>", columnType.Name);
                }
            }
        }
示例#6
0
        private static void AddSuggestionsForColumnNames(IReadOnlyCollection <Table> targetTables, IntelliSenseGuidance guidance, ref bool spaceIsSafeCompletionCharacter, List <IntelliSenseItem> suggestions)
        {
            List <IntelliSenseItem> selectedColumns = new List <IntelliSenseItem>();

            foreach (Table table in targetTables)
            {
                foreach (ColumnDetails column in table.ColumnDetails)
                {
                    if (column.Name.StartsWith(guidance.Value, StringComparison.OrdinalIgnoreCase))
                    {
                        // Add the matching column
                        // Hack: Set the Display value to the bare column name and CompleteAs to the wrapped [ColumnName], so sort order is right
                        selectedColumns.Add(new IntelliSenseItem(QueryTokenCategory.ColumnName, column.Name, String.Format("{0}, {1}", column.Type, table.Name), "[" + column.Name + "]"));

                        if (column.Name.Length > guidance.Value.Length && column.Name[guidance.Value.Length] == ' ')
                        {
                            // Space is unsafe to complete with if a suggest column has a space next in the value
                            spaceIsSafeCompletionCharacter = false;
                        }
                    }
                }
            }

            // Sort selected columns alphabetically *by bare column name*, so [Count] will be before [Count of Options].
            selectedColumns.Sort((left, right) => left.Display.CompareTo(right.Display));

            // Unhack: Set the display value back to the wrapped [ColumnName]
            foreach (IntelliSenseItem item in selectedColumns)
            {
                item.Display = item.CompleteAs;
            }

            // Remove entries where the same column name was suggested by multiple tables (set the hint to '<Multiple Tables>')
            for (int i = 1; i < selectedColumns.Count; ++i)
            {
                if (selectedColumns[i - 1].Display == selectedColumns[i].Display)
                {
                    selectedColumns[i - 1].Hint = MultipleTables;
                    selectedColumns.RemoveAt(i);
                    --i;
                }
            }

            // Add 'All Columns' hint (last, only if nothing or '*' typed so far)
            if ("*".StartsWith(guidance.Value))
            {
                selectedColumns.Add(AllColumnNames);
            }

            // Add results to IntelliSense suggestions
            suggestions.AddRange(selectedColumns);
        }
示例#7
0
        private static void AddSuggestionsForCompareOperator(IReadOnlyCollection <Table> targetTables, TermExpression lastTerm, IntelliSenseGuidance guidance, List <IntelliSenseItem> suggestions)
        {
            Type          columnType = null;
            Table         singleTable;
            ColumnDetails singleColumn;

            if (TryFindSingleMatchingColumn(targetTables, lastTerm, out singleTable, out singleColumn))
            {
                columnType = singleTable.GetColumnType(singleColumn.Name);
            }

            if (columnType == null)
            {
                AddWhenPrefixes(CompareOperatorsForString, guidance.Value, suggestions);
            }
            else if (columnType == typeof(ByteBlock))
            {
                AddWhenPrefixes(CompareOperatorsForString, guidance.Value, suggestions);
            }
            else if (columnType == typeof(bool))
            {
                AddWhenPrefixes(CompareOperatorsForBoolean, guidance.Value, suggestions);
            }
            else
            {
                AddWhenPrefixes(CompareOperatorsForOther, guidance.Value, suggestions);
            }
        }
示例#8
0
        /// <summary>
        ///  Get the set of IntelliSense suggestions valid at the current position.
        ///  It's filtered to the set of valid query parts are the current position,
        ///  as well as the set of values the current partial value is a prefix for.
        /// </summary>
        /// <param name="queryBeforeCursor">Current Arriba Query up to the cursor position</param>
        /// <param name="targetTables">Table[s] which are valid for the current query</param>
        /// <returns>IntelliSenseResult reporting what to show</returns>
        public IntelliSenseResult GetIntelliSenseItems(string queryBeforeCursor, IReadOnlyCollection <Table> targetTables)
        {
            IntelliSenseResult result = new IntelliSenseResult()
            {
                Query = queryBeforeCursor, Incomplete = "", Complete = "", SyntaxHint = "", CompletionCharacters = new char[0], Suggestions = new List <IntelliSenseItem>()
            };

            // If no tables were passed, show no IntelliSense (hint that there's an error blocking all tables)
            if (queryBeforeCursor == null || targetTables == null || targetTables.Count == 0)
            {
                return(result);
            }

            // Filter the set of tables to those valid for the query so far
            targetTables = FilterToValidTablesForQuery(targetTables, queryBeforeCursor);

            // If no tables remain valid, show no IntelliSense (hint that there's an error blocking all tables)
            if (targetTables == null || targetTables.Count == 0)
            {
                return(result);
            }

            // Get grammatical categories valid after the query prefix
            TermExpression       lastTerm;
            IExpression          query;
            IntelliSenseGuidance guidance       = GetCurrentTokenOptions(queryBeforeCursor, out lastTerm, out query);
            bool spaceIsSafeCompletionCharacter = !String.IsNullOrEmpty(guidance.Value);

            // If there are no tokens suggested here, return empty completion
            if (guidance.Options == QueryTokenCategory.None)
            {
                return(result);
            }

            // Compute the CurrentCompleteValue, *before* considering values, so value IntelliSense can use them
            string queryWithoutIncompleteValue = queryBeforeCursor;

            if (!queryWithoutIncompleteValue.EndsWith(guidance.Value))
            {
                throw new ArribaException("Error: IntelliSense suggestion couldn't be applied.");
            }
            queryWithoutIncompleteValue = queryWithoutIncompleteValue.Substring(0, queryWithoutIncompleteValue.Length - guidance.Value.Length);

            // If the CurrentIncompleteValue is an explicit column name, remove and re-complete that, also
            if (queryWithoutIncompleteValue.EndsWith("["))
            {
                queryWithoutIncompleteValue = queryWithoutIncompleteValue.Substring(0, queryWithoutIncompleteValue.Length - 1);
            }

            result.Complete   = queryWithoutIncompleteValue;
            result.Incomplete = guidance.Value;

            // Build a ranked list of suggestions - preferred token categories, filtered to the prefix already typed
            List <IntelliSenseItem> suggestions = new List <IntelliSenseItem>();

            if (guidance.Options.HasFlag(QueryTokenCategory.BooleanOperator))
            {
                AddWhenPrefixes(BooleanOperators, guidance.Value, suggestions);
            }

            if (guidance.Options.HasFlag(QueryTokenCategory.CompareOperator))
            {
                AddSuggestionsForCompareOperator(targetTables, lastTerm, guidance, suggestions);
            }

            if (guidance.Options.HasFlag(QueryTokenCategory.ColumnName))
            {
                AddSuggestionsForColumnNames(targetTables, guidance, ref spaceIsSafeCompletionCharacter, suggestions);
            }

            // Space isn't safe to complete values (except when all explicit values shown, bool below)
            if (guidance.Options.HasFlag(QueryTokenCategory.Value))
            {
                spaceIsSafeCompletionCharacter = false;

                AddSuggestionsForTerm(targetTables, result, lastTerm, guidance, suggestions);
            }

            // If *only* a value is valid here, provide a syntax hint for the value type (and reconsider if space is safe to complete)
            if (guidance.Options == QueryTokenCategory.Value)
            {
                AddSuggestionsForValue(targetTables, result, lastTerm, guidance, ref spaceIsSafeCompletionCharacter, suggestions);
            }

            if (guidance.Options.HasFlag(QueryTokenCategory.TermPrefixes))
            {
                AddWhenPrefixes(TermPrefixes, guidance.Value, suggestions);
            }

            // Build a list of valid completion characters
            List <char> completionCharacters = new List <char>();

            completionCharacters.Add('\t');
            if (spaceIsSafeCompletionCharacter)
            {
                completionCharacters.Add(' ');
            }

            // If column names are valid here but term prefixes or compare operators, operator start characters are valid completion characters
            if (guidance.Options.HasFlag(QueryTokenCategory.ColumnName) && !guidance.Options.HasFlag(QueryTokenCategory.CompareOperator) && !guidance.Options.HasFlag(QueryTokenCategory.TermPrefixes))
            {
                completionCharacters.AddRange(ColumnNameCompletionCharacters);
            }

            // If there's only one suggestion and it's been fully typed, remove it
            if (suggestions.Count == 1 && suggestions[0].Display == guidance.Value)
            {
                suggestions.Clear();
            }

            // Finish populating the result
            result.Suggestions          = suggestions;
            result.CompletionCharacters = completionCharacters;

            return(result);
        }
示例#9
0
        private static void AddTopColumnValues(IntelliSenseResult result, TermExpression lastTerm, IntelliSenseGuidance guidance, List <IntelliSenseItem> suggestions, Table singleTable, ColumnDetails singleColumn)
        {
            // Lame, to turn single terms into AllQuery [normally they return nothing]
            string completeQuery = QueryParser.Parse(result.Complete).ToString();

            // Recommend the top ten values in the column with the prefix typed so far
            DistinctResult topValues = singleTable.Query(new DistinctQueryTop(singleColumn.Name, completeQuery, 10));
            int            total     = (int)topValues.Total;

            if (topValues.Total == 0)
            {
                return;
            }

            // Walk values in order for ==, :, ::, backwards with inverse percentages for !=
            bool isNotEquals = lastTerm.Operator == Operator.NotEquals;
            int  start       = isNotEquals ? topValues.Values.RowCount - 1 : 0;
            int  end         = isNotEquals ? -1 : topValues.Values.RowCount;
            int  step        = isNotEquals ? -1 : 1;

            for (int i = start; i != end; i += step)
            {
                string value         = topValues.Values[i, 0].ToString();
                int    countForValue = (int)topValues.Values[i, 1];
                if (isNotEquals)
                {
                    countForValue = (int)topValues.Total - countForValue;
                }
                double frequency = (double)countForValue / (double)(topValues.Total);

                if ((countForValue > 1 || total <= 10) && value.StartsWith(guidance.Value, StringComparison.OrdinalIgnoreCase))
                {
                    string hint = (countForValue == topValues.Total ? "all" : frequency.ToString("P0"));
                    suggestions.Add(new IntelliSenseItem(QueryTokenCategory.Value, QueryScanner.WrapValue(value), hint));
                }
            }
        }