public override void VisitRazorDirectiveBody(RazorDirectiveBodySyntax node) { // We can't provide colors for CSharp because if we both provided them then they would overlap, which violates the LSP spec. if (node.Keyword.Kind != SyntaxKind.CSharpStatementLiteral) { AddSemanticRange(node.Keyword, SyntaxKind.RazorDirective); } base.VisitRazorDirectiveBody(node); }
private void FormatCodeBlockEnd(FormattingContext context, SourceText changedText, RazorDirectiveBodySyntax directiveBody, SyntaxNode innerCodeBlock, List <TextEdit> edits) { var sourceText = context.SourceText; var originalBodySpan = TextSpan.FromBounds(directiveBody.Position, directiveBody.EndPosition); var originalBodyRange = originalBodySpan.AsRange(sourceText); if (context.Range.End.Line < originalBodyRange.End.Line) { return; } // Last line is within the selected range. Let's try and format the end. TrackChangeInSpan(sourceText, originalBodySpan, changedText, out var changedBodySpan, out _); var changedBodyRange = changedBodySpan.AsRange(changedText); var firstLine = changedText.Lines[(int)changedBodyRange.Start.Line]; var desiredIndentationLevel = context.Indentations[firstLine.LineNumber].IndentationLevel; var desiredIndentation = context.GetIndentationLevelString(desiredIndentationLevel); // we want to keep the close '}' on its own line. So bring it to the next line. var originalInnerCodeBlockSpan = TextSpan.FromBounds(innerCodeBlock.Position, innerCodeBlock.EndPosition); TrackChangeInSpan(sourceText, originalInnerCodeBlockSpan, changedText, out var changedInnerCodeBlockSpan, out _); var closeCurlyLocation = changedInnerCodeBlockSpan.End; var closeCurlyLine = changedText.Lines.GetLineFromPosition(closeCurlyLocation); var firstNonWhitespaceOffset = closeCurlyLine.GetFirstNonWhitespaceOffset() ?? 0; if (closeCurlyLine.Start + firstNonWhitespaceOffset != closeCurlyLocation) { // This means the '}' is on the same line as some C# code. // Bring it down to the next line and apply the desired indentation. var edit = new TextEdit() { Range = new Range( new Position(closeCurlyLine.LineNumber, closeCurlyLocation - closeCurlyLine.Start), new Position(closeCurlyLine.LineNumber, closeCurlyLocation - closeCurlyLine.Start)), NewText = Environment.NewLine + desiredIndentation }; edits.Add(edit); } else if (firstNonWhitespaceOffset != desiredIndentation.Length) { // This means the '}' is on its own line but is not indented correctly. Correct it. var edit = new TextEdit() { Range = new Range( new Position(closeCurlyLine.LineNumber, 0), new Position(closeCurlyLine.LineNumber, firstNonWhitespaceOffset)), NewText = desiredIndentation }; edits.Add(edit); } }
private void FormatCodeBlockStart(FormattingContext context, SourceText changedText, RazorDirectiveBodySyntax directiveBody, SyntaxNode innerCodeBlock, List <TextEdit> edits) { var sourceText = context.SourceText; var originalBodySpan = TextSpan.FromBounds(directiveBody.Position, directiveBody.EndPosition); var originalBodyRange = originalBodySpan.AsRange(sourceText); if (context.Range.Start.Line > originalBodyRange.Start.Line) { return; } // First line is within the selected range. Let's try and format the start. TrackChangeInSpan(sourceText, originalBodySpan, changedText, out var changedBodySpan, out _); var changedBodyRange = changedBodySpan.AsRange(changedText); // First, make sure the first line is indented correctly. var firstLine = changedText.Lines[(int)changedBodyRange.Start.Line]; var desiredIndentationLevel = context.Indentations[firstLine.LineNumber].IndentationLevel; var desiredIndentation = context.GetIndentationLevelString(desiredIndentationLevel); var firstNonWhitespaceOffset = firstLine.GetFirstNonWhitespaceOffset(); if (firstNonWhitespaceOffset.HasValue) { var edit = new TextEdit() { Range = new Range( new Position(firstLine.LineNumber, 0), new Position(firstLine.LineNumber, firstNonWhitespaceOffset.Value)), NewText = desiredIndentation }; edits.Add(edit); } // We should also move any code that comes after '{' down to its own line. var originalInnerCodeBlockSpan = TextSpan.FromBounds(innerCodeBlock.Position, innerCodeBlock.EndPosition); TrackChangeInSpan(sourceText, originalInnerCodeBlockSpan, changedText, out var changedInnerCodeBlockSpan, out _); var innerCodeBlockRange = changedInnerCodeBlockSpan.AsRange(changedText); var innerCodeBlockLine = changedText.Lines[(int)innerCodeBlockRange.Start.Line]; var textAfterBlockStart = innerCodeBlockLine.ToString().Substring(innerCodeBlock.Position - innerCodeBlockLine.Start); var isBlockStartOnSeparateLine = string.IsNullOrWhiteSpace(textAfterBlockStart); var innerCodeBlockIndentationLevel = desiredIndentationLevel + 1; var desiredInnerCodeBlockIndentation = context.GetIndentationLevelString(innerCodeBlockIndentationLevel); var whitespaceAfterBlockStart = textAfterBlockStart.GetLeadingWhitespace(); if (!isBlockStartOnSeparateLine) { // If the first line contains code, add a newline at the beginning and indent it. var edit = new TextEdit() { Range = new Range( new Position(innerCodeBlockLine.LineNumber, innerCodeBlock.Position - innerCodeBlockLine.Start), new Position(innerCodeBlockLine.LineNumber, innerCodeBlock.Position + whitespaceAfterBlockStart.Length - innerCodeBlockLine.Start)), NewText = Environment.NewLine + desiredInnerCodeBlockIndentation }; edits.Add(edit); } else { // // The code inside the code block directive is on its own line. Ideally the C# formatter would have already taken care of it. // Except, the first line of the code block is not indented because of how our SourceMappings work. // E.g, // @code { // ... // } // Our source mapping for this code block only ranges between the { and }, exclusive. // If the C# formatter provides any edits that start from before the {, we won't be able to map it back and we will ignore it. // Unfortunately because of this, we partially lose some edits which would have indented the first line of the code block correctly. // So let's manually indent the first line here. // var innerCodeBlockText = changedText.GetSubTextString(changedInnerCodeBlockSpan); if (!string.IsNullOrWhiteSpace(innerCodeBlockText)) { var codeStart = innerCodeBlockText.GetFirstNonWhitespaceOffset() + changedInnerCodeBlockSpan.Start; if (codeStart.HasValue && codeStart != changedInnerCodeBlockSpan.End) { // If we got here, it means this is a non-empty code block. We can safely indent the first line. var codeStartLine = changedText.Lines.GetLineFromPosition(codeStart.Value); var existingCodeStartIndentation = codeStartLine.GetFirstNonWhitespaceOffset() ?? 0; var edit = new TextEdit() { Range = new Range( new Position(codeStartLine.LineNumber, 0), new Position(codeStartLine.LineNumber, existingCodeStartIndentation)), NewText = desiredInnerCodeBlockIndentation }; edits.Add(edit); } } } }