using System;
using System.Text;
using Cobilas.Collections;
namespace Cobilas.IO.Atlf.Components {
/// Allows you to read and modify a string of characters.
public sealed class CharacterCursor : IDisposable {
private char[] characters;
private long index;
private long line;
private long column;
/// Returns the current line of the reading cursor.
public long Line => line;
/// Returns the current index of the string being read.
public long Index => index;
/// Returns the current reading cursor column.
public long Column => column;
/// Returns a current representation of the row, column, and index of the string being read.
public LineEndColumn Cursor => new LineEndColumn(line, column, index);
/// Returns the length of the string.
public long Count => ArrayManipulation.ArrayLongLength(characters);
/// Returns the current character.
public char CurrentCharacter => index < Count ? characters[index] : '\0';
#pragma warning disable CS1591 // O comentário XML ausente não foi encontrado para o tipo ou membro visível publicamente
public char this[long index] => characters[index];
public CharacterCursor(char[] characters) {
this.characters = characters;
index = -1L;
line = 1L;
column = 0L;
}
public CharacterCursor(string text) :
this(text.ToCharArray()) { }
public CharacterCursor(StringBuilder text) :
this(text.ToString()) { }
public CharacterCursor(byte[] bytes, Encoding encoding) :
this(encoding.GetChars(bytes)) { }
#pragma warning restore CS1591 // O comentário XML ausente não foi encontrado para o tipo ou membro visível publicamente
/// Move the reading cursor to the next character.
/// How many jumps the cursor must make to move to the next character.
/// The method will return true whenever there are characters to read.
public bool MoveToCharacter(long index) {
bool res = (this.index += index) < Count;
++column;
if (CurrentCharacter == '\n') {
++line;
column = 0L;
}
return res;
}
/// Add escape character.
public void AddEscape(char escape) {
char[] newCharacters = new char[Count + 1];
for (long I = 0; I < characters.LongLength; I++) {
if (I == index) {
newCharacters[I] = escape;
newCharacters[I + 1L] = characters[I];
} else if (I > index)
newCharacters[I + 1L] = characters[I];
else
newCharacters[I] = characters[I];
}
characters = Array.Empty();
characters = newCharacters;
}
/// Move the reading cursor to the next character.
/// The method will return true whenever there are characters to read.
public bool MoveToCharacter()
=> MoveToCharacter(1L);
/// Reset the position of the index, line and column of the reading course.
public void Reset() {
line = 1;
column = 1;
index = -1L;
}
/// Allows you to make a cut in the character string.
/// The index that will start the clipping.
/// The number of characters that will be cut.
/// The method will return a string containing the string of characters that were cut.
public string SliceText(long index, long count) {
StringBuilder builder = new StringBuilder();
for (long I = index; I < count + index && I < Count; I++)
builder.Append(this[I]);
return builder.ToString();
}
/// Compare a character with the current character.
public bool CharIsEqualToIndex(char character)
=> index < Count && character == characters[index];
/// Compare a character with the current character.
public bool CharIsEqualToIndex(params char[] characters) {
for (long I = 0; I < ArrayManipulation.ArrayLongLength(characters) && index < Count; I++)
if (characters[I] == this.characters[index])
return true;
return false;
}
/// Compare a character with the current character.
public bool CharIsEqualToIndex(string text) {
if (text is null) return false;
char[] list = text.ToCharArray();
for (long I = index, C = 0L; C < list.LongLength && I < Count; I++, C++)
if (text[(int)C] != characters[I])
return false;
return true;
}
/// Compare a character with the current character.
public bool CharIsEqualToIndex(params string[] texts) {
if (texts is null) return false;
foreach (string item in texts)
if (CharIsEqualToIndex(item))
return true;
return false;
}
#pragma warning disable CS1591
public void Dispose() => ArrayManipulation.ClearArraySafe(ref characters);
public override string ToString() => new string(this.characters);
#pragma warning restore
/// Represents the current column, row and index of the reading cursor.
public readonly struct LineEndColumn {
private readonly long line;
private readonly long index;
private readonly long column;
/// Returns the line where the reading cursor is.
public long Line => line;
/// Returns the index of where the reading cursor is.
public long Index => index;
/// Returns the column where the reading cursor is.
public long Column => column;
/// Default value.(L:1, C:1, I:0)
public static LineEndColumn Default => new LineEndColumn(1, 1, 0);
#pragma warning disable CS1591 // O comentário XML ausente não foi encontrado para o tipo ou membro visível publicamente
public LineEndColumn(long line, long column, long index) {
this.line = line;
this.column = column;
this.index = index + 1;
}
public override string ToString() => string.Format("(L:{0} C:{1} I:{2})", line, column, index);
#pragma warning restore CS1591 // O comentário XML ausente não foi encontrado para o tipo ou membro visível publicamente
}
}
}