From d86f4f106f24b68e6fb5f836afe3051812f22c6a Mon Sep 17 00:00:00 2001 From: Jonas Arnold Date: Fri, 26 May 2023 09:56:17 +0200 Subject: [PATCH] implemented newline sequence handling in ConsoleView, removed some unnecessary code, minor changes in UI --- MultiTerm.Core/ViewModel/ConsoleViewModel.cs | 175 ++++++++++++++++++- MultiTerm.Core/ViewModel/ShellViewModel.cs | 3 - MultiTerm.Wpf/View/ShellView.xaml | 114 ++++++------ 3 files changed, 231 insertions(+), 61 deletions(-) diff --git a/MultiTerm.Core/ViewModel/ConsoleViewModel.cs b/MultiTerm.Core/ViewModel/ConsoleViewModel.cs index 84c3016..adc2215 100644 --- a/MultiTerm.Core/ViewModel/ConsoleViewModel.cs +++ b/MultiTerm.Core/ViewModel/ConsoleViewModel.cs @@ -6,6 +6,7 @@ using CommunityToolkit.Mvvm.Messaging; using MultiTerm.Core.Model; using MultiTerm.Core.Types; using MultiTerm.Protocols.Model; +using System.Collections.ObjectModel; using System.Text; namespace MultiTerm.Core.ViewModel; @@ -16,11 +17,17 @@ public partial class ConsoleViewModel : TerminalViewModel private readonly ILogger logger; private Encoding currentEncoding; + private List? listOfPrevCharacters; /* settings */ private readonly Encoding defaultEncoding = Encoding.ASCII; // default encoding instance private const EncodingType defaultEncodingType = EncodingType.ASCII; // default encoding type - private const int DataMaxLength = 5000; // maximum amount of characters stored int the data textbox + private const int DataMaxLength = 20000; // limit of maximum shown characters + + /// + /// Newline Sequence string inserted on every newline in the . + /// + public static readonly string NewlineSequence = Environment.NewLine; #region Observable Properties /// @@ -53,7 +60,9 @@ public partial class ConsoleViewModel : TerminalViewModel // extract bytes var bytes = newData.Select(b => b.Byte).ToArray(); string newDataDecoded = currentEncoding.GetString(bytes); - // calculate new legth + // replace newlines + newDataDecoded = ReplaceNewlineCharacters(newDataDecoded, ref this.listOfPrevCharacters, this.DataDisplayNewlineSeparatorType, currentEncoding); + // calculate new lnegth int newTotalLength = this.Data.Length + newDataDecoded.Length; // trim current data at the beginning if new length is too long if (newTotalLength > DataMaxLength) @@ -109,6 +118,158 @@ public partial class ConsoleViewModel : TerminalViewModel this.currentEncoding = this.GetEncoding(value); } + #region Newline Insertion + /// + /// Returns if the is a known newline characters. + /// + /// character to test if it is a known newline characters + /// true if the character is a newline character + private static bool IsKnownNewlineCharacter(char character) + { + // must be extended if new newline characters shall be detected! + const string knownNewlineCharacters = "\n\r"; + + return knownNewlineCharacters.Contains(character); + } + + /// + /// Searches for known Newline characters (using . Removes all known Newline characters from the text. + /// If the newline sequence according to is found, the configured is introduced. + /// + /// text to search for newline characters + /// list of previous characters, managed by this method. to identify newline sequences over multiple calls of this method + /// separator type + /// encoding to use + /// string with replaced newline characters + private static string ReplaceNewlineCharacters( + string text, + ref List? previousCharacters, + NewlineSeparatorType newlineSeparatorType, + Encoding encoding) + { + StringBuilder stringBuilder = new(); + + // go through characters + foreach (char character in text) + { + // add to collection if the character is not a known newline character + if (IsKnownNewlineCharacter(character) == false) + { + stringBuilder.Append(character); + } + + // check if a newline should be introduced after this character + switch (ShouldIntroduceNewlineAfterThisCharacter(character, previousCharacters, newlineSeparatorType, encoding)) + { + case ShouldIntroduceNewlineAfterThisCharacterResult.NoNewline: + // a decision could be made, previousCharacters can be cleared + if (previousCharacters != null) { previousCharacters = null; } + // nothing to do + break; + + case ShouldIntroduceNewlineAfterThisCharacterResult.IntroduceNewline: + // a decision could be made, previousCharacters can be cleared + if (previousCharacters != null) { previousCharacters = null; } + // append line in string + stringBuilder.Append(NewlineSequence); + break; + + case ShouldIntroduceNewlineAfterThisCharacterResult.RequiresMoreCharacters: + // first time that more characters are required => create new list + previousCharacters ??= new List(); + // add current character to list + previousCharacters.Add(character); + break; + + default: + throw new Exception($"'{nameof(ReplaceNewlineCharacters)}()' failed because of error when checking if a newline should be introduced."); + } + } + + return stringBuilder.ToString(); + } + + /// + /// Result type for + /// + public enum ShouldIntroduceNewlineAfterThisCharacterResult + { + /// + /// No newline is required. + /// + NoNewline, + + /// + /// A newline shall be introduced after this byte. + /// + IntroduceNewline, + + /// + /// Following characters are required to finalize result wether a newline shall be introduced or not. + /// + RequiresMoreCharacters + } + + /// + /// Function to check wether a newline shall be introduced after the given . + /// Since some newline sequences will require multiple characters in correct order, a more complex handling is required, which is possible using this function. + /// + /// the current character in the collection + /// list of previous character, newest at the last position of the list, null if not required + /// separator type + /// encoding to be used to check if the characters are equal + /// enum result of type + /// if the handling for the is not implemented + private static ShouldIntroduceNewlineAfterThisCharacterResult ShouldIntroduceNewlineAfterThisCharacter( + char character, + List? previousCharacters, + NewlineSeparatorType newlineSeparatorType, + Encoding encoding) + { + var result = ShouldIntroduceNewlineAfterThisCharacterResult.NoNewline; + + switch (newlineSeparatorType) + { + case NewlineSeparatorType.None: + break; + + case NewlineSeparatorType.CR: + if (EncodedCharacterEqualsAsciiCharacter(character, '\r', encoding)) + { + result = ShouldIntroduceNewlineAfterThisCharacterResult.IntroduceNewline; + } + break; + + case NewlineSeparatorType.LF: + if (EncodedCharacterEqualsAsciiCharacter(character, '\n', encoding)) + { + result = ShouldIntroduceNewlineAfterThisCharacterResult.IntroduceNewline; + } + break; + + case NewlineSeparatorType.CR_LF: + if (EncodedCharacterEqualsAsciiCharacter(character, '\r', encoding)) + { + result = ShouldIntroduceNewlineAfterThisCharacterResult.RequiresMoreCharacters; + } + if (EncodedCharacterEqualsAsciiCharacter(character, '\n', encoding)) + { + if (previousCharacters != null && EncodedCharacterEqualsAsciiCharacter(previousCharacters.Last(), '\r', encoding)) + { + result = ShouldIntroduceNewlineAfterThisCharacterResult.IntroduceNewline; + } + } + break; + + default: + throw new NotImplementedException($"'{nameof(ShouldIntroduceNewlineAfterThisCharacter)}()' does not implement handling for {nameof(NewlineSeparatorType)} {newlineSeparatorType}"); + } + + return result; + } + + #endregion + #region Unused Methods public override void DisplayNewSentData(IEnumerable newData) { @@ -155,5 +316,15 @@ public partial class ConsoleViewModel : TerminalViewModel return encoding!; } + + /// + /// Returns wether the equals the . + /// must be encoded with . + /// + /// true if equal + private static bool EncodedCharacterEqualsAsciiCharacter(char encodedChar, char asciiChar, Encoding encoding) + { + return encodedChar.ToString() == encoding.GetString(Encoding.ASCII.GetBytes(new char[] { asciiChar })); + } #endregion } diff --git a/MultiTerm.Core/ViewModel/ShellViewModel.cs b/MultiTerm.Core/ViewModel/ShellViewModel.cs index 385d8f1..5e74a0b 100644 --- a/MultiTerm.Core/ViewModel/ShellViewModel.cs +++ b/MultiTerm.Core/ViewModel/ShellViewModel.cs @@ -71,9 +71,6 @@ public partial class ShellViewModel : ObservableObject, IRecipient diff --git a/MultiTerm.Wpf/View/ShellView.xaml b/MultiTerm.Wpf/View/ShellView.xaml index 5fe4ccc..9ebcc5c 100644 --- a/MultiTerm.Wpf/View/ShellView.xaml +++ b/MultiTerm.Wpf/View/ShellView.xaml @@ -263,7 +263,7 @@ - - - + + - - -