AvalonStudio.TextEditor.Indentation.CSharp.IndentationReformatter.Step C# (CSharp) Method

Step() public method

public Step ( IDocumentAccessor doc, IndentationSettings set ) : void
doc IDocumentAccessor
set IndentationSettings
return void
		public void Step(IDocumentAccessor doc, IndentationSettings set)
		{
			var line = doc.Text;
			if (set.LeaveEmptyLines && line.Length == 0) return; // leave empty lines empty
			line = line.TrimStart();

			var indent = new StringBuilder();
			if (line.Length == 0)
			{
				// Special treatment for empty lines:
				if (blockComment || (inString && verbatim))
					return;
				indent.Append(block.InnerIndent);
				indent.Append(Repeat(set.IndentString, block.OneLineBlock));
				if (block.Continuation)
					indent.Append(set.IndentString);
				if (doc.Text != indent.ToString())
					doc.Text = indent.ToString();
				return;
			}

			if (TrimEnd(doc))
				line = doc.Text.TrimStart();

			var oldBlock = block;
			var startInComment = blockComment;
			var startInString = inString && verbatim;

			#region Parse char by char

			lineComment = false;
			inChar = false;
			escape = false;
			if (!verbatim) inString = false;

			lastRealChar = '\n';

			var lastchar = ' ';
			var c = ' ';
			var nextchar = line[0];
			for (var i = 0; i < line.Length; i++)
			{
				if (lineComment) break; // cancel parsing current line

				lastchar = c;
				c = nextchar;
				if (i + 1 < line.Length)
					nextchar = line[i + 1];
				else
					nextchar = '\n';

				if (escape)
				{
					escape = false;
					continue;
				}

				#region Check for comment/string chars

				switch (c)
				{
					case '/':
						if (blockComment && lastchar == '*')
							blockComment = false;
						if (!inString && !inChar)
						{
							if (!blockComment && nextchar == '/')
								lineComment = true;
							if (!lineComment && nextchar == '*')
								blockComment = true;
						}
						break;
					case '#':
						if (!(inChar || blockComment || inString))
							lineComment = true;
						break;
					case '"':
						if (!(inChar || lineComment || blockComment))
						{
							inString = !inString;
							if (!inString && verbatim)
							{
								if (nextchar == '"')
								{
									escape = true; // skip escaped quote
									inString = true;
								}
								else
								{
									verbatim = false;
								}
							}
							else if (inString && lastchar == '@')
							{
								verbatim = true;
							}
						}
						break;
					case '\'':
						if (!(inString || lineComment || blockComment))
						{
							inChar = !inChar;
						}
						break;
					case '\\':
						if ((inString && !verbatim) || inChar)
							escape = true; // skip next character
						break;
				}

				#endregion

				if (lineComment || blockComment || inString || inChar)
				{
					if (wordBuilder.Length > 0)
						block.LastWord = wordBuilder.ToString();
					wordBuilder.Length = 0;
					continue;
				}

				if (!char.IsWhiteSpace(c) && c != '[' && c != '/')
				{
					if (block.Bracket == '{')
						block.Continuation = true;
				}

				if (char.IsLetterOrDigit(c))
				{
					wordBuilder.Append(c);
				}
				else
				{
					if (wordBuilder.Length > 0)
						block.LastWord = wordBuilder.ToString();
					wordBuilder.Length = 0;
				}

				#region Push/Pop the blocks

				switch (c)
				{
					case '{':
						block.ResetOneLineBlock();
						blocks.Push(block);
						block.StartLine = doc.LineNumber;
						if (block.LastWord == "switch")
						{
							block.Indent(set.IndentString + set.IndentString);
							/* oldBlock refers to the previous line, not the previous block
							 * The block we want is not available anymore because it was never pushed.
							 * } else if (oldBlock.OneLineBlock) {
							// Inside a one-line-block is another statement
							// with a full block: indent the inner full block
							// by one additional level
							block.Indent(set, set.IndentString + set.IndentString);
							block.OuterIndent += set.IndentString;
							// Indent current line if it starts with the '{' character
							if (i == 0) {
								oldBlock.InnerIndent += set.IndentString;
							}*/
						}
						else
						{
							block.Indent(set);
						}
						block.Bracket = '{';
						break;
					case '}':
						while (block.Bracket != '{')
						{
							if (blocks.Count == 0) break;
							block = blocks.Pop();
						}
						if (blocks.Count == 0) break;
						block = blocks.Pop();
						block.Continuation = false;
						block.ResetOneLineBlock();
						break;
					case '(':
					case '[':
						blocks.Push(block);
						if (block.StartLine == doc.LineNumber)
							block.InnerIndent = block.OuterIndent;
						else
							block.StartLine = doc.LineNumber;
						block.Indent(Repeat(set.IndentString, oldBlock.OneLineBlock) +
						             (oldBlock.Continuation ? set.IndentString : "") +
						             (i == line.Length - 1 ? set.IndentString : new string(' ', i + 1)));
						block.Bracket = c;
						break;
					case ')':
						if (blocks.Count == 0) break;
						if (block.Bracket == '(')
						{
							block = blocks.Pop();
							if (IsSingleStatementKeyword(block.LastWord))
								block.Continuation = false;
						}
						break;
					case ']':
						if (blocks.Count == 0) break;
						if (block.Bracket == '[')
							block = blocks.Pop();
						break;
					case ';':
					case ',':
						block.Continuation = false;
						block.ResetOneLineBlock();
						break;
					case ':':
						if (block.LastWord == "case"
						    || line.StartsWith("case ", StringComparison.Ordinal)
						    || line.StartsWith(block.LastWord + ":", StringComparison.Ordinal))
						{
							block.Continuation = false;
							block.ResetOneLineBlock();
						}
						break;
				}

				if (!char.IsWhiteSpace(c))
				{
					// register this char as last char
					lastRealChar = c;
				}

				#endregion
			}

			#endregion

			if (wordBuilder.Length > 0)
				block.LastWord = wordBuilder.ToString();
			wordBuilder.Length = 0;

			if (startInString) return;
			if (startInComment && line[0] != '*') return;
			if (doc.Text.StartsWith("//\t", StringComparison.Ordinal) || doc.Text == "//")
				return;

			if (line[0] == '}')
			{
				indent.Append(oldBlock.OuterIndent);
				oldBlock.ResetOneLineBlock();
				oldBlock.Continuation = false;
			}
			else
			{
				indent.Append(oldBlock.InnerIndent);
			}

			if (indent.Length > 0 && oldBlock.Bracket == '(' && line[0] == ')')
			{
				indent.Remove(indent.Length - 1, 1);
			}
			else if (indent.Length > 0 && oldBlock.Bracket == '[' && line[0] == ']')
			{
				indent.Remove(indent.Length - 1, 1);
			}

			if (line[0] == ':')
			{
				oldBlock.Continuation = true;
			}
			else if (lastRealChar == ':' && indent.Length >= set.IndentString.Length)
			{
				if (block.LastWord == "case" || line.StartsWith("case ", StringComparison.Ordinal) ||
				    line.StartsWith(block.LastWord + ":", StringComparison.Ordinal))
					indent.Remove(indent.Length - set.IndentString.Length, set.IndentString.Length);
			}
			else if (lastRealChar == ')')
			{
				if (IsSingleStatementKeyword(block.LastWord))
				{
					block.OneLineBlock++;
				}
			}
			else if (lastRealChar == 'e' && block.LastWord == "else")
			{
				block.OneLineBlock = Math.Max(1, block.PreviousOneLineBlock);
				block.Continuation = false;
				oldBlock.OneLineBlock = block.OneLineBlock - 1;
			}

			if (doc.IsReadOnly)
			{
				// We can't change the current line, but we should accept the existing
				// indentation if possible (=if the current statement is not a multiline
				// statement).
				if (!oldBlock.Continuation && oldBlock.OneLineBlock == 0 &&
				    oldBlock.StartLine == block.StartLine &&
				    block.StartLine < doc.LineNumber && lastRealChar != ':')
				{
					// use indent StringBuilder to get the indentation of the current line
					indent.Length = 0;
					line = doc.Text; // get untrimmed line
					for (var i = 0; i < line.Length; ++i)
					{
						if (!char.IsWhiteSpace(line[i]))
							break;
						indent.Append(line[i]);
					}
					// /* */ multiline comments have an extra space - do not count it
					// for the block's indentation.
					if (startInComment && indent.Length > 0 && indent[indent.Length - 1] == ' ')
					{
						indent.Length -= 1;
					}
					block.InnerIndent = indent.ToString();
				}
				return;
			}

			if (line[0] != '{')
			{
				if (line[0] != ')' && oldBlock.Continuation && oldBlock.Bracket == '{')
					indent.Append(set.IndentString);
				indent.Append(Repeat(set.IndentString, oldBlock.OneLineBlock));
			}

			// this is only for blockcomment lines starting with *,
			// all others keep their old indentation
			if (startInComment)
				indent.Append(' ');

			if (indent.Length != doc.Text.Length - line.Length ||
			    !doc.Text.StartsWith(indent.ToString(), StringComparison.Ordinal) ||
			    char.IsWhiteSpace(doc.Text[indent.Length]))
			{
				doc.Text = indent + line;
			}
		}