/// <summary> /// Formats the specified snapshot range. /// </summary> /// <param name="snapshotRange"> The snapshot range. </param> public void Format(TextSnapshotRange snapshotRange) { // Get the snapshot var snapshot = snapshotRange.Snapshot; if (snapshot == null) return; // Get the snapshot reader var reader = snapshot.GetReader(snapshotRange.StartOffset); // Get the code document var document = snapshot.Document as ICodeDocument; if (document == null) return; // Get the tab size var tabSize = document.TabSize; // Create a text change object for the document var options = new TextChangeOptions(); // Changes must occur sequentially so that we can use unmodified offsets while looping over the document options.OffsetDelta = TextChangeOffsetDelta.SequentialOnly; options.RetainSelection = true; var change = document.CreateTextChange(TextChangeTypes.AutoFormat, options); // Keep track of the last non whitespace token Id var lastNonWhitespaceTokenId = -1; // Keep track of the indent level var indentLevel = 0; // Loop through the document while ((reader.Token != null) && (reader.Offset < snapshotRange.EndOffset)) { // If the token is whitespace, delete the text if (reader.Token.Id == JsonTokenId.Whitespace) { change.DeleteText(reader.Token.TextRange); } else { // The token is not whitespace // Create a variable that will contain the text to be inserted var insertText = ""; // Determine the insertText value based on the previous non-whitespace token and the current token switch (lastNonWhitespaceTokenId) { case JsonTokenId.OpenCurlyBrace: case JsonTokenId.OpenSquareBrace: { if (reader.Token.Id != JsonTokenId.CloseCurlyBrace && reader.Token.Id != JsonTokenId.CloseSquareBrace) { indentLevel++; } insertText = Environment.NewLine + StringHelper.GetIndentText(document.AutoConvertTabsToSpaces, tabSize, indentLevel * tabSize); break; } case JsonTokenId.Colon : { insertText = " "; break; } case JsonTokenId.Comma : { if (reader.Token.Id == JsonTokenId.CloseCurlyBrace || reader.Token.Id == JsonTokenId.CloseSquareBrace) { indentLevel = Math.Max(0, indentLevel - 1); } insertText = Environment.NewLine + StringHelper.GetIndentText(document.AutoConvertTabsToSpaces, tabSize, indentLevel * tabSize); break; } case JsonTokenId.CloseCurlyBrace: case JsonTokenId.CloseSquareBrace: case JsonTokenId.False: case JsonTokenId.True: case JsonTokenId.Number: case JsonTokenId.StringEndDelimiter: case JsonTokenId.Null: { if (reader.Token.Id == JsonTokenId.CloseCurlyBrace || reader.Token.Id == JsonTokenId.CloseSquareBrace) { indentLevel = Math.Max(0, indentLevel - 1); insertText = Environment.NewLine + StringHelper.GetIndentText(document.AutoConvertTabsToSpaces, tabSize, indentLevel*tabSize); } } break; } // Insert the replacement text change.InsertText(reader.Token.StartOffset, insertText); // Update the last non-whitespace token Id lastNonWhitespaceTokenId = reader.Token.Id; } // Go to the next token reader.GoToNextToken(); } // If the entire document was formatted, add a newline to the end if ((snapshot.SnapshotRange.StartOffset == snapshotRange.StartOffset) && (snapshot.SnapshotRange.EndOffset == snapshotRange.EndOffset)) change.InsertText(snapshotRange.EndOffset, "\n"); // Apply the changes change.Apply(); }