aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation
diff options
context:
space:
mode:
Diffstat (limited to 'Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation')
-rw-r--r--Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation/CSharp/CSharpIndentationHelper.cs124
-rw-r--r--Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation/CSharp/CSharpIndentationStrategy.cs96
-rw-r--r--Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation/CSharp/DocumentAccessor.cs107
-rw-r--r--Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation/CSharp/IndentationReformatter.cs474
-rw-r--r--Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation/DefaultIndentationStrategy.cs40
-rw-r--r--Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation/IIndentationStrategy.cs25
6 files changed, 866 insertions, 0 deletions
diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation/CSharp/CSharpIndentationHelper.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation/CSharp/CSharpIndentationHelper.cs
new file mode 100644
index 000000000..36aeb213b
--- /dev/null
+++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation/CSharp/CSharpIndentationHelper.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.Scripting.Editors.Indentation.CSharp
+{
+ class CSharpIndentationHelper
+ {
+ public static string IndentCSharpCode(string code)
+ {
+ const string INDENT_STEP = "\t";
+
+ if (string.IsNullOrWhiteSpace(code))
+ {
+ return code;
+ }
+
+ var result = new StringBuilder();
+ var indent = string.Empty;
+ var lineContent = false;
+ var stringDefinition = false;
+
+ for (var i = 0; i < code.Length; i++)
+ {
+ var ch = code[i];
+
+ if (ch == '"' && !stringDefinition)
+ {
+ result.Append(ch);
+ stringDefinition = true;
+ continue;
+ }
+
+ if (ch == '"' && stringDefinition)
+ {
+ result.Append(ch);
+ stringDefinition = false;
+ continue;
+ }
+
+ if (stringDefinition)
+ {
+ result.Append(ch);
+ continue;
+ }
+
+ if (ch == '{' && !stringDefinition)
+ {
+ if (lineContent)
+ {
+ result.AppendLine();
+ }
+
+ result.Append(indent).Append("{");
+
+ if (lineContent)
+ {
+ result.AppendLine();
+ }
+
+ indent += INDENT_STEP;
+ lineContent = false;
+
+ continue;
+ }
+
+ if (ch == '}' && !stringDefinition)
+ {
+ if (indent.Length != 0)
+ {
+ indent = indent.Substring(0, indent.Length - INDENT_STEP.Length);
+ }
+
+ if (lineContent)
+ {
+ result.AppendLine();
+ }
+
+ result.Append(indent).Append("}");
+
+ if (lineContent)
+ {
+ result.AppendLine();
+ }
+
+
+ lineContent = false;
+
+ continue;
+ }
+
+ if (ch == '\r')
+ {
+ continue;
+ }
+
+ if ((ch == ' ' || ch == '\t') && !lineContent)
+ {
+ continue;
+ }
+
+ if (ch == '\n')
+ {
+ lineContent = false;
+ result.AppendLine();
+
+ continue;
+ }
+
+ if (!lineContent)
+ {
+ result.Append(indent);
+ lineContent = true;
+ }
+
+ result.Append(ch);
+ }
+
+ return result.ToString();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation/CSharp/CSharpIndentationStrategy.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation/CSharp/CSharpIndentationStrategy.cs
new file mode 100644
index 000000000..7b47099fe
--- /dev/null
+++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation/CSharp/CSharpIndentationStrategy.cs
@@ -0,0 +1,96 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Indentation.CSharp
+{
+ /// <summary>
+ /// Smart indentation for C#.
+ /// </summary>
+ public class CSharpIndentationStrategy : DefaultIndentationStrategy
+ {
+ /// <summary>
+ /// Creates a new CSharpIndentationStrategy.
+ /// </summary>
+ public CSharpIndentationStrategy()
+ {
+ }
+
+ /// <summary>
+ /// Creates a new CSharpIndentationStrategy and initializes the settings using the text editor options.
+ /// </summary>
+ public CSharpIndentationStrategy(TextEditorOptions options)
+ {
+ this.IndentationString = options.IndentationString;
+ }
+
+ string indentationString = "\t";
+
+ /// <summary>
+ /// Gets/Sets the indentation string.
+ /// </summary>
+ public string IndentationString
+ {
+ get { return indentationString; }
+ set
+ {
+ if (string.IsNullOrEmpty(value))
+ throw new ArgumentException("Indentation string must not be null or empty");
+ indentationString = value;
+ }
+ }
+
+ /// <summary>
+ /// Performs indentation using the specified document accessor.
+ /// </summary>
+ /// <param name="document">Object used for accessing the document line-by-line</param>
+ /// <param name="keepEmptyLines">Specifies whether empty lines should be kept</param>
+ public void Indent(IDocumentAccessor document, bool keepEmptyLines)
+ {
+ if (document == null)
+ throw new ArgumentNullException("document");
+ IndentationSettings settings = new IndentationSettings();
+ settings.IndentString = this.IndentationString;
+ settings.LeaveEmptyLines = keepEmptyLines;
+
+ IndentationReformatter r = new IndentationReformatter();
+ r.Reformat(document, settings);
+ }
+
+ /// <inheritdoc cref="IIndentationStrategy.IndentLine"/>
+ public override void IndentLine(TextDocument document, DocumentLine line)
+ {
+ bool keepEmptyLines = true;
+
+ if (line.PreviousLine != null && line.NextLine != null)
+ {
+ string previous_text = document.GetText(line.PreviousLine.Offset, line.PreviousLine.Length);
+ string next_text = document.GetText(line.NextLine.Offset, line.NextLine.Length);
+
+ if (previous_text.Contains("{") && next_text.Contains("}") && previous_text.Length == next_text.Length)
+ {
+ keepEmptyLines = false;
+ }
+ }
+
+ int lineNr = line.LineNumber;
+ TextDocumentAccessor acc = new TextDocumentAccessor(document, lineNr, lineNr);
+ Indent(acc, keepEmptyLines);
+
+ string t = acc.Text;
+ if (t.Length == 0)
+ {
+ // use AutoIndentation for new lines in comments / verbatim strings.
+ base.IndentLine(document, line);
+ }
+ }
+
+ /// <inheritdoc cref="IIndentationStrategy.IndentLines"/>
+ public override void IndentLines(TextDocument document, int beginLine, int endLine)
+ {
+ Indent(new TextDocumentAccessor(document, beginLine, endLine), true);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation/CSharp/DocumentAccessor.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation/CSharp/DocumentAccessor.cs
new file mode 100644
index 000000000..dbf6b21a9
--- /dev/null
+++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation/CSharp/DocumentAccessor.cs
@@ -0,0 +1,107 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Indentation.CSharp
+{
+ /// <summary>
+ /// Interface used for the indentation class to access the document.
+ /// </summary>
+ public interface IDocumentAccessor
+ {
+ /// <summary>Gets if the current line is read only (because it is not in the
+ /// selected text region)</summary>
+ bool IsReadOnly { get; }
+ /// <summary>Gets the number of the current line.</summary>
+ int LineNumber { get; }
+ /// <summary>Gets/Sets the text of the current line.</summary>
+ string Text { get; set; }
+ /// <summary>Advances to the next line.</summary>
+ bool MoveNext();
+ }
+
+ #region TextDocumentAccessor
+ /// <summary>
+ /// Adapter IDocumentAccessor -> TextDocument
+ /// </summary>
+ public sealed class TextDocumentAccessor : IDocumentAccessor
+ {
+ readonly TextDocument doc;
+ readonly int minLine;
+ readonly int maxLine;
+
+ /// <summary>
+ /// Creates a new TextDocumentAccessor.
+ /// </summary>
+ public TextDocumentAccessor(TextDocument document)
+ {
+ if (document == null)
+ throw new ArgumentNullException("document");
+ doc = document;
+ this.minLine = 1;
+ this.maxLine = doc.LineCount;
+ }
+
+ /// <summary>
+ /// Creates a new TextDocumentAccessor that indents only a part of the document.
+ /// </summary>
+ public TextDocumentAccessor(TextDocument document, int minLine, int maxLine)
+ {
+ if (document == null)
+ throw new ArgumentNullException("document");
+ doc = document;
+ this.minLine = minLine;
+ this.maxLine = maxLine;
+ }
+
+ int num;
+ string text;
+ DocumentLine line;
+
+ /// <inheritdoc/>
+ public bool IsReadOnly {
+ get {
+ return num < minLine;
+ }
+ }
+
+ /// <inheritdoc/>
+ public int LineNumber {
+ get {
+ return num;
+ }
+ }
+
+ bool lineDirty;
+
+ /// <inheritdoc/>
+ public string Text {
+ get { return text; }
+ set {
+ if (num < minLine) return;
+ text = value;
+ lineDirty = true;
+ }
+ }
+
+ /// <inheritdoc/>
+ public bool MoveNext()
+ {
+ if (lineDirty) {
+ doc.Replace(line, text);
+ lineDirty = false;
+ }
+ ++num;
+ if (num > maxLine) return false;
+ line = doc.GetLineByNumber(num);
+ text = doc.GetText(line);
+ return true;
+ }
+ }
+ #endregion
+}
diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation/CSharp/IndentationReformatter.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation/CSharp/IndentationReformatter.cs
new file mode 100644
index 000000000..09e20c909
--- /dev/null
+++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation/CSharp/IndentationReformatter.cs
@@ -0,0 +1,474 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+
+namespace Tango.Scripting.Editors.Indentation.CSharp
+{
+ sealed class IndentationSettings
+ {
+ public string IndentString = "\t";
+ /// <summary>Leave empty lines empty.</summary>
+ public bool LeaveEmptyLines = true;
+ }
+
+ sealed class IndentationReformatter
+ {
+ /// <summary>
+ /// An indentation block. Tracks the state of the indentation.
+ /// </summary>
+ struct Block
+ {
+ /// <summary>
+ /// The indentation outside of the block.
+ /// </summary>
+ public string OuterIndent;
+
+ /// <summary>
+ /// The indentation inside the block.
+ /// </summary>
+ public string InnerIndent;
+
+ /// <summary>
+ /// The last word that was seen inside this block.
+ /// Because parenthesis open a sub-block and thus don't change their parent's LastWord,
+ /// this property can be used to identify the type of block statement (if, while, switch)
+ /// at the position of the '{'.
+ /// </summary>
+ public string LastWord;
+
+ /// <summary>
+ /// The type of bracket that opened this block (, [ or {
+ /// </summary>
+ public char Bracket;
+
+ /// <summary>
+ /// Gets whether there's currently a line continuation going on inside this block.
+ /// </summary>
+ public bool Continuation;
+
+ /// <summary>
+ /// Gets whether there's currently a 'one-line-block' going on. 'one-line-blocks' occur
+ /// with if statements that don't use '{}'. They are not represented by a Block instance on
+ /// the stack, but are instead handled similar to line continuations.
+ /// This property is an integer because there might be multiple nested one-line-blocks.
+ /// As soon as there is a finished statement, OneLineBlock is reset to 0.
+ /// </summary>
+ public int OneLineBlock;
+
+ /// <summary>
+ /// The previous value of one-line-block before it was reset.
+ /// Used to restore the indentation of 'else' to the correct level.
+ /// </summary>
+ public int PreviousOneLineBlock;
+
+ public void ResetOneLineBlock()
+ {
+ PreviousOneLineBlock = OneLineBlock;
+ OneLineBlock = 0;
+ }
+
+ /// <summary>
+ /// Gets the line number where this block started.
+ /// </summary>
+ public int StartLine;
+
+ public void Indent(IndentationSettings set)
+ {
+ Indent(set.IndentString);
+ }
+
+ public void Indent(string indentationString)
+ {
+ OuterIndent = InnerIndent;
+ InnerIndent += indentationString;
+ Continuation = false;
+ ResetOneLineBlock();
+ LastWord = "";
+ }
+
+ public override string ToString()
+ {
+ return string.Format(
+ CultureInfo.InvariantCulture,
+ "[Block StartLine={0}, LastWord='{1}', Continuation={2}, OneLineBlock={3}, PreviousOneLineBlock={4}]",
+ this.StartLine, this.LastWord, this.Continuation, this.OneLineBlock, this.PreviousOneLineBlock);
+ }
+ }
+
+ StringBuilder wordBuilder;
+ Stack<Block> blocks; // blocks contains all blocks outside of the current
+ Block block; // block is the current block
+
+ bool inString;
+ bool inChar;
+ bool verbatim;
+ bool escape;
+
+ bool lineComment;
+ bool blockComment;
+
+ char lastRealChar; // last non-comment char
+
+ public void Reformat(IDocumentAccessor doc, IndentationSettings set)
+ {
+ Init();
+
+ while (doc.MoveNext()) {
+ Step(doc, set);
+ }
+ }
+
+ public void Init()
+ {
+ wordBuilder = new StringBuilder();
+ blocks = new Stack<Block>();
+ block = new Block();
+ block.InnerIndent = "";
+ block.OuterIndent = "";
+ block.Bracket = '{';
+ block.Continuation = false;
+ block.LastWord = "";
+ block.OneLineBlock = 0;
+ block.PreviousOneLineBlock = 0;
+ 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;
+ if (set.LeaveEmptyLines && line.Length == 0) return; // leave empty lines empty
+ line = line.TrimStart();
+
+ StringBuilder 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();
+
+ 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
+ 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 (int 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.ToString() + line;
+ }
+ }
+
+ static string Repeat(string text, int count)
+ {
+ if (count == 0)
+ return string.Empty;
+ if (count == 1)
+ return text;
+ StringBuilder b = new StringBuilder(text.Length * count);
+ for (int i = 0; i < count; i++)
+ b.Append(text);
+ return b.ToString();
+ }
+
+ static bool IsSingleStatementKeyword(string keyword)
+ {
+ switch (keyword) {
+ case "if":
+ case "for":
+ case "while":
+ case "do":
+ case "foreach":
+ case "using":
+ case "lock":
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ static 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("// ", StringComparison.Ordinal) || line.EndsWith("* ", StringComparison.Ordinal))
+ return false;
+
+ doc.Text = line.TrimEnd();
+ return true;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation/DefaultIndentationStrategy.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation/DefaultIndentationStrategy.cs
new file mode 100644
index 000000000..b5a1f0c2c
--- /dev/null
+++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation/DefaultIndentationStrategy.cs
@@ -0,0 +1,40 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using Tango.Scripting.Editors.Utils;
+using System;
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Indentation
+{
+ /// <summary>
+ /// Handles indentation by copying the indentation from the previous line.
+ /// Does not support indenting multiple lines.
+ /// </summary>
+ public class DefaultIndentationStrategy : IIndentationStrategy
+ {
+ /// <inheritdoc/>
+ public virtual void IndentLine(TextDocument document, DocumentLine line)
+ {
+ if (document == null)
+ throw new ArgumentNullException("document");
+ if (line == null)
+ throw new ArgumentNullException("line");
+ DocumentLine previousLine = line.PreviousLine;
+ if (previousLine != null) {
+ ISegment indentationSegment = TextUtilities.GetWhitespaceAfter(document, previousLine.Offset);
+ string indentation = document.GetText(indentationSegment);
+ // copy indentation to line
+ indentationSegment = TextUtilities.GetWhitespaceAfter(document, line.Offset);
+ document.Replace(indentationSegment, indentation);
+ }
+ }
+
+ /// <summary>
+ /// Does nothing: indenting multiple lines is useless without a smart indentation strategy.
+ /// </summary>
+ public virtual void IndentLines(TextDocument document, int beginLine, int endLine)
+ {
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation/IIndentationStrategy.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation/IIndentationStrategy.cs
new file mode 100644
index 000000000..0697fb642
--- /dev/null
+++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Indentation/IIndentationStrategy.cs
@@ -0,0 +1,25 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using Tango.Scripting.Editors.Document;
+using System;
+
+namespace Tango.Scripting.Editors.Indentation
+{
+ /// <summary>
+ /// Strategy how the text editor handles indentation when new lines are inserted.
+ /// </summary>
+ public interface IIndentationStrategy
+ {
+ /// <summary>
+ /// Sets the indentation for the specified line.
+ /// Usually this is constructed from the indentation of the previous line.
+ /// </summary>
+ void IndentLine(TextDocument document, DocumentLine line);
+
+ /// <summary>
+ /// Reindents a set of lines.
+ /// </summary>
+ void IndentLines(TextDocument document, int beginLine, int endLine);
+ }
+}