#develop (short for SharpDevelop) is a free IDE for .NET programming languages.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

324 lines
7.3 KiB

using System;
using ICSharpCode.NRefactory.Editor;
using System.Text;
namespace ICSharpCode.NRefactory.CSharp
{
public class CSharpIndentEngine
{
readonly IDocument document;
readonly CSharpFormattingOptions options;
readonly TextEditorOptions textEditorOptions;
readonly StringBuilder wordBuf = new StringBuilder();
Indent thisLineindent;
Indent indent;
public string ThisLineIndent {
get {
return thisLineindent.IndentString;
}
}
public string NewLineIndent {
get {
return indent.IndentString;
}
}
public CSharpIndentEngine(IDocument document, TextEditorOptions textEditorOptions, CSharpFormattingOptions formattingOptions)
{
this.document = document;
this.options = formattingOptions;
this.textEditorOptions = textEditorOptions;
this.indent = new Indent(textEditorOptions);
this.thisLineindent = new Indent(textEditorOptions);
}
int offset;
void Reset()
{
offset = 0;
thisLineindent.Reset();
indent.Reset();
pc = '\0';
IsLineStart = true;
addContinuation = false;
inside = Inside.Empty;
nextBody = currentBody = Body.None;
}
public void UpdateToOffset (int toOffset)
{
if (toOffset < offset)
Reset();
for (int i = offset; i < toOffset; i++)
Push(document.GetCharAt(i));
}
Inside inside = Inside.Empty;
bool IsLineStart = true;
bool IsInStringOrChar {
get {
return inside.HasFlag (Inside.StringOrChar);
}
}
bool IsInComment {
get {
return inside.HasFlag (Inside.Comment);
}
}
[Flags]
public enum Inside {
Empty = 0,
PreProcessor = (1 << 0),
MultiLineComment = (1 << 1),
LineComment = (1 << 2),
DocComment = (1 << 11),
Comment = (MultiLineComment | LineComment | DocComment),
VerbatimString = (1 << 3),
StringLiteral = (1 << 4),
CharLiteral = (1 << 5),
String = (VerbatimString | StringLiteral),
StringOrChar = (String | CharLiteral),
Attribute = (1 << 6),
ParenList = (1 << 7),
FoldedStatement = (1 << 8),
Block = (1 << 9),
Case = (1 << 10),
FoldedOrBlock = (FoldedStatement | Block),
FoldedBlockOrCase = (FoldedStatement | Block | Case)
}
char pc;
int parens = 0;
void Push(char ch)
{
if (inside.HasFlag (Inside.VerbatimString) && pc == '"' && ch != '"') {
inside &= ~Inside.String;
}
Console.WriteLine(ch);
switch (ch) {
case '#':
if (IsLineStart)
inside = Inside.PreProcessor;
break;
case '/':
if (IsInStringOrChar)
break;
if (pc == '/') {
if (inside.HasFlag (Inside.Comment)) {
inside |= Inside.DocComment;
} else {
inside |= Inside.Comment;
}
}
break;
case '*':
if (IsInStringOrChar || IsInComment)
break;
if (pc == '/')
inside |= Inside.MultiLineComment;
break;
case '\n':
case '\r':
inside &= ~(Inside.Comment | Inside.String | Inside.CharLiteral | Inside.PreProcessor);
CheckKeyword(wordBuf.ToString());
wordBuf.Length = 0;
if (addContinuation) {
indent.Push (IndentType.Continuation);
}
thisLineindent = indent.Clone ();
addContinuation = false;
IsLineStart = true;
break;
case '"':
if (IsInComment)
break;
if (inside.HasFlag (Inside.String)) {
if (pc != '\\')
inside &= ~Inside.String;
break;
}
if (pc =='@') {
inside |= Inside.VerbatimString;
} else {
inside |= Inside.String;
}
break;
case '<':
case '[':
case '(':
if (IsInComment || IsInStringOrChar)
break;
parens++;
indent.Push (IndentType.Block);
break;
case '>':
case ']':
case ')':
if (IsInComment || IsInStringOrChar)
break;
parens--;
indent.Pop ();
break;
case '{':
if (IsInComment || IsInStringOrChar)
break;
currentBody = nextBody;
if (indent.Count > 0 && indent.Peek() == IndentType.Continuation)
indent.Pop();
addContinuation = false;
AddIndentation (currentBody);
break;
case '}':
if (IsInComment || IsInStringOrChar)
break;
indent.Pop ();
if (indent.Count > 0 && indent.Peek() == IndentType.Continuation)
indent.Pop();
break;
case ';':
if (indent.Count > 0 && indent.Peek() == IndentType.Continuation)
indent.Pop();
break;
case '\'':
if (IsInComment || inside.HasFlag (Inside.String))
break;
if (inside.HasFlag (Inside.CharLiteral)) {
if (pc != '\\')
inside &= ~Inside.CharLiteral;
} else {
inside &= Inside.CharLiteral;
}
break;
}
if (!IsInComment && !IsInStringOrChar) {
if ((wordBuf.Length == 0 ? char.IsLetter(ch) : char.IsLetterOrDigit(ch)) || ch == '_') {
wordBuf.Append(ch);
} else {
CheckKeyword(wordBuf.ToString());
wordBuf.Length = 0;
}
}
if (addContinuation) {
indent.Push (IndentType.Continuation);
addContinuation = false;
}
IsLineStart &= ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
pc = ch;
}
void AddIndentation(BraceStyle braceStyle)
{
switch (braceStyle) {
case BraceStyle.DoNotChange:
case BraceStyle.EndOfLine:
case BraceStyle.EndOfLineWithoutSpace:
case BraceStyle.NextLine:
case BraceStyle.NextLineShifted:
case BraceStyle.BannerStyle:
indent.Push (IndentType.Block);
break;
case BraceStyle.NextLineShifted2:
indent.Push (IndentType.DoubleBlock);
break;
}
}
void AddIndentation(Body body)
{
switch (body) {
case Body.None:
indent.Push (IndentType.Block);
break;
case Body.Namespace:
AddIndentation (options.NamespaceBraceStyle);
break;
case Body.Class:
AddIndentation (options.ClassBraceStyle);
break;
case Body.Struct:
AddIndentation (options.StructBraceStyle);
break;
case Body.Interface:
AddIndentation (options.InterfaceBraceStyle);
break;
case Body.Enum:
AddIndentation (options.EnumBraceStyle);
break;
case Body.Switch:
if (options.IndentSwitchBody)
indent.Push (IndentType.Empty);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
enum Body
{
None,
Namespace,
Class, Struct, Interface, Enum,
Switch
}
Body currentBody;
Body nextBody;
bool addContinuation;
void CheckKeyword (string keyword)
{
switch (currentBody) {
case Body.None:
if (keyword == "namespace") {
nextBody = Body.Namespace;
return;
}
goto case Body.Namespace;
case Body.Namespace:
if (keyword == "class") {
nextBody = Body.Class;
return;
}
if (keyword == "enum") {
nextBody = Body.Enum;
return;
}
if (keyword == "struct") {
nextBody = Body.Struct;
return;
}
if (keyword == "interface") {
nextBody = Body.Interface;
return;
}
break;
case Body.Class:
case Body.Enum:
case Body.Struct:
case Body.Interface:
if (keyword == "switch")
nextBody = Body.Switch;
if (keyword == "do" || keyword == "if" || keyword == "for" || keyword == "foreach" || keyword == "while") {
addContinuation = true;
}
break;
}
}
}
}