Exemplo n.º 1
0
        public virtual async Task TriggerCompletion(CompletionTriggerReason reason)
        {
            if (Editor.SelectionMode == SelectionMode.Block)
            {
                return;
            }

            if (CompletionWindowManager.IsVisible)
            {
                CompletionWindowManager.ToggleCategoryMode();
                return;
            }
            Editor.EnsureCaretIsNotVirtual();
            ICompletionDataList completionList = null;
            int cpos, wlen;

            if (!GetCompletionCommandOffset(out cpos, out wlen))
            {
                cpos = Editor.CaretOffset;
                wlen = 0;
            }

            completionTokenSrc.Cancel();
            completionTokenSrc = new CancellationTokenSource();
            var token = completionTokenSrc.Token;

            CurrentCompletionContext = CompletionWidget.CreateCodeCompletionContext(cpos);
            CurrentCompletionContext.TriggerWordLength = wlen;

            var  timer = CurrentCompletionContext.BeginTiming();
            bool failure = false;

            try {
                completionList = await DoHandleCodeCompletionAsync(CurrentCompletionContext, new CompletionTriggerInfo (reason), token);

                if (completionList != null && completionList.TriggerWordStart >= 0)
                {
                    CurrentCompletionContext.TriggerOffset     = completionList.TriggerWordStart;
                    CurrentCompletionContext.TriggerWordLength = completionList.TriggerWordLength;
                }
                if (completionList == null || !CompletionWindowManager.ShowWindow(this, (char)0, completionList, CompletionWidget, CurrentCompletionContext))
                {
                    CurrentCompletionContext = null;
                }
            } catch (Exception) {
                failure = true;
                throw;
            } finally {
                timer.End();
                if (failure)
                {
                    completionStats.OnFailure(timer.Duration);
                }
                else
                {
                    completionStats.OnSuccess(timer.Duration);
                }
            }
        }
        /// <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;
            }
        }
Exemplo n.º 3
0
        // When a key is pressed, and before the key is processed by the editor, this method will be invoked.
        // Return true if the key press should be processed by the editor.
        public override bool KeyPress(KeyDescriptor descriptor)
        {
            if (!IsActiveExtension())
            {
                return(base.KeyPress(descriptor));
            }
            bool res;

            if (CurrentCompletionContext != null && CompletionWindowManager.Wnd != null)
            {
                if (CompletionWindowManager.PreProcessKeyEvent(descriptor))
                {
                    CompletionWindowManager.PostProcessKeyEvent(descriptor);
                    autoHideCompletionWindow = true;
                    // in named parameter case leave the parameter window open.
                    autoHideParameterWindow = descriptor.KeyChar != ':';
                    if (!autoHideParameterWindow && ParameterInformationWindowManager.IsWindowVisible)
                    {
                        ParameterInformationWindowManager.PostProcessKeyEvent(this, CompletionWidget, descriptor);
                    }

                    return(false);
                }
                autoHideCompletionWindow = autoHideParameterWindow = false;
            }

            if (ParameterInformationWindowManager.IsWindowVisible)
            {
                if (ParameterInformationWindowManager.ProcessKeyEvent(this, CompletionWidget, descriptor))
                {
                    return(false);
                }
                autoHideCompletionWindow = autoHideParameterWindow = false;
            }

            //			int oldPos = Editor.CursorPosition;
            //			int oldLen = Editor.TextLength;
            char deleteOrBackspaceTriggerChar = '\0';

            if (descriptor.SpecialKey == SpecialKey.Delete && Editor.CaretOffset < Editor.Length)
            {
                deleteOrBackspaceTriggerChar = Editor.GetCharAt(Editor.CaretOffset);
            }
            if (descriptor.SpecialKey == SpecialKey.BackSpace && Editor.CaretOffset > 0)
            {
                deleteOrBackspaceTriggerChar = Editor.GetCharAt(Editor.CaretOffset - 1);
            }
            var impl = Editor.GetContent <ITextEditorImpl> ();

            if (CompletionWindowManager.IsVisible)
            {
                impl.LockFixIndentation = true;
            }
            try {
                res = base.KeyPress(descriptor);
            } finally {
                impl.LockFixIndentation = false;
            }
            if (Editor.EditMode == EditMode.TextLink && Editor.TextLinkPurpose == TextLinkPurpose.Rename)
            {
                return(res);
            }
            if (descriptor.KeyChar == (char)16 || descriptor.KeyChar == (char)17)
            {
                return(res);
            }

            CompletionWindowManager.PostProcessKeyEvent(descriptor);

            var ignoreMods = ModifierKeys.Control | ModifierKeys.Alt
                             | ModifierKeys.Command;

            // Handle parameter completion
            if (ParameterInformationWindowManager.IsWindowVisible)
            {
                ParameterInformationWindowManager.PostProcessKeyEvent(this, CompletionWidget, descriptor);
            }

            if ((descriptor.ModifierKeys & ignoreMods) != 0)
            {
                return(res);
            }

            // don't complete on block selection
            if (!IdeApp.Preferences.EnableAutoCodeCompletion || Editor.SelectionMode == MonoDevelop.Ide.Editor.SelectionMode.Block)
            {
                return(res);
            }

            // Handle code completion
            if (descriptor.KeyChar != '\0' && CompletionWidget != null && !CompletionWindowManager.IsVisible)
            {
                completionTokenSrc.Cancel();
                CurrentCompletionContext = CompletionWidget.CurrentCodeCompletionContext;
                completionTokenSrc       = new CancellationTokenSource();
                var          caretOffset = Editor.CaretOffset;
                var          token       = completionTokenSrc.Token;
                ITimeTracker timer       = null;

                try {
                    timer = CurrentCompletionContext.BeginTiming();
                    var task = DoHandleCodeCompletionAsync(CurrentCompletionContext, new CompletionTriggerInfo(CompletionTriggerReason.CharTyped, descriptor.KeyChar), token);
                    if (task != null)
                    {
                        // Show the completion window in two steps. The call to PrepareShowWindow creates the window but
                        // it doesn't show it. It is used only to process the keys while the completion data is being retrieved.
                        CompletionWindowManager.PrepareShowWindow(this, descriptor.KeyChar, CompletionWidget, CurrentCompletionContext);
                        EventHandler windowClosed = delegate(object o, EventArgs a) {
                            completionTokenSrc.Cancel();
                        };
                        CompletionWindowManager.WindowClosed += windowClosed;
                        task.ContinueWith(t => {
                            try {
                                CompletionWindowManager.WindowClosed -= windowClosed;
                                if (token.IsCancellationRequested)
                                {
                                    return;
                                }
                                var result = t.Result;
                                if (result != null)
                                {
                                    int triggerWordLength = result.TriggerWordLength + (Editor.CaretOffset - caretOffset);
                                    if (triggerWordLength > 0 && (triggerWordLength < Editor.CaretOffset ||
                                                                  (triggerWordLength == 1 && Editor.CaretOffset == 1)))
                                    {
                                        CurrentCompletionContext = CompletionWidget.CreateCodeCompletionContext(Editor.CaretOffset - triggerWordLength);
                                        if (result.TriggerWordStart >= 0)
                                        {
                                            CurrentCompletionContext.TriggerOffset = result.TriggerWordStart;
                                        }
                                        CurrentCompletionContext.TriggerWordLength = triggerWordLength;
                                    }
                                    // Now show the window for real.
                                    if (!CompletionWindowManager.ShowWindow(result, CurrentCompletionContext))
                                    {
                                        CurrentCompletionContext = null;
                                    }
                                }
                                else
                                {
                                    CompletionWindowManager.HideWindow();
                                    CurrentCompletionContext = null;
                                }
                            } finally {
                                timer.End();
                                if (token.IsCancellationRequested)
                                {
                                    completionStats.OnUserCanceled(timer.Duration);
                                }
                                else
                                {
                                    completionStats.OnSuccess(timer.Duration);
                                }
                            }
                        }, Runtime.MainTaskScheduler);
                    }
                    else
                    {
                        CurrentCompletionContext = null;
                        timer.End();
                        completionStats.OnSuccess(timer.Duration);
                    }
                } catch (TaskCanceledException) {
                    timer.End();
                    completionStats.OnUserCanceled(timer.Duration);
                } catch (AggregateException) {
                    timer.End();
                    completionStats.OnFailure(timer.Duration);
                } catch {
                    timer.End();
                    completionStats.OnFailure(timer.Duration);
                    throw;
                }
            }

            if ((descriptor.SpecialKey == SpecialKey.Delete || descriptor.SpecialKey == SpecialKey.BackSpace) && CompletionWidget != null && !CompletionWindowManager.IsVisible)
            {
                if (!char.IsLetterOrDigit(deleteOrBackspaceTriggerChar) && deleteOrBackspaceTriggerChar != '_')
                {
                    return(res);
                }
                CurrentCompletionContext = CompletionWidget.CurrentCodeCompletionContext;

                int cpos, wlen;
                if (!GetCompletionCommandOffset(out cpos, out wlen))
                {
                    cpos = Editor.CaretOffset;
                    wlen = 0;
                }

                CurrentCompletionContext.TriggerOffset     = cpos;
                CurrentCompletionContext.TriggerWordLength = wlen;

                completionTokenSrc.Cancel();
                completionTokenSrc = new CancellationTokenSource();
                var caretOffset = Editor.CaretOffset;
                var token       = completionTokenSrc.Token;

                ITimeTracker timer = null;
                try {
                    timer = CurrentCompletionContext.BeginTiming();
                    var task = DoHandleCodeCompletionAsync(CurrentCompletionContext, new CompletionTriggerInfo(CompletionTriggerReason.BackspaceOrDeleteCommand, deleteOrBackspaceTriggerChar), token);
                    if (task != null)
                    {
                        // Show the completion window in two steps. The call to PrepareShowWindow creates the window but
                        // it doesn't show it. It is used only to process the keys while the completion data is being retrieved.
                        CompletionWindowManager.PrepareShowWindow(this, descriptor.KeyChar, CompletionWidget, CurrentCompletionContext);
                        EventHandler windowClosed = delegate(object o, EventArgs a) {
                            completionTokenSrc.Cancel();
                        };
                        CompletionWindowManager.WindowClosed += windowClosed;

                        task.ContinueWith(t => {
                            try {
                                CompletionWindowManager.WindowClosed -= windowClosed;
                                if (token.IsCancellationRequested)
                                {
                                    return;
                                }
                                var result = t.Result;
                                if (result != null)
                                {
                                    int triggerWordLength = result.TriggerWordLength + (Editor.CaretOffset - caretOffset);

                                    if (triggerWordLength > 0 && (triggerWordLength < Editor.CaretOffset ||
                                                                  (triggerWordLength == 1 && Editor.CaretOffset == 1)))
                                    {
                                        CurrentCompletionContext = CompletionWidget.CreateCodeCompletionContext(Editor.CaretOffset - triggerWordLength);
                                        if (result.TriggerWordStart >= 0)
                                        {
                                            CurrentCompletionContext.TriggerOffset = result.TriggerWordStart;
                                        }
                                        CurrentCompletionContext.TriggerWordLength = triggerWordLength;
                                    }
                                    // Now show the window for real.
                                    if (!CompletionWindowManager.ShowWindow(result, CurrentCompletionContext))
                                    {
                                        CurrentCompletionContext = null;
                                    }
                                    else
                                    {
                                        CompletionWindowManager.Wnd.StartOffset = CurrentCompletionContext.TriggerOffset;
                                    }
                                }
                                else
                                {
                                    CompletionWindowManager.HideWindow();
                                    CurrentCompletionContext = null;
                                }
                            } finally {
                                timer.End();
                                if (token.IsCancellationRequested)
                                {
                                    completionStats.OnUserCanceled(timer.Duration);
                                }
                                else
                                {
                                    completionStats.OnSuccess(timer.Duration);
                                }
                            }
                        }, Runtime.MainTaskScheduler);
                    }
                    else
                    {
                        CurrentCompletionContext = null;
                    }
                } catch (TaskCanceledException) {
                    CurrentCompletionContext = null;
                    timer.End();
                    completionStats.OnUserCanceled(timer.Duration);
                } catch (AggregateException) {
                    CurrentCompletionContext = null;
                    timer.End();
                    completionStats.OnFailure(timer.Duration);
                } catch {
                    timer.End();
                    completionStats.OnFailure(timer.Duration);
                    throw;
                }
            }

            if (CompletionWidget != null && ParameterInformationWindowManager.CurrentMethodGroup == null)
            {
                CodeCompletionContext ctx  = CompletionWidget.CurrentCodeCompletionContext;
                var newparameterHintingSrc = new CancellationTokenSource();
                var token = newparameterHintingSrc.Token;
                try {
                    var task = HandleParameterCompletionAsync(ctx, new SignatureHelpTriggerInfo(SignatureHelpTriggerReason.TypeCharCommand, descriptor.KeyChar), token);
                    if (task != null)
                    {
                        parameterHintingSrc.Cancel();
                        parameterHintingSrc = newparameterHintingSrc;
                        parameterHingtingCursorPositionChanged = false;
                        task.ContinueWith(t => {
                            if (!token.IsCancellationRequested && t.Result != null)
                            {
                                ParameterInformationWindowManager.ShowWindow(this, CompletionWidget, ctx, t.Result);
                                if (parameterHingtingCursorPositionChanged)
                                {
                                    ParameterInformationWindowManager.UpdateCursorPosition(this, CompletionWidget);
                                }
                            }
                        }, token, TaskContinuationOptions.None, Runtime.MainTaskScheduler);
                    }
                    else
                    {
                        //Key was typed that was filtered out, no heavy processing will be performed(task==null)
                        //but we still want to update ParameterInfo window to avoid displaying it outside method call
                        parameterHingtingCursorPositionChanged = true;
                    }
                } catch (TaskCanceledException) {
                } catch (AggregateException) {
                }
            }
            return(res);
        }