Esempio n. 1
0
        public override async Task <LSP.VSInternalDocumentOnAutoInsertResponseItem?> HandleRequestAsync(
            LSP.VSInternalDocumentOnAutoInsertParams request,
            RequestContext context,
            CancellationToken cancellationToken)
        {
            var document = context.Document;

            if (document == null)
            {
                return(null);
            }

            var service = document.GetRequiredLanguageService <IDocumentationCommentSnippetService>();

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

            var options = DocumentationCommentOptions.From(documentOptions);

            // The editor calls this handler for C# and VB comment characters, but we only need to process the one for the language that matches the document
            if (request.Character == "\n" || request.Character == service.DocumentationCommentCharacter)
            {
                var documentationCommentResponse = await GetDocumentationCommentResponseAsync(
                    request, document, service, options, cancellationToken).ConfigureAwait(false);

                if (documentationCommentResponse != null)
                {
                    return(documentationCommentResponse);
                }
            }

            // Only support this for razor as LSP doesn't support overtype yet.
            // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1165179/
            // Once LSP supports overtype we can move all of brace completion to LSP.
            if (request.Character == "\n" && context.ServerKind == WellKnownLspServerKinds.RazorLspServer)
            {
                var indentationOptions = IndentationOptions.From(documentOptions, document.Project.Solution.Workspace.Services, document.Project.Language);

                var braceCompletionAfterReturnResponse = await GetBraceCompletionAfterReturnResponseAsync(
                    request, document, indentationOptions, cancellationToken).ConfigureAwait(false);

                if (braceCompletionAfterReturnResponse != null)
                {
                    return(braceCompletionAfterReturnResponse);
                }
            }

            return(null);
        }
Esempio n. 2
0
        public async Task <ImmutableArray <TextChange> > GetFormattingChangesAsync(
            Document document,
            char typedChar,
            int caretPosition,
            DocumentOptionSet?documentOptions,
            CancellationToken cancellationToken)
        {
            // first, find the token user just typed.
            var token = await GetTokenBeforeTheCaretAsync(document, caretPosition, cancellationToken).ConfigureAwait(false);

            if (token.IsMissing ||
                !ValidSingleOrMultiCharactersTokenKind(typedChar, token.Kind()) ||
                token.IsKind(SyntaxKind.EndOfFileToken, SyntaxKind.None))
            {
                return(ImmutableArray <TextChange> .Empty);
            }

            var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var formattingRules = GetFormattingRules(document, caretPosition, token);

            var service = document.GetLanguageService <ISyntaxFactsService>();

            if (service != null && service.IsInNonUserCode(token.SyntaxTree, caretPosition, cancellationToken))
            {
                return(ImmutableArray <TextChange> .Empty);
            }

            var shouldNotFormat = await TokenShouldNotFormatOnTypeCharAsync(token, cancellationToken).ConfigureAwait(false);

            if (shouldNotFormat)
            {
                return(ImmutableArray <TextChange> .Empty);
            }

            var services = document.Project.Solution.Workspace.Services;

            if (documentOptions == null)
            {
                var inferredIndentationService = services.GetRequiredService <IInferredIndentationService>();
                documentOptions = await inferredIndentationService.GetDocumentOptionsWithInferredIndentationAsync(document, explicitFormat : false, cancellationToken : cancellationToken).ConfigureAwait(false);
            }

            var options = IndentationOptions.From(documentOptions, document.Project.Solution.Workspace.Services, document.Project.Language);

            // Do not attempt to format on open/close brace if autoformat on close brace feature is
            // off, instead just smart indent.
            //
            // We want this behavior because it's totally reasonable for a user to want to not have
            // on automatic formatting because they feel it is too aggressive.  However, by default,
            // if you have smart-indentation on and are just hitting enter, you'll common have the
            // caret placed one indent higher than your current construct.  For example, if you have:
            //
            //      if (true)
            //          $ <-- smart indent will have placed the caret here here.
            //
            // This is acceptable given that the user may want to just write a simple statement there.
            // However, if they start writing `{`, then things should snap over to be:
            //
            //      if (true)
            //      {
            //
            // Importantly, this is just an indentation change, no actual 'formatting' is done.  We do
            // the same with close brace.  If you have:
            //
            //      if (...)
            //      {
            //          bad . ly ( for (mmated+code) )  ;
            //          $ <-- smart indent will have placed the care here.
            //
            // If the user hits `}` then we will properly smart indent the `}` to match the `{`.
            // However, we won't touch any of the other code in that block, unlike if we were
            // formatting.
            var onlySmartIndent =
                (token.IsKind(SyntaxKind.CloseBraceToken) && OnlySmartIndentCloseBrace(options.AutoFormattingOptions)) ||
                (token.IsKind(SyntaxKind.OpenBraceToken) && OnlySmartIndentOpenBrace(options.AutoFormattingOptions));

            if (onlySmartIndent)
            {
                // if we're only doing smart indent, then ignore all edits to this token that occur before
                // the span of the token. They're irrelevant and may screw up other code the user doesn't
                // want touched.
                var tokenEdits = await FormatTokenAsync(document, options, token, formattingRules, cancellationToken).ConfigureAwait(false);

                return(tokenEdits.Where(t => t.Span.Start >= token.FullSpan.Start).ToImmutableArray());
            }

            // if formatting range fails, do format token one at least
            var changes = await FormatRangeAsync(document, options, token, formattingRules, cancellationToken).ConfigureAwait(false);

            if (changes.Length > 0)
            {
                return(changes);
            }

            return((await FormatTokenAsync(document, options, token, formattingRules, cancellationToken).ConfigureAwait(false)).ToImmutableArray());
        }