public async Task <TextEdit[]> HandleRequestAsync(DocumentOnTypeFormattingParams request, RequestContext context, CancellationToken cancellationToken) { var edits = new ArrayBuilder <TextEdit>(); var document = context.Document; if (document != null) { var formattingService = document.Project.LanguageServices.GetRequiredService <IEditorFormattingService>(); var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); if (string.IsNullOrEmpty(request.Character)) { return(edits.ToArrayAndFree()); } IList <TextChange>?textChanges; if (SyntaxFacts.IsNewLine(request.Character[0])) { textChanges = await GetFormattingChangesOnReturnAsync(formattingService, document, position, cancellationToken).ConfigureAwait(false); } else { textChanges = await GetFormattingChangesAsync(formattingService, document, request.Character[0], position, cancellationToken).ConfigureAwait(false); } var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); if (textChanges != null) { edits.AddRange(textChanges.Select(change => ProtocolConversions.TextChangeToTextEdit(change, text))); } } return(edits.ToArrayAndFree()); }
public override async Task <TextEdit[]> HandleRequestAsync(DocumentOnTypeFormattingParams request, RequestContext context, CancellationToken cancellationToken) { var edits = new ArrayBuilder <TextEdit>(); if (string.IsNullOrEmpty(request.Character)) { return(edits.ToArrayAndFree()); } var document = context.Document; var formattingService = document?.Project.LanguageServices.GetService <IXamlFormattingService>(); if (document != null && formattingService != null) { var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); var options = new XamlFormattingOptions { InsertSpaces = request.Options.InsertSpaces, TabSize = request.Options.TabSize, OtherOptions = request.Options.OtherOptions }; var textChanges = await formattingService.GetFormattingChangesAsync(document, options, request.Character[0], position, cancellationToken).ConfigureAwait(false); if (textChanges != null) { var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); edits.AddRange(textChanges.Select(change => ProtocolConversions.TextChangeToTextEdit(change, text))); } } return(edits.ToArrayAndFree()); }
public override async Task <TextEdit[]?> HandleRequestAsync( DocumentOnTypeFormattingParams request, RequestContext context, CancellationToken cancellationToken) { var document = context.Document; if (document == null) { return(null); } var edits = new ArrayBuilder <TextEdit>(); var formattingService = document.Project.LanguageServices.GetRequiredService <IFormattingInteractionService>(); var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); if (string.IsNullOrEmpty(request.Character)) { return(edits.ToArrayAndFree()); } // We should use the options passed in by LSP instead of the document's options. var documentOptions = await ProtocolConversions.FormattingOptionsToDocumentOptionsAsync( request.Options, document, cancellationToken).ConfigureAwait(false); IList <TextChange>?textChanges; if (SyntaxFacts.IsNewLine(request.Character[0])) { textChanges = await GetFormattingChangesOnReturnAsync( formattingService, document, position, documentOptions, cancellationToken).ConfigureAwait(false); } else { textChanges = await GetFormattingChangesAsync( formattingService, document, request.Character[0], position, documentOptions, cancellationToken).ConfigureAwait(false); } var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); if (textChanges != null) { edits.AddRange(textChanges.Select(change => ProtocolConversions.TextChangeToTextEdit(change, text))); } return(edits.ToArrayAndFree()); }
protected static async Task <LSP.TextEdit[]?> GetTextEditsAsync( RequestContext context, LSP.FormattingOptions options, IGlobalOptionService globalOptions, CancellationToken cancellationToken, LSP.Range?range = null) { var document = context.Document; if (document == null) { return(null); } var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var rangeSpan = (range != null) ? ProtocolConversions.RangeToTextSpan(range, text) : new TextSpan(0, root.FullSpan.Length); var formattingSpan = CommonFormattingHelpers.GetFormattingSpan(root, rangeSpan); // We should use the options passed in by LSP instead of the document's options. var formattingOptions = await ProtocolConversions.GetFormattingOptionsAsync(options, document, globalOptions, cancellationToken).ConfigureAwait(false); var services = document.Project.Solution.Services; var textChanges = Formatter.GetFormattedTextChanges(root, SpecializedCollections.SingletonEnumerable(formattingSpan), services, formattingOptions, rules: null, cancellationToken); var edits = new ArrayBuilder <LSP.TextEdit>(); edits.AddRange(textChanges.Select(change => ProtocolConversions.TextChangeToTextEdit(change, text))); return(edits.ToArrayAndFree()); }
public async Task <object[]> HandleAsync(object param, RequestContext <Solution> requestContext, CancellationToken cancellationToken) { var projects = new ArrayBuilder <CustomProtocol.Project>(); var solution = requestContext.Context; foreach (var project in solution.Projects) { var externalUris = new ArrayBuilder <Uri>(); foreach (var sourceFile in project.Documents) { var uri = new Uri(sourceFile.FilePath); #pragma warning disable 0612 if (!requestContext.ProtocolConverter.IsContainedInRootFolders(uri)) #pragma warning restore 0612 { externalUris.Add(uri); } } #pragma warning disable 0612 await requestContext.ProtocolConverter.RegisterExternalFilesAsync(externalUris.ToArrayAndFree()).ConfigureAwait(false); #pragma warning restore 0612 var lspProject = new CustomProtocol.Project { Name = project.Name, SourceFiles = project.Documents.Select(d => requestContext.ProtocolConverter.ToProtocolUri(new Uri(d.FilePath))).ToArray(), Language = project.Language }; projects.Add(lspProject); } return(projects.ToArrayAndFree()); }
protected async Task <LSP.TextEdit[]?> GetTextEditsAsync( RequestContext context, LSP.FormattingOptions options, CancellationToken cancellationToken, LSP.Range?range = null) { var document = context.Document; if (document == null) { return(null); } var edits = new ArrayBuilder <LSP.TextEdit>(); var formattingService = document.Project.LanguageServices.GetRequiredService <IFormattingInteractionService>(); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); TextSpan?textSpan = null; if (range != null) { textSpan = ProtocolConversions.RangeToTextSpan(range, text); } // We should use the options passed in by LSP instead of the document's options. var documentOptions = await ProtocolConversions.FormattingOptionsToDocumentOptionsAsync( options, document, cancellationToken).ConfigureAwait(false); var textChanges = await GetFormattingChangesAsync(formattingService, document, textSpan, documentOptions, cancellationToken).ConfigureAwait(false); edits.AddRange(textChanges.Select(change => ProtocolConversions.TextChangeToTextEdit(change, text))); return(edits.ToArrayAndFree()); }
private static ClassifiedTextElement GetSignatureClassifiedText(SignatureHelpItem item) { var taggedTexts = new ArrayBuilder <TaggedText>(); taggedTexts.AddRange(item.PrefixDisplayParts); var separators = item.SeparatorDisplayParts; for (var i = 0; i < item.Parameters.Length; i++) { var param = item.Parameters[i]; if (i > 0) { taggedTexts.AddRange(separators); } taggedTexts.AddRange(param.PrefixDisplayParts); taggedTexts.AddRange(param.DisplayParts); taggedTexts.AddRange(param.SuffixDisplayParts); } taggedTexts.AddRange(item.SuffixDisplayParts); taggedTexts.AddRange(item.DescriptionParts); return(new ClassifiedTextElement( taggedTexts .ToArrayAndFree() .Select( part => new ClassifiedTextRun(part.Tag.ToClassificationTypeName(), part.Text) ) )); }
private XNode[] RewriteMany( XNode[] nodes, string currentXmlFilePath, CSharpSyntaxNode originatingSyntax ) { Debug.Assert(nodes != null); ArrayBuilder <XNode> builder = null; foreach (XNode child in nodes) { if (builder == null) { builder = ArrayBuilder <XNode> .GetInstance(); } builder.AddRange(Rewrite(child, currentXmlFilePath, originatingSyntax)); } // Nodes returned by this method are going to be attached to a new parent, so it's // important that they don't already have parents. If a node with a parent is // attached to a new parent, it is copied and its annotations are dropped. Debug.Assert(builder == null || builder.All(node => node.Parent == null)); return(builder == null?Array.Empty <XNode>() : builder.ToArrayAndFree()); }
private static void VerifySpans( DynamicAnalysisDataReader reader, DynamicAnalysisMethod methodData, string[] sourceLines, params SpanResult[] expected ) { ArrayBuilder <string> expectedSpanSpellings = ArrayBuilder <string> .GetInstance( expected.Length ); foreach (SpanResult expectedSpanResult in expected) { Assert.True( sourceLines[expectedSpanResult.StartLine] .Substring(expectedSpanResult.StartColumn) .StartsWith(expectedSpanResult.TextStart) ); expectedSpanSpellings.Add( string.Format( "({0},{1})-({2},{3})", expectedSpanResult.StartLine, expectedSpanResult.StartColumn, expectedSpanResult.EndLine, expectedSpanResult.EndColumn ) ); } VerifySpans(reader, methodData, expectedSpanSpellings.ToArrayAndFree()); }
private WarningStateMapEntry[] CoalesceBuilderEntries(ImmutableArray <WarningStateMapBuilderEntry> builderEntries) { var builder = new ArrayBuilder <WarningStateMapEntry>(); int currentPosition = 0; var accumulatedSpecificWarningState = ImmutableDictionary <string, ReportDiagnostic> .Empty; foreach (var entry in builderEntries.Sort()) { if (entry.Position != currentPosition) { // Commit the previous map entry builder.Add(new WarningStateMapEntry(currentPosition, ReportDiagnostic.Default, accumulatedSpecificWarningState)); // Start building up a new map entry currentPosition = entry.Position; } accumulatedSpecificWarningState = accumulatedSpecificWarningState.SetItem(entry.Id, entry.SpecificWarningOption); } // Commit the final map entry builder.Add(new WarningStateMapEntry(currentPosition, ReportDiagnostic.Default, accumulatedSpecificWarningState)); var entries = builder.ToArrayAndFree(); #if DEBUG // Make sure the entries array is correctly sorted. for (int i = 1; i < entries.Length - 1; ++i) { Debug.Assert(entries[i].CompareTo(entries[i + 1]) < 0); } #endif return(entries); }
private ParameterInfo[] ParseParameterList() { // Consume the opening parenthesis or bracket Debug.Assert(PeekNextChar() == '(' || PeekNextChar() == '['); ++_index; var nextChar = PeekNextChar(); if (nextChar == ')' || nextChar == ']') { // Empty parameter list ++_index; return(s_noParameters); } var builder = new ArrayBuilder <ParameterInfo>(); while (true) { var parameter = ParseParameter(); if (parameter != null) { builder.Add(parameter.Value); } else { builder.Free(); return(null); } if (PeekNextChar() == ',') { ++_index; } else { break; } } nextChar = PeekNextChar(); if (nextChar == ')' || nextChar == ']') { // Consume the closing parenthesis or bracket ++_index; } else { // Malformed parameter list: missing close parenthesis or bracket builder.Free(); return(null); } return(builder.ToArrayAndFree()); }
public async Task <WorkspaceEdit> HandleRequestAsync(Solution solution, RenameParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken) { WorkspaceEdit workspaceEdit = null; var document = solution.GetDocumentFromURI(request.TextDocument.Uri); if (document != null) { var renameService = document.Project.LanguageServices.GetService <IEditorInlineRenameService>(); var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); var renameInfo = await renameService.GetRenameInfoAsync(document, position, cancellationToken).ConfigureAwait(false); if (!renameInfo.CanRename) { return(workspaceEdit); } var renameLocationSet = await renameInfo.FindRenameLocationsAsync(solution.Workspace.Options, cancellationToken).ConfigureAwait(false); var renameReplacementInfo = await renameLocationSet.GetReplacementsAsync(request.NewName, solution.Workspace.Options, cancellationToken).ConfigureAwait(false); var newSolution = renameReplacementInfo.NewSolution; var solutionChanges = newSolution.GetChanges(solution); var changedDocuments = solutionChanges .GetProjectChanges() .SelectMany(p => p.GetChangedDocuments(onlyGetDocumentsWithTextChanges: true)); var documentEdits = new ArrayBuilder <TextDocumentEdit>(); foreach (var docId in changedDocuments) { var oldDoc = solution.GetDocument(docId); var newDoc = newSolution.GetDocument(docId); var textChanges = await newDoc.GetTextChangesAsync(oldDoc, cancellationToken).ConfigureAwait(false); var oldText = await oldDoc.GetTextAsync(cancellationToken).ConfigureAwait(false); var textDocumentEdit = new TextDocumentEdit { TextDocument = new VersionedTextDocumentIdentifier { Uri = newDoc.GetURI() }, Edits = textChanges.Select(tc => ProtocolConversions.TextChangeToTextEdit(tc, oldText)).ToArray() }; documentEdits.Add(textDocumentEdit); } workspaceEdit = new WorkspaceEdit { DocumentChanges = documentEdits.ToArrayAndFree() }; } return(workspaceEdit); }
private int[] ParseLineStarts() { int position = 0; int index = 0; int lastCr = -1; ArrayBuilder <int> arrayBuilder = ArrayBuilder <int> .GetInstance(); // The following loop goes through every character in the text. It is highly // performance critical, and thus inlines knowledge about common line breaks // and non-line breaks. foreach (char[] chunk in _chunks) { foreach (char c in chunk) { index++; // Common case - ASCII & not a line break const uint bias = '\r' + 1; if (unchecked (c - bias) <= (127 - bias)) { continue; } switch (c) { case '\r': lastCr = index; goto line_break; case '\n': // Assumes that the only 2-char line break sequence is CR+LF if (lastCr == (index - 1)) { position = index; break; } goto line_break; case '\u0085': case '\u2028': case '\u2029': line_break: arrayBuilder.Add(position); position = index; break; } } } // Create a start for the final line. arrayBuilder.Add(position); return(arrayBuilder.ToArrayAndFree()); }
private async Task <LSP.ReferenceGroup[]> GetReferenceGroupsAsync(LSP.ReferenceParams request, SimpleFindUsagesContext context, CancellationToken cancellationToken) { var definitionMap = new Dictionary <DefinitionItem, List <SourceReferenceItem> >(); foreach (var reference in context.GetReferences()) { if (!definitionMap.ContainsKey(reference.Definition)) { definitionMap.Add(reference.Definition, new List <SourceReferenceItem>()); } definitionMap[reference.Definition].Add(reference); } var referenceGroups = ArrayBuilder <LSP.ReferenceGroup> .GetInstance(); foreach (var keyValuePair in definitionMap) { var definition = keyValuePair.Key; var references = keyValuePair.Value; var referenceGroup = new LSP.ReferenceGroup(); var text = definition.GetClassifiedText(); referenceGroup.Definition = await ProtocolConversions.DocumentSpanToLocationWithTextAsync(definition.SourceSpans.First(), text, cancellationToken).ConfigureAwait(false); referenceGroup.DefinitionIcon = new ImageElement(definition.Tags.GetFirstGlyph().GetImageId()); var locationWithTexts = new ArrayBuilder <LSP.LocationWithText>(); foreach (var reference in references) { var classifiedSpansAndHighlightSpan = await ClassifiedSpansAndHighlightSpanFactory.ClassifyAsync(reference.SourceSpan, context.CancellationToken).ConfigureAwait(false); var classifiedSpans = classifiedSpansAndHighlightSpan.ClassifiedSpans; var referenceLocation = await ProtocolConversions.DocumentSpanToLocationAsync(reference.SourceSpan, cancellationToken).ConfigureAwait(false); var docText = await reference.SourceSpan.Document.GetTextAsync(context.CancellationToken).ConfigureAwait(false); var classifiedText = new ClassifiedTextElement(classifiedSpans.Select(cspan => new ClassifiedTextRun(cspan.ClassificationType, docText.ToString(cspan.TextSpan)))); var locationWithText = new LSP.LocationWithText { Range = referenceLocation.Range, Uri = referenceLocation.Uri, Text = classifiedText }; locationWithTexts.Add(locationWithText); } referenceGroup.References = locationWithTexts.ToArrayAndFree(); referenceGroups.Add(referenceGroup); } return(referenceGroups.ToArrayAndFree()); }
public async Task <TextEdit[]> HandleRequestAsync(Solution solution, DocumentOnTypeFormattingParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken, bool keepThreadContext = false) { var edits = new ArrayBuilder <TextEdit>(); var document = solution.GetDocumentFromURI(request.TextDocument.Uri); if (document != null) { var formattingService = document.Project.LanguageServices.GetService <IEditorFormattingService>(); var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(keepThreadContext); if (string.IsNullOrEmpty(request.Character)) { return(edits.ToArrayAndFree()); } IList <TextChange> textChanges; if (SyntaxFacts.IsNewLine(request.Character[0])) { textChanges = await formattingService.GetFormattingChangesOnReturnAsync(document, position, cancellationToken).ConfigureAwait(keepThreadContext); } else { textChanges = await formattingService.GetFormattingChangesAsync(document, request.Character[0], position, cancellationToken).ConfigureAwait(keepThreadContext); } var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(keepThreadContext); if (textChanges != null) { edits.AddRange(textChanges.Select(change => ProtocolConversions.TextChangeToTextEdit(change, text))); } } return(edits.ToArrayAndFree()); }
/// <summary> /// Read UTF8 string with null terminator. /// </summary> private static string ReadUtf8String(ImmutableArray <byte> bytes, ref int offset) { ArrayBuilder <byte> builder = ArrayBuilder <byte> .GetInstance(); while (true) { byte b = ReadByte(bytes, ref offset); if (b == 0) { break; } builder.Add(b); } byte[] block = builder.ToArrayAndFree(); return(Encoding.UTF8.GetString(block, 0, block.Length)); }
public async Task <TextEdit[]?> HandleRequestAsync( DocumentOnTypeFormattingParams request, RequestContext context, CancellationToken cancellationToken) { var document = context.Document; if (document == null) { return(null); } var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); if (string.IsNullOrEmpty(request.Character) || SyntaxFacts.IsNewLine(request.Character[0])) { return(Array.Empty <TextEdit>()); } var formattingService = document.Project.LanguageServices.GetRequiredService <ISyntaxFormattingService>(); var documentSyntax = await ParsedDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false); if (!formattingService.ShouldFormatOnTypedCharacter(documentSyntax, request.Character[0], position, cancellationToken)) { return(Array.Empty <TextEdit>()); } // We should use the options passed in by LSP instead of the document's options. var formattingOptions = await ProtocolConversions.GetFormattingOptionsAsync(request.Options, document, _globalOptions, cancellationToken).ConfigureAwait(false); var indentationOptions = new IndentationOptions(formattingOptions) { AutoFormattingOptions = _globalOptions.GetAutoFormattingOptions(document.Project.Language) }; var textChanges = formattingService.GetFormattingChangesOnTypedCharacter(documentSyntax, position, indentationOptions, cancellationToken); if (textChanges.IsEmpty) { return(Array.Empty <TextEdit>()); } var edits = new ArrayBuilder <TextEdit>(); edits.AddRange(textChanges.Select(change => ProtocolConversions.TextChangeToTextEdit(change, documentSyntax.Text))); return(edits.ToArrayAndFree()); }
public override async Task <LSP.SumType <LSP.Command, LSP.CodeAction>[]> HandleRequestAsync(LSP.CodeActionParams request, LSP.ClientCapabilities clientCapabilities, string?clientName, CancellationToken cancellationToken) { var document = SolutionProvider.GetDocument(request.TextDocument, clientName); var codeActions = await GetCodeActionsAsync(document, _codeFixService, _codeRefactoringService, request.Range, cancellationToken).ConfigureAwait(false); // Filter out code actions with options since they'll show dialogs and we can't remote the UI and the options. codeActions = codeActions.Where(c => !(c is CodeActionWithOptions)); var result = new ArrayBuilder <LSP.SumType <LSP.Command, LSP.CodeAction> >(); foreach (var codeAction in codeActions) { // Always return the Command instead of a precalculated set of workspace edits. // The edits will be calculated when the code action is either previewed or // invoked. // It's safe for the client to pass back the range/filename in the command to run // on the server because the client will always re-issue a get code actions request // before invoking a preview or running the command on the server. result.Add( new LSP.Command { CommandIdentifier = RunCodeActionCommandName, Title = codeAction.Title, Arguments = new object[] { new RunCodeActionParams { CodeActionParams = request, Title = codeAction.Title } } }); } return(result.ToArrayAndFree()); }
public async Task <SumType <Command, CodeAction>[]> HandleAsync(CodeActionParams param, RequestContext <Solution> requestContext, CancellationToken cancellationToken) { var result = await base.HandleRequestAsync(param, requestContext.GetClientCapabilities(), null, cancellationToken).ConfigureAwait(false); var commands = new ArrayBuilder <SumType <Command, CodeAction> >(); foreach (var resultObj in result) { var commandArguments = resultObj; var title = resultObj.Value is CodeAction codeAction ? codeAction.Title : ((Command)resultObj).Title; commands.Add(new Command { Title = title, // Overwrite the command identifier to match the expected liveshare remote command identifier. CommandIdentifier = $"{RemoteCommandNamePrefix}.{ProviderName}", Arguments = new object[] { commandArguments } }); } return(commands.ToArrayAndFree()); }
private TypeInfo[] ParseTypeArgumentList(ISymbol bindingContext) { Debug.Assert(PeekNextChar() == '<'); ++_index; var builder = new ArrayBuilder <TypeInfo>(); while (true) { var type = ParseType(bindingContext); if (type == null) { builder.Free(); return(null); } builder.Add(type.Value); if (PeekNextChar() == ',') { ++_index; } else { break; } } if (PeekNextChar() == '>') { ++_index; } else { builder.Free(); return(null); } return(builder.ToArrayAndFree()); }
protected async Task <LSP.TextEdit[]> GetTextEdits(Solution solution, Uri documentUri, bool keepThreadContext, CancellationToken cancellationToken, LSP.Range range = null) { var edits = new ArrayBuilder <LSP.TextEdit>(); var document = solution.GetDocumentFromURI(documentUri); if (document != null) { var formattingService = document.Project.LanguageServices.GetService <IEditorFormattingService>(); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(keepThreadContext); TextSpan?textSpan = null; if (range != null) { textSpan = ProtocolConversions.RangeToTextSpan(range, text); } var textChanges = await formattingService.GetFormattingChangesAsync(document, textSpan, cancellationToken).ConfigureAwait(keepThreadContext); edits.AddRange(textChanges.Select(change => ProtocolConversions.TextChangeToTextEdit(change, text))); } return(edits.ToArrayAndFree()); }
protected async Task <LSP.TextEdit[]> GetTextEditsAsync(RequestContext context, CancellationToken cancellationToken, LSP.Range?range = null) { var edits = new ArrayBuilder <LSP.TextEdit>(); var document = context.Document; if (document != null) { var formattingService = document.Project.LanguageServices.GetRequiredService <IEditorFormattingService>(); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); TextSpan?textSpan = null; if (range != null) { textSpan = ProtocolConversions.RangeToTextSpan(range, text); } var textChanges = await GetFormattingChangesAsync(formattingService, document, textSpan, cancellationToken).ConfigureAwait(false); edits.AddRange(textChanges.Select(change => ProtocolConversions.TextChangeToTextEdit(change, text))); } return(edits.ToArrayAndFree()); }
public override async Task <LSP.SignatureHelp?> HandleRequestAsync( LSP.TextDocumentPositionParams request, RequestContext context, CancellationToken cancellationToken ) { var document = context.Document; if (document == null) { return(null); } var position = await document .GetPositionFromLinePositionAsync( ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken ) .ConfigureAwait(false); var providers = _allProviders.Where( p => p.Metadata.Language == document.Project.Language ); var triggerInfo = new SignatureHelpTriggerInfo( SignatureHelpTriggerReason.InvokeSignatureHelpCommand ); foreach (var provider in providers) { var items = await provider.Value .GetItemsAsync(document, position, triggerInfo, cancellationToken) .ConfigureAwait(false); if (items != null) { var sigInfos = new ArrayBuilder <LSP.SignatureInformation>(); foreach (var item in items.Items) { LSP.SignatureInformation sigInfo; if (context.ClientCapabilities?.HasVisualStudioLspCapability() == true) { sigInfo = new LSP.VSSignatureInformation { ColorizedLabel = GetSignatureClassifiedText(item) }; } else { sigInfo = new LSP.SignatureInformation(); } sigInfo.Label = GetSignatureText(item); sigInfo.Documentation = new LSP.MarkupContent { Kind = LSP.MarkupKind.PlainText, Value = item.DocumentationFactory(cancellationToken).GetFullText() }; sigInfo.Parameters = item.Parameters .Select( p => new LSP.ParameterInformation { Label = p.Name, Documentation = new LSP.MarkupContent { Kind = LSP.MarkupKind.PlainText, Value = p.DocumentationFactory(cancellationToken) .GetFullText() } } ) .ToArray(); sigInfos.Add(sigInfo); } var sigHelp = new LSP.SignatureHelp { ActiveSignature = GetActiveSignature(items), ActiveParameter = items.ArgumentIndex, Signatures = sigInfos.ToArrayAndFree() }; return(sigHelp); } } return(null); }
private ParameterInfo[] ParseParameterList() { // Consume the opening parenthesis or bracket Debug.Assert(PeekNextChar() == '(' || PeekNextChar() == '['); ++_index; var nextChar = PeekNextChar(); if (nextChar == ')' || nextChar == ']') { // Empty parameter list ++_index; return s_noParameters; } var builder = new ArrayBuilder<ParameterInfo>(); while (true) { var parameter = ParseParameter(); if (parameter != null) { builder.Add(parameter.Value); } else { builder.Free(); return null; } if (PeekNextChar() == ',') { ++_index; } else { break; } } nextChar = PeekNextChar(); if (nextChar == ')' || nextChar == ']') { // Consume the closing parenthesis or bracket ++_index; } else { // Malformed parameter list: missing close parenthesis or bracket builder.Free(); return null; } return builder.ToArrayAndFree(); }
private TypeInfo[] ParseTypeArgumentList(ISymbol bindingContext) { Debug.Assert(PeekNextChar() == '<'); ++_index; var builder = new ArrayBuilder<TypeInfo>(); while (true) { var type = ParseType(bindingContext); if (type == null) { builder.Free(); return null; } builder.Add(type.Value); if (PeekNextChar() == ',') { ++_index; } else { break; } } if (PeekNextChar() == '>') { ++_index; } else { builder.Free(); return null; } return builder.ToArrayAndFree(); }
/// <summary> /// Decodes a type name. A type name is a string which is terminated by the end of the string or one of the /// delimiters '+', ',', '[', ']'. '+' separates nested classes. '[' and ']' /// enclosed generic type arguments. ',' separates types. /// </summary> internal AssemblyQualifiedTypeName DecodeTypeName(bool isTypeArgument = false, bool isTypeArgumentWithAssemblyName = false) { Debug.Assert(!isTypeArgumentWithAssemblyName || isTypeArgument); string topLevelType = null; ArrayBuilder <string> nestedTypesBuilder = null; AssemblyQualifiedTypeName[] typeArguments = null; int pointerCount = 0; ArrayBuilder <int> arrayRanksBuilder = null; string assemblyName = null; bool decodingTopLevelType = true; bool isGenericTypeName = false; var pooledStrBuilder = PooledStringBuilder.GetInstance(); StringBuilder typeNameBuilder = pooledStrBuilder.Builder; while (!EndOfInput) { int i = _input.IndexOfAny(s_typeNameDelimiters, _offset); if (i >= 0) { char c = _input[i]; // Found name, which could be a generic name with arity. // Generic type parameter count, if any, are handled in DecodeGenericName. string decodedString = DecodeGenericName(i); Debug.Assert(decodedString != null); // Type name is generic if the decoded name of the top level type OR any of the outer types of a nested type had the '`' character. isGenericTypeName = isGenericTypeName || decodedString.IndexOf(GenericTypeNameManglingChar) >= 0; typeNameBuilder.Append(decodedString); switch (c) { case '*': if (arrayRanksBuilder != null) { // Error case, array shape must be specified at the end of the type name. // Process as a regular character and continue. typeNameBuilder.Append(c); } else { pointerCount++; } Advance(); break; case '+': if (arrayRanksBuilder != null || pointerCount > 0) { // Error case, array shape must be specified at the end of the type name. // Process as a regular character and continue. typeNameBuilder.Append(c); } else { // Type followed by nested type. Handle nested class separator and collect the nested types. HandleDecodedTypeName(typeNameBuilder.ToString(), decodingTopLevelType, ref topLevelType, ref nestedTypesBuilder); typeNameBuilder.Clear(); decodingTopLevelType = false; } Advance(); break; case '[': // Is type followed by generic type arguments? if (isGenericTypeName && typeArguments == null) { Advance(); if (arrayRanksBuilder != null || pointerCount > 0) { // Error case, array shape must be specified at the end of the type name. // Process as a regular character and continue. typeNameBuilder.Append(c); } else { // Decode type arguments. typeArguments = DecodeTypeArguments(); } } else { // Decode array shape. DecodeArrayShape(typeNameBuilder, ref arrayRanksBuilder); } break; case ']': if (isTypeArgument) { // End of type arguments. This occurs when the last type argument is a type in the // current assembly. goto ExitDecodeTypeName; } else { // Error case, process as a regular character and continue. typeNameBuilder.Append(c); Advance(); break; } case ',': // A comma may separate a type name from its assembly name or a type argument from // another type argument. // If processing non-type argument or a type argument with assembly name, // process the characters after the comma as an assembly name. if (!isTypeArgument || isTypeArgumentWithAssemblyName) { Advance(); if (!EndOfInput && Char.IsWhiteSpace(Current)) { Advance(); } assemblyName = DecodeAssemblyName(isTypeArgumentWithAssemblyName); } goto ExitDecodeTypeName; default: throw ExceptionUtilities.UnexpectedValue(c); } } else { typeNameBuilder.Append(DecodeGenericName(_input.Length)); goto ExitDecodeTypeName; } } ExitDecodeTypeName: HandleDecodedTypeName(typeNameBuilder.ToString(), decodingTopLevelType, ref topLevelType, ref nestedTypesBuilder); pooledStrBuilder.Free(); return(new AssemblyQualifiedTypeName( topLevelType, nestedTypesBuilder?.ToArrayAndFree(), typeArguments, pointerCount, arrayRanksBuilder?.ToArrayAndFree(), assemblyName)); }
/// <summary> /// Computes line starts faster given already computed line starts from text before the change. /// </summary> protected override TextLineCollection GetLinesCore() { if (!_info.WeakOldText.TryGetTarget(out SourceText oldText) || !oldText.TryGetLines(out TextLineCollection oldLineInfo)) { // no old line starts? do it the hard way. return(base.GetLinesCore()); } // compute line starts given changes and line starts already computed from previous text ArrayBuilder <int> lineStarts = ArrayBuilder <int> .GetInstance(); lineStarts.Add(0); // position in the original document int position = 0; // delta generated by already processed changes (position in the new document = position + delta) int delta = 0; // true if last segment ends with CR and we need to check for CR+LF code below assumes that both CR and LF are also line breaks alone bool endsWithCR = false; foreach (TextChangeRange change in _info.ChangeRanges) { // include existing line starts that occur before this change if (change.Span.Start > position) { if (endsWithCR && _newText[position + delta] == '\n') { // remove last added line start (it was due to previous CR) // a new line start including the LF will be added next lineStarts.RemoveLast(); } LinePositionSpan lps = oldLineInfo.GetLinePositionSpan(TextSpan.FromBounds(position, change.Span.Start)); for (int i = lps.Start.Line + 1; i <= lps.End.Line; i++) { lineStarts.Add(oldLineInfo[i].Start + delta); } endsWithCR = oldText[change.Span.Start - 1] == '\r'; // in case change is inserted between CR+LF we treat CR as line break alone, // but this line break might be retracted and replaced with new one in case LF is inserted if (endsWithCR && change.Span.Start < oldText.Length && oldText[change.Span.Start] == '\n') { lineStarts.Add(change.Span.Start + delta); } } // include line starts that occur within newly inserted text if (change.NewLength > 0) { int changeStart = change.Span.Start + delta; SourceText text = GetSubText(new TextSpan(changeStart, change.NewLength)); if (endsWithCR && text[0] == '\n') { // remove last added line start (it was due to previous CR) // a new line start including the LF will be added next lineStarts.RemoveLast(); } // Skip first line (it is always at offset 0 and corresponds to the previous line) for (int i = 1; i < text.Lines.Count; i++) { lineStarts.Add(changeStart + text.Lines[i].Start); } endsWithCR = text[change.NewLength - 1] == '\r'; } position = change.Span.End; delta += (change.NewLength - change.Span.Length); } // include existing line starts that occur after all changes if (position < oldText.Length) { if (endsWithCR && _newText[position + delta] == '\n') { // remove last added line start (it was due to previous CR) // a new line start including the LF will be added next lineStarts.RemoveLast(); } LinePositionSpan lps = oldLineInfo.GetLinePositionSpan(TextSpan.FromBounds(position, oldText.Length)); for (int i = lps.Start.Line + 1; i <= lps.End.Line; i++) { lineStarts.Add(oldLineInfo[i].Start + delta); } } return(new LineInfo(this, lineStarts.ToArrayAndFree())); }
public async Task <object[]> HandleRequestAsync(Solution solution, LSP.CodeActionParams request, LSP.ClientCapabilities clientCapabilities, CancellationToken cancellationToken) { var codeActions = await GetCodeActionsAsync(solution, request.TextDocument.Uri, request.Range, cancellationToken).ConfigureAwait(false); // Filter out code actions with options since they'll show dialogs and we can't remote the UI and the options. codeActions = codeActions.Where(c => !(c is CodeActionWithOptions)); var commands = new ArrayBuilder <LSP.Command>(); foreach (var codeAction in codeActions) { object[] remoteCommandArguments; // If we have a codeaction with a single applychangesoperation, we want to send the codeaction with the edits. var operations = await codeAction.GetOperationsAsync(cancellationToken).ConfigureAwait(false); var clientSupportsWorkspaceEdits = true; if (clientCapabilities?.Experimental is JObject clientCapabilitiesExtensions) { clientSupportsWorkspaceEdits = clientCapabilitiesExtensions.SelectToken("supportsWorkspaceEdits")?.Value <bool>() ?? clientSupportsWorkspaceEdits; } if (clientSupportsWorkspaceEdits && operations.Length == 1 && operations.First() is ApplyChangesOperation applyChangesOperation) { var workspaceEdit = new LSP.WorkspaceEdit { Changes = new Dictionary <string, LSP.TextEdit[]>() }; var changes = applyChangesOperation.ChangedSolution.GetChanges(solution); var changedDocuments = changes.GetProjectChanges().SelectMany(pc => pc.GetChangedDocuments()); foreach (var docId in changedDocuments) { var newDoc = applyChangesOperation.ChangedSolution.GetDocument(docId); var oldDoc = solution.GetDocument(docId); var oldText = await oldDoc.GetTextAsync(cancellationToken).ConfigureAwait(false); var textChanges = await newDoc.GetTextChangesAsync(oldDoc).ConfigureAwait(false); var edits = textChanges.Select(tc => new LSP.TextEdit { NewText = tc.NewText, Range = ProtocolConversions.TextSpanToRange(tc.Span, oldText) }); workspaceEdit.Changes.Add(newDoc.FilePath, edits.ToArray()); } remoteCommandArguments = new object[] { new LSP.CodeAction { Title = codeAction.Title, Edit = workspaceEdit } }; } // Otherwise, send the original request to be executed on the host. else { // Note that we can pass through the params for this // request (like range, filename) because between getcodeaction and runcodeaction there can be no // changes on the IDE side (it will requery for codeactions if there are changes). remoteCommandArguments = new object[] { new LSP.Command { CommandIdentifier = RunCodeActionCommandName, Title = codeAction.Title, Arguments = new object[] { new RunCodeActionParams { CodeActionParams = request, Title = codeAction.Title } } } }; } // We need to return a command that is a generic wrapper that VS Code can execute. // The argument to this wrapper will either be a RunCodeAction command which will carry // enough information to run the command or a CodeAction with the edits. var command = new LSP.Command { Title = codeAction.Title, CommandIdentifier = $"{RemoteCommandNamePrefix}.{ProviderName}", Arguments = remoteCommandArguments }; commands.Add(command); } return(commands.ToArrayAndFree()); }
private int[] ParseLineStarts() { // Corner case check if (0 == this.Length) { return(new[] { 0 }); } ArrayBuilder <int> lineStarts = ArrayBuilder <int> .GetInstance(); lineStarts.Add(0); // there is always the first line bool lastWasCR = false; // The following loop goes through every character in the text. It is highly // performance critical, and thus inlines knowledge about common line breaks // and non-line breaks. EnumerateChars((int position, char[] buffer, int length) => { int index = 0; if (lastWasCR) { if (length > 0 && buffer[0] == '\n') { index++; } lineStarts.Add(position + index); lastWasCR = false; } while (index < length) { char c = buffer[index]; index++; // Common case - ASCII & not a line break // if (c > '\r' && c <= 127) // if (c >= ('\r'+1) && c <= 127) const uint bias = '\r' + 1; if (unchecked (c - bias) <= (127 - bias)) { continue; } // Assumes that the only 2-char line break sequence is CR+LF if (c == '\r') { if (index < length && buffer[index] == '\n') { index++; } else if (index >= length) { lastWasCR = true; continue; } } else if (!TextUtilities.IsAnyLineBreakCharacter(c)) { continue; } // next line starts at index lineStarts.Add(position + index); } }); return(lineStarts.ToArrayAndFree()); }
public async Task <WorkspaceEdit> HandleAsync(RenameParams request, RequestContext <Solution> requestContext, CancellationToken cancellationToken) { var solution = requestContext.Context; WorkspaceEdit workspaceEdit = null; var document = solution.GetDocumentFromURI(request.TextDocument.Uri); if (document != null) { var renameService = document.Project.LanguageServices.GetService <IEditorInlineRenameService>(); var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); // We need to be on the UI thread to call GetRenameInfo which computes the rename locations. // This is because Roslyn reads the readonly regions of the buffer to compute the locations in the document. // This is typically quick. It's marked configureawait(false) so that the bulk of the rename operation can happen // in background threads. await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); var renameInfo = await renameService.GetRenameInfoAsync(document, position, cancellationToken).ConfigureAwait(false); if (!renameInfo.CanRename) { return(workspaceEdit); } var renameLocationSet = await renameInfo.FindRenameLocationsAsync(solution.Workspace.Options, cancellationToken).ConfigureAwait(false); var renameReplacementInfo = await renameLocationSet.GetReplacementsAsync(request.NewName, solution.Workspace.Options, cancellationToken).ConfigureAwait(false); var newSolution = renameReplacementInfo.NewSolution; var solutionChanges = newSolution.GetChanges(solution); var changedDocuments = solutionChanges .GetProjectChanges() .SelectMany(p => p.GetChangedDocuments(onlyGetDocumentsWithTextChanges: true)); var documentEdits = new ArrayBuilder <TextDocumentEdit>(); foreach (var docId in changedDocuments) { var oldDoc = solution.GetDocument(docId); var newDoc = newSolution.GetDocument(docId); var textChanges = await newDoc.GetTextChangesAsync(oldDoc, cancellationToken).ConfigureAwait(false); var oldText = await oldDoc.GetTextAsync(cancellationToken).ConfigureAwait(false); var textDocumentEdit = new TextDocumentEdit { TextDocument = new VersionedTextDocumentIdentifier { Uri = newDoc.GetURI() }, Edits = textChanges.Select(tc => ProtocolConversions.TextChangeToTextEdit(tc, oldText)).ToArray() }; documentEdits.Add(textDocumentEdit); } workspaceEdit = new WorkspaceEdit { DocumentChanges = documentEdits.ToArrayAndFree() }; } return(workspaceEdit); }