03-25-2024, 06:48 PM
print.clear();
var s = """
0
1
2
3
""";
//print.it(s.LineAppend("append"));
//print.it(s.LineReplace(2, "repl"));
//print.it(s.LineRemove(2));
//print.it(s.LineInsert(2, "ins"));
//print.it(s.LineGetRange(1, out var r), r);
//print.it(s.LineAt(6, out var r), r);
static class MyExtString {
/// <summary>
/// Appends string as new line.
/// </summary>
/// <param name="t"></param>
/// <param name="s"></param>
public static string LineAppend(this string t, string s)
=> t.NE() || t.Ends('\n') ? t + s + "\r\n" : t + "\r\n" + s + "\r\n";
/// <summary>
/// Replaces the specified line.
/// </summary>
/// <param name="t"></param>
/// <param name="line">0-based line index.</param>
/// <param name="repl">Replacement string.</param>
/// <exception cref="ArgumentOutOfRangeException">Invalid line index.</exception>
public static string LineReplace(this string t, int line, string repl) {
if (!LineGetRange(t, line, out var r)) throw new ArgumentOutOfRangeException("line");
return t.ReplaceAt(r.start..r.end, repl);
}
/// <summary>
/// Removes the specified line.
/// </summary>
/// <param name="t"></param>
/// <param name="line">0-based line index.</param>
/// <exception cref="ArgumentOutOfRangeException">Invalid line index.</exception>
public static string LineRemove(this string t, int line) {
if (!LineGetRange(t, line, out var r)) throw new ArgumentOutOfRangeException("line");
return t.Remove(r.start..r.endRN);
}
/// <summary>
/// Inserts string as new line before the specified line.
/// </summary>
/// <param name="t"></param>
/// <param name="line">0-based line index.</param>
/// <param name="s"></param>
/// <exception cref="ArgumentOutOfRangeException">Invalid line index.</exception>
public static string LineInsert(this string t, int line, string s) {
if (!LineGetRange(t, line, out var r)) throw new ArgumentOutOfRangeException("line");
return t.Insert(r.start, s + "\r\n");
}
/// <summary>
/// Finds start/end offsets of the specified line.
/// </summary>
/// <param name="t"></param>
/// <param name="line">0-based line index.</param>
/// <param name="r">Results. The <b>endRN</b> field is the start of next line or equal to <b>end</b>.</param>
/// <returns>true if found.</returns>
/// <exception cref="ArgumentOutOfRangeException">Negative <i>line</i>.</exception>
public static bool LineGetRange(this string t, int line, out (int start, int end, int endRN) r) {
if (line < 0) throw new ArgumentOutOfRangeException("line");
int start = 0;
while (line-- > 0) {
start = t.IndexOf('\n', start) + 1;
if (start == 0) { r = default; return false; }
}
int end = t.IndexOf('\n', start);
r = end < 0 ? (start, t.Length, t.Length) : (start, end - (end > 0 && t[end - 1] == '\r' ? 1 : 0), end + 1);
return true;
}
/// <summary>
/// Finds line containing the specified character index.
/// </summary>
/// <param name="t"></param>
/// <param name="i">0-based character index.</param>
/// <param name="r">Results. The <b>endRN</b> field is the start of next line or equal to <b>end</b>.</param>
/// <returns>0-based line index.</returns>
/// <exception cref="ArgumentOutOfRangeException"><i>line</i> < 0 or > string length.</exception>
public static int LineAt(this string t, int i, out (int start, int end, int endRN) r) {
if ((uint)i > t.Length) throw new ArgumentOutOfRangeException("i");
int line = 0, start = 0, end;
for (; ; start = end, line++) {
end = t.IndexOf('\n', start) + 1;
if (end > i || end == 0) break;
}
r = end == 0 ? (start, t.Length, t.Length) : (start, end - (end > 0 && t[end - 2] == '\r' ? 2 : 1), end);
return line;
}
}