/// <summary> /// Define CSharp specific smart indenting for a line :) /// </summary> protected override int SmartIndentLine(TextArea textArea, int lineNr) { if (lineNr <= 0) return AutoIndentLine(textArea, lineNr); string oldText = textArea.Document.GetText(textArea.Document.GetLineSegment(lineNr)); DocumentAccessor acc = new DocumentAccessor(textArea.Document, lineNr, lineNr); IndentationSettings set = new IndentationSettings(); set.IndentString = Tab.GetIndentationString(textArea.Document); set.LeaveEmptyLines = false; CSharpIndentationReformatter r = new CSharpIndentationReformatter(); r.Reformat(acc, set); string t = acc.Text; if (t.Length == 0) { // use AutoIndentation for new lines in comments / verbatim strings. return AutoIndentLine(textArea, lineNr); } else { int newIndentLength = t.Length - t.TrimStart().Length; int oldIndentLength = oldText.Length - oldText.TrimStart().Length; if (oldIndentLength != newIndentLength && lineNr == textArea.Caret.Position.Y) { // fix cursor position if indentation was changed int newX = textArea.Caret.Position.X - oldIndentLength + newIndentLength; textArea.Caret.Position = new TextLocation(Math.Max(newX, 0), lineNr); } return newIndentLength; } }
/// <summary> /// This function sets the indentation level in a range of lines. /// </summary> /// <param name="textArea">The text area.</param> /// <param name="begin">The begin.</param> /// <param name="end">The end.</param> public override void IndentLines(TextArea textArea, int begin, int end) { if (textArea.Document.TextEditorProperties.IndentStyle != IndentStyle.Smart) { base.IndentLines(textArea, begin, end); return; } int cursorPos = textArea.Caret.Position.Y; int oldIndentLength = 0; if (cursorPos >= begin && cursorPos <= end) oldIndentLength = GetIndentation(textArea, cursorPos).Length; IndentationSettings set = new IndentationSettings(); set.IndentString = Tab.GetIndentationString(textArea.Document); CSharpIndentationReformatter r = new CSharpIndentationReformatter(); DocumentAccessor acc = new DocumentAccessor(textArea.Document, begin, end); r.Reformat(acc, set); if (cursorPos >= begin && cursorPos <= end) { int newIndentLength = GetIndentation(textArea, cursorPos).Length; if (oldIndentLength != newIndentLength) { // fix cursor position if indentation was changed int newX = textArea.Caret.Position.X - oldIndentLength + newIndentLength; textArea.Caret.Position = new TextLocation(Math.Max(newX, 0), cursorPos); } } }
/// <summary> /// This function sets the indentation level in a range of lines. /// </summary> /// <param name="textArea">The text area.</param> /// <param name="begin">The begin.</param> /// <param name="end">The end.</param> public override void IndentLines(TextArea textArea, int begin, int end) { if (textArea.Document.TextEditorProperties.IndentStyle != IndentStyle.Smart) { base.IndentLines(textArea, begin, end); return; } int cursorPos = textArea.Caret.Position.Y; int oldIndentLength = 0; if (cursorPos >= begin && cursorPos <= end) { oldIndentLength = GetIndentation(textArea, cursorPos).Length; } IndentationSettings set = new IndentationSettings(); set.IndentString = Tab.GetIndentationString(textArea.Document); CSharpIndentationReformatter r = new CSharpIndentationReformatter(); DocumentAccessor acc = new DocumentAccessor(textArea.Document, begin, end); r.Reformat(acc, set); if (cursorPos >= begin && cursorPos <= end) { int newIndentLength = GetIndentation(textArea, cursorPos).Length; if (oldIndentLength != newIndentLength) { // fix cursor position if indentation was changed int newX = textArea.Caret.Position.X - oldIndentLength + newIndentLength; textArea.Caret.Position = new TextLocation(Math.Max(newX, 0), cursorPos); } } }
public void Indent(IndentationSettings set, string str) { OuterIndent = InnerIndent; InnerIndent += str; Continuation = false; OneLineBlock = false; LastWord = ""; }
public void Reformat(IDocumentAccessor document, IndentationSettings settings) { Init(); while (document.Next()) { Step(document, settings); } }
char lastRealChar = ' '; // last non-comment char public void Reformat(IDocumentAccessor doc, IndentationSettings set) { Init(); while (doc.Next()) { Step(doc, set); } }
/// <summary> /// Indents the specified line. /// </summary> protected override int SmartIndentLine(TextArea textArea, int lineNr) { if (lineNr <= 0) { return(AutoIndentLine(textArea, lineNr)); } string oldText = textArea.Document.GetText(textArea.Document.GetLineSegment(lineNr)); DocumentAccessor acc = new DocumentAccessor(textArea.Document, lineNr, lineNr); IndentationSettings set = new IndentationSettings(); set.IndentString = Tab.GetIndentationString(textArea.Document); set.LeaveEmptyLines = false; CSharpIndentationReformatter r = new CSharpIndentationReformatter(); r.Reformat(acc, set); string t = acc.Text; if (t.Length == 0) { // use AutoIndentation for new lines in comments / verbatim strings. return(AutoIndentLine(textArea, lineNr)); } else { int newIndentLength = t.Length - t.TrimStart().Length; int oldIndentLength = oldText.Length - oldText.TrimStart().Length; if (oldIndentLength != newIndentLength && lineNr == textArea.Caret.Position.Y) { // fix cursor position if indentation was changed int newX = textArea.Caret.Position.X - oldIndentLength + newIndentLength; textArea.Caret.Position = new TextLocation(Math.Max(newX, 0), lineNr); } return(newIndentLength); } }
public void Reformat(IDocumentAccessor document, IndentationSettings settings) { Init(); while (document.Next()) Step(document, settings); }
public void Indent(IndentationSettings set) { Indent(set, set.IndentString); }
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); 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 c = ' '; char nextchar = line[0]; for (int i = 0; i < line.Length; i++) { if (_lineComment) break; // cancel parsing current line char lastchar = c; c = nextchar; nextchar = (i + 1 < line.Length) ? line[i + 1] : '\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.OneLineBlock = false; _blocks.Push(_block); _block.StartLine = doc.LineNumber; if (_block.LastWord == "switch") { _block.Indent(set, 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.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 : "") + (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.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") || doc.Text == "//") return; if (line[0] == '}') { indent.Append(oldBlock.OuterIndent); 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 (_lastRealChar == 'e' && _block.LastWord == "else") { _block.OneLineBlock = true; _block.Continuation = false; } if (doc.ReadOnly) { // 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 && 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); 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(' '); if (indent.Length != (doc.Text.Length - line.Length) || !doc.Text.StartsWith(indent.ToString()) || Char.IsWhiteSpace(doc.Text[indent.Length])) { doc.Text = indent + line; } }
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); 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 c = ' '; char nextchar = line[0]; for (int i = 0; i < line.Length; i++) { if (_lineComment) { break; // cancel parsing current line } char lastchar = c; c = nextchar; nextchar = (i + 1 < line.Length) ? line[i + 1] : '\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.OneLineBlock = false; _blocks.Push(_block); _block.StartLine = doc.LineNumber; if (_block.LastWord == "switch") { _block.Indent(set, 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.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 : "") + (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.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") || doc.Text == "//") { return; } if (line[0] == '}') { indent.Append(oldBlock.OuterIndent); 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 (_lastRealChar == 'e' && _block.LastWord == "else") { _block.OneLineBlock = true; _block.Continuation = false; } if (doc.ReadOnly) { // 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 && 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); } 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(' '); } if (indent.Length != (doc.Text.Length - line.Length) || !doc.Text.StartsWith(indent.ToString()) || Char.IsWhiteSpace(doc.Text[indent.Length])) { doc.Text = indent + line; } }