using System.Collections; using System; using Beefy.gfx; using Beefy.geom; using Beefy.widgets; using System.Threading; using Beefy.utils; using IDE.util; using Beefy.theme.dark; using System.IO; using IDE.ui; using System.Diagnostics; namespace IDE { [Reflect(.All | .ApplyToInnerTypes)] class Settings { public class VSSettings { public String mBin32Path = new .() ~ delete _; public String mBin64Path = new .() ~ delete _; public List mLib32Paths = new .() ~ DeleteContainerAndItems!(_); public List mLib64Paths = new .() ~ DeleteContainerAndItems!(_); public bool mManuallySet; public void Serialize(StructuredData sd) { sd.Add("ManuallySet", mManuallySet); sd.Add("Bin32Path", mBin32Path); sd.Add("Bin64Path", mBin64Path); using (sd.CreateArray("Lib32Paths")) { for (let str in mLib32Paths) sd.Add(str); } using (sd.CreateArray("Lib64Paths")) { for (let str in mLib64Paths) sd.Add(str); } } public void Deserialize(StructuredData sd) { mManuallySet = sd.GetBool("ManuallySet"); if ((!mManuallySet) && (!mBin64Path.IsEmpty)) return; sd.GetString("Bin32Path", mBin32Path); sd.GetString("Bin64Path", mBin64Path); void ReadPaths(String pathName, List paths) { HashSet newPaths = scope .(); List prevPaths = scope .(); for (var str in paths) prevPaths.Add(str); paths.Clear(); for ( sd.Enumerate(pathName)) { var str = new String(); sd.GetCurString(str); if (newPaths.Add(str)) paths.Add(str); else delete str; } for (var path in prevPaths) { if (!newPaths.Contains(path)) paths.Add(path); else delete path; } } ReadPaths("Lib32Paths", mLib32Paths); ReadPaths("Lib64Paths", mLib64Paths); } [CLink, CallingConvention(.Stdcall)] static extern char8* VSSupport_Find(); public bool IsConfigured() { return !mLib32Paths.IsEmpty || !mLib64Paths.IsEmpty || !mBin32Path.IsEmpty || !mBin64Path.IsEmpty; } public void SetDefaults() { mManuallySet = false; #if BF_PLATFORM_WINDOWS StringView vsInfo = .(VSSupport_Find()); ClearAndDeleteItems(mLib32Paths); ClearAndDeleteItems(mLib64Paths); for (var infoStr in vsInfo.Split('\n')) { if (infoStr.IsEmpty) continue; var infos = infoStr.Split('\t'); switch (infos.GetNext().Get()) { case "TOOL32": mBin32Path.Set(infos.GetNext()); case "TOOL64": mBin64Path.Set(infos.GetNext()); case "LIB32": mLib32Paths.Add(new String(infos.GetNext())); case "LIB64": mLib64Paths.Add(new String(infos.GetNext())); } } #endif } bool Equals(List lhs, List rhs) { if (lhs.Count != rhs.Count) return false; for (int idx < lhs.Count) if (lhs[idx] != rhs[idx]) return false; return true; } public bool Equals(VSSettings vsSettings) { return (Equals(mLib32Paths, vsSettings.mLib32Paths)) && (Equals(mLib64Paths, vsSettings.mLib64Paths)) && (mBin32Path == vsSettings.mBin32Path) && (mBin64Path == vsSettings.mBin64Path); } } public class DebuggerSettings { public enum SymbolServerKind { Yes, No, Ask } public SymbolServerKind mUseSymbolServers = .Yes; public String mSymCachePath = new .("C:\\SymCache") ~ delete _; public List mSymbolSearchPath = new .() ~ DeleteContainerAndItems!(_); public List mAutoFindPaths = new .() ~ DeleteContainerAndItems!(_); public int32 mProfileSampleRate = 1000; public bool mAutoEvaluatePropertiesOnHover = false; public bool mAutoRefreshWatches = false; public void Serialize(StructuredData sd) { sd.Add("UseSymbolServers", mUseSymbolServers); sd.Add("SymCachePath", mSymCachePath); using (sd.CreateArray("SymbolSearchPath")) { for (let str in mSymbolSearchPath) sd.Add(str); } using (sd.CreateArray("AutoFindPaths")) { for (let str in mAutoFindPaths) sd.Add(str); } using (sd.CreateArray("StepFilters")) { for (var stepFilter in gApp.mDebugger.mStepFilterList.Values) { if (!stepFilter.mIsGlobal) continue; if (stepFilter.mKind == .Filtered) sd.Add(stepFilter.mFilter); } sd.RemoveIfEmpty(); } using (sd.CreateArray("StepNotFilters")) { for (var stepFilter in gApp.mDebugger.mStepFilterList.Values) { if (!stepFilter.mIsGlobal) continue; if (stepFilter.mKind == .NotFiltered) sd.Add(stepFilter.mFilter); } sd.RemoveIfEmpty(); } sd.Add("ProfileSampleRate", mProfileSampleRate); sd.Add("AutoEvaluateProperties", mAutoEvaluatePropertiesOnHover); sd.Add("AutoRefreshWatches", mAutoRefreshWatches); } public void Deserialize(StructuredData sd) { sd.Get("UseSymbolServers", ref mUseSymbolServers); sd.Get("SymCachePath", mSymCachePath); ClearAndDeleteItems(mSymbolSearchPath); for ( sd.Enumerate("SymbolSearchPath")) { var str = new String(); sd.GetCurString(str); mSymbolSearchPath.Add(str); } ClearAndDeleteItems(mAutoFindPaths); for ( sd.Enumerate("AutoFindPaths")) { var str = new String(); sd.GetCurString(str); mAutoFindPaths.Add(str); } if (gApp.mDebugger != null) { for ( sd.Enumerate("StepFilters")) { String filter = scope String(); sd.GetCurString(filter); gApp.mDebugger.CreateStepFilter(filter, true, .Filtered); } for ( sd.Enumerate("StepNotFilters")) { String filter = scope String(); sd.GetCurString(filter); gApp.mDebugger.CreateStepFilter(filter, true, .NotFiltered); } } sd.Get("ProfileSampleRate", ref mProfileSampleRate); sd.Get("AutoEvaluateProperties", ref mAutoEvaluatePropertiesOnHover); sd.Get("AutoRefreshWatches", ref mAutoRefreshWatches); } public void Apply() { #if CLI return; #endif #unwarn String symbolServerPath = scope String()..Join("\n", mSymbolSearchPath.GetEnumerator()); if (mUseSymbolServers == .No) { gApp.mDebugger.SetSymSrvOptions("", "", .Disable); } gApp.mDebugger.SetSymSrvOptions(mSymCachePath, symbolServerPath, .None); String remapStr = scope .(); for (var entry in mAutoFindPaths) { if (entry.Contains('@')) { if (!remapStr.IsEmpty) remapStr.Append("\n"); remapStr.Append(entry); } } remapStr.Replace('@', '='); gApp.mDebugger.SetSourcePathRemap(remapStr); mProfileSampleRate = Math.Clamp(mProfileSampleRate, 10, 10000); gApp.mDebugger.IncrementStateIdx(); gApp.RefreshWatches(); } public void SetDefaults() { /*String appDataPath = scope String(); Platform.GetStrHelper(appDataPath, scope (outPtr, outSize, outResult) => { Platform.BfpDirectory_GetSysDirectory(.AppData_Local, outPtr, outSize, (Platform.BfpFileResult*)outResult); });*/ mSymbolSearchPath.Add(new String("https://msdl.microsoft.com/download/symbols")); mAutoFindPaths.Add(new String(@"C:\Program Files (x86)\Microsoft Visual Studio*")); mAutoFindPaths.Add(new String(@"C:\Program Files (x86)\Windows Kits\10\Source")); } } public class CompilerSettings { public int32 mWorkerThreads = 6; public void Serialize(StructuredData sd) { sd.Add("WorkerThreads", mWorkerThreads); } public void Deserialize(StructuredData sd) { sd.Get("WorkerThreads", ref mWorkerThreads); } public void SetDefaults() { Platform.BfpSystemResult result; mWorkerThreads = Platform.BfpSystem_GetNumLogicalCPUs(&result); } } public class Colors { public Color mText = 0xFFFFFFFF; public Color mTextDisabled = 0xFFA8A8A8; public Color mTextSelected = 0xFF2f5c88; public Color mWindow = 0xFF44444D; public Color mDialogOutlineIn = 0xFF404040; public Color mDialogOutlineOut = 0xFF202020; public Color mGrid = 0x0CFFFFFF; public Color mBackground = 0xFF1C1C24; public Color mSelectedOutline = 0xFFCFAE11; public Color mMenuFocused = 0xFFE5A910; public Color mMenuSelected = 0xFFCB9B80; public Color mAutoCompleteSubText = 0xFFB0B0B0; public Color mAutoCompleteDocText = 0xFFC0C0C0; public Color mAutoCompleteActiveText = 0xFFB0B0FF; public Color mWorkspaceDisabledText = 0xFFA0A0A0; public Color mWorkspaceFailedText = 0xFFE04040; public Color mWorkspaceManualIncludeText = 0xFFE0E0FF; public Color mWorkspaceIgnoredText = 0xFF909090; public Color mWorkspaceCutText = 0xFFC0B0B0; public Color mCode = 0xFFFFFFFF; public Color mKeyword = 0xFFE1AE9A; public Color mLiteral = 0XFFC8A0FF; public Color mIdentifier = 0xFFFFFFFF; public Color mComment = 0xFF75715E; public Color mLocal = 0xFFFFFFFF; public Color mParameter = 0xFFFFFFFF; public Color mMember = 0xFFFFFFFF; public Color mMethod = 0xFFA6E22A; public Color mType = 0xFF66D9EF; public Color mPrimitiveType = 0xFF66D9EF; public Color mStruct = 0xFF66D9EF; public Color mGenericParam = 0xFF66D9EF; public Color mRefType = 0xFF66A0EF; public Color mInterface = 0xFF9A9EEB; public Color mNamespace = 0xFF7BEEB7; public Color mDisassemblyText = 0xFFB0B0B0; public Color mDisassemblyFileName = 0XFFFF0000; public Color mError = 0xFFFF0000; public Color mBuildError = 0xFFFF8080; public Color mBuildWarning = 0xFFFFFF80; public Color mBuildSuccess = 0xFF80FF80; public Color mVisibleWhiteSpace = 0xFF9090C0; public Color mCurrentLineHilite = 0xFF4C4C54; public Color mCurrentLineNumberHilite = 0x18FFFFFF; public Color mCharPairHilite = 0x1DFFFFFF; public Color mCodeHilite = 0xFF384858; public Color mCodeHiliteUnfocused = 0x80384858; public void Deserialize(StructuredData sd) { void GetColor(String name, ref Color color) { sd.Get(name, ref *(int32*)&color); if ((color & 0xFF000000) == 0) color |= 0xFF000000; } GetColor("Text", ref mText); GetColor("TextDisabled", ref mTextDisabled); GetColor("TextSelected", ref mTextSelected); GetColor("Window", ref mWindow); GetColor("DialogOutlineIn", ref mDialogOutlineIn); GetColor("DialogOutlineOut", ref mDialogOutlineOut); GetColor("Grid", ref mGrid); GetColor("Background", ref mBackground); GetColor("SelectedOutline", ref mSelectedOutline); GetColor("MenuFocused", ref mMenuFocused); GetColor("MenuSelected", ref mMenuSelected); GetColor("AutoCompleteSubText", ref mAutoCompleteSubText); GetColor("AutoCompleteDocText", ref mAutoCompleteDocText); GetColor("AutoCompleteActiveText", ref mAutoCompleteActiveText); GetColor("WorkspaceDisabledText", ref mWorkspaceDisabledText); GetColor("WorkspaceFailedText", ref mWorkspaceFailedText); GetColor("WorkspaceManualIncludeText", ref mWorkspaceManualIncludeText); GetColor("WorkspaceIgnoredText", ref mWorkspaceIgnoredText); GetColor("CurrentLineHilite", ref mCurrentLineHilite); GetColor("Code", ref mCode); GetColor("Keyword", ref mKeyword); GetColor("Literal", ref mLiteral); GetColor("Identifier", ref mIdentifier); mLocal = mIdentifier; GetColor("Local", ref mLocal); mParameter = mLocal; GetColor("Parameter", ref mParameter); mMember = mIdentifier; GetColor("Member", ref mMember); mMethod = mMember; GetColor("Method", ref mMethod); GetColor("Comment", ref mComment); if (sd.Contains("Type")) { GetColor("Type", ref mType); if (!sd.Contains("PrimitiveType")) mPrimitiveType = mType; if (!sd.Contains("Struct")) mStruct = mType; if (!sd.Contains("RefType")) mRefType = mType; if (!sd.Contains("Interface")) mInterface = mType; if (!sd.Contains("GenericParam")) mGenericParam = mType; } GetColor("PrimitiveType", ref mPrimitiveType); GetColor("Struct", ref mStruct); GetColor("RefType", ref mRefType); GetColor("Interface", ref mInterface); GetColor("GenericParam", ref mGenericParam); GetColor("Namespace", ref mNamespace); GetColor("DisassemblyText", ref mDisassemblyText); GetColor("DisassemblyFileName", ref mDisassemblyFileName); GetColor("Error", ref mError); GetColor("BuildError", ref mBuildError); GetColor("BuildWarning", ref mBuildWarning); GetColor("BuildSuccess", ref mBuildSuccess); GetColor("VisibleWhiteSpace", ref mVisibleWhiteSpace); GetColor("CurrentLineHilite", ref mCurrentLineHilite); GetColor("CurrentLineNumberHilite", ref mCurrentLineNumberHilite); GetColor("CharPairHilite", ref mCharPairHilite); GetColor("CodeHilite", ref mCodeHilite); GetColor("CodeHiliteUnfocused", ref mCodeHiliteUnfocused); } public void Apply() { SourceEditWidgetContent.sTextColors[(.)SourceElementType.Normal] = mCode; SourceEditWidgetContent.sTextColors[(.)SourceElementType.Keyword] = mKeyword; SourceEditWidgetContent.sTextColors[(.)SourceElementType.Literal] = mLiteral; SourceEditWidgetContent.sTextColors[(.)SourceElementType.Comment] = mComment; SourceEditWidgetContent.sTextColors[(.)SourceElementType.Identifier] = mIdentifier; SourceEditWidgetContent.sTextColors[(.)SourceElementType.Local] = mLocal; SourceEditWidgetContent.sTextColors[(.)SourceElementType.Parameter] = mParameter; SourceEditWidgetContent.sTextColors[(.)SourceElementType.Member] = mMember; SourceEditWidgetContent.sTextColors[(.)SourceElementType.Method] = mMethod; SourceEditWidgetContent.sTextColors[(.)SourceElementType.Type] = mType; SourceEditWidgetContent.sTextColors[(.)SourceElementType.PrimitiveType] = mPrimitiveType; SourceEditWidgetContent.sTextColors[(.)SourceElementType.Struct] = mStruct; SourceEditWidgetContent.sTextColors[(.)SourceElementType.GenericParam] = mGenericParam; SourceEditWidgetContent.sTextColors[(.)SourceElementType.RefType] = mRefType; SourceEditWidgetContent.sTextColors[(.)SourceElementType.Interface] = mInterface; SourceEditWidgetContent.sTextColors[(.)SourceElementType.Namespace] = mNamespace; SourceEditWidgetContent.sTextColors[(.)SourceElementType.Disassembly_Text] = mDisassemblyText; SourceEditWidgetContent.sTextColors[(.)SourceElementType.Disassembly_FileName] = mDisassemblyFileName; SourceEditWidgetContent.sTextColors[(.)SourceElementType.Error] = mError; SourceEditWidgetContent.sTextColors[(.)SourceElementType.BuildError] = mBuildError; SourceEditWidgetContent.sTextColors[(.)SourceElementType.BuildWarning] = mBuildWarning; SourceEditWidgetContent.sTextColors[(.)SourceElementType.BuildSuccess] = mBuildSuccess; SourceEditWidgetContent.sTextColors[(.)SourceElementType.VisibleWhiteSpace] = mVisibleWhiteSpace; DarkTheme.COLOR_TEXT = mText; DarkTheme.COLOR_TEXT_DISABLED = mTextDisabled; DarkTheme.COLOR_TEXT_SELECTED = mTextSelected; DarkTheme.COLOR_WINDOW = mWindow; DarkTheme.COLOR_DIALOG_OUTLINE_IN = mDialogOutlineIn; DarkTheme.COLOR_DIALOG_OUTLINE_OUT = mDialogOutlineOut; DarkTheme.COLOR_GRID = mGrid; DarkTheme.COLOR_BKG = mBackground; DarkTheme.COLOR_SELECTED_OUTLINE = mSelectedOutline; DarkTheme.COLOR_MENU_FOCUSED = mMenuFocused; DarkTheme.COLOR_MENU_SELECTED = mMenuSelected; DarkTheme.COLOR_CURRENT_LINE_HILITE = mCurrentLineHilite; DarkTheme.COLOR_CHAR_PAIR_HILITE = mCharPairHilite; } } public class UISettings { public enum InsertNewTabsKind { LeftOfExistingTabs, RightOfExistingTabs, } public Colors mColors = new .() ~ delete _; public float mScale = 100; public InsertNewTabsKind mInsertNewTabs = .LeftOfExistingTabs; public List mTheme = new .() ~ DeleteContainerAndItems!(_); public bool mShowStartupPanel = true; public void SetDefaults() { DeleteAndNullify!(mColors); mColors = new .(); mScale = 100; mInsertNewTabs = .LeftOfExistingTabs; ClearAndDeleteItems(mTheme); mShowStartupPanel = true; } public void Apply() { DeleteAndNullify!(mColors); mColors = new .(); if (DarkTheme.sDarkTheme == null) return; for (int scale < 3) DarkTheme.sDarkTheme.mUIFileNames[scale].Clear(); void LoadTheme(StringView themeFilePath) { if (!File.Exists(themeFilePath)) return; StructuredData sd = scope .(); if (sd.Load(themeFilePath) case .Err(var err)) { gApp.OutputErrorLine($"Failed to load theme file '{themeFilePath}': {err}"); return; } using (sd.Open("Colors")) mColors.Deserialize(sd); } String imgCreatePath = scope String(gApp.mInstallDir, "images/ImgCreate.exe"); let imgCreateExeTime = File.GetLastWriteTime(imgCreatePath).GetValueOrDefault(); for (let theme in mTheme) { String relPath = scope .()..Append(gApp.mInstallDir, "themes/"); String absPath = scope .(); Path.GetAbsolutePath(theme, relPath, absPath); if (absPath.EndsWith(".TOML", .OrdinalIgnoreCase)) { LoadTheme(absPath); continue; } absPath.Append("/"); bool needsRebuild = false; let origImageTime = File.GetLastWriteTime(scope String(absPath, "../../images/DarkUI.png")).GetValueOrDefault(); if (origImageTime != default) { DateTime maxSrcImgTime = default; DateTime minDestImgTime = default; for (int scale < 3) { String srcImgPath = scope .(absPath); String srcImgPath2 = scope .(absPath); String destImgPath = scope .(absPath); switch (scale) { case 0: srcImgPath.Append("UI.psd"); srcImgPath2.Append("UI.png"); destImgPath.Append("cache/UI.png"); case 1: srcImgPath.Append("UI_2.psd"); srcImgPath2.Append("UI_2.png"); destImgPath.Append("cache/UI_2.png"); case 2: srcImgPath.Append("UI_4.psd"); srcImgPath2.Append("UI_2.png"); destImgPath.Append("cache/UI_2.png"); } maxSrcImgTime = Math.Max(maxSrcImgTime, Math.Max(File.GetLastWriteTime(srcImgPath).GetValueOrDefault(), File.GetLastWriteTime(srcImgPath2).GetValueOrDefault())); let destImageTime = File.GetLastWriteTime(destImgPath).GetValueOrDefault(); if (scale == 0) minDestImgTime = destImageTime; else minDestImgTime = Math.Min(minDestImgTime, destImageTime); } if (maxSrcImgTime > minDestImgTime) needsRebuild = true; if (origImageTime > minDestImgTime) needsRebuild = true; if (imgCreateExeTime > minDestImgTime) needsRebuild = true; } if (needsRebuild) { ProcessStartInfo procInfo = scope ProcessStartInfo(); procInfo.UseShellExecute = false; procInfo.RedirectStandardError = true; procInfo.RedirectStandardOutput = true; procInfo.SetFileName(imgCreatePath); procInfo.SetWorkingDirectory(absPath); procInfo.CreateNoWindow = true; SpawnedProcess process = scope SpawnedProcess(); if (process.Start(procInfo) case .Ok) { //Windows.MessageBoxA(default, "Rebuilding theme images...", "Rebuilding Theme", Windows.MB_OK); process.WaitFor(); } } for (int scale < 3) { String imgPath = scope .(absPath); switch (scale) { case 0: imgPath.Append("cache/UI.png"); case 1: imgPath.Append("cache/UI_2.png"); case 2: imgPath.Append("cache/UI_4.png"); } if (File.Exists(imgPath)) { DarkTheme.sDarkTheme.mUIFileNames[scale].Set(imgPath); } } String themeFilePath = scope .()..Append(absPath, "theme.toml"); LoadTheme(themeFilePath); String userFilePath = scope .()..Append(absPath, "user.toml"); LoadTheme(userFilePath); } mColors.Apply(); } public void Serialize(StructuredData sd) { sd.Add("Scale", mScale); using (sd.CreateArray("Theme")) { for (let str in mTheme) sd.Add(str); } sd.Add("InsertNewTabs", mInsertNewTabs); sd.Add("ShowStartupPanel", mShowStartupPanel); } public void Deserialize(StructuredData sd) { sd.Get("Scale", ref mScale); ClearAndDeleteItems(mTheme); for (sd.Enumerate("Theme")) { var str = new String(); sd.GetCurString(str); mTheme.Add(str); } sd.Get("InsertNewTabs", ref mInsertNewTabs); sd.Get("ShowStartupPanel", ref mShowStartupPanel); } } public class EditorSettings { public enum LockWhileDebuggingKind { Never, Always, WhenNotHotSwappable } public enum AutoCompleteShowKind { Popup, Panel, PanelIfVisible, None } public enum FileRecoveryKind { No, Yes, BackupOnly } public enum TabsOrSpaces { Tabs, Spaces } public enum CompilerKind { Resolve, Build } public List mFonts = new .() ~ DeleteContainerAndItems!(_); public float mFontSize = 12; public float mLineHeightScale = 1.0f; public AutoCompleteShowKind mAutoCompleteShowKind = .PanelIfVisible; public bool mAutoCompleteRequireControl = true; public bool mAutoCompleteRequireTab = false; public bool mAutoCompleteOnEnter = true; public bool mAutoCompleteShowDocumentation = true; public bool mFuzzyAutoComplete = false; public bool mShowLocatorAnim = true; public bool mHiliteCursorReferences = true; public bool mDebugMultiCursor = false; public bool mHiliteCurrentLine = false; public bool mLockEditing; public LockWhileDebuggingKind mLockEditingWhenDebugging = .WhenNotHotSwappable;// Only applicable for public CompilerKind mEmitCompiler; // non-Beef sources public bool mPerforceAutoCheckout = true; public bool mSpellCheckEnabled = true; public bool mShowLineNumbers = true; public bool mFreeCursorMovement; public FileRecoveryKind mEnableFileRecovery = .Yes; public bool mSyncWithWorkspacePanel = false; public int32 mWrapCommentsAt = 0; public bool mFormatOnSave = false; public bool mIndentCaseLabels = false; public bool mLeftAlignPreprocessor = true; public TabsOrSpaces mTabsOrSpaces = .Tabs; public int32 mTabSize = 4; public void Serialize(StructuredData sd) { using (sd.CreateArray("Fonts")) { for (let str in mFonts) sd.Add(str); } sd.Add("FontSize", mFontSize); sd.Add("LineHeightScale", mLineHeightScale); sd.Add("AutoCompleteShowKind", mAutoCompleteShowKind); sd.Add("AutoCompleteRequireControl", mAutoCompleteRequireControl); sd.Add("AutoCompleteRequireTab", mAutoCompleteRequireTab); sd.Add("AutoCompleteOnEnter", mAutoCompleteOnEnter); sd.Add("AutoCompleteShowDocumentation", mAutoCompleteShowDocumentation); sd.Add("FuzzyAutoComplete", mFuzzyAutoComplete); sd.Add("ShowLocatorAnim", mShowLocatorAnim); sd.Add("HiliteCursorReferences", mHiliteCursorReferences); sd.Add("HiliteCurrentLine", mHiliteCurrentLine); sd.Add("LockEditing", mLockEditing); sd.Add("LockEditingWhenDebugging", mLockEditingWhenDebugging); sd.Add("EmitCompiler", mEmitCompiler); sd.Add("PerforceAutoCheckout", mPerforceAutoCheckout); sd.Add("SpellCheckEnabled", mSpellCheckEnabled); sd.Add("ShowLineNumbers", mShowLineNumbers); sd.Add("FreeCursorMovement", mFreeCursorMovement); sd.Add("EnableFileRecovery", mEnableFileRecovery); sd.Add("SyncWithWorkspacePanel", mSyncWithWorkspacePanel); sd.Add("WrapCommentsAt", mWrapCommentsAt); sd.Add("FormatOnSave", mFormatOnSave); sd.Add("TabsOrSpaces", mTabsOrSpaces); sd.Add("TabSize", mTabSize); sd.Add("IndentCaseLabels", mIndentCaseLabels); sd.Add("LeftAlignPreprocessor", mLeftAlignPreprocessor); } public void Deserialize(StructuredData sd) { ClearAndDeleteItems(mFonts); for (sd.Enumerate("Fonts")) { var str = new String(); sd.GetCurString(str); mFonts.Add(str); } sd.Get("UIScale", ref gApp.mSettings.mUISettings.mScale); // Legacy sd.Get("FontSize", ref mFontSize); sd.Get("LineHeightScale", ref mLineHeightScale); sd.Get("AutoCompleteShowKind", ref mAutoCompleteShowKind); sd.Get("AutoCompleteRequireControl", ref mAutoCompleteRequireControl); sd.Get("AutoCompleteRequireTab", ref mAutoCompleteRequireTab); sd.Get("AutoCompleteOnEnter", ref mAutoCompleteOnEnter); sd.Get("AutoCompleteShowDocumentation", ref mAutoCompleteShowDocumentation); sd.Get("FuzzyAutoComplete", ref mFuzzyAutoComplete); sd.Get("ShowLocatorAnim", ref mShowLocatorAnim); sd.Get("HiliteCursorReferences", ref mHiliteCursorReferences); sd.Get("HiliteCurrentLine", ref mHiliteCurrentLine); sd.Get("LockEditing", ref mLockEditing); sd.Get("LockEditingWhenDebugging", ref mLockEditingWhenDebugging); sd.Get("EmitCompiler", ref mEmitCompiler); sd.Get("PerforceAutoCheckout", ref mPerforceAutoCheckout); sd.Get("SpellCheckEnabled", ref mSpellCheckEnabled); sd.Get("ShowLineNumbers", ref mShowLineNumbers); sd.Get("FreeCursorMovement", ref mFreeCursorMovement); sd.GetEnum("EnableFileRecovery", ref mEnableFileRecovery); sd.Get("SyncWithWorkspacePanel", ref mSyncWithWorkspacePanel); sd.Get("WrapCommentsAt", ref mWrapCommentsAt); sd.Get("FormatOnSave", ref mFormatOnSave); sd.Get("TabsOrSpaces", ref mTabsOrSpaces); sd.Get("TabSize", ref mTabSize); sd.Get("IndentCaseLabels", ref mIndentCaseLabels); sd.Get("LeftAlignPreprocessor", ref mLeftAlignPreprocessor); } public void SetDefaults() { mFonts.Add(new String("fonts/SourceCodePro-Regular.ttf")); mFonts.Add(new String("Segoe UI")); mFonts.Add(new String("?Segoe UI Symbol")); mFonts.Add(new String("?Segoe UI Historic")); mFonts.Add(new String("?Segoe UI Emoji")); } public void Apply() { mTabSize = Math.Clamp(mTabSize, 1, 16); if (mSpellCheckEnabled) { if (gApp.mSpellChecker == null) gApp.CreateSpellChecker(); } else { if (gApp.mSpellChecker != null) DeleteAndNullify!(gApp.mSpellChecker); } } } public class KeySettings { public class Entry { public KeyState[] mKeys ~ DeleteContainerAndItems!(_); public String mCommand ~ delete _; } public List mEntries = new .() ~ DeleteContainerAndItems!(_); public void Clear() { ClearAndDeleteItems(mEntries); } void Add(StringView cmd, StringView keys) { let entry = new Entry(); entry.mCommand = new String(cmd); let keyList = scope List(); KeyState.Parse(keys, keyList); let keyArr = new KeyState[keyList.Count]; keyList.CopyTo(keyArr); entry.mKeys = keyArr; mEntries.Add(entry); } public void SetDefaults() { Add("Add Cursor Above", "Ctrl+Alt+Up"); Add("Add Cursor Below", "Ctrl+Alt+Down"); Add("Add Selection to Next Find Match", "Ctrl+D"); Add("Autocomplete", "Ctrl+Space"); Add("Bookmark Next", "F2"); Add("Bookmark Prev", "Shift+F2"); Add("Bookmark Toggle", "Ctrl+F2"); Add("Bookmark Clear", "Ctrl+K, Ctrl+L"); Add("Break All", "Ctrl+Alt+Break"); Add("Breakpoint Configure", "Alt+F9"); Add("Breakpoint Disable", "Ctrl+F9"); Add("Breakpoint Toggle", "F9"); Add("Breakpoint Toggle Thread", "Shift+F9"); Add("Build Workspace", "F7"); Add("Cancel Build", "Ctrl+Break"); Add("Close Document", "Ctrl+W"); Add("Collapse All", "Ctrl+M, Ctrl+A"); Add("Collapse To Definition", "Ctrl+M, Ctrl+O"); Add("Collapse Redo", "Ctrl+M, Ctrl+Y"); Add("Collapse Toggle", "Ctrl+M, Ctrl+M"); Add("Collapse Toggle All", "Ctrl+M, Ctrl+L"); Add("Collapse Undo", "Ctrl+M, Ctrl+Z"); Add("Compile File", "Ctrl+F7"); Add("Comment Block", "Ctrl+K, Ctrl+C"); Add("Comment Lines", "Ctrl+K, Ctrl+/"); Add("Comment Toggle", "Ctrl+K, Ctrl+T"); Add("Debug Comptime", "Alt+F7"); Add("Find Class", "Alt+Shift+L"); Add("Find in Document", "Ctrl+F"); Add("Find in Files", "Ctrl+Shift+F"); Add("Find Next", "F3"); Add("Find Prev", "Shift+F3"); Add("Goto Definition", "F12"); Add("Goto Line", "Ctrl+G"); Add("Goto Method", "Alt+M"); Add("Goto Next Item", "F4"); Add("Make Lowercase", "Ctrl+U"); Add("Make Uppercase", "Ctrl+Shift+U"); Add("Match Brace Select", "Ctrl+Shift+RBracket"); Add("Match Brace", "Ctrl+RBracket"); //Add("Move Last Selection to Next Find Match, "Ctrl+K, Ctrl+D"); Add("Move Line Down", "Alt+Shift+Down"); Add("Move Line Up", "Alt+Shift+Up"); Add("Move Statement Down", "Ctrl+Shift+Down"); Add("Move Statement Up", "Ctrl+Shift+Up"); Add("Navigate Backwards", "Alt+Left"); Add("Navigate Forwards", "Alt+Right"); Add("Next Document Panel", "Ctrl+Comma"); Add("Open Corresponding", "Alt+O"); Add("Open File in Workspace", "Shift+Alt+O"); Add("Open File", "Ctrl+O"); Add("Open Workspace", "Ctrl+Shift+O"); Add("Quick Info", "Ctrl+K, Ctrl+I"); Add("Recent File Next", "Ctrl+Tab"); Add("Recent File Prev", "Shift+Ctrl+Tab"); Add("Reformat Document", "Ctrl+K, Ctrl+D"); Add("Remove All Breakpoints", "Ctrl+Shift+F9"); Add("Rename Symbol", "Ctrl+R"); Add("Rename Item", "F2"); Add("Replace in Document", "Ctrl+H"); Add("Replace in Files", "Ctrl+Shift+H"); Add("Report Memory", "Ctrl+Alt+Shift+M"); Add("Run To Cursor", "Ctrl+F10"); Add("Run Without Compiling", "Ctrl+Shift+F5"); Add("Save All", "Ctrl+Shift+S"); Add("Save File", "Ctrl+S"); Add("Scope Prev", "Alt+Up"); Add("Scope Next", "Alt+Down"); Add("Set Next Statement", "Ctrl+Shift+F10"); Add("Show Auto Watches", "Ctrl+Alt+A"); Add("Show Autocomplete Panel", "Ctrl+Alt+E"); Add("Show Breakpoints", "Ctrl+Alt+B"); Add("Show Call Stack", "Ctrl+Alt+C"); Add("Show Class View", "Ctrl+Alt+L"); Add("Show Current", "Alt+C"); Add("Show Errors", "Ctrl+Alt+R"); Add("Show Diagnostics", "Ctrl+Alt+D"); Add("Show Error Next", "Ctrl+Shift+F12"); Add("Show File Externally", "Ctrl+Tilde"); Add("Show Find Results", "Ctrl+Alt+F"); Add("Show Fixit", "Ctrl+Period"); Add("Show Immediate", "Ctrl+Alt+I"); Add("Show Memory", "Ctrl+Alt+M"); Add("Show Modules", "Ctrl+Alt+U"); Add("Show Output", "Ctrl+Alt+O"); Add("Show Profiler", "Ctrl+Alt+P"); Add("Show QuickWatch", "Shift+Alt+W"); Add("Show Threads", "Ctrl+Alt+H"); Add("Show Watches", "Ctrl+Alt+W"); Add("Show Console", "Ctrl+Alt+N"); Add("Show Terminal", "Ctrl+Alt+T"); Add("Show Workspace Explorer", "Ctrl+Alt+S"); Add("Start Debugging", "F5"); Add("Start Without Debugging", "Ctrl+F5"); Add("Start Without Compiling", "Alt+F5"); Add("Step Into", "F11"); Add("Step Out", "Shift+F11"); Add("Step Over", "F10"); Add("Stop Debugging", "Shift+F5"); Add("Sync With Workspace Panel", "Ctrl+[, S"); Add("Tab First", "Ctrl+Alt+Home"); Add("Tab Last", "Ctrl+Alt+End"); Add("Tab Next", "Ctrl+Alt+PageDown"); Add("Tab Prev", "Ctrl+Alt+PageUp"); Add("Uncomment Selection", "Ctrl+K, Ctrl+U"); Add("Zoom In", "Ctrl+Equals"); Add("Zoom Out", "Ctrl+Minus"); Add("Zoom Reset", "Ctrl+0"); } public void Apply() { #if CLI return; #endif #unwarn for (var cmd in gApp.mCommands.mCommandMap.Values) cmd.mNext = null; gApp.mCommands.mKeyMap.Clear(); for (let entry in mEntries) { var curCmdMap = gApp.mCommands.mKeyMap; for (let keyState in entry.mKeys) { bool isChordEntry = @keyState < entry.mKeys.Count - 1; IDECommand ideCommand = null; if (!isChordEntry) { if (!gApp.mCommands.mCommandMap.TryGetValue(entry.mCommand, out ideCommand)) { gApp.OutputLineSmart("ERROR: Unable to locate IDE command {0}", entry.mCommand); break;// Boo } ideCommand.mParent = curCmdMap; ideCommand.mBoundKeyState = keyState; } KeyState* keyStatePtr; IDECommandBase* valuePtr; if (curCmdMap.mMap.TryAdd(keyState, out keyStatePtr, out valuePtr)) { if (isChordEntry) { let newCmdMap = new CommandMap(); newCmdMap.mParent = curCmdMap; newCmdMap.mBoundKeyState = keyState; curCmdMap = newCmdMap; *valuePtr = curCmdMap; } else { *valuePtr = ideCommand; } } else { if (isChordEntry) { curCmdMap = (*valuePtr) as CommandMap; if (curCmdMap == null) { gApp.OutputLineSmart("ERROR: The same key is bound for '{0}' and as part of a key chord", entry.mCommand); break; } } else { var prevIDECommand = (*valuePtr) as IDECommand; if (prevIDECommand != null) { var checkPrevCmd = prevIDECommand; while (true) { if (checkPrevCmd.mContextFlags == ideCommand.mContextFlags) gApp.OutputLineSmart("ERROR: The same key is bound for '{0}' and '{1}'", checkPrevCmd.mName, entry.mCommand); if (checkPrevCmd.mNext == null) { curCmdMap.FailValues.Add(ideCommand); break; } checkPrevCmd = checkPrevCmd.mNext; } checkPrevCmd.mNext = ideCommand; } else gApp.OutputLineSmart("ERROR: The same key is bound for '{0}' and as part of a key chord", entry.mCommand); } } } } String keyStr = scope String(); for (var ideCommand in gApp.mCommands.mCommandMap.Values) { if (ideCommand.mMenuItem == null) continue; if (ideCommand.mBoundKeyState != null) { keyStr.Clear(); keyStr.Append("#"); ideCommand.mBoundKeyState.ToString(keyStr); ideCommand.mMenuItem.SetHotKey(keyStr); } else ideCommand.mMenuItem.SetHotKey(.()); } } public void Serialize(StructuredData sd) { let mKeyEntries = scope List(); for (var kv in gApp.mCommands.mCommandMap) { mKeyEntries.Add(kv.value); } mKeyEntries.Sort(scope (lhs, rhs) => String.Compare(lhs.mName, rhs.mName, true)); for (var ideCommand in mKeyEntries) { String keyStr = scope .(); ideCommand.ToString(keyStr); sd.Add(ideCommand.mName, keyStr); } } public void Deserialize(StructuredData sd) { HashSet usedCommands = scope .(); List allocatedStrs = scope .(); defer { ClearAndDeleteItems(allocatedStrs); } Dictionary mappedEntries = scope .(); void AddEntry(List newEntries, Entry entry, bool isNew) { newEntries.Add(entry); // Delete previous command bindings when we are adding new entries let keyEntryStr = scope String(); KeyState.ToString(entry.mKeys, keyEntryStr); //keyEntryStr.Append(" "); //entry.mContextFlags.To keyEntryStr); String* keyPtr; Entry* valuePtr; if (mappedEntries.TryAdd(keyEntryStr, out keyPtr, out valuePtr)) { *keyPtr = new String(keyEntryStr); *valuePtr = entry; } else { if (isNew) { newEntries.Remove(*valuePtr); delete *valuePtr; } } } List newEntries = new .(); for (let cmdStr in sd.Enumerate()) { var keyStr = sd.GetCurrent() as String; if (keyStr == null) continue; if (keyStr.IsWhiteSpace) { var str = new String(cmdStr); usedCommands.Add(str); allocatedStrs.Add(str); continue; } let entry = new Entry(); entry.mCommand = new String(cmdStr); // Fix for command rename if ((cmdStr == "Close Panel") && (!sd.Contains("Close Document"))) entry.mCommand.Set("Close Document"); let keyList = scope List(); KeyState.Parse(keyStr, keyList); let keyArr = new KeyState[keyList.Count]; keyList.CopyTo(keyArr); entry.mKeys = keyArr; usedCommands.Add(entry.mCommand); AddEntry(newEntries, entry, false); } for (var entry in mEntries) { if (usedCommands.Contains(entry.mCommand)) continue; mEntries[@entry.Index] = null; AddEntry(newEntries, entry, true); } DeleteContainerAndItems!(mEntries); mEntries = newEntries; for (let keyEntryStr in mappedEntries.Keys) delete keyEntryStr; } } public struct TutorialsFinished { public bool mCtrlCursor; public bool mRanDebug; public bool mDependencies; } public enum ConsoleKind { Native, Embedded, RedirectToOutput, RedirectToImmediate, } public bool mLoadedSettings; public String mSettingFileText ~ delete _; public DateTime mSettingFileDateTime; public UISettings mUISettings = new .() ~ delete _; public EditorSettings mEditorSettings = new .() ~ delete _; public CompilerSettings mCompilerSettings = new .() ~ delete _; public VSSettings mVSSettings = new .() ~ delete _; public DebuggerSettings mDebuggerSettings = new .() ~ delete _; public KeySettings mKeySettings = new .() ~ delete _; public RecentFiles mRecentFiles = new RecentFiles() ~ delete _; public String mWakaTimeKey = new .() ~ delete _; public String mWindowsTerminal = new .("Powershell") ~ delete _; public ConsoleKind mDebugConsoleKind; public bool mAlwaysEnableConsole; public bool mKeepNativeConsoleOpen; public String mEmscriptenPath = new .() ~ delete _; public bool mEmscriptenPendingInstall; public bool mEnableDevMode; public TutorialsFinished mTutorialsFinished = .(); public this() { SetDefaults(); } public this(Settings prevSettings) { Swap!(mRecentFiles, prevSettings.mRecentFiles); for (var recent in mRecentFiles.mRecents) { recent.mList.ClearAndDeleteItems(); for (var item in recent.mMenuItems) item.Dispose(); recent.mMenuItems.ClearAndDeleteItems(); } } public void SetDefaults() { mVSSettings.SetDefaults(); mUISettings.SetDefaults(); mEditorSettings.SetDefaults(); mCompilerSettings.SetDefaults(); mDebuggerSettings.SetDefaults(); mKeySettings.SetDefaults(); } void GetSettingsPath(String outPath) { outPath.Append(gApp.mInstallDir, "/BeefSettings.toml"); } public void Save() { String path = scope .(); GetSettingsPath(path); let sd = scope StructuredData(); sd.CreateNew(); sd.Add("FileVersion", 1); using (sd.CreateObject("UI")) mUISettings.Serialize(sd); using (sd.CreateObject("Editor")) mEditorSettings.Serialize(sd); using (sd.CreateObject("Keys")) mKeySettings.Serialize(sd); using (sd.CreateObject("Compiler")) mCompilerSettings.Serialize(sd); using (sd.CreateObject("Debugger")) mDebuggerSettings.Serialize(sd); using (sd.CreateObject("VisualStudio")) mVSSettings.Serialize(sd); using (sd.CreateObject("Console")) { sd.Add("WindowsTerminal", mWindowsTerminal); sd.Add("DebugConsole", mDebugConsoleKind); sd.Add("AlwaysEnableConsole", mAlwaysEnableConsole); sd.Add("KeepNativeConsoleOpen", mKeepNativeConsoleOpen); } using (sd.CreateObject("Wasm")) sd.Add("EmscriptenPath", mEmscriptenPath); using (sd.CreateObject("RecentFiles")) { for (RecentFiles.RecentKind recentKind = default; recentKind < RecentFiles.RecentKind.COUNT; recentKind++) { String name = scope .(); recentKind.ToString(name); using (sd.CreateArray(name)) { for (var recentFile in mRecentFiles.GetRecentList(recentKind)) sd.Add(recentFile); } } } using (sd.CreateObject("Options")) { sd.Add("WakaTimeKey", mWakaTimeKey); sd.Add("EnableDevMode", mEnableDevMode); sd.Add("DebugMultiCursor", DarkEditWidgetContent.sDebugMultiCursor); } using (sd.CreateObject("TutorialsFinished")) { sd.Add("CtrlCursor", mTutorialsFinished.mCtrlCursor); sd.Add("RanDebug", mTutorialsFinished.mRanDebug); sd.Add("Dependencies", mTutorialsFinished.mDependencies); } String dataStr = scope String(); sd.ToTOML(dataStr); if ((mSettingFileText == null) || (mSettingFileText != dataStr)) { String.NewOrSet!(mSettingFileText, dataStr); gApp.SafeWriteTextFile(path, dataStr); if (File.GetLastWriteTime(path) case .Ok(let dt)) mSettingFileDateTime = dt; } } public bool WantsReload() { if (mSettingFileDateTime == default) return false; String path = scope .(); GetSettingsPath(path); if (File.GetLastWriteTime(path) case .Ok(let dt)) { if (dt != mSettingFileDateTime) return true; } return false; } public void Load() { String path = scope .(); GetSettingsPath(path); let sd = scope StructuredData(); if (sd.Load(path) case .Err) return; if (File.GetLastWriteTime(path) case .Ok(let dt)) mSettingFileDateTime = dt; String.NewOrSet!(mSettingFileText, sd.[Friend]mSource); mSettingFileText.Replace("\r\n", "\n"); mSettingFileText.Replace('\r', '\n'); mLoadedSettings = true; using (sd.Open("UI")) mUISettings.Deserialize(sd); using (sd.Open("Editor")) mEditorSettings.Deserialize(sd); using (sd.Open("Keys")) mKeySettings.Deserialize(sd); using (sd.Open("Compiler")) mCompilerSettings.Deserialize(sd); using (sd.Open("Debugger")) mDebuggerSettings.Deserialize(sd); using (sd.Open("VisualStudio")) mVSSettings.Deserialize(sd); using (sd.Open("Console")) { sd.Get("WindowsTerminal", mWindowsTerminal); mDebugConsoleKind = sd.GetEnum("DebugConsole", .Native); mAlwaysEnableConsole = sd.GetBool("AlwaysEnableConsole"); mKeepNativeConsoleOpen = sd.GetBool("KeepNativeConsoleOpen"); } using (sd.Open("Wasm")) sd.Get("EmscriptenPath", mEmscriptenPath); using (sd.Open("RecentFiles")) { for (RecentFiles.RecentKind recentKind = default; recentKind < RecentFiles.RecentKind.COUNT; recentKind++) { let recentList = mRecentFiles.GetRecentList(recentKind); String name = scope .(); recentKind.ToString(name); for ( sd.Enumerate(name)) { String fileStr = new String(); sd.GetCurString(fileStr); IDEUtils.FixFilePath(fileStr); recentList.Add(fileStr); } } } using (sd.Open("Options")) { sd.Get("WakaTimeKey", mWakaTimeKey); sd.Get("EnableDevMode", ref mEnableDevMode); sd.Get("DebugMultiCursor", ref DarkEditWidgetContent.sDebugMultiCursor); } using (sd.Open("TutorialsFinished")) { sd.Get("CtrlCursor", ref mTutorialsFinished.mCtrlCursor); sd.Get("RanDebug", ref mTutorialsFinished.mRanDebug); if (!sd.Get("Dependencies", ref mTutorialsFinished.mDependencies)) mTutorialsFinished.mDependencies = true; } } public void Apply() { gApp.mSettings.mUISettings.mScale = Math.Clamp(gApp.mSettings.mUISettings.mScale, 50, 400); gApp.mSettings.mEditorSettings.mFontSize = Math.Clamp(gApp.mSettings.mEditorSettings.mFontSize, 6.0f, 72.0f); gApp.mSettings.mEditorSettings.mLineHeightScale = Math.Clamp(gApp.mSettings.mEditorSettings.mLineHeightScale, 0.125f, 10.0f); mUISettings.Apply(); mEditorSettings.Apply(); Font.ClearFontNameCache(); gApp.PhysSetScale(gApp.mSettings.mUISettings.mScale / 100.0f, true); DeleteAndNullify!(gApp.mKeyChordState); mKeySettings.Apply(); mDebuggerSettings.Apply(); for (var window in gApp.mWindows) { if (var widgetWindow = window as WidgetWindow) { widgetWindow.mRootWidget.RehupSize(); } } for (let value in gApp.mFileEditData.Values) if (value.mEditWidget != null) { var ewc = (SourceEditWidgetContent)value.mEditWidget.Content; ewc.mHiliteCurrentLine = gApp.mSettings.mEditorSettings.mHiliteCurrentLine; ewc.mLineHeightScale = gApp.mSettings.mEditorSettings.mLineHeightScale; ewc.RehupLineCoords(); } if (!mWakaTimeKey.IsEmpty) { if (gApp.mWakaTime == null) gApp.mWakaTime = new WakaTime(mWakaTimeKey); } else { DeleteAndNullify!(gApp.mWakaTime); } } } }