private IEnumerable <CompletionResult> MakeSnippetedResponses(CompletionRequest request, ISymbol symbol) { var completions = new List <CompletionResult>(); var methodSymbol = symbol as IMethodSymbol; if (methodSymbol != null) { if (methodSymbol.Parameters.Any(p => p.IsOptional)) { completions.Add(MakeAutoCompleteResponse(request, symbol, false)); } completions.Add(MakeAutoCompleteResponse(request, symbol)); return(completions); } var typeSymbol = symbol as INamedTypeSymbol; if (typeSymbol != null) { completions.Add(MakeAutoCompleteResponse(request, symbol)); if (typeSymbol.TypeKind != TypeKind.Enum) { foreach (var ctor in typeSymbol.InstanceConstructors) { completions.Add(MakeAutoCompleteResponse(request, ctor)); } } return(completions); } return(new[] { MakeAutoCompleteResponse(request, symbol) }); }
private CompletionResult MakeAutoCompleteResponse(CompletionRequest request, ISymbol symbol, bool includeOptionalParams = true) { var displayNameGenerator = new SnippetGenerator(); displayNameGenerator.IncludeMarkers = false; displayNameGenerator.IncludeOptionalParameters = includeOptionalParams; var response = new CompletionResult(); response.CompletionText = symbol.Name; if (request.WantKind) { response.Kind = symbol.GetKind(); } // Handle special case for constructors. var methodSymbol = symbol as IMethodSymbol; if (methodSymbol?.MethodKind == MethodKind.Constructor) { response.CompletionText = methodSymbol.ContainingType.Name; if (request.WantKind) { response.Kind = "Class"; } } // TODO: Do something more intelligent here response.DisplayText = displayNameGenerator.Generate(symbol); if (request.WantDocumentationForEveryCompletionResult) { response.Description = DocumentationConverter.ConvertDocumentation(symbol.GetDocumentationCommentXml(), "\n"); } if (request.WantReturnType) { response.ReturnType = ReturnTypeFormatter.GetReturnType(symbol); } if (request.WantSnippet) { var snippetGenerator = new SnippetGenerator(); snippetGenerator.IncludeMarkers = true; snippetGenerator.IncludeOptionalParameters = includeOptionalParams; response.Snippet = snippetGenerator.Generate(symbol); } if (request.WantMethodHeader) { response.MethodHeader = displayNameGenerator.Generate(symbol); } return(response); }
public async Task <CompletionResult[]> GetCompletionsAsync(CompletionRequest request, string code) { var workspace = new AdhocWorkspace(); var projectName = RandomString(6, "Project"); var assemblyName = RandomString(6, "Assembly"); var documentName = RandomString(6, "Document"); var project = workspace.CurrentSolution.AddProject(projectName, assemblyName, LanguageNames.CSharp); // We need to reference assemblies that the code relies on. var references = GetMetadataReferences(); project = project.WithMetadataReferences(references); var document = project.AddDocument(documentName, SourceText.From(code)); var text = await document.GetTextAsync(); var position = text.Lines.GetPosition(new LinePosition(request.Line, request.Character)); var model = await document.GetSemanticModelAsync(); var completions = new List <CompletionResult>(); AddKeywords(workspace, completions, model, position, request.WantKind, request.WordToComplete); var symbols = Recommender.GetRecommendedSymbolsAtPosition(model, position, workspace); foreach (var symbol in symbols.Where(s => s.Name.IsValidCompletionFor(request.WordToComplete))) { if (request.WantSnippet) { foreach (var completion in MakeSnippetedResponses(request, symbol)) { completions.Add(completion); } } else { completions.Add(MakeAutoCompleteResponse(request, symbol)); } } return(completions .OrderByDescending(c => c.CompletionText.IsValidCompletionStartsWithExactCase(request.WordToComplete)) .ThenByDescending(c => c.CompletionText.IsValidCompletionStartsWithIgnoreCase(request.WordToComplete)) .ThenByDescending(c => c.CompletionText.IsCamelCaseMatch(request.WordToComplete)) .ThenByDescending(c => c.CompletionText.IsSubsequenceMatch(request.WordToComplete)) .ThenBy(c => c.CompletionText) .ToArray()); }
public async Task <CompletionResult[]> GetCompletionsAsync(CompletionRequest request, Source source) { if (source == null) { throw new ArgumentNullException(nameof(source)); } // We need to ensure all dependencies have been downloaded and located. ResolveReferences() gets a // firm reference for each reference and any additional dependencies. var references = _metadataProvider.ResolveReferences(source.Dependencies); var referenceAssemblies = references.Select(r => Assembly.LoadFrom(r.Display)).ToArray(); var compositionContext = new ContainerConfiguration() .WithAssemblies(MefHostServices.DefaultAssemblies .Concat(new[] { // These assemblies are necessary to enable language services. Assembly.Load("Microsoft.CodeAnalysis.Features"), Assembly.Load("Microsoft.CodeAnalysis.CSharp.Features") }) .Concat(referenceAssemblies)) .CreateContainer(); var host = MefHostServices.Create(compositionContext); // Setup a workspace for this code file, since we aren't actively managing a project or solutions. var workspace = new AdhocWorkspace(host); var projectName = RandomString(6, "Project"); var assemblyName = RandomString(6, "Assembly"); var documentName = RandomString(6, "Document"); var document = workspace.CurrentSolution .AddProject(projectName, assemblyName, LanguageNames.CSharp) .WithMetadataReferences(references) .AddDocument(documentName, SourceText.From(source.Code)); // Determine position of cursor so we know which symbols to recommend. var text = await document.GetTextAsync(); var position = text.Lines.GetPosition(new LinePosition(request.Line, request.Character)); // The code analysis libraries have rolled in all the intellisense code // so all we need to do is get a reference to the CompletionService and // request completions for the given character position. var service = CompletionService.GetService(document); var completions = await service.GetCompletionsAsync(document, position); var completionResults = new List <CompletionResult>(); // We'll handle special cases for keywords, and determine whether the // completions we received are valid for the given text position. if (completions != null) { foreach (var item in completions.Items) { if (item.Tags.Contains(CompletionTags.Keyword)) { // For keywords we'll assume the completion text is the same as the display text. var keyword = item.DisplayText; if (keyword.IsValidCompletionFor(request.WordToComplete)) { var response = new CompletionResult() { CompletionText = item.DisplayText, DisplayText = item.DisplayText, Snippet = item.DisplayText, Kind = request.WantKind ? "Keyword" : null }; completionResults.Add(response); } } } } // Now we'll add completions based on the semantics model and referenced assemblies. var model = await document.GetSemanticModelAsync(); var symbols = await Recommender.GetRecommendedSymbolsAtPositionAsync(model, position, workspace); foreach (var symbol in symbols.Where(s => s.Name.IsValidCompletionFor(request.WordToComplete))) { if (request.WantSnippet) { foreach (var completion in MakeSnippetedResponses(request, symbol)) { completionResults.Add(completion); } } else { completionResults.Add(MakeAutoCompleteResponse(request, symbol)); } } // Order the list to the most appropriate completions first. return(completionResults .OrderByDescending(c => c.CompletionText.IsValidCompletionStartsWithExactCase(request.WordToComplete)) .ThenByDescending(c => c.CompletionText.IsValidCompletionStartsWithIgnoreCase(request.WordToComplete)) .ThenByDescending(c => c.CompletionText.IsCamelCaseMatch(request.WordToComplete)) .ThenByDescending(c => c.CompletionText.IsSubsequenceMatch(request.WordToComplete)) .ThenBy(c => c.CompletionText) .ToArray()); }