📄 csharpformattingstrategy.cs
字号:
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
// <version value="$version"/>
// </file>
using System;
using System.IO;
using System.Collections;
using System.Diagnostics;
using System.Drawing;
using System.Text;
using SharpDevelop.Internal.Parser;
using ICSharpCode.TextEditor;
using ICSharpCode.TextEditor.Actions;
using ICSharpCode.TextEditor.Document;
using ICSharpCode.Core.Properties;
using ICSharpCode.Core.Services;
using ICSharpCode.SharpDevelop.Services;
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);
//Console.WriteLine("Indent line #{0}", lineNr + 1);
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);
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)
{
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 (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;
}
IParserService parserService = (IParserService)ICSharpCode.Core.Services.ServiceManager.Services.GetService(typeof(IParserService));
bool IsBeforeRegion(TextArea textArea, IRegion region, int lineNr)
{
if (region == null) {
return false;
}
return region.BeginLine - 2 <= lineNr && lineNr <= region.BeginLine;
}
object GetClassMember(TextArea textArea, int lineNr, IClass c)
{
if (IsBeforeRegion(textArea, c.Region, lineNr)) {
return c;
}
foreach (IClass inner in c.InnerClasses) {
object o = GetClassMember(textArea, lineNr, inner);
if (o != null) {
return o;
}
}
foreach (IField f in c.Fields) {
if (IsBeforeRegion(textArea, f.Region, lineNr)) {
return f;
}
}
foreach (IProperty p in c.Properties) {
if (IsBeforeRegion(textArea, p.Region, lineNr)) {
return p;
}
}
foreach (IIndexer i in c.Indexer) {
if (IsBeforeRegion(textArea, i.Region, lineNr)) {
return i;
}
}
foreach (IEvent e in c.Events) {
if (IsBeforeRegion(textArea, e.Region, lineNr)) {
return e;
}
}
foreach (IMethod m in c.Methods) {
if (IsBeforeRegion(textArea, m.Region, lineNr)) {
return m;
}
}
return null;
}
object GetMember(TextArea textArea, int lineNr)
{
string fileName = textArea.MotherTextEditorControl.FileName;
if (fileName != null && fileName.Length > 0 ) {
string fullPath = Path.GetFullPath(fileName);
IParseInformation parseInfo = parserService.GetParseInformation(fullPath);
if (parseInfo != null) {
ICompilationUnit currentCompilationUnit = (ICompilationUnit)parseInfo.BestCompilationUnit;
if (currentCompilationUnit != null) {
foreach (IClass c in currentCompilationUnit.Classes) {
object o = GetClassMember(textArea, lineNr, c);
if (o != null) {
return o;
}
}
}
}
}
return null;
}
#endregion
#region FormatLine
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 = GetMember(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.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, String.Concat("</", tag.Substring(1)));
}
}
}
break;
case ':':
case ')':
case ']':
case '}':
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -