A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

261 lines
7.3 KiB

// -----------------------------------------------------------------------
// <copyright file="StringUtils.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls.Utils
{
using System.Globalization;
internal static class StringUtils
{
private enum CharClass
{
CharClassUnknown,
CharClassWhitespace,
CharClassAlphaNumeric,
}
public static bool IsEol(char c)
{
return c == '\r' || c == '\n';
}
public static bool IsStartOfWord(string text, int index)
{
// A 'word' starts with an AlphaNumeric or some punctuation symbols immediately
// preceeded by lwsp.
if (index > 0 && !char.IsWhiteSpace(text[index - 1]))
{
return false;
}
switch (CharUnicodeInfo.GetUnicodeCategory(text[index]))
{
case UnicodeCategory.LowercaseLetter:
case UnicodeCategory.TitlecaseLetter:
case UnicodeCategory.UppercaseLetter:
case UnicodeCategory.DecimalDigitNumber:
case UnicodeCategory.LetterNumber:
case UnicodeCategory.OtherNumber:
case UnicodeCategory.DashPunctuation:
case UnicodeCategory.InitialQuotePunctuation:
case UnicodeCategory.OpenPunctuation:
case UnicodeCategory.CurrencySymbol:
case UnicodeCategory.MathSymbol:
return true;
// TODO: How do you do this in .NET?
// case UnicodeCategory.OtherPunctuation:
// // words cannot start with '.', but they can start with '&' or '*' (for example)
// return g_unichar_break_type(buffer->text[index]) == G_UNICODE_BREAK_ALPHABETIC;
default:
return false;
}
}
public static int PreviousWord(string text, int cursor, bool gtkMode)
{
int begin;
int i;
int cr;
int lf;
lf = LineBegin(text, cursor) - 1;
if (lf > 0 && text[lf] == '\n' && text[lf - 1] == '\r')
{
cr = lf - 1;
}
else
{
cr = lf;
}
// if the cursor is at the beginning of the line, return the end of the prev line
if (cursor - 1 == lf)
{
return (cr > 0) ? cr : 0;
}
if (gtkMode)
{
CharClass cc = GetCharClass(text[cursor - 1]);
begin = lf + 1;
i = cursor;
// skip over the word, punctuation, or run of whitespace
while (i > begin && GetCharClass(text[i - 1]) == cc)
{
i--;
}
// if the cursor was at whitespace, skip back a word too
if (cc == CharClass.CharClassWhitespace && i > begin)
{
cc = GetCharClass(text[i - 1]);
while (i > begin && GetCharClass(text[i - 1]) == cc)
{
i--;
}
}
}
else
{
begin = lf + 1;
i = cursor;
if (cursor < text.Length)
{
// skip to the beginning of this word
while (i > begin && !char.IsWhiteSpace(text[i - 1]))
{
i--;
}
if (i < cursor && IsStartOfWord(text, i))
{
return i;
}
}
// skip to the start of the lwsp
while (i > begin && char.IsWhiteSpace(text[i - 1]))
{
i--;
}
if (i > begin)
{
i--;
}
// skip to the beginning of the word
while (i > begin && !IsStartOfWord(text, i))
{
i--;
}
}
return i;
}
public static int NextWord(string text, int cursor, bool gtkMode)
{
int i, lf, cr;
cr = LineEnd(text, cursor);
if (cr < text.Length && text[cr] == '\r' && text[cr + 1] == '\n')
{
lf = cr + 1;
}
else
{
lf = cr;
}
// if the cursor is at the end of the line, return the starting offset of the next line
if (cursor == cr || cursor == lf)
{
if (lf < text.Length)
{
return lf + 1;
}
return cursor;
}
if (gtkMode)
{
CharClass cc = GetCharClass(text[cursor]);
i = cursor;
// skip over the word, punctuation, or run of whitespace
while (i < cr && GetCharClass(text[i]) == cc)
{
i++;
}
// skip any whitespace after the word/punct
while (i < cr && char.IsWhiteSpace(text[i]))
{
i++;
}
}
else
{
i = cursor;
// skip to the end of the current word
while (i < cr && !char.IsWhiteSpace(text[i]))
{
i++;
}
// skip any whitespace after the word
while (i < cr && char.IsWhiteSpace(text[i]))
{
i++;
}
// find the start of the next word
while (i < cr && !IsStartOfWord(text, i))
{
i++;
}
}
return i;
}
private static CharClass GetCharClass(char c)
{
if (char.IsWhiteSpace(c))
{
return CharClass.CharClassWhitespace;
}
else if (char.IsLetterOrDigit(c))
{
return CharClass.CharClassAlphaNumeric;
}
else
{
return CharClass.CharClassUnknown;
}
}
private static int LineBegin(string text, int pos)
{
while (pos > 0 && !IsEol(text[pos - 1]))
{
pos--;
}
return pos;
}
private static int LineEnd(string text, int cursor, bool include = false)
{
while (cursor < text.Length && !IsEol(text[cursor]))
{
cursor++;
}
if (include && cursor < text.Length)
{
if (text[cursor] == '\r' && text[cursor + 1] == '\n')
{
cursor += 2;
}
else
{
cursor++;
}
}
return cursor;
}
}
}