public override async Task <CompletionList> Completion(CompletionParams @params, CancellationToken cancellationToken)
        {
            var uri = @params.textDocument.uri;

            ProjectFiles.GetEntry(@params.textDocument, @params._version, out var entry, out var tree);
            TraceMessage($"Completions in {uri} at {@params.position}");

            tree = GetParseTree(entry, uri, cancellationToken, out var version) ?? tree;
            var analysis = entry != null ? await entry.GetAnalysisAsync(50, cancellationToken) : null;

            if (analysis == null)
            {
                TraceMessage($"No analysis found for {uri}");
                return(new CompletionList());
            }

            var opts = GetOptions(@params.context);
            var ctxt = new CompletionAnalysis(analysis, tree, @params.position, opts, Settings.completion, _displayTextBuilder, Logger, () => entry.ReadDocument(ProjectFiles.GetPart(uri), out _));

            var members = string.IsNullOrEmpty(@params._expr)
                ? ctxt.GetCompletions()
                : ctxt.GetCompletionsFromString(@params._expr);

            if (members == null)
            {
                TraceMessage($"No completions at {@params.position} in {uri}");
                return(new CompletionList());
            }

            if (!Settings.completion.showAdvancedMembers)
            {
                members = members.Where(m => !m.label.StartsWith("__"));
            }

            var filterKind = @params.context?._filterKind;

            if (filterKind.HasValue && filterKind != CompletionItemKind.None)
            {
                TraceMessage($"Only returning {filterKind.Value} items");
                members = members.Where(m => m.kind == filterKind.Value);
            }

            var res = new CompletionList {
                items            = members.ToArray(),
                _expr            = ctxt.ParentExpression?.ToCodeString(tree, CodeFormattingOptions.Traditional),
                _commitByDefault = ctxt.ShouldCommitByDefault,
                _allowSnippet    = ctxt.ShouldAllowSnippets
            };

            res._applicableSpan = GetApplicableSpan(ctxt, @params, tree);
            LogMessage(MessageType.Info, $"Found {res.items.Length} completions for {uri} at {@params.position} after filtering");

            await InvokeExtensionsAsync((ext, token)
                                        => (ext as ICompletionExtension)?.HandleCompletionAsync(uri, analysis, tree, @params.position, res, cancellationToken), cancellationToken);

            return(res);
        }
        private SourceSpan?GetApplicableSpan(CompletionAnalysis ca, CompletionParams @params, PythonAst tree)
        {
            if (ca.ApplicableSpan.HasValue)
            {
                return(ca.ApplicableSpan);
            }

            SourceLocation trigger = @params.position;

            if (ca.Node != null)
            {
                var span = ca.Node.GetSpan(tree);
                if (@params.context?.triggerKind == CompletionTriggerKind.TriggerCharacter)
                {
                    if (span.End > trigger)
                    {
                        // Span start may be after the trigger if there is bunch of whitespace
                        // between dot and next token such as in 'sys  .  version'
                        span = new SourceSpan(new SourceLocation(span.Start.Line, Math.Min(span.Start.Column, trigger.Column)), span.End);
                    }
                }
                if (span.End != span.Start)
                {
                    return(span);
                }
            }

            if (@params.context?.triggerKind == CompletionTriggerKind.TriggerCharacter)
            {
                var ch = @params.context?.triggerCharacter.FirstOrDefault() ?? '\0';
                return(new SourceSpan(
                           trigger.Line,
                           Tokenizer.IsIdentifierStartChar(ch) ? Math.Max(1, trigger.Column - 1) : trigger.Column,
                           trigger.Line,
                           trigger.Column
                           ));
            }

            return(null);
        }
示例#3
0
        public override async Task <CompletionList> Completion(CompletionParams @params, CancellationToken cancellationToken)
        {
            var uri = @params.textDocument.uri;

            ProjectFiles.GetEntry(@params.textDocument, @params._version, out var entry, out var tree);
            TraceMessage($"Completions in {uri} at {@params.position}");

            tree = GetParseTree(entry, uri, cancellationToken, out var version) ?? tree;
            var analysis = entry != null ? await entry.GetAnalysisAsync(50, cancellationToken) : null;

            if (analysis == null)
            {
                TraceMessage($"No analysis found for {uri}");
                return(new CompletionList());
            }

            var opts = GetOptions(@params.context);
            var ctxt = new CompletionAnalysis(analysis, tree, @params.position, opts, _displayTextBuilder, this,
                                              () => entry.ReadDocument(ProjectFiles.GetPart(uri), out _));

            var members = ctxt.GetCompletionsFromString(@params._expr) ?? ctxt.GetCompletions();

            if (members == null)
            {
                TraceMessage($"Do not trigger at {@params.position} in {uri}");
                return(new CompletionList());
            }

            if (!Settings.completion.showAdvancedMembers)
            {
                members = members.Where(m => !m.label.StartsWith("__"));
            }

            var filterKind = @params.context?._filterKind;

            if (filterKind.HasValue && filterKind != CompletionItemKind.None)
            {
                TraceMessage($"Only returning {filterKind.Value} items");
                members = members.Where(m => m.kind == filterKind.Value);
            }

            var completions = members.ToArray();

            if (Settings.completion.addBrackets && (!filterKind.HasValue || CanHaveBrackets(filterKind.Value)))
            {
                foreach (var completionItem in completions.Where(ci => CanHaveBrackets(ci.kind)))
                {
                    completionItem.insertText      += "($0)";
                    completionItem.insertTextFormat = InsertTextFormat.Snippet;
                }
            }

            bool CanHaveBrackets(CompletionItemKind kind)
            => kind == CompletionItemKind.Constructor ||
            kind == CompletionItemKind.Function ||
            kind == CompletionItemKind.Method;

            var res = new CompletionList {
                items            = completions,
                _expr            = ctxt.ParentExpression?.ToCodeString(tree, CodeFormattingOptions.Traditional),
                _commitByDefault = ctxt.ShouldCommitByDefault,
                _allowSnippet    = ctxt.ShouldAllowSnippets
            };

            SourceLocation trigger = @params.position;

            if (ctxt.ApplicableSpan.HasValue)
            {
                res._applicableSpan = ctxt.ApplicableSpan;
            }
            else if (ctxt.Node != null)
            {
                var span = ctxt.Node.GetSpan(tree);
                if (@params.context?.triggerKind == CompletionTriggerKind.TriggerCharacter)
                {
                    if (span.End > trigger)
                    {
                        span = new SourceSpan(span.Start, trigger);
                    }
                }
                if (span.End != span.Start)
                {
                    res._applicableSpan = span;
                }
            }
            else if (@params.context?.triggerKind == CompletionTriggerKind.TriggerCharacter)
            {
                var ch = @params.context?.triggerCharacter.FirstOrDefault() ?? '\0';
                res._applicableSpan = new SourceSpan(
                    trigger.Line,
                    Tokenizer.IsIdentifierStartChar(ch) ? Math.Max(1, trigger.Column - 1) : trigger.Column,
                    trigger.Line,
                    trigger.Column
                    );
            }

            LogMessage(MessageType.Info, $"Found {res.items.Length} completions for {uri} at {@params.position} after filtering");

            if (HandleOldStyleCompletionExtension(analysis as ModuleAnalysis, tree, @params.position, res))
            {
                return(res);
            }

            await InvokeExtensionsAsync((ext, token)
                                        => (ext as ICompletionExtension)?.HandleCompletionAsync(uri, analysis, tree, @params.position, res, cancellationToken), cancellationToken);

            return(res);
        }