diff --git a/BeefLibs/SDL2/src/SDLApp.bf b/BeefLibs/SDL2/src/SDLApp.bf index 90026bbd..14a8eadd 100644 --- a/BeefLibs/SDL2/src/SDLApp.bf +++ b/BeefLibs/SDL2/src/SDLApp.bf @@ -117,7 +117,7 @@ namespace SDL2 SDL.EventState(.JoyDeviceAdded, .Disable); SDL.EventState(.JoyDeviceRemoved, .Disable); - mWindow = SDL.CreateWindow(mTitle, .Undefined, .Undefined, mWidth, mHeight, .Shown); + mWindow = SDL.CreateWindow(mTitle, .Undefined, .Undefined, mWidth, mHeight, .Hidden); // Initially hide window mRenderer = SDL.CreateRenderer(mWindow, -1, .Accelerated); mScreen = SDL.GetWindowSurface(mWindow); SDLImage.Init(.PNG | .JPG); @@ -246,7 +246,9 @@ namespace SDL2 if (curPhysTickCount == 0) { // Initial render - Render(); + Render(); + // Show initially hidden window, mitigates white flash on slow startups + SDL.ShowWindow(mWindow); } else { diff --git a/IDE/src/Commands.bf b/IDE/src/Commands.bf index f0b2de76..c7eeb2cf 100644 --- a/IDE/src/Commands.bf +++ b/IDE/src/Commands.bf @@ -194,6 +194,8 @@ namespace IDE Add("Close Document", new () => { gApp.[Friend]TryCloseCurrentDocument(); }); Add("Close Panel", new () => { gApp.[Friend]TryCloseCurrentPanel(); }); Add("Close Workspace", new => gApp.[Friend]Cmd_CloseWorkspaceAndSetupNew); + Add("Comment Block", new => gApp.[Friend]CommentBlock); + Add("Comment Lines", new => gApp.[Friend]CommentLines); Add("Comment Selection", new => gApp.[Friend]CommentSelection); Add("Compile File", new => gApp.Cmd_CompileFile); Add("Debug All Tests", new () => { gApp.[Friend]RunTests(true, true); }); diff --git a/IDE/src/IDEApp.bf b/IDE/src/IDEApp.bf index 103c65fd..0c695e54 100644 --- a/IDE/src/IDEApp.bf +++ b/IDE/src/IDEApp.bf @@ -2314,6 +2314,11 @@ namespace IDE widget.RemoveSelf(); } + WithSourceViewPanels(scope (sourceViewPanel) => + { + sourceViewPanel.Dispose(); + }); + if (!mRunningTestScript) { mActiveDocumentsTabbedView = null; @@ -2424,6 +2429,22 @@ namespace IDE sewc.DuplicateLine(); } + [IDECommand] + void CommentBlock() + { + var sewc = GetActiveSourceEditWidgetContent(); + if (sewc != null) + sewc.CommentBlock(); + } + + [IDECommand] + void CommentLines() + { + var sewc = GetActiveSourceEditWidgetContent(); + if (sewc != null) + sewc.CommentLines(); + } + [IDECommand] void CommentSelection() { @@ -5386,6 +5407,8 @@ namespace IDE advancedEditMenu.AddMenuItem(null); AddMenuItem(advancedEditMenu, "Make Uppercase", "Make Uppercase"); AddMenuItem(advancedEditMenu, "Make Lowercase", "Make Lowercase"); + AddMenuItem(advancedEditMenu, "Comment Block", "Comment Block"); + AddMenuItem(advancedEditMenu, "Comment Lines", "Comment Lines"); AddMenuItem(advancedEditMenu, "Comment Selection", "Comment Selection"); AddMenuItem(advancedEditMenu, "Uncomment Selection", "Uncomment Selection"); AddMenuItem(advancedEditMenu, "Reformat Document", "Reformat Document"); @@ -5712,14 +5735,20 @@ namespace IDE { let sewc = editWidget.mEditWidgetContent as SourceEditWidgetContent; if (sewc != null) - return sewc; + { + if (sewc.mEditWidget.mHasFocus) + return sewc; + return null; + } } } var activeTextPanel = GetActivePanel() as TextPanel; if (activeTextPanel != null) { - return activeTextPanel.EditWidget.mEditWidgetContent as SourceEditWidgetContent; + let sewc = activeTextPanel.EditWidget.mEditWidgetContent as SourceEditWidgetContent; + if ((sewc != null) && (sewc.mEditWidget.mHasFocus)) + return sewc; } return null; diff --git a/IDE/src/Settings.bf b/IDE/src/Settings.bf index 712f6d45..e81f8344 100644 --- a/IDE/src/Settings.bf +++ b/IDE/src/Settings.bf @@ -756,6 +756,8 @@ namespace IDE Add("Cancel Build", "Ctrl+Break"); Add("Close Document", "Ctrl+W"); Add("Compile File", "Ctrl+F7"); + Add("Comment Block", "Ctrl+K, Ctrl+B"); + Add("Comment Lines", "Ctrl+K, Ctrl+L"); Add("Comment Selection", "Ctrl+K, Ctrl+C"); Add("Duplicate Line", "Ctrl+D"); Add("Find Class", "Alt+Shift+L"); diff --git a/IDE/src/ui/OutputPanel.bf b/IDE/src/ui/OutputPanel.bf index e99e10a6..a60696f8 100644 --- a/IDE/src/ui/OutputPanel.bf +++ b/IDE/src/ui/OutputPanel.bf @@ -139,7 +139,8 @@ namespace IDE.ui mOutputWidget.SetText(""); for (var widgetEntry in mInlineWidgets) { - widgetEntry.mWidget.RemoveSelf(); + if (widgetEntry.mWidget.mParent != null) + widgetEntry.mWidget.RemoveSelf(); delete widgetEntry.mWidget; } mInlineWidgets.Clear(); diff --git a/IDE/src/ui/SourceEditWidgetContent.bf b/IDE/src/ui/SourceEditWidgetContent.bf index dbb19ef7..755484b5 100644 --- a/IDE/src/ui/SourceEditWidgetContent.bf +++ b/IDE/src/ui/SourceEditWidgetContent.bf @@ -2160,8 +2160,10 @@ namespace IDE.ui return true; } - public bool ToggleComment(bool? doComment = null) + public bool CommentBlock() { + bool? doComment = true; + if (CheckReadOnly()) return false; @@ -2173,11 +2175,11 @@ namespace IDE.ui mSelection = .(CursorTextPos, cursorEndPos); } - if ((HasSelection()) && (mSelection.Value.Length > 1)) - { + if ((HasSelection()) && (mSelection.Value.Length > 1)) + { var startLineAndCol = CursorLineAndColumn; - UndoBatchStart undoBatchStart = new UndoBatchStart("embeddedToggleComment"); + UndoBatchStart undoBatchStart = new UndoBatchStart("embeddedCommentBlock"); mData.mUndoManager.Add(undoBatchStart); mData.mUndoManager.Add(new SetCursorAction(this)); @@ -2200,20 +2202,7 @@ namespace IDE.ui int firstCharPos = minPos + (startLen - afterTrimStart); int lastCharPos = maxPos - (afterTrimStart - afterTrimEnd); - if ((doComment != true) && (trimmedStr.StartsWith("/*"))) - { - if (trimmedStr.EndsWith("*/")) - { - mSelection = EditSelection(firstCharPos, firstCharPos + 2); - DeleteChar(); - mSelection = EditSelection(lastCharPos - 4, lastCharPos - 2); - DeleteChar(); - - if (doComment != null) - mSelection = EditSelection(firstCharPos, lastCharPos - 4); - } - } - else if (doComment != false) + if (doComment != false) { CursorTextPos = firstCharPos; InsertAtCursor("/*"); @@ -2232,8 +2221,244 @@ namespace IDE.ui if (doComment == null) mSelection = null; - return true; - } + return true; + } + + return false; + } + + public bool CommentLines() + { + bool? doComment = true; + if (CheckReadOnly()) + return false; + bool noStar = false; + + var startLineAndCol = CursorLineAndColumn; + if ((!HasSelection()) && (doComment != null)) + { + CursorToLineEnd(); + int cursorEndPos = CursorTextPos; + + mSelection = .(CursorTextPos, cursorEndPos); + noStar = true; + } + + if (true || (HasSelection()) && (mSelection.Value.Length > 0)) + { + // set selection to begin from line start + int lineIdx; + int lineChar; + GetLineCharAtIdx(mSelection.GetValueOrDefault().MinPos,out lineIdx, out lineChar); + MoveCursorTo(lineIdx, 0); + mSelection = .(CursorTextPos, mSelection.GetValueOrDefault().MaxPos); + + UndoBatchStart undoBatchStart = new UndoBatchStart("embeddedCommentLines"); + mData.mUndoManager.Add(undoBatchStart); + + mData.mUndoManager.Add(new SetCursorAction(this)); + + int minPos = mSelection.GetValueOrDefault().MinPos; + int maxPos = mSelection.GetValueOrDefault().MaxPos; + mSelection = null; + + var str = scope String(); + ExtractString(minPos, maxPos - minPos, str); + var trimmedStr = scope String(); + trimmedStr.Append(str); + int32 startLen = (int32)trimmedStr.Length; + trimmedStr.TrimStart(); + int32 afterTrimStart = (int32)trimmedStr.Length; + trimmedStr.TrimEnd(); + //int32 afterTrimEnd = (int32)trimmedStr.Length; + trimmedStr.Append('\n'); + + int firstCharPos = minPos + (startLen - afterTrimStart); + //int lastCharPos = maxPos - (afterTrimStart - afterTrimEnd); + + int q = 0; + + if (doComment != false) + { + while (firstCharPos >= 0 && SafeGetChar(firstCharPos) != '\n') + { + firstCharPos--; + } + bool blank=true; + for (int i = firstCharPos + 1; i < maxPos + q; i++) + { + blank=false; + CursorTextPos = i; // needed to add i < maxPos + q; for this to work with InsertAtCursor + InsertAtCursor("//"); q++; q++; + + while (SafeGetChar(i) != '\n' && i < maxPos + q) + { + i++; + } + } + mSelection = EditSelection(minPos, maxPos + q); + } + + if (undoBatchStart != null) + mData.mUndoManager.Add(undoBatchStart.mBatchEnd); + + CursorLineAndColumn = startLineAndCol; + + if (doComment == null) + mSelection = null; + + return true; + } + + //return false; + } + + public bool ToggleComment(bool? doComment = null) + { + if (CheckReadOnly()) + return false; + bool noStar = false; + + var startLineAndCol = CursorLineAndColumn; + if ((!HasSelection()) && (doComment != null)) + { + CursorToLineEnd(); + int cursorEndPos = CursorTextPos; + CursorToLineStart(false); + mSelection = .(CursorTextPos, cursorEndPos); + noStar = true; + } + + if ((HasSelection()) && (mSelection.Value.Length > 0)) + { + int lineIdx; + int lineChar; + GetLineCharAtIdx(mSelection.GetValueOrDefault().MinPos, out lineIdx, out lineChar); + MoveCursorTo(lineIdx, 0); + mSelection = .(CursorTextPos, mSelection.GetValueOrDefault().MaxPos); + + + UndoBatchStart undoBatchStart = new UndoBatchStart("embeddedToggleComment"); + mData.mUndoManager.Add(undoBatchStart); + + mData.mUndoManager.Add(new SetCursorAction(this)); + + int minPos = mSelection.GetValueOrDefault().MinPos; + int maxPos = mSelection.GetValueOrDefault().MaxPos; + mSelection = null; + + var str = scope String(); + ExtractString(minPos, maxPos - minPos, str); + var trimmedStr = scope String(); + trimmedStr.Append(str); + int32 startLen = (int32)trimmedStr.Length; + trimmedStr.TrimStart(); + int32 afterTrimStart = (int32)trimmedStr.Length; + trimmedStr.TrimEnd(); + int32 afterTrimEnd = (int32)trimmedStr.Length; + trimmedStr.Append('\n'); + + int firstCharPos = minPos + (startLen - afterTrimStart); + int lastCharPos = maxPos - (afterTrimStart - afterTrimEnd); + + int q = 0; + var nc = trimmedStr.Count('\n'); + + if(afterTrimEnd == 0) + { + if (undoBatchStart != null) + mData.mUndoManager.Add(undoBatchStart.mBatchEnd); + + CursorLineAndColumn = startLineAndCol; + + if (doComment == null) + mSelection = null; + + return false; // not sure if this should be false in blank/only whitespace selection case + } + else if ((doComment != true) && (trimmedStr.StartsWith("//"))) + { + for (int i = firstCharPos; i <= lastCharPos; i++) + { + if ((minPos == 0 && i == 0) || (minPos>=0 && SafeGetChar(i - 1) == '\n' || SafeGetChar(i - 1) == '\t')) + if (SafeGetChar(i - 0) == '/' && SafeGetChar(i + 1) == '/') + { + mSelection = EditSelection(i - 0, i + 2); + DeleteSelection(); + lastCharPos -= 2; + while (i < maxPos && SafeGetChar(i) != '\n') + { + i++; + } + } + } + + CursorToLineEnd(); + int cursorEndPos = CursorTextPos; + mSelection = .(minPos, cursorEndPos); + + } + else if ((doComment != true) && (trimmedStr.StartsWith("/*"))) + { + if (trimmedStr.EndsWith("*/\n")) + { + mSelection = EditSelection(firstCharPos, firstCharPos + 2); + DeleteChar(); + mSelection = EditSelection(lastCharPos - 4, lastCharPos - 2); + DeleteChar(); + + if (doComment != null) + mSelection = EditSelection(firstCharPos, lastCharPos - 4); + } + } + else if (doComment != false && minPos >=0 && ((nc <= 1 && (SafeGetChar(minPos-1) != ' ' && SafeGetChar(minPos-1) != '\t') && SafeGetChar(minPos-1) != '\n') + || nc >= 1 && (SafeGetChar(maxPos-1) != ' ' && SafeGetChar(maxPos-1) != '\t') && SafeGetChar(maxPos-1) != '\n')) + { //if selection is from beginning of the line then we want to use // comment, that's why the check for line count and ' ' and tab + CursorTextPos = firstCharPos; + if (noStar) { + CursorTextPos = minPos; + + InsertAtCursor("//"); //goes here if no selection + } + else + { + InsertAtCursor("/*"); + CursorTextPos = lastCharPos + 2; + InsertAtCursor("*/"); + } + if (!noStar && doComment != null) + mSelection = EditSelection(firstCharPos, lastCharPos + 4); + } + else if (doComment != false) + { + while (firstCharPos >= 0 && SafeGetChar(firstCharPos) != '\n') + { + firstCharPos--; + } + + for (int i = firstCharPos + 1; i < maxPos + q; i++) + { + CursorTextPos = i; // needed to add i < maxPos + q; for this to work with InsertAtCursor + InsertAtCursor("//"); q++; q++; + + while (SafeGetChar(i) != '\n' && i < maxPos + q) + { + i++; + } + } + mSelection = EditSelection(minPos, maxPos + q); + } + + if (undoBatchStart != null) + mData.mUndoManager.Add(undoBatchStart.mBatchEnd); + + CursorLineAndColumn = startLineAndCol; + + if (doComment == null) + mSelection = null; + + return true; + } return false; } @@ -2275,6 +2500,9 @@ namespace IDE.ui public void DuplicateLine() { + if ((CheckReadOnly()) || (!mAllowVirtualCursor)) + return; + UndoBatchStart undoBatchStart = new UndoBatchStart("duplicateLine"); mData.mUndoManager.Add(undoBatchStart); diff --git a/IDE/src/ui/SourceViewPanel.bf b/IDE/src/ui/SourceViewPanel.bf index 6e814b31..c1d98643 100644 --- a/IDE/src/ui/SourceViewPanel.bf +++ b/IDE/src/ui/SourceViewPanel.bf @@ -546,11 +546,6 @@ namespace IDE.ui public ~this() { - if (mProjectSource?.mEditData?.HasTextChanged() == true) - { - mProjectSource.ClearEditData(); - } - if (mInPostRemoveUpdatePanels) { //Debug.WriteLine("Removing sourceViewPanel from mPostRemoveUpdatePanel {0} in ~this ", this); @@ -1185,7 +1180,7 @@ namespace IDE.ui //if (mCurParser != null) { if (gApp.mWorkspace.mProjectLoadState != .Loaded) - return false; + return true; if (!isHi) Debug.Assert(!mIsPerformingBackgroundClassify); @@ -2333,6 +2328,11 @@ namespace IDE.ui if (mDisposed) return; + if (mProjectSource?.mEditData?.HasTextChanged() == true) + { + mProjectSource.ClearEditData(); + } + ProcessDeferredResolveResults(-1); if (IDEApp.sApp.mLastActiveSourceViewPanel == this) diff --git a/IDEHelper/Compiler/BfPrinter.cpp b/IDEHelper/Compiler/BfPrinter.cpp index f30db3a9..a5e473df 100644 --- a/IDEHelper/Compiler/BfPrinter.cpp +++ b/IDEHelper/Compiler/BfPrinter.cpp @@ -300,11 +300,8 @@ int BfPrinter::CalcOrigLineSpacing(BfAstNode* bfAstNode, int* lineStartIdx) void BfPrinter::WriteIgnoredNode(BfAstNode* node) { - if ((!mOutString.IsEmpty()) && (!isspace((uint8)mOutString[mOutString.mLength - 1]))) - { - Write(" "); - } - + bool startsWithSpace = false; + bool wasExpectingNewLine = mExpectingNewLine; mTriviaIdx = std::max(mTriviaIdx, node->GetTriviaStart()); @@ -315,12 +312,17 @@ void BfPrinter::WriteIgnoredNode(BfAstNode* node) for (int i = mTriviaIdx; i < srcEnd; i++) { char c = astNodeSrc->mSrc[i]; + if ((i == mTriviaIdx) && (isspace((uint8)c))) + startsWithSpace = true; + if ((c == '\n') && (i < node->GetSrcStart())) crCount++; if (((c != ' ') && (c != '\t')) || (!mReformatting)) endIdx = i + 1; } + bool wantsPrefixSpace = (!mOutString.IsEmpty()) && (!isspace((uint8)mOutString[mOutString.mLength - 1])); + bool expectingNewLine = mNextStateModify.mWantNewLineIdx != mVirtualNewLineIdx; int startIdx = mTriviaIdx; @@ -333,7 +335,10 @@ void BfPrinter::WriteIgnoredNode(BfAstNode* node) if ((origLineSpacing != -1) && (lineStart != -1)) { for (int i = 0; i < crCount; i++) + { Write("\n"); + wantsPrefixSpace = false; + } startIdx = node->GetSrcStart(); // Leave left-aligned preprocessor nodes if ((node->GetSourceData()->mSrc[node->GetSrcStart()] != '#') || (origLineSpacing > 0)) @@ -439,6 +444,10 @@ void BfPrinter::WriteIgnoredNode(BfAstNode* node) bool emitChar = true; char c = astNodeSrc->mSrc[srcIdx]; + + if ((wantsPrefixSpace) && (isspace((uint8)c))) + wantsPrefixSpace = false; + if (c == '\n') { isNewLine = true; @@ -592,7 +601,12 @@ void BfPrinter::WriteIgnoredNode(BfAstNode* node) } } - FlushIndent(); + if (wantsPrefixSpace) + { + mQueuedSpaceCount++; + wantsPrefixSpace = false; + } + FlushIndent(); for (int idx = startIdx; idx <= BF_MIN(srcIdx, endIdx - 1); idx++) { @@ -1038,6 +1052,8 @@ void BfPrinter::Visit(BfLabelableStatement* labelableStmt) void BfPrinter::Visit(BfCommentNode* commentNode) { WriteIgnoredNode(commentNode); + if (commentNode->mCommentKind == BfCommentKind_Line) + ExpectNewLine(); } void BfPrinter::Visit(BfPreprocesorIgnoredSectionNode* preprocesorIgnoredSection)