Esempio n. 1
0
        /// <summary>
        /// Returns the indentation for the given line.
        /// </summary>
        /// <param name="indentLine">The line to be indented.</param>
        /// <returns></returns>
        public int?GetDesiredIndentation(ITextSnapshotLine indentLine)
        {
            // Make sure there is a line before the requested line
            if (indentLine.LineNumber == 0 || indentLine.Start.Position < 1)
            {
                return(0);
            }

            // Get indentation of previous line
            string previousLineText        = indentLine.Snapshot.GetLineFromLineNumber(indentLine.LineNumber - 1).GetText();
            int    previousLineIndentation = 0;

            while (previousLineIndentation < previousLineText.Length && char.IsWhiteSpace(previousLineText[previousLineIndentation]))
            {
                ++previousLineIndentation;
            }

            // Find first token before this line
            var tokens = _aiParser.GetTokens(indentLine.Start - 1, true);

            if (!tokens.Any())
            {
                return(0);
            }
            AiToken firstToken = tokens.First();

            switch (firstToken.Type)
            {
            case AiTokenTypes.ClosingBrace:
                return(previousLineIndentation);

            default:
                return(previousLineIndentation + (_useTabs ? 4 : _indentSize));    // TODO _useTabs is always true, 4 is hardcoded as long as this is not fixed
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Gets all the <see cref="ClassificationSpan"/> objects that intersect with the given range of text.
        /// </summary>
        /// <param name="span">The span currently being classified.</param>
        /// <returns>A list of ClassificationSpans that represent spans identified to be of this classification.</returns>
        public IList <ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
        {
            // Initialize result list
            List <ClassificationSpan> spans = new List <ClassificationSpan>();

            // Get tokens beginning with the start of the first line of the given span (to avoid starting within a word)
            foreach (AiToken token in _aiParser.GetTokens(span.Start.GetContainingLine().Start))
            {
                // Finished?
                if (token.StartPoint >= span.End)
                {
                    break;
                }

                // Classify depending on token type
                string classifierType = AiClassifierTypes.Default;
                switch (token.Type)
                {
                case AiTokenTypes.Comment:
                    classifierType = AiClassifierTypes.Comment;
                    break;

                case AiTokenTypes.Defrule:
                case AiTokenTypes.BooleanFactName:
                case AiTokenTypes.RuleArrow:
                case AiTokenTypes.Defconst:
                case AiTokenTypes.Load:
                case AiTokenTypes.LoadRandom:
                    classifierType = AiClassifierTypes.Keyword;
                    break;

                case AiTokenTypes.OpeningBrace:
                case AiTokenTypes.ClosingBrace:
                    classifierType = AiClassifierTypes.Delimiter;
                    break;

                case AiTokenTypes.String:
                    classifierType = AiClassifierTypes.String;
                    break;

                case AiTokenTypes.Number:
                    classifierType = AiClassifierTypes.Number;
                    break;

                case AiTokenTypes.FactName:
                    classifierType = AiClassifierTypes.RuleFactName;
                    break;

                case AiTokenTypes.ActionName:
                    classifierType = AiClassifierTypes.RuleActionName;
                    break;

                case AiTokenTypes.Word:
                    if (Constants.AiOperators.Contains(token.Content))
                    {
                        classifierType = AiClassifierTypes.Operator;
                    }
                    else if (Constants.AiIdentifiers.Contains(token.Content))
                    {
                        classifierType = AiClassifierTypes.Identifier;
                    }
                    else
                    {
                        classifierType = AiClassifierTypes.Default;
                    }
                    break;
                }
                spans.Add(new ClassificationSpan(new SnapshotSpan(token.StartPoint, token.Length), _classificationTypeRegistry.GetClassificationType(classifierType)));
            }

            /*// Get first and last line, use these to get the whole text in between (including line breaks)
             * var startLine = span.Start.GetContainingLine();
             * var endLine = (span.End - 1).GetContainingLine();
             * var text = span.Snapshot.GetText(new SnapshotSpan(startLine.Start, endLine.End));
             *
             * // Step through text char by char
             * int lastClassificationEndIndex = 0;
             * for(int charIndex = 0; charIndex < text.Length; ++charIndex)
             * {
             *      // Check current character
             *      if(text[charIndex] == ';')
             *      {
             *              // Move unrecognized content or whitespace before this position into default class
             *              if(charIndex > lastClassificationEndIndex)
             *                      spans.Add(CreateClassificationSpan(span, lastClassificationEndIndex, charIndex - lastClassificationEndIndex, AiClassifierTypes.Default));
             *
             *              // The remainder of the line is a comment
             *              var currentLine = span.Snapshot.GetLineFromPosition(startLine.Start.Position + charIndex);
             *              int remainingCharCount = currentLine.EndIncludingLineBreak.Position - (startLine.Start.Position + charIndex);
             *              spans.Add(CreateClassificationSpan(span, charIndex, remainingCharCount, AiClassifierTypes.Comment));
             *              charIndex += remainingCharCount;
             *              lastClassificationEndIndex = charIndex + 1;
             *      }
             *      else if(text[charIndex] == '\r' || text[charIndex] == '\n')
             *      {
             *              // Update indices to next line (maybe with another intermediate line break character)
             *              lastClassificationEndIndex = charIndex + 1;
             *      }
             *      else if(Constants.AiDelimiters.Any(c => c == text[charIndex]))
             *      {
             *              // Move unrecognized content or whitespace before this position into default class
             *              if(charIndex > lastClassificationEndIndex)
             *                      spans.Add(CreateClassificationSpan(span, lastClassificationEndIndex, charIndex - lastClassificationEndIndex, AiClassifierTypes.Default));
             *
             *              // The current char is a delimiter
             *              spans.Add(CreateClassificationSpan(span, charIndex, 1, AiClassifierTypes.Delimiter));
             *              lastClassificationEndIndex = charIndex + 1;
             *      }
             *      else if(text[charIndex] == '"')
             *      {
             *              // Move unrecognized content or whitespace before this position into default class
             *              if(charIndex > lastClassificationEndIndex)
             *                      spans.Add(CreateClassificationSpan(span, lastClassificationEndIndex, charIndex - lastClassificationEndIndex, AiClassifierTypes.Default));
             *
             *              // Search for string termination character
             *              int stringBeginIndex = charIndex;
             *              var currentLine = span.Snapshot.GetLineFromPosition(startLine.Start.Position + charIndex);
             *              while(startLine.Start.Position + charIndex < currentLine.End.Position - 1)
             *                      if(text[++charIndex] == '"')
             *                              break;
             *
             *              // Set string class
             *              spans.Add(CreateClassificationSpan(span, stringBeginIndex, charIndex - stringBeginIndex + 1, AiClassifierTypes.String));
             *              lastClassificationEndIndex = charIndex + 1;
             *      }
             *      else if(char.IsDigit(text[charIndex]))
             *      {
             *              // Move unrecognized content or whitespace before this position into default class
             *              if(charIndex > lastClassificationEndIndex)
             *                      spans.Add(CreateClassificationSpan(span, lastClassificationEndIndex, charIndex - lastClassificationEndIndex, AiClassifierTypes.Default));
             *
             *              // Search for last number character
             *              int numberBeginIndex = charIndex;
             *              var currentLine = span.Snapshot.GetLineFromPosition(startLine.Start.Position + charIndex);
             *              while(startLine.Start.Position + charIndex < currentLine.End.Position - 1)
             *                      if(!char.IsDigit(text[++charIndex]))
             *                              break;
             *
             *              // Ignore delimiters
             *              if(Constants.AiDelimiters.Any(c => c == text[charIndex]))
             *                      --charIndex;
             *
             *              // The last character must be a digit (occurs if a delimiter follows), whitespace or span end
             *              if(char.IsDigit(text[charIndex]) || char.IsWhiteSpace(text[charIndex]) || startLine.Start.Position + charIndex == currentLine.End.Position - 1)
             *              {
             *                      // Set number class
             *                      spans.Add(CreateClassificationSpan(span, numberBeginIndex, charIndex - numberBeginIndex + 1, AiClassifierTypes.Number));
             *              }
             *              else
             *              {
             *                      // Use default class
             *                      spans.Add(CreateClassificationSpan(span, numberBeginIndex, charIndex - numberBeginIndex + 1, AiClassifierTypes.Default));
             *              }
             *              lastClassificationEndIndex = charIndex + 1;
             *      }
             * else if(text[charIndex] == '=' && charIndex < text.Length-1 && text[charIndex + 1] == '>')
             * {
             * // Move unrecognized content or whitespace before this position into default class
             * if(charIndex > lastClassificationEndIndex)
             * spans.Add(CreateClassificationSpan(span, lastClassificationEndIndex, charIndex - lastClassificationEndIndex, AiClassifierTypes.Default));
             *
             * // Set as keyword (must be handled separately from the other keywords, since this one might conflict with the operators)
             * spans.Add(CreateClassificationSpan(span, charIndex, 2, AiClassifierTypes.Keyword));
             ++charIndex;
             * lastClassificationEndIndex = charIndex + 1;
             * }
             * else if(!char.IsWhiteSpace(text[charIndex]))
             * {
             * // Move unrecognized content or whitespace before this position into default class
             * if(charIndex > lastClassificationEndIndex)
             * spans.Add(CreateClassificationSpan(span, lastClassificationEndIndex, charIndex - lastClassificationEndIndex, AiClassifierTypes.Default));
             *
             * // Search for whitespace or delimiter
             * int wordBeginIndex = charIndex;
             * var currentLine = span.Snapshot.GetLineFromPosition(startLine.Start.Position + charIndex);
             * while(startLine.Start.Position + charIndex < currentLine.End.Position - 1)
             * if(char.IsWhiteSpace(text[++charIndex]) || Constants.AiDelimiters.Any(c => c == text[charIndex]))
             * {
             *  // Ignore this char
             *  --charIndex;
             *  break;
             * }
             *
             * // Classify this word
             * string word = text.Substring(wordBeginIndex, charIndex - wordBeginIndex + 1);
             * if(Constants.AiKeywords.Contains(word))
             * spans.Add(CreateClassificationSpan(span, wordBeginIndex, charIndex - wordBeginIndex + 1, AiClassifierTypes.Keyword));
             * else if(Constants.AiOperators.Contains(word))
             * spans.Add(CreateClassificationSpan(span, wordBeginIndex, charIndex - wordBeginIndex + 1, AiClassifierTypes.Operator));
             * else if(Constants.AiCommands.Contains(word))
             * spans.Add(CreateClassificationSpan(span, wordBeginIndex, charIndex - wordBeginIndex + 1, AiClassifierTypes.Command));
             * else if(Constants.AiIdentifiers.Contains(word))
             * spans.Add(CreateClassificationSpan(span, wordBeginIndex, charIndex - wordBeginIndex + 1, AiClassifierTypes.Identifier));
             * else
             * {
             * // Use default class
             * spans.Add(CreateClassificationSpan(span, wordBeginIndex, charIndex - wordBeginIndex + 1, AiClassifierTypes.Default));
             * }
             * lastClassificationEndIndex = charIndex + 1;
             * }
             * }
             *
             * // Classify remaining whitespace
             * int remainingCharCountTillEnd = endLine.End.Position - (startLine.Start.Position + lastClassificationEndIndex);
             * if(remainingCharCountTillEnd > 0)
             *      spans.Add(CreateClassificationSpan(span, lastClassificationEndIndex, remainingCharCountTillEnd, AiClassifierTypes.Default));*/

            // Return classication list
            return(spans);
        }
        /// <summary>
        /// Does a loose scan of the surrounding code to determine the completion context, and returns a list of meaningful completions.
        /// </summary>
        /// <param name="completionSession">The current completion session.</param>
        /// <param name="completionSets">The target list to store the generated completion sets into.</param>
        public void AugmentCompletionSession(ICompletionSession completionSession, IList <CompletionSet> completionSets)
        {
            // Run text parser, skip all tokens behind the current word
            SnapshotPoint cursorPoint = (completionSession.TextView.Caret.Position.BufferPosition) - 1;
            var           tokens      = _aiParser.GetTokens(cursorPoint, true).SkipWhile(t => t.StartPoint > cursorPoint);

            // Check whether we are in a comment or in a string; skip the current word
            AiToken firstToken = tokens.First();

            if (firstToken.Type == AiTokenTypes.Comment || firstToken.Type == AiTokenTypes.String)
            {
                return;
            }
            if (firstToken.Type != AiTokenTypes.OpeningBrace)
            {
                tokens = tokens.Skip(1);
            }

            // Go backwards and find "(" with known keyword or "=>"
            CurrentCompletionContext completionContext = CurrentCompletionContext.Unknown;
            int            closingBraceCount           = 0;
            List <AiToken> tokensTillFirstOpeningBrace = new List <AiToken>();

            Constants.CommandDefinition commandDefinition = null; // Used for storing the parameter type list of a fact/action.
            bool encounteredOpeningBrace = false;

            foreach (AiToken token in tokens)
            {
                // Check token type
                switch (token.Type)
                {
                case AiTokenTypes.OpeningBrace:
                {
                    // Found an opening brace, set flag to stop copying tokens
                    if (!encounteredOpeningBrace)
                    {
                        encounteredOpeningBrace = true;
                    }
                    else
                    {
                        --closingBraceCount;
                    }
                    break;
                }

                case AiTokenTypes.ClosingBrace:
                {
                    // If a closing brace is encountered before an opening one, an error must have occured
                    if (encounteredOpeningBrace)
                    {
                        ++closingBraceCount;
                    }
                    else
                    {
                        completionContext = CurrentCompletionContext.Error;
                    }
                    break;
                }

                case AiTokenTypes.Defconst:
                case AiTokenTypes.Load:
                case AiTokenTypes.LoadRandom:
                {
                    // These commands hint a top level context, but completion within one of them is not supported
                    if (encounteredOpeningBrace)
                    {
                        completionContext = CurrentCompletionContext.TopLevel;
                    }
                    else
                    {
                        completionContext = CurrentCompletionContext.NotSupported;
                    }
                    break;
                }

                case AiTokenTypes.Defrule:
                case AiTokenTypes.BooleanFactName:
                {
                    // If an opening brace was found, we have a fact
                    if (encounteredOpeningBrace)
                    {
                        completionContext = CurrentCompletionContext.RuleFacts;
                    }
                    else
                    {
                        completionContext = CurrentCompletionContext.Error;     // "defrule" has no parameters
                    }
                    break;
                }

                case AiTokenTypes.RuleArrow:
                {
                    // If an opening brace was found, we have an action
                    if (encounteredOpeningBrace && closingBraceCount == 0)
                    {
                        completionContext = CurrentCompletionContext.RuleActions;
                    }
                    else if (encounteredOpeningBrace && closingBraceCount == 1)
                    {
                        completionContext = CurrentCompletionContext.TopLevel;
                    }
                    else
                    {
                        completionContext = CurrentCompletionContext.Error;     // "=>" has no parameters
                    }
                    break;
                }

                case AiTokenTypes.FactName:
                {
                    // We probably have a fact/action with parameter list
                    if (!encounteredOpeningBrace)
                    {
                        // Get parameter list
                        commandDefinition = Constants.AiRuleFacts[token.Content];
                        completionContext = CurrentCompletionContext.Parameters;
                    }
                    // TODO error state if directly in front of a brace
                    break;
                }

                case AiTokenTypes.ActionName:
                {
                    // We probably have a fact/action with parameter list
                    if (!encounteredOpeningBrace)
                    {
                        // Get parameter list
                        commandDefinition = Constants.AiRuleActions[token.Content];
                        completionContext = CurrentCompletionContext.Parameters;
                    }
                    // TODO error state if directly in front of a brace
                    break;
                }

                case AiTokenTypes.Word:
                case AiTokenTypes.Number:
                case AiTokenTypes.String:
                {
                    // Put this token into the list to determine the current parameter position of a given fact or action
                    if (!encounteredOpeningBrace)
                    {
                        tokensTillFirstOpeningBrace.Add(token);
                    }
                    // TODO error state if directly in front of a brace
                    break;
                }
                }

                // Stop if a context was deduced or an error occured
                if (completionContext != CurrentCompletionContext.Unknown)
                {
                    break;
                }
            }

            // Handle input at document start
            if (completionContext == CurrentCompletionContext.Unknown && encounteredOpeningBrace)
            {
                completionContext = CurrentCompletionContext.TopLevel;
            }

            // Show completion set depending on current context
            switch (completionContext)
            {
            case CurrentCompletionContext.TopLevel:
                completionSets.Add(new CompletionSet("Commands", "Commands", GetTrackingSpan(firstToken.StartPoint, cursorPoint), _aiTopLevelKeywordCompletions, null));
                break;

            case CurrentCompletionContext.RuleFacts:
                completionSets.Add(new CompletionSet("Facts", "Facts", GetTrackingSpan(firstToken.StartPoint, cursorPoint), _aiFactCompletions, null));
                break;

            case CurrentCompletionContext.RuleActions:
                completionSets.Add(new CompletionSet("Actions", "Actions", GetTrackingSpan(firstToken.StartPoint, cursorPoint), _aiActionCompletions, null));
                break;

            case CurrentCompletionContext.Parameters:
            {
                // Compare scanned parameters with parameter list
                tokensTillFirstOpeningBrace.Reverse();
                if (tokensTillFirstOpeningBrace.Count > commandDefinition.Parameters.Count())
                {
                    break;
                }
                int i = 0;
                foreach (string parameterType in commandDefinition.Parameters)
                {
                    // Reached current parameter?
                    if (i == tokensTillFirstOpeningBrace.Count)
                    {
                        // Show completion list, if there is one
                        if (Constants.AiCommandParameters.ContainsKey(parameterType))
                        {
                            completionSets.Add(new CompletionSet("Parameter", "Parameter values", GetTrackingSpan(firstToken.StartPoint, cursorPoint), _aiCommandParameterCompletions[parameterType], null));
                        }
                        break;
                    }
                    else
                    {
                        // Check type
                        AiToken token = tokensTillFirstOpeningBrace[i];
                        if (parameterType == "string" && token.Type != AiTokenTypes.String)
                        {
                            goto case CurrentCompletionContext.Error;
                        }
                        else if (parameterType == "value" && token.Type != AiTokenTypes.Number)
                        {
                            goto case CurrentCompletionContext.Error;
                        }
                        else if (Constants.AiCommandParameters.ContainsKey(parameterType) && !Constants.AiCommandParameters[parameterType].PossibleValues.Contains(token.Content))
                        {
                            goto case CurrentCompletionContext.Error;
                        }
                    }
                    ++i;
                }
                break;
            }

            case CurrentCompletionContext.Error:
                // Do nothing
                break;
            }
        }