⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 indentation.cs

📁 全功能c#编译器
💻 CS
字号:
using System;
using System.Collections;
using System.Text;

namespace CSharpBinding.FormattingStrategy
{
	public class IndentationSettings
	{
		public string IndentString = "\t";
	}
	
	public class IndentationReformatter
	{
		public struct Block
		{
			public string OuterIndent;
			public string InnerIndent;
			public string LastWord;
			public char Bracket;
			public bool Continuation;
			public bool OneLineBlock;
			public int StartLine;
			
			public void Indent(IndentationSettings set)
			{
				Indent(set, set.IndentString);
			}
			
			public void Indent(IndentationSettings set, string str)
			{
				OuterIndent = InnerIndent;
				InnerIndent += str;
				Continuation = false;
				OneLineBlock = false;
				LastWord = "";
			}
		}
		
		StringBuilder wordBuilder;
		Stack blocks; // blocks contains all blocks outside of the current
		Block block;  // block is the current block
		
		bool inString = false;
		bool inChar   = false;
		bool verbatim = false;
		bool escape   = false;
		
		bool lineComment  = false;
		bool blockComment = false;
		
		char lastRealChar = ' '; // last non-comment char
		
		public void Reformat(IDocumentAccessor doc, IndentationSettings set)
		{
			Init();
			
			while (doc.Next()) {
				Step(doc, set);
			}
		}
		
		public void Init()
		{
			wordBuilder = new StringBuilder();
			blocks = new Stack();
			block = new Block();
			block.InnerIndent = "";
			block.OuterIndent = "";
			block.Bracket = '{';
			block.Continuation = false;
			block.LastWord = "";
			block.OneLineBlock = false;
			block.StartLine = 0;
			
			inString = false;
			inChar   = false;
			verbatim = false;
			escape   = false;
			
			lineComment  = false;
			blockComment = false;
			
			lastRealChar = ' '; // last non-comment char
		}
		
		public void Step(IDocumentAccessor doc, IndentationSettings set)
		{
			string line = doc.Text.TrimStart();
			
			StringBuilder indent = new StringBuilder();
			if (line.Length == 0) {
				// Special threatment for empty lines:
				if (blockComment || (inString && verbatim))
					return;
				indent.Append(block.InnerIndent);
				if (block.OneLineBlock)
					indent.Append(set.IndentString);
				if (block.Continuation)
					indent.Append(set.IndentString);
				if (doc.Text != indent.ToString())
					doc.Text = indent.ToString();
				return;
			}
			
			if (TrimEnd(doc))
				line = doc.Text.TrimStart();
			
			Block oldBlock = block;
			bool startInComment = blockComment;
			bool startInString = (inString && verbatim);
			
			#region Parse char by char
			lineComment = false;
			inChar = false;
			escape = false;
			if (!verbatim) inString = false;
			
			lastRealChar = '\n';
			
			char lastchar = ' ';
			char c = ' ';
			char nextchar = line[0];
			for (int 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
				//Console.Write(c);
				switch (c) {
					case '{':
						//Console.Write(block.LastWord);
						block.OneLineBlock = false;
						blocks.Push(block);
						block.StartLine = doc.LineNumber;
						if (block.LastWord == "switch")
							block.Indent(set, set.IndentString + set.IndentString);
						else
							block.Indent(set);
						block.Bracket = '{';
						//Console.Write(block.InnerIndent + "|");
						break;
					case '}':
						while (block.Bracket != '{') {
							if (blocks.Count == 0) break;
							block = (Block)blocks.Pop();
						}
						if (blocks.Count == 0) break;
						block = (Block)blocks.Pop();
						block.Continuation = false;
						block.OneLineBlock = false;
						break;
					case '(':
					case '[':
						blocks.Push(block);
						if (block.StartLine == doc.LineNumber)
							block.InnerIndent = block.OuterIndent;
						else
							block.StartLine = doc.LineNumber;
						block.Indent(set,
						             (oldBlock.OneLineBlock ? set.IndentString : "") +
						             (oldBlock.Continuation ? set.IndentString : "") +
						             new String(' ', i + 1));
						block.Bracket = c;
						break;
					case ')':
						if (blocks.Count == 0) break;
						if (block.Bracket == '(') {
							block = (Block)blocks.Pop();
							if (IsSingleStatementKeyword(block.LastWord))
								block.Continuation = false;
						}
						break;
					case ']':
						if (blocks.Count == 0) break;
						if (block.Bracket == '[')
							block = (Block)blocks.Pop();
						break;
					case ';':
					case ',':
						block.Continuation = false;
						block.OneLineBlock = false;
						break;
					case ':':
						if (block.LastWord == "case" || line.StartsWith("case ") || line.StartsWith(block.LastWord + ":")) {
							block.Continuation = false;
							block.OneLineBlock = false;
						}
						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"))
				return;
			
			if (line[0] == '}') {
				indent.Append(oldBlock.OuterIndent);
				//Console.WriteLine("'" + indent.Replace("\t", "--->") + "'");
				//Console.WriteLine(oldBlock.LastWord);
				oldBlock.OneLineBlock = false;
				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 ") || line.StartsWith(block.LastWord + ":"))
					indent.Remove(indent.Length - set.IndentString.Length, set.IndentString.Length);
			} else if (lastRealChar == ')') {
				if (IsSingleStatementKeyword(block.LastWord)) {
					block.OneLineBlock = true;
				}
			} else if (block.LastWord == "else" && lastRealChar == 'e') {
				block.OneLineBlock = true;
				block.Continuation = false;
			}
			if (line[0] != '{') {
				if (line[0] != ')' && oldBlock.Continuation && oldBlock.Bracket == '{')
					indent.Append(set.IndentString);
				if (oldBlock.OneLineBlock)
					indent.Append(set.IndentString);
			}
			
			// this is only for blockcomment lines starting with *,
			// all others keep their old indentation
			if (startInComment)
				indent.Append(' ');
			
			//Console.WriteLine("{0} != {1} - {2}", indent.Length, doc.Text.Length, line.Length);
			if (indent.Length != (doc.Text.Length - line.Length) ||
			    !doc.Text.StartsWith(indent.ToString()) ||
			    Char.IsWhiteSpace(doc.Text[indent.Length]))
			{
				doc.Text = indent.ToString() + line;
			}
		}
		
		bool IsSingleStatementKeyword(string keyword) {
			switch (block.LastWord) {
				case "if":
				case "for":
				case "while":
				case "do":
				case "foreach":
				case "using":
				case "lock":
					return true;
				default:
					return false;
			}
		}
		
		bool TrimEnd(IDocumentAccessor doc)
		{
			string line = doc.Text;
			if (!Char.IsWhiteSpace(line[line.Length - 1])) return false;
			
			// one space after an empty comment is allowed
			if (line.EndsWith("// "))
				return false;
			
			doc.Text = line.TrimEnd();
			return true;
		}
	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -