?? csharpformattingstrategy.cs
字號:
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
// <version>$Revision: 1430 $</version>
// </file>
using System;
using System.IO;
using System.Collections;
using System.Diagnostics;
using System.Drawing;
using System.Text;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.TextEditor;
using ICSharpCode.TextEditor.Actions;
using ICSharpCode.TextEditor.Document;
using ICSharpCode.Core;
namespace CSharpBinding.FormattingStrategy
{
/// <summary>
/// This class handles the auto and smart indenting in the textbuffer while
/// you type.
/// </summary>
public class CSharpFormattingStrategy : DefaultFormattingStrategy
{
public CSharpFormattingStrategy()
{
}
#region SmartIndentLine
/// <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;
IndentationReformatter r = new IndentationReformatter();
r.Reformat(acc, set);
if (acc.ChangedLines > 0)
textArea.Document.UndoStack.UndoLast(2);
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 Point(Math.Max(newX, 0), lineNr);
}
return newIndentLength;
}
}
/// <summary>
/// This function sets the indentlevel in a range of lines.
/// </summary>
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);
IndentationReformatter r = new IndentationReformatter();
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 Point(Math.Max(newX, 0), cursorPos);
}
}
if (acc.ChangedLines > 0)
textArea.Document.UndoStack.UndoLast(acc.ChangedLines);
}
#endregion
#region Private functions
bool NeedCurlyBracket(string text)
{
int curlyCounter = 0;
bool inString = false;
bool inChar = false;
bool verbatim = false;
bool lineComment = false;
bool blockComment = false;
for (int i = 0; i < text.Length; ++i) {
switch (text[i]) {
case '\r':
case '\n':
lineComment = false;
inChar = false;
if (!verbatim) inString = false;
break;
case '/':
if (blockComment) {
Debug.Assert(i > 0);
if (text[i - 1] == '*') {
blockComment = false;
}
}
if (!inString && !inChar && i + 1 < text.Length) {
if (!blockComment && text[i + 1] == '/') {
lineComment = true;
}
if (!lineComment && text[i + 1] == '*') {
blockComment = true;
}
}
break;
case '"':
if (!(inChar || lineComment || blockComment)) {
if (inString && verbatim) {
if (i + 1 < text.Length && text[i + 1] == '"') {
++i; // skip escaped quote
inString = false; // let the string go on
} else {
verbatim = false;
}
} else if (!inString && i > 0 && text[i - 1] == '@') {
verbatim = true;
}
inString = !inString;
}
break;
case '\'':
if (!(inString || lineComment || blockComment)) {
inChar = !inChar;
}
break;
case '{':
if (!(inString || inChar || lineComment || blockComment)) {
++curlyCounter;
}
break;
case '}':
if (!(inString || inChar || lineComment || blockComment)) {
--curlyCounter;
}
break;
case '\\':
if ((inString && !verbatim) || inChar)
++i; // skip next character
break;
}
}
return curlyCounter > 0;
}
bool IsInsideStringOrComment(TextArea textArea, LineSegment curLine, int cursorOffset)
{
// scan cur line if it is inside a string or single line comment (//)
bool insideString = false;
char stringstart = ' ';
bool verbatim = false; // true if the current string is verbatim (@-string)
char c = ' ';
char lastchar;
for (int i = curLine.Offset; i < cursorOffset; ++i) {
lastchar = c;
c = textArea.Document.GetCharAt(i);
if (insideString) {
if (c == stringstart) {
if (verbatim && i + 1 < cursorOffset && textArea.Document.GetCharAt(i + 1) == '"') {
++i; // skip escaped character
} else {
insideString = false;
}
} else if (c == '\\' && !verbatim) {
++i; // skip escaped character
}
} else if (c == '/' && i + 1 < cursorOffset && textArea.Document.GetCharAt(i + 1) == '/') {
return true;
} else if (c == '"' || c == '\'') {
stringstart = c;
insideString = true;
verbatim = (c == '"') && (lastchar == '@');
}
}
return insideString;
}
bool IsInsideDocumentationComment(TextArea textArea, LineSegment curLine, int cursorOffset)
{
for (int i = curLine.Offset; i < cursorOffset; ++i) {
char ch = textArea.Document.GetCharAt(i);
if (ch == '"') {
// parsing strings correctly is too complicated (see above),
// but I don't now any case where a doc comment is after a string...
return false;
}
if (ch == '/' && i + 2 < cursorOffset && textArea.Document.GetCharAt(i + 1) == '/' && textArea.Document.GetCharAt(i + 2) == '/') {
return true;
}
}
return false;
}
/// <summary>
/// Gets the next member after the specified caret position.
/// </summary>
object GetMemberAfter(TextArea textArea, int caretLine)
{
string fileName = textArea.MotherTextEditorControl.FileName;
object nextElement = null;
if (fileName != null && fileName.Length > 0 ) {
ParseInformation parseInfo = ParserService.ParseFile(fileName, textArea.Document.TextContent);
if (parseInfo != null) {
ICompilationUnit currentCompilationUnit = parseInfo.BestCompilationUnit;
if (currentCompilationUnit != null) {
IClass currentClass = currentCompilationUnit.GetInnermostClass(caretLine, 0);
int nextElementLine = int.MaxValue;
if (currentClass == null) {
foreach (IClass c in currentCompilationUnit.Classes) {
if (c.Region.BeginLine < nextElementLine && c.Region.BeginLine > caretLine) {
nextElementLine = c.Region.BeginLine;
nextElement = c;
}
}
} else {
foreach (IClass c in currentClass.InnerClasses) {
if (c.Region.BeginLine < nextElementLine && c.Region.BeginLine > caretLine) {
nextElementLine = c.Region.BeginLine;
nextElement = c;
}
}
foreach (IMember m in currentClass.Methods) {
if (m.Region.BeginLine < nextElementLine && m.Region.BeginLine > caretLine) {
nextElementLine = m.Region.BeginLine;
nextElement = m;
}
}
foreach (IMember m in currentClass.Properties) {
if (m.Region.BeginLine < nextElementLine && m.Region.BeginLine > caretLine) {
nextElementLine = m.Region.BeginLine;
nextElement = m;
}
}
foreach (IMember m in currentClass.Fields) {
if (m.Region.BeginLine < nextElementLine && m.Region.BeginLine > caretLine) {
nextElementLine = m.Region.BeginLine;
nextElement = m;
}
}
foreach (IMember m in currentClass.Events) {
if (m.Region.BeginLine < nextElementLine && m.Region.BeginLine > caretLine) {
nextElementLine = m.Region.BeginLine;
nextElement = m;
}
}
}
}
}
}
return nextElement;
}
#endregion
#region FormatLine
bool NeedEndregion(IDocument document)
{
int regions = 0;
int endregions = 0;
foreach (LineSegment line in document.LineSegmentCollection) {
string text = document.GetText(line).Trim();
if (text.StartsWith("#region")) {
++regions;
} else if (text.StartsWith("#endregion")) {
++endregions;
}
}
return regions > endregions;
}
public override int FormatLine(TextArea textArea, int lineNr, int cursorOffset, char ch) // used for comment tag formater/inserter
{
LineSegment curLine = textArea.Document.GetLineSegment(lineNr);
LineSegment lineAbove = lineNr > 0 ? textArea.Document.GetLineSegment(lineNr - 1) : null;
//// local string for curLine segment
string curLineText="";
if (ch == '/') {
curLineText = textArea.Document.GetText(curLine);
string lineAboveText = lineAbove == null ? "" : textArea.Document.GetText(lineAbove);
if (curLineText != null && curLineText.EndsWith("///") && (lineAboveText == null || !lineAboveText.Trim().StartsWith("///"))) {
string indentation = base.GetIndentation(textArea, lineNr);
object member = GetMemberAfter(textArea, lineNr);
if (member != null) {
StringBuilder sb = new StringBuilder();
sb.Append(" <summary>\n");
sb.Append(indentation);
sb.Append("/// \n");
sb.Append(indentation);
sb.Append("/// </summary>");
if (member is IMethod) {
IMethod method = (IMethod)member;
if (method.Parameters != null && method.Parameters.Count > 0) {
for (int i = 0; i < method.Parameters.Count; ++i) {
sb.Append("\n");
sb.Append(indentation);
sb.Append("/// <param name=\"");
sb.Append(method.Parameters[i].Name);
sb.Append("\"></param>");
}
}
if (method.ReturnType != null && !method.IsConstructor && method.ReturnType.FullyQualifiedName != "System.Void") {
sb.Append("\n");
sb.Append(indentation);
sb.Append("/// <returns></returns>");
}
}
textArea.Document.Insert(cursorOffset, sb.ToString());
textArea.Refresh();
textArea.Caret.Position = textArea.Document.OffsetToPosition(cursorOffset + indentation.Length + "/// ".Length + " <summary>\n".Length);
return 0;
}
}
return 0;
}
if (ch != '\n' && ch != '>') {
if (IsInsideStringOrComment(textArea, curLine, cursorOffset)) {
return 0;
}
}
switch (ch) {
case '>':
if (IsInsideDocumentationComment(textArea, curLine, cursorOffset)) {
curLineText = textArea.Document.GetText(curLine);
int column = textArea.Caret.Offset - curLine.Offset;
int index = Math.Min(column - 1, curLineText.Length - 1);
while (index >= 0 && curLineText[index] != '<') {
--index;
if(curLineText[index] == '/')
return 0; // the tag was an end tag or already
}
if (index > 0) {
StringBuilder commentBuilder = new StringBuilder("");
for (int i = index; i < curLineText.Length && i < column && !Char.IsWhiteSpace(curLineText[i]); ++i) {
commentBuilder.Append(curLineText[ i]);
}
string tag = commentBuilder.ToString().Trim();
if (!tag.EndsWith(">")) {
tag += ">";
}
if (!tag.StartsWith("/")) {
textArea.Document.Insert(textArea.Caret.Offset, "</" + tag.Substring(1));
}
}
}
break;
case ':':
case ')':
case ']':
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -