HexManiacAdvance/src/HexManiac.Core/ViewModels/TextEditorViewModel.cs
haven1433 e6c8e561b2 first pass at an improved text editor
for script editing, etc
2022-09-28 22:49:42 -05:00

161 lines
5.8 KiB
C#

using System;
using System.Collections.ObjectModel;
using System.Linq;
namespace HavenSoft.HexManiac.Core.ViewModels {
public class TextEditorViewModel : ViewModelCore {
public ObservableCollection<string> Keywords { get; } = new();
public TextEditorViewModel() {
foreach (var word in new[] { "for", "do", "while", "in", "print" }) {
Keywords.Add(word);
}
}
private string content = string.Empty;
public string Content {
get => content;
set {
if (content == value) return;
content = value;
UpdateLayers();
NotifyPropertyChanged();
}
}
public string AccentContent { get; private set; } = string.Empty;
public string PlainContent { get; private set; } = string.Empty;
public string ConstantContent { get; private set; } = string.Empty;
public string CommentContent { get; private set; } = string.Empty;
private void UpdateLayers() {
var basic = new MutableString(Content);
var accent = new MutableString(basic.Length);
var constants = new MutableString(basic.Length);
var comments = new MutableString(basic.Length);
accent.CopyNewlines(basic);
constants.CopyNewlines(basic);
comments.CopyNewlines(basic);
// comments
while (true) {
var index = basic.IndexOf("//");
if (index == -1) break;
var endIndex = basic.IndexOfCharacter(index, '\n', '\r');
if (index == -1) break;
comments.Replace(index, basic, index, endIndex - index);
basic.Clear(index, endIndex - index);
}
// accent
foreach (var keyword in Keywords) {
while (true) {
var index = basic.IndexOfKeyword(keyword);
if (index == -1) break;
accent.Replace(index, keyword);
basic.Clear(index, keyword.Length);
}
}
// constants
foreach (var keyword in new[] { "true", "false" }) {
while (true) {
var index = basic.IndexOfKeyword(keyword);
if (index == -1) break;
constants.Replace(index, keyword);
basic.Clear(index, keyword.Length);
}
}
int start = 0;
while (true) {
var (index, length) = basic.IndexOfNumber(start);
if (index == -1) break;
start = index;
constants.Replace(index, basic, index, length);
basic.Clear(index, length);
}
PlainContent = basic.ToString();
AccentContent = accent.ToString();
ConstantContent = constants.ToString();
CommentContent = comments.ToString();
NotifyPropertyChanged(nameof(PlainContent));
NotifyPropertyChanged(nameof(AccentContent));
NotifyPropertyChanged(nameof(ConstantContent));
NotifyPropertyChanged(nameof(CommentContent));
}
}
public class MutableString {
private readonly char[] content;
public int Length => content.Length;
public MutableString(string initialContent) => content = initialContent.ToCharArray();
public MutableString(int length) => content = new char[length];
public override string ToString() => new(content);
public int IndexOfKeyword(string keyword) {
if (keyword.Length == 0) return -1;
for (int i = 0; i < content.Length - keyword.Length + 1; i++) {
if (i > 0 && char.IsLetterOrDigit(content[i - 1])) continue; // not valid keyword start
if (i < content.Length - keyword.Length && char.IsLetterOrDigit(content[i + keyword.Length])) continue; // not valid keyword end
var matchLength = 0;
for (int j = 0; j < keyword.Length && keyword[j] == content[i + j]; j++) matchLength = j + 1;
if (matchLength == keyword.Length) return i;
}
return -1;
}
public int IndexOf(string text) {
if (text.Length == 0) return -1;
for (int i = 0; i < content.Length - text.Length + 1; i++) {
var matchLength = 0;
for (int j = 0; j < text.Length && content[i + j] == text[j]; j++) matchLength = j + 1;
if (matchLength == text.Length) return i;
}
return -1;
}
public int IndexOfCharacter(int start, params char[] options) {
for (int i = start; i < content.Length; i++) {
if (options.Contains(content[i])) return i;
}
return content.Length;
}
public (int, int) IndexOfNumber(int start) {
for (int i = start; i < content.Length; i++) {
if (i > 0 && char.IsLetterOrDigit(content[i - 1])) continue;
if (!char.IsDigit(content[i])) continue;
int length = 1;
while (i + length < content.Length && char.IsDigit(content[i + length])) length++;
if (i + length < content.Length && char.IsLetter(content[i + length])) continue;
return (i, length);
}
return (-1, -1);
}
public void Replace(int index, string keyword) {
for (int i = 0; i < keyword.Length; i++) content[index + i] = keyword[i];
}
public void Replace(int index, MutableString source, int start, int length) {
for (int i = 0; i < length; i++) content[index + i] = source.content[start + i];
}
public void Clear(int index, int length) {
for (int i = 0; i < length; i++) content[index + i] = ' ';
}
public void CopyNewlines(MutableString other) {
for (int i = 0; i < content.Length; i++) {
if (other.content[i] == '\n' || other.content[i] == '\r') {
content[i] = other.content[i];
}
}
}
}
}