Decompiled source of BetterChatBox v1.0.2

BetterChatBox.dll

Decompiled a week ago
using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.InputSystem.Utilities;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: IgnoresAccessChecksTo("Autodesk.Fbx")]
[assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")]
[assembly: IgnoresAccessChecksTo("FbxBuildTestAssets")]
[assembly: IgnoresAccessChecksTo("Klattersynth")]
[assembly: IgnoresAccessChecksTo("Photon3Unity3D")]
[assembly: IgnoresAccessChecksTo("PhotonChat")]
[assembly: IgnoresAccessChecksTo("PhotonRealtime")]
[assembly: IgnoresAccessChecksTo("PhotonUnityNetworking")]
[assembly: IgnoresAccessChecksTo("PhotonUnityNetworking.Utilities")]
[assembly: IgnoresAccessChecksTo("PhotonVoice.API")]
[assembly: IgnoresAccessChecksTo("PhotonVoice")]
[assembly: IgnoresAccessChecksTo("PhotonVoice.PUN")]
[assembly: IgnoresAccessChecksTo("SingularityGroup.HotReload.Runtime")]
[assembly: IgnoresAccessChecksTo("SingularityGroup.HotReload.Runtime.Public")]
[assembly: IgnoresAccessChecksTo("Sirenix.OdinInspector.Attributes")]
[assembly: IgnoresAccessChecksTo("Sirenix.Serialization.Config")]
[assembly: IgnoresAccessChecksTo("Sirenix.Serialization")]
[assembly: IgnoresAccessChecksTo("Sirenix.Utilities")]
[assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")]
[assembly: IgnoresAccessChecksTo("Unity.Formats.Fbx.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.InputSystem")]
[assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")]
[assembly: IgnoresAccessChecksTo("Unity.Postprocessing.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")]
[assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")]
[assembly: IgnoresAccessChecksTo("Unity.Timeline")]
[assembly: IgnoresAccessChecksTo("Unity.VisualScripting.Antlr3.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.VisualScripting.Core")]
[assembly: IgnoresAccessChecksTo("Unity.VisualScripting.Flow")]
[assembly: IgnoresAccessChecksTo("Unity.VisualScripting.State")]
[assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")]
[assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")]
[assembly: IgnoresAccessChecksTo("UnityEngine.UI")]
[assembly: IgnoresAccessChecksTo("websocket-sharp")]
[assembly: AssemblyCompany("NoSpawnn")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+89ef4a342e5b0a073074124718e6399010a34357")]
[assembly: AssemblyProduct("BetterChatBox")]
[assembly: AssemblyTitle("BetterChatBox")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BetterChatBox
{
	[BepInPlugin("NoSpawnn.BetterChatBox", "BetterChatBox", "1.0.2")]
	public class BetterChatBox : BaseUnityPlugin
	{
		internal const string CursorString = "<b>|</b>";

		internal const string EmptyCursorString = "<b> </b>";

		internal const int MaxChars = 50;

		internal static BetterChatBox Instance { get; private set; }

		internal static ManualLogSource Logger => Instance._logger;

		private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger;

		internal Harmony? Harmony { get; set; }

		internal int CursorPos { get; set; }

		internal float DeleteTimer { get; set; }

		internal bool BackspaceHeld { get; set; }

		internal bool DeleteHeld { get; set; }

		internal bool LeftArrowHeld { get; set; }

		internal bool RightArrowHeld { get; set; }

		internal float CursorMoveTimer { get; set; }

		public static ConfigEntry<float> CursorMoveSpeed { get; private set; }

		public static ConfigEntry<float> DeleteRepeatSpeed { get; private set; }

		public static ConfigEntry<float> InputHoldDelay { get; private set; }

		public static ConfigEntry<bool> SavePreviousChatMessage { get; private set; }

		public static ConfigEntry<bool> SaveCursorPosition { get; private set; }

		public static ConfigEntry<bool> CutToSystemClipboard { get; private set; }

		private void Awake()
		{
			Instance = this;
			((Component)this).gameObject.transform.parent = null;
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			Patch();
			BindConfigs();
			Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} has loaded!");
		}

		internal void Patch()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_0025: Expected O, but got Unknown
			if (Harmony == null)
			{
				Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID);
				Harmony val2 = val;
				Harmony = val;
			}
			Harmony.PatchAll();
		}

		internal void Unpatch()
		{
			Harmony? harmony = Harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		internal void ResetButtonsHeldState()
		{
			BackspaceHeld = false;
			DeleteHeld = false;
			LeftArrowHeld = false;
			RightArrowHeld = false;
		}

		private void BindConfigs()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Expected O, but got Unknown
			CursorMoveSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Input", "Cursor Tick Speed", 0.05f, new ConfigDescription("Cursor move speed when holding an arrow key", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 0.1f), Array.Empty<object>()));
			DeleteRepeatSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Input", "Delete Tick Speed", 0.05f, new ConfigDescription("Character delete speed when holding backspace or delete", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 0.1f), Array.Empty<object>()));
			InputHoldDelay = ((BaseUnityPlugin)this).Config.Bind<float>("Input", "Input Hold Delay", 0.5f, new ConfigDescription("How long you need to hold a key before it starts repeating", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), Array.Empty<object>()));
			SavePreviousChatMessage = ((BaseUnityPlugin)this).Config.Bind<bool>("Behavior", "Save Last Chat Message", true, "Should the chat box save your unsent message on close");
			SaveCursorPosition = ((BaseUnityPlugin)this).Config.Bind<bool>("Behavior", "Save Cursor Position", true, "Should the chat box save the cursor's position on close (this has no effect if the above is disabled)");
			CutToSystemClipboard = ((BaseUnityPlugin)this).Config.Bind<bool>("Behavior", "Cut to system clipboard", false, "Should cutting (Ctrl + X) text be placed onto the system clipboard");
		}
	}
	[HarmonyPatch(typeof(ChatManager))]
	public class ChatPatch
	{
		[HarmonyPrefix]
		[HarmonyPatch("StateActive")]
		public static bool StateActivePrefix(ChatManager __instance)
		{
			int cursorPos = BetterChatBox.Instance.CursorPos;
			string chatMessage = __instance.chatMessage;
			SemiFunc.InputDisableMovement();
			InputHandlers.HandleCursorNavigation(ref __instance, ref cursorPos, ref chatMessage);
			InputHandlers.HandleClipboardOperation(ref __instance, ref cursorPos, ref chatMessage);
			InputHandlers.HandleHistoryNavigation(ref __instance, ref cursorPos, ref chatMessage);
			InputHandlers.HandleTextManip(ref __instance, ref cursorPos, ref chatMessage);
			string value = ((Mathf.Sin(Time.time * 10f) > 0f) ? "<b>|</b>" : "<b> </b>");
			((TMP_Text)__instance.chatText).text = chatMessage.Insert(cursorPos, value);
			__instance.chatMessage = chatMessage;
			if (SemiFunc.InputDown((InputKey)9))
			{
				__instance.StateSet((ChatState)((!Utility.IsNullOrWhiteSpace(chatMessage)) ? 3 : 0));
			}
			if (SemiFunc.InputDown((InputKey)18))
			{
				__instance.StateSet((ChatState)0);
				((SemiUI)ChatUI.instance).SemiUISpringShakeX(10f, 10f, 0.3f);
				((SemiUI)ChatUI.instance).SemiUISpringScale(0.05f, 5f, 0.2f);
				MenuManager.instance.MenuEffectClick((MenuClickEffectType)2, (MenuPage)null, 1f, 1f, true);
				__instance.StateSet((ChatState)0);
			}
			BetterChatBox.Instance.CursorPos = cursorPos;
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch("StateInactive")]
		public static bool StateInactivePrefix(ChatManager __instance)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Invalid comparison between Unknown and I4
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Invalid comparison between Unknown and I4
			((SemiUI)ChatUI.instance).Hide();
			__instance.chatActive = false;
			MenuManager instance = MenuManager.instance;
			MenuPage currentMenuPage = instance.currentMenuPage;
			if ((currentMenuPage == null || ((int)currentMenuPage.menuPageIndex != 1 && (int)currentMenuPage.menuPageIndex != 2)) && SemiFunc.InputDown((InputKey)7))
			{
				TutorialDirector.instance.playerChatted = true;
				instance.MenuEffectClick((MenuClickEffectType)0, (MenuPage)null, 1f, 1f, true);
				__instance.chatActive = !__instance.chatActive;
				__instance.StateSet((ChatState)1);
			}
			return false;
		}

		[HarmonyPostfix]
		[HarmonyPatch("ChatReset")]
		private static void ChatResetPostfix(ChatManager __instance)
		{
			BetterChatBox.Instance.CursorPos = 0;
		}

		[HarmonyPostfix]
		[HarmonyPatch("StateSet")]
		public static void StateSetPostfix(ChatManager __instance, ChatState state)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			if (__instance != null && __instance.chatText != null && (int)state == 1)
			{
				BetterChatBox.Instance.ResetButtonsHeldState();
				if (BetterChatBox.SavePreviousChatMessage.Value)
				{
					__instance.chatMessage = ((TMP_Text)__instance.chatText).text.Replace("<b>|</b>", string.Empty).Replace("<b> </b>", string.Empty);
				}
				else
				{
					__instance.chatMessage = string.Empty;
					((TMP_Text)__instance.chatText).text = string.Empty;
				}
				if (!BetterChatBox.SaveCursorPosition.Value || __instance.chatMessage.Length == 0)
				{
					BetterChatBox.Instance.CursorPos = __instance.chatMessage.Length;
				}
			}
		}

		internal static void ChatHistoryChangeEffect()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			((SemiUI)ChatUI.instance).SemiUITextFlashColor(Color.cyan, 0.2f);
			((SemiUI)ChatUI.instance).SemiUISpringShakeY(2f, 5f, 0.2f);
			MenuManager.instance.MenuEffectClick((MenuClickEffectType)4, (MenuPage)null, 1f, 0.2f, true);
		}

		internal static void ChatFailInputEffect()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			((SemiUI)ChatUI.instance).SemiUITextFlashColor(Color.red, 0.2f);
			((SemiUI)ChatUI.instance).SemiUISpringShakeX(10f, 10f, 0.3f);
			((SemiUI)ChatUI.instance).SemiUISpringScale(0.05f, 5f, 0.2f);
			MenuManager.instance.MenuEffectClick((MenuClickEffectType)2, (MenuPage)null, 1f, 1f, true);
		}
	}
	internal static class InputHandlers
	{
		internal static void HandleTextManip(ref ChatManager chatManagerInstance, ref int cursorPos, ref string chatMessage)
		{
			//IL_0249: Unknown result type (might be due to invalid IL or missing references)
			if (Input.GetKeyUp((KeyCode)8))
			{
				BetterChatBox.Instance.BackspaceHeld = false;
			}
			if (Input.GetKeyUp((KeyCode)127))
			{
				BetterChatBox.Instance.DeleteHeld = false;
			}
			if (chatMessage.Length > 0)
			{
				if (Input.GetKeyDown((KeyCode)8) && cursorPos > 0)
				{
					if (Input.GetKey((KeyCode)306))
					{
						int previousWordIdxFromIdx = GetPreviousWordIdxFromIdx(cursorPos, chatMessage, keepWhitespace: true);
						chatMessage = chatMessage.Remove(previousWordIdxFromIdx, cursorPos - previousWordIdxFromIdx);
						cursorPos = previousWordIdxFromIdx;
					}
					else
					{
						BetterChatBox.Instance.BackspaceHeld = true;
						BetterChatBox.Instance.DeleteTimer = BetterChatBox.InputHoldDelay.Value;
						chatMessage = chatMessage.Remove(cursorPos - 1, 1);
						cursorPos--;
					}
					chatManagerInstance.CharRemoveEffect();
				}
				else if (BetterChatBox.Instance.BackspaceHeld && cursorPos > 0)
				{
					BetterChatBox.Instance.DeleteTimer -= Time.deltaTime;
					if (BetterChatBox.Instance.DeleteTimer <= 0f)
					{
						BetterChatBox.Instance.DeleteTimer = BetterChatBox.DeleteRepeatSpeed.Value;
						chatMessage = chatMessage.Remove(cursorPos - 1, 1);
						chatManagerInstance.CharRemoveEffect();
						cursorPos--;
					}
				}
				if (Input.GetKeyDown((KeyCode)127) && cursorPos < chatMessage.Length)
				{
					if (Input.GetKey((KeyCode)306))
					{
						int nextWordIdxFromIdx = GetNextWordIdxFromIdx(cursorPos, chatMessage, keepWhitespace: true);
						chatMessage = chatMessage.Remove(cursorPos, nextWordIdxFromIdx - cursorPos);
					}
					else
					{
						BetterChatBox.Instance.DeleteHeld = true;
						BetterChatBox.Instance.DeleteTimer = BetterChatBox.InputHoldDelay.Value;
						chatMessage = chatMessage.Remove(cursorPos, 1);
					}
					chatManagerInstance.CharRemoveEffect();
				}
				else if (BetterChatBox.Instance.DeleteHeld && cursorPos < chatMessage.Length)
				{
					BetterChatBox.Instance.DeleteTimer -= Time.deltaTime;
					if (BetterChatBox.Instance.DeleteTimer <= 0f)
					{
						BetterChatBox.Instance.DeleteTimer = BetterChatBox.DeleteRepeatSpeed.Value;
						chatMessage = chatMessage.Remove(cursorPos, 1);
						chatManagerInstance.CharRemoveEffect();
					}
				}
			}
			foreach (char item in Input.inputString.Where((char c) => !char.IsControl(c)))
			{
				if (chatMessage.Length < 50)
				{
					chatManagerInstance.prevChatMessage = chatMessage;
					chatMessage = chatMessage.Insert(cursorPos, item.ToString());
					cursorPos++;
					chatManagerInstance.TypeEffect(Color.yellow);
				}
				else
				{
					ChatPatch.ChatFailInputEffect();
				}
			}
		}

		internal static void HandleHistoryNavigation(ref ChatManager chatManagerInstance, ref int cursorPos, ref string chatMessage)
		{
			if (chatManagerInstance.chatHistory.Count <= 0)
			{
				return;
			}
			if (Input.GetKeyDown((KeyCode)273))
			{
				if (chatManagerInstance.chatHistoryIndex > 0)
				{
					ChatManager obj = chatManagerInstance;
					obj.chatHistoryIndex--;
				}
				else
				{
					chatManagerInstance.chatHistoryIndex = chatManagerInstance.chatHistory.Count - 1;
				}
				chatMessage = chatManagerInstance.chatHistory[chatManagerInstance.chatHistoryIndex];
				ChatPatch.ChatHistoryChangeEffect();
				cursorPos = chatMessage.Length;
			}
			if (Input.GetKeyDown((KeyCode)274))
			{
				if (chatManagerInstance.chatHistoryIndex < chatManagerInstance.chatHistory.Count - 1)
				{
					ChatManager obj2 = chatManagerInstance;
					obj2.chatHistoryIndex++;
				}
				else
				{
					chatManagerInstance.chatHistoryIndex = 0;
				}
				chatMessage = chatManagerInstance.chatHistory[chatManagerInstance.chatHistoryIndex];
				ChatPatch.ChatHistoryChangeEffect();
				cursorPos = chatMessage.Length;
			}
		}

		internal static void HandleCursorNavigation(ref ChatManager chatManagerInstance, ref int cursorPos, ref string chatMessage)
		{
			if (Input.GetKeyUp((KeyCode)275))
			{
				BetterChatBox.Instance.RightArrowHeld = false;
			}
			if (Input.GetKeyUp((KeyCode)276))
			{
				BetterChatBox.Instance.LeftArrowHeld = false;
			}
			if (Input.GetKeyDown((KeyCode)276) && cursorPos > 0)
			{
				if (Input.GetKey((KeyCode)306))
				{
					cursorPos = GetPreviousWordIdxFromIdx(cursorPos, chatMessage, keepWhitespace: false);
				}
				else
				{
					BetterChatBox.Instance.LeftArrowHeld = true;
					BetterChatBox.Instance.CursorMoveTimer = BetterChatBox.InputHoldDelay.Value;
					cursorPos--;
				}
			}
			else if (BetterChatBox.Instance.LeftArrowHeld && cursorPos > 0)
			{
				BetterChatBox.Instance.CursorMoveTimer -= Time.deltaTime;
				if (BetterChatBox.Instance.CursorMoveTimer <= 0f)
				{
					BetterChatBox.Instance.CursorMoveTimer = BetterChatBox.CursorMoveSpeed.Value;
					cursorPos--;
				}
			}
			if (Input.GetKeyDown((KeyCode)275) && cursorPos < chatMessage.Length)
			{
				if (Input.GetKey((KeyCode)306))
				{
					cursorPos = GetNextWordIdxFromIdx(cursorPos, chatMessage, keepWhitespace: false);
					return;
				}
				BetterChatBox.Instance.RightArrowHeld = true;
				BetterChatBox.Instance.CursorMoveTimer = BetterChatBox.InputHoldDelay.Value;
				cursorPos++;
			}
			else if (BetterChatBox.Instance.RightArrowHeld && cursorPos < chatMessage.Length)
			{
				BetterChatBox.Instance.CursorMoveTimer -= Time.deltaTime;
				if (BetterChatBox.Instance.CursorMoveTimer <= 0f)
				{
					BetterChatBox.Instance.CursorMoveTimer = BetterChatBox.CursorMoveSpeed.Value;
					cursorPos++;
				}
			}
		}

		internal static void HandleClipboardOperation(ref ChatManager chatManagerInstance, ref int cursorPos, ref string chatMessage)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			if (!Input.GetKey((KeyCode)306))
			{
				return;
			}
			if (Input.GetKeyDown((KeyCode)99))
			{
				GUIUtility.systemCopyBuffer = chatMessage;
			}
			else if (Input.GetKeyDown((KeyCode)118))
			{
				string text = StringHelpers.WithAllWhitespaceStripped(GUIUtility.systemCopyBuffer);
				string text2 = chatMessage.Insert(cursorPos, text);
				if (Utility.IsNullOrWhiteSpace(text) || text2.Length >= 50)
				{
					ChatPatch.ChatFailInputEffect();
					return;
				}
				chatMessage = text2;
				cursorPos += text.Length;
				chatManagerInstance.TypeEffect(Color.yellow);
			}
			else if (Input.GetKeyDown((KeyCode)120))
			{
				if (BetterChatBox.CutToSystemClipboard.Value)
				{
					GUIUtility.systemCopyBuffer = chatMessage;
				}
				chatMessage = string.Empty;
				cursorPos = 0;
				chatManagerInstance.CharRemoveEffect();
			}
		}

		private static int GetNextWordIdxFromIdx(int from, string str, bool keepWhitespace)
		{
			int i;
			for (i = from; i < str.Length && char.IsWhiteSpace(str[i]); i++)
			{
			}
			if (!char.IsWhiteSpace(str[from]))
			{
				for (; i < str.Length && !char.IsWhiteSpace(str[i]); i++)
				{
				}
			}
			if (!keepWhitespace)
			{
				for (; i < str.Length && !char.IsWhiteSpace(str[i]); i++)
				{
				}
			}
			return i;
		}

		private static int GetPreviousWordIdxFromIdx(int from, string str, bool keepWhitespace)
		{
			int num = from - 1;
			while (num >= 0 && char.IsWhiteSpace(str[num]))
			{
				num--;
			}
			if (!char.IsWhiteSpace(str[from - 1]))
			{
				while (num >= 0 && !char.IsWhiteSpace(str[num]))
				{
					num--;
				}
			}
			if (!keepWhitespace)
			{
				while (num >= 0 && !char.IsWhiteSpace(str[num]))
				{
					num--;
				}
			}
			return num + 1;
		}
	}
}