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); }
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); }