first commit
This commit is contained in:
313
ICSharpCode.TextEditor/Project/Src/Document/TextUtilities.cs
Normal file
313
ICSharpCode.TextEditor/Project/Src/Document/TextUtilities.cs
Normal file
@@ -0,0 +1,313 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt"/>
|
||||
// <license see="prj:///doc/license.txt"/>
|
||||
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace ICSharpCode.TextEditor.Document
|
||||
{
|
||||
|
||||
public sealed class TextUtilities
|
||||
{
|
||||
/// <remarks>
|
||||
/// This function takes a string and converts the whitespace in front of
|
||||
/// it to tabs. If the length of the whitespace at the start of the string
|
||||
/// was not a whole number of tabs then there will still be some spaces just
|
||||
/// before the text starts.
|
||||
/// the output string will be of the form:
|
||||
/// 1. zero or more tabs
|
||||
/// 2. zero or more spaces (less than tabIndent)
|
||||
/// 3. the rest of the line
|
||||
/// </remarks>
|
||||
public static string LeadingWhiteSpaceToTabs(string line, int tabIndent) {
|
||||
StringBuilder sb = new StringBuilder(line.Length);
|
||||
int consecutiveSpaces = 0;
|
||||
int i = 0;
|
||||
for(i = 0; i < line.Length; i++) {
|
||||
if(line[i] == ' ') {
|
||||
consecutiveSpaces++;
|
||||
if(consecutiveSpaces == tabIndent) {
|
||||
sb.Append('\t');
|
||||
consecutiveSpaces = 0;
|
||||
}
|
||||
}
|
||||
else if(line[i] == '\t') {
|
||||
sb.Append('\t');
|
||||
// if we had say 3 spaces then a tab and tabIndent was 4 then
|
||||
// we would want to simply replace all of that with 1 tab
|
||||
consecutiveSpaces = 0;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(i < line.Length) {
|
||||
sb.Append(line.Substring(i-consecutiveSpaces));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static bool IsLetterDigitOrUnderscore(char c)
|
||||
{
|
||||
if(!char.IsLetterOrDigit(c)) {
|
||||
return c == '_';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public enum CharacterType {
|
||||
LetterDigitOrUnderscore,
|
||||
WhiteSpace,
|
||||
Other
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// This method returns the expression before a specified offset.
|
||||
/// That method is used in code completion to determine the expression given
|
||||
/// to the parser for type resolve.
|
||||
/// </remarks>
|
||||
public static string GetExpressionBeforeOffset(TextArea textArea, int initialOffset)
|
||||
{
|
||||
IDocument document = textArea.Document;
|
||||
int offset = initialOffset;
|
||||
while (offset - 1 > 0) {
|
||||
switch (document.GetCharAt(offset - 1)) {
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '}':
|
||||
goto done;
|
||||
// offset = SearchBracketBackward(document, offset - 2, '{','}');
|
||||
// break;
|
||||
case ']':
|
||||
offset = SearchBracketBackward(document, offset - 2, '[',']');
|
||||
break;
|
||||
case ')':
|
||||
offset = SearchBracketBackward(document, offset - 2, '(',')');
|
||||
break;
|
||||
case '.':
|
||||
--offset;
|
||||
break;
|
||||
case '"':
|
||||
if (offset < initialOffset - 1) {
|
||||
return null;
|
||||
}
|
||||
return "\"\"";
|
||||
case '\'':
|
||||
if (offset < initialOffset - 1) {
|
||||
return null;
|
||||
}
|
||||
return "'a'";
|
||||
case '>':
|
||||
if (document.GetCharAt(offset - 2) == '-') {
|
||||
offset -= 2;
|
||||
break;
|
||||
}
|
||||
goto done;
|
||||
default:
|
||||
if (char.IsWhiteSpace(document.GetCharAt(offset - 1))) {
|
||||
--offset;
|
||||
break;
|
||||
}
|
||||
int start = offset - 1;
|
||||
if (!IsLetterDigitOrUnderscore(document.GetCharAt(start))) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
while (start > 0 && IsLetterDigitOrUnderscore(document.GetCharAt(start - 1))) {
|
||||
--start;
|
||||
}
|
||||
string word = document.GetText(start, offset - start).Trim();
|
||||
switch (word) {
|
||||
case "ref":
|
||||
case "out":
|
||||
case "in":
|
||||
case "return":
|
||||
case "throw":
|
||||
case "case":
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (word.Length > 0 && !IsLetterDigitOrUnderscore(word[0])) {
|
||||
goto done;
|
||||
}
|
||||
offset = start;
|
||||
break;
|
||||
}
|
||||
}
|
||||
done:
|
||||
//// simple exit fails when : is inside comment line or any other character
|
||||
//// we have to check if we got several ids in resulting line, which usually happens when
|
||||
//// id. is typed on next line after comment one
|
||||
//// Would be better if lexer would parse properly such expressions. However this will cause
|
||||
//// modifications in this area too - to get full comment line and remove it afterwards
|
||||
if (offset < 0)
|
||||
return string.Empty;
|
||||
|
||||
string resText=document.GetText(offset, textArea.Caret.Offset - offset ).Trim();
|
||||
int pos=resText.LastIndexOf('\n');
|
||||
if (pos>=0) {
|
||||
offset+=pos+1;
|
||||
//// whitespaces and tabs, which might be inside, will be skipped by trim below
|
||||
}
|
||||
string expression = document.GetText(offset, textArea.Caret.Offset - offset ).Trim();
|
||||
return expression;
|
||||
}
|
||||
|
||||
|
||||
public static CharacterType GetCharacterType(char c)
|
||||
{
|
||||
if(IsLetterDigitOrUnderscore(c))
|
||||
return CharacterType.LetterDigitOrUnderscore;
|
||||
if(char.IsWhiteSpace(c))
|
||||
return CharacterType.WhiteSpace;
|
||||
return CharacterType.Other;
|
||||
}
|
||||
|
||||
public static int GetFirstNonWSChar(IDocument document, int offset)
|
||||
{
|
||||
while (offset < document.TextLength && char.IsWhiteSpace(document.GetCharAt(offset))) {
|
||||
++offset;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
public static int FindWordEnd(IDocument document, int offset)
|
||||
{
|
||||
LineSegment line = document.GetLineSegmentForOffset(offset);
|
||||
int endPos = line.Offset + line.Length;
|
||||
while (offset < endPos && IsLetterDigitOrUnderscore(document.GetCharAt(offset))) {
|
||||
++offset;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
public static int FindWordStart(IDocument document, int offset)
|
||||
{
|
||||
LineSegment line = document.GetLineSegmentForOffset(offset);
|
||||
int lineOffset = line.Offset;
|
||||
while (offset > lineOffset && IsLetterDigitOrUnderscore(document.GetCharAt(offset - 1))) {
|
||||
--offset;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
// go forward to the start of the next word
|
||||
// if the cursor is at the start or in the middle of a word we move to the end of the word
|
||||
// and then past any whitespace that follows it
|
||||
// if the cursor is at the start or in the middle of some whitespace we move to the start of the
|
||||
// next word
|
||||
public static int FindNextWordStart(IDocument document, int offset)
|
||||
{
|
||||
int originalOffset = offset;
|
||||
LineSegment line = document.GetLineSegmentForOffset(offset);
|
||||
int endPos = line.Offset + line.Length;
|
||||
// lets go to the end of the word, whitespace or operator
|
||||
CharacterType t = GetCharacterType(document.GetCharAt(offset));
|
||||
while (offset < endPos && GetCharacterType(document.GetCharAt(offset)) == t) {
|
||||
++offset;
|
||||
}
|
||||
|
||||
// now we're at the end of the word, lets find the start of the next one by skipping whitespace
|
||||
while (offset < endPos && GetCharacterType(document.GetCharAt(offset)) == CharacterType.WhiteSpace) {
|
||||
++offset;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
// go back to the start of the word we are on
|
||||
// if we are already at the start of a word or if we are in whitespace, then go back
|
||||
// to the start of the previous word
|
||||
public static int FindPrevWordStart(IDocument document, int offset)
|
||||
{
|
||||
int originalOffset = offset;
|
||||
if (offset > 0) {
|
||||
LineSegment line = document.GetLineSegmentForOffset(offset);
|
||||
CharacterType t = GetCharacterType(document.GetCharAt(offset - 1));
|
||||
while (offset > line.Offset && GetCharacterType(document.GetCharAt(offset - 1)) == t) {
|
||||
--offset;
|
||||
}
|
||||
|
||||
// if we were in whitespace, and now we're at the end of a word or operator, go back to the beginning of it
|
||||
if(t == CharacterType.WhiteSpace && offset > line.Offset) {
|
||||
t = GetCharacterType(document.GetCharAt(offset - 1));
|
||||
while (offset > line.Offset && GetCharacterType(document.GetCharAt(offset - 1)) == t) {
|
||||
--offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
public static string GetLineAsString(IDocument document, int lineNumber)
|
||||
{
|
||||
LineSegment line = document.GetLineSegment(lineNumber);
|
||||
return document.GetText(line.Offset, line.Length);
|
||||
}
|
||||
|
||||
public static int SearchBracketBackward(IDocument document, int offset, char openBracket, char closingBracket)
|
||||
{
|
||||
return document.FormattingStrategy.SearchBracketBackward(document, offset, openBracket, closingBracket);
|
||||
}
|
||||
|
||||
public static int SearchBracketForward(IDocument document, int offset, char openBracket, char closingBracket)
|
||||
{
|
||||
return document.FormattingStrategy.SearchBracketForward(document, offset, openBracket, closingBracket);
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Returns true, if the line lineNumber is empty or filled with whitespaces.
|
||||
/// </remarks>
|
||||
public static bool IsEmptyLine(IDocument document, int lineNumber)
|
||||
{
|
||||
return IsEmptyLine(document, document.GetLineSegment(lineNumber));
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Returns true, if the line lineNumber is empty or filled with whitespaces.
|
||||
/// </remarks>
|
||||
public static bool IsEmptyLine(IDocument document, LineSegment line)
|
||||
{
|
||||
for (int i = line.Offset; i < line.Offset + line.Length; ++i) {
|
||||
char ch = document.GetCharAt(i);
|
||||
if (!char.IsWhiteSpace(ch)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool IsWordPart(char ch)
|
||||
{
|
||||
return IsLetterDigitOrUnderscore(ch) || ch == '.';
|
||||
}
|
||||
|
||||
public static string GetWordAt(IDocument document, int offset)
|
||||
{
|
||||
if (offset < 0 || offset >= document.TextLength - 1 || !IsWordPart(document.GetCharAt(offset))) {
|
||||
return string.Empty;
|
||||
}
|
||||
int startOffset = offset;
|
||||
int endOffset = offset;
|
||||
while (startOffset > 0 && IsWordPart(document.GetCharAt(startOffset - 1))) {
|
||||
--startOffset;
|
||||
}
|
||||
|
||||
while (endOffset < document.TextLength - 1 && IsWordPart(document.GetCharAt(endOffset + 1))) {
|
||||
++endOffset;
|
||||
}
|
||||
|
||||
Debug.Assert(endOffset >= startOffset);
|
||||
return document.GetText(startOffset, endOffset - startOffset + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user