///////////////////////////////////////////////////////////////////////////////////////////////////// // NON-PUBLIC PROCEDURES ///////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Formats the specified snapshot range. /// </summary> /// <param name="change">The <see cref="ITextChange"/> to use.</param> /// <param name="snapshotRange">The snapshot range.</param> private void FormatCore(ITextChange change, TextSnapshotRange snapshotRange) { // Get the snapshot ITextSnapshot snapshot = snapshotRange.Snapshot; if (snapshot == null) { return; } // Get the snapshot reader ITextSnapshotReader reader = snapshot.GetReader(snapshotRange.StartOffset); // Optimize reader options reader.Options.PrimaryScanDirection = TextScanDirection.Forward; reader.Options.InitialTokenLoadBufferLength = Math.Min(100000, snapshotRange.Length); reader.Options.DefaultTokenLoadBufferLength = reader.Options.InitialTokenLoadBufferLength; // Get the code document ICodeDocument document = snapshot.Document as ICodeDocument; if (document == null) { return; } // Get the tab size int tabSize = document.TabSize; // Keep track of the last non whitespace token Id int lastNonWhitespaceTokenId = -1; // Keep track of the indent level int 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 == SimpleTokenId.Whitespace) { change.DeleteText(reader.Token.TextRange); } // The token is not whitespace else { // Create a variable that will contain the text to be inserted string insertText = ""; // Determine the insertText value based on the previous non-whitespace token and the current token switch (lastNonWhitespaceTokenId) { case SimpleTokenId.CloseCurlyBrace: // If the token is a close curly brace, decrement the indent level if (reader.Token.Id == SimpleTokenId.CloseCurlyBrace) { indentLevel = Math.Max(0, indentLevel - 1); insertText = "\n" + StringHelper.GetIndentText(document.AutoConvertTabsToSpaces, tabSize, indentLevel * tabSize); } else { // If the indent level is zero, a function declaration just finished, which means we want an extra newline if (indentLevel == 0) { insertText = "\n\n"; } else { insertText = "\n" + StringHelper.GetIndentText(document.AutoConvertTabsToSpaces, tabSize, indentLevel * tabSize); } } break; case SimpleTokenId.CloseParenthesis: // If the current token is an OpenCurlyBrace, determine whether the brace goes on a new line or not if (reader.Token.Id == SimpleTokenId.OpenCurlyBrace) { if (openingBraceOnNewLine) { insertText = "\n" + StringHelper.GetIndentText(document.AutoConvertTabsToSpaces, tabSize, indentLevel * tabSize); } else { insertText = " "; } } break; case SimpleTokenId.Identifier: case SimpleTokenId.Number: // Sometimes a space should be added after an identifier or number, sometimes not if (reader.Token.Id != SimpleTokenId.SemiColon && reader.Token.Id != SimpleTokenId.CloseParenthesis && reader.Token.Id != SimpleTokenId.OpenParenthesis && reader.Token.Id != SimpleTokenId.Comma) { insertText = " "; } break; case SimpleTokenId.Comma: case SimpleTokenId.Function: case SimpleTokenId.Return: case SimpleTokenId.Var: case SimpleTokenId.Multiplication: case SimpleTokenId.Equality: case SimpleTokenId.Inequality: case SimpleTokenId.Assignment: case SimpleTokenId.Subtraction: case SimpleTokenId.Addition: case SimpleTokenId.Division: // Keywords and operators get a space insertText = " "; break; case SimpleTokenId.MultiLineCommentText: // Multiline comments get an extra newline insertText = "\n\n" + StringHelper.GetIndentText(document.AutoConvertTabsToSpaces, tabSize, indentLevel * tabSize); break; case SimpleTokenId.OpenCurlyBrace: // If the token is not a close curly brace, increment the indent level if (reader.Token.Id != SimpleTokenId.CloseCurlyBrace) { indentLevel++; } insertText = "\n" + StringHelper.GetIndentText(document.AutoConvertTabsToSpaces, tabSize, indentLevel * tabSize); break; case SimpleTokenId.SemiColon: case SimpleTokenId.SingleLineCommentText: // If the token is a close curly brace, decrement the indent level if (reader.Token.Id == SimpleTokenId.CloseCurlyBrace) { indentLevel = Math.Max(0, indentLevel - 1); } insertText = "\n" + 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"); } }