Exemple #1
0
    async void _ShowSignature(SciCode doc, char ch, bool methodCompletion = false)
    {
        //using var p1 = perf.local();
        if (!CodeInfo.GetContextAndDocument(out var cd, -2) || cd.pos < 2)
        {
            return;                                                                        //returns false if position is in meta comments
        }
        _cancelTS?.Cancel();
        _cancelTS = new CancellationTokenSource();
        var cancelTS    = _cancelTS;
        var cancelToken = cancelTS.Token;

#if DEBUG
        if (Debugger.IsAttached)
        {
            cancelToken = default; _cancelTS = null;
        }
#endif

        SyntaxNode root = null;
        //ISignatureHelpProvider provider = null;
        SignatureHelpItems r = null;
        try {
            //could be sync, quite fast, but then sometimes reenters (GetItemsAsync waits/dispatches) and sometimes hangs
            r = await Task.Run(async() => {
                //p1.Next();
                root = cd.syntaxRoot;
                //p1.Next('r');
                var providers = _SignatureHelpProviders;
                //print.it(providers);
                SignatureHelpItems r = null;
                var trigger          = new SignatureHelpTriggerInfo(ch == default ? SignatureHelpTriggerReason.InvokeSignatureHelpCommand : SignatureHelpTriggerReason.TypeCharCommand, ch);
                foreach (var p in providers)
                {
                    //print.it(p);
                    var r2 = await p.GetItemsAsync(cd.document, cd.pos, trigger, SignatureHelpOptions.Default, cancelToken).ConfigureAwait(false);
                    if (cancelToken.IsCancellationRequested) /*print.it("IsCancellationRequested");*/ return {
                        (null);
                    }                                                                                                                      //often
                    if (r2 == null)
                    {
                        continue;
                    }
                    if (r == null || r2.ApplicableSpan.Start > r.ApplicableSpan.Start)
                    {
                        r = r2;
                        //provider = p;
                    }
                    //Example: 'print.it(new Something())'.
                    //	The first provider probably is for Write (invocation).
                    //	Then the second is for Something (object creation).
                    //	We need the innermost, in this case Something.
                }
                return(r);
            });
        }
        catch (OperationCanceledException) { /*Debug_.Print("canceled");*/ return; }         //never noticed
        //catch (AggregateException e1) when (e1.InnerException is TaskCanceledException) { return; }
        finally {
            cancelTS.Dispose();
            if (cancelTS == _cancelTS)
            {
                _cancelTS = null;
            }
        }
        //print.it(r, cancelToken.IsCancellationRequested);

        if (cancelToken.IsCancellationRequested)
        {
            return;
        }
        if (r == null)
        {
            _CancelUI();
            return;
        }
        Debug.Assert(doc == Panels.Editor.ZActiveDoc);         //when active doc changed, cancellation must be requested
        if (cd.pos != doc.zCurrentPos16 || (object)cd.code != doc.zText)
        {
            return;                                                                      //changed while awaiting
        }
        //p1.Next('s');

        //print.it($"<><c orange>pos={cd.pos}, span={r.ApplicableSpan},    nItems={r.Items.Count},  argCount={r.ArgumentCount}, argIndex={r.ArgumentIndex}, argName={r.ArgumentName}, sel={r.SelectedItemIndex},    provider={provider}<>");

        //get span of the arglist. r.ApplicableSpan.Start is of the statement, not of the arglist. In chained methods it is the chain start.
        var fullSpan = r.ApplicableSpan;
        //CiUtil.HiliteRange(fullSpan); wait.doEvents(500);
        var start = fullSpan.Start;
        var tok   = root.FindToken(cd.pos);
        if (tok.Kind() is SyntaxKind.OpenParenToken or SyntaxKind.OpenBracketToken or SyntaxKind.LessThanToken)
        {
            tok = tok.GetPreviousToken();
        }
        var argNode = tok.Parent;
        while (argNode != null)
        {
            int i = argNode.SpanStart; if (i <= start)
            {
                break;
            }
            if (argNode is BaseArgumentListSyntax or AttributeArgumentListSyntax or TypeArgumentListSyntax)
            {
                start = i + 1;
                break;
            }
            //CiUtil.PrintNode(argNode);
            argNode = argNode.Parent;
        }
        var argSpan = new TextSpan(start, fullSpan.End - start);
        //CiUtil.PrintNode(argNode); CiUtil.HiliteRange(argSpan); //print.it(argSpan);

        var span = new _Span(argSpan, cd.code);
        int iSel = 0, iSel2 = 0;
        if (r.Items.Count > 1)
        {
            iSel2 = r.SelectedItemIndex ?? -1;
            if (_data?.IsSameArglist(span, r) ?? false)
            {
                iSel = _data.iUserSelected;                 //preserve user selection in same session
                if (iSel2 < 0)
                {
                    iSel2 = _data.iRoslynSelected;                            //on error use last good Roslyn selection in same session, like in VS
                }
            }
            else
            {
                iSel = -1;
            }
        }

        _data = new _Data {
            r               = r,
            span            = span,
            iUserSelected   = iSel,
            iRoslynSelected = iSel2,
            sci             = doc,
        };

        if (iSel < 0)
        {
            iSel = iSel2;
        }
        if (iSel < 0)
        {
            //r.SelectedItemIndex is null when cannot resolve overloads, eg when arglist is partially typed. Example: wnd.find(1, );
            iSel = r.SelectedItemIndex ?? (r.ArgumentCount == 0 ? 0 : -1);
            if (iSel < 0)
            {
                for (int i = 0; i < r.Items.Count; i++)
                {
                    if (r.Items[i].Parameters.Length >= r.ArgumentCount)
                    {
                        iSel = i; break;
                    }
                }
                if (iSel < 0)
                {
                    for (int i = 0; i < r.Items.Count; i++)
                    {
                        if (r.Items[i].IsVariadic)
                        {
                            iSel = i; break;
                        }
                    }
                    if (iSel < 0)
                    {
                        iSel = 0;
                    }
                }
            }
        }

        doc.ZTempRanges_Add(this, argSpan.Start, argSpan.End, onLeave: () => {
            if (doc.ZTempRanges_Enum(doc.zCurrentPos8, this, utf8: true).Any())
            {
                return;
            }
            _CancelUI();
        }, SciCode.ZTempRangeFlags.NoDuplicate);

        var rect = RECT.Union(CiUtil.GetCaretRectFromPos(doc, fullSpan.Start), CiUtil.GetCaretRectFromPos(doc, cd.pos));
        doc.Hwnd.MapClientToScreen(ref rect);
        rect.Width += Dpi.Scale(200, doc.Hwnd);
        rect.left  -= 6;

        _textPopup ??= new CiPopupText(CiPopupText.UsedBy.Signature, onHiddenOrDestroyed: (_, _) => _data = null)
        {
            OnLinkClick = (ph, e) => ph.Text = _FormatText(e.ToInt(1), userSelected: true)
        };
        _textPopup.Text = _FormatText(iSel, userSelected: false);

        if (!_textPopup.IsVisible)
        {
            CodeInfo.HideTextPopupAndTempWindows();
            if (CodeInfo._compl.IsVisibleUI)             //without this does not show completions with selected enum when typed Function( when first parameter is enum
            {
                CodeInfo._compl.Cancel();
            }
            if (methodCompletion)
            {
                CodeInfo._compl.ShowList(ch);                               //when autocompletion added (); may need to show enum list
            }
        }

        _textPopup.Show(Panels.Editor.ZActiveDoc, rect, System.Windows.Controls.Dock.Bottom);

        //also show Keys/Regex tool?
        //CiUtil.PrintNode(node);
        if (argNode is ArgumentListSyntax or BracketedArgumentListSyntax && cd.code.Eq(cd.pos - 1, "\"\""))
        {
            //print.it("string");
            var semo  = cd.semanticModel;
            var token = root.FindToken(cd.pos);
            if (true == token.IsInString(cd.pos, cd.code, out var stringInfo))
            {
                var stringFormat = CiUtil.GetParameterStringFormat(stringInfo.stringNode, semo, true);
                if (stringFormat != 0)
                {
                    CodeInfo._tools.ShowForStringParameter(stringFormat, cd, stringInfo, _textPopup.PopupWindow.Hwnd);
                }
            }
        }
    }