From a650c7ab9c6b8bc48eaa2e30b4277b904d0012f4 Mon Sep 17 00:00:00 2001 From: Brian Fiete Date: Sun, 6 Sep 2020 09:05:00 -0700 Subject: [PATCH] Early support for line/statement moving --- IDE/src/Commands.bf | 4 + IDE/src/IDEApp.bf | 54 ++++- IDE/src/Settings.bf | 4 + IDE/src/VertDir.bf | 9 + IDE/src/ui/SourceEditWidgetContent.bf | 335 ++++++++++++++++++-------- 5 files changed, 291 insertions(+), 115 deletions(-) create mode 100644 IDE/src/VertDir.bf diff --git a/IDE/src/Commands.bf b/IDE/src/Commands.bf index dff55f17..e6b03603 100644 --- a/IDE/src/Commands.bf +++ b/IDE/src/Commands.bf @@ -212,6 +212,10 @@ namespace IDE Add("Make Uppercase", new () => { gApp.[Friend]ChangeCase(true); }); Add("Match Brace Select", new => gApp.Cmd_MatchBrace); Add("Match Brace", new => gApp.Cmd_MatchBrace); + Add("Move Line Down", new () => gApp.Cmd_MoveLine(.Down)); + Add("Move Line Up", new () => gApp.Cmd_MoveLine(.Up)); + Add("Move Statement Down", new () => gApp.Cmd_MoveStatement(.Down)); + Add("Move Statement Up", new () => gApp.Cmd_MoveStatement(.Up)); Add("Navigate Backwards", new => gApp.[Friend]NavigateBackwards); Add("Navigate Forwards", new => gApp.[Friend]NavigateForwards); Add("New Debug Session", new => gApp.Cmd_NewDebugSession); diff --git a/IDE/src/IDEApp.bf b/IDE/src/IDEApp.bf index effff7a7..0bbc46eb 100644 --- a/IDE/src/IDEApp.bf +++ b/IDE/src/IDEApp.bf @@ -2065,6 +2065,17 @@ namespace IDE mBfResolveSystem.AddProject(project); #endif mWorkspace.ClearProjectNameCache(); + + if (!mWorkspace.mLoading) + { + for (var checkProject in mWorkspace.mProjects) + { + int idx = checkProject.mDependencies.FindIndex(scope (dep) => dep.mProjectName == project.mProjectName); + if (idx != -1) + ProjectOptionsChanged(checkProject); + } + ProjectOptionsChanged(project, false); + } } public void AddNewProjectToWorkspace(Project project, VerSpecRecord verSpec = null) @@ -2102,14 +2113,6 @@ namespace IDE dep.mVerSpec = new .(); dep.mVerSpec.SetSemVer("*"); project.mDependencies.Add(dep); - - for (var checkProject in mWorkspace.mProjects) - { - int idx = checkProject.mDependencies.FindIndex(scope (dep) => dep.mProjectName == project.mProjectName); - if (idx != -1) - ProjectOptionsChanged(checkProject); - } - ProjectOptionsChanged(project, false); } public Project CreateProject(String projName, String projDir, Project.TargetType targetType) @@ -2348,7 +2351,7 @@ namespace IDE return .Err; } - void FlushDeferredLoadProjects() + void FlushDeferredLoadProjects(bool addToUI = false) { while (true) { @@ -2369,6 +2372,8 @@ namespace IDE } AddProjectToWorkspace(project, false); + if (addToUI) + mProjectPanel.InitProject(project); } } if (!hadLoad) @@ -2401,7 +2406,13 @@ namespace IDE bool isNew = false; bool wantSave = false; - + + mWorkspace.mLoading = true; + defer + { + mWorkspace.mLoading = false; + } + if (StructuredLoad(data, workspaceFileName) case .Err(let err)) { mBeefConfig.Refresh(); @@ -3668,6 +3679,22 @@ namespace IDE sourceViewPanel.MatchBrace(); } + [IDECommand] + public void Cmd_MoveLine(VertDir dir) + { + var sewc = GetActiveSourceEditWidgetContent(); + if (sewc != null) + sewc.MoveLine(dir); + } + + [IDECommand] + public void Cmd_MoveStatement(VertDir dir) + { + var sewc = GetActiveSourceEditWidgetContent(); + if (sewc != null) + sewc.MoveStatement(dir); + } + [IDECommand] public void Cmd_GotoNextItem() { @@ -5169,6 +5196,12 @@ namespace IDE AddMenuItem(bookmarkMenu, "&Clear Bookmarks", "Bookmark Clear"); var advancedEditMenu = subMenu.AddMenuItem("Advanced"); + AddMenuItem(advancedEditMenu, "Duplicate Line", "Duplicate Line"); + AddMenuItem(advancedEditMenu, "Move Line Up", "Move Line Up"); + AddMenuItem(advancedEditMenu, "Move Line Down", "Move Line Down"); + AddMenuItem(advancedEditMenu, "Move Statement Up", "Move Statement Up"); + AddMenuItem(advancedEditMenu, "Move Statement Down", "Move Statement Down"); + advancedEditMenu.AddMenuItem(null); AddMenuItem(advancedEditMenu, "Make Uppercase", "Make Uppercase"); AddMenuItem(advancedEditMenu, "Make Lowercase", "Make Lowercase"); mViewWhiteSpace.mMenu = AddMenuItem(advancedEditMenu, "View White Space", "View White Space", null, null, true, mViewWhiteSpace.Bool ? 1 : 0); @@ -8692,6 +8725,7 @@ namespace IDE { RemoveProjectItems(project); + project.mDeleted = true; mWorkspace.SetChanged(); mWorkspace.mProjects.Remove(project); #if IDE_C_SUPPORT diff --git a/IDE/src/Settings.bf b/IDE/src/Settings.bf index 64fb6d62..3392c4df 100644 --- a/IDE/src/Settings.bf +++ b/IDE/src/Settings.bf @@ -687,6 +687,10 @@ namespace IDE Add("Make Uppercase", "Ctrl+Shift+U"); Add("Match Brace Select", "Ctrl+Shift+RBracket"); Add("Match Brace", "Ctrl+RBracket"); + 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"); diff --git a/IDE/src/VertDir.bf b/IDE/src/VertDir.bf new file mode 100644 index 00000000..86d27d7e --- /dev/null +++ b/IDE/src/VertDir.bf @@ -0,0 +1,9 @@ +namespace IDE +{ + enum VertDir + { + Up = -1, + None = 0, + Down = 1 + } +} diff --git a/IDE/src/ui/SourceEditWidgetContent.bf b/IDE/src/ui/SourceEditWidgetContent.bf index e278b49f..90359590 100644 --- a/IDE/src/ui/SourceEditWidgetContent.bf +++ b/IDE/src/ui/SourceEditWidgetContent.bf @@ -2139,21 +2139,51 @@ namespace IDE.ui mData.mUndoManager.Add(undoBatchStart.mBatchEnd); } - bool GetStatementRange(int checkIdx, out int startIdx, out int endIdx, out char8 endChar) + enum StatementRangeFlags { + None, + IncludeBlock, + AllowInnerMethodSelect + } + + enum StatementKind + { + None, + Normal, + Method + } + + StatementKind GetStatementRange(int startCheckPos, StatementRangeFlags flags, out int startIdx, out int endIdx, out char8 endChar) + { + StatementKind statementKind = .Normal; startIdx = -1; endIdx = -1; endChar = 0; - GetLineCharAtIdx(checkIdx, var line, var lineChar); + GetLineCharAtIdx(startCheckPos, var line, var lineChar); GetBlockStart(line, var foundBlockStartIdx, var blockOpenSpaceCount); + if (foundBlockStartIdx < 0) + return .None; + bool expectingStatement = true; + int stmtCommentStart = -1; int stmtStart = -1; + int prevLineEnd = -1; int lastNonWS = -1; int parenCount = 0; int lastCrPos = -1; + int braceCount = 0; + int innerBraceCount = 0; + bool hadOuterMethodColoring = false; + int lastOpenBrace = -1; + int lastCloseBrace = -1; + + int commentLineStart = -1; + int commentStart = -1; + bool lineIsComment = false; + bool lineHasNonComment = false; for (int checkPos = foundBlockStartIdx; true; checkPos++) { @@ -2162,19 +2192,62 @@ namespace IDE.ui char8 checkC = mData.mText[checkPos].mChar; if (checkC == '\n') + { + prevLineEnd = checkPos; lastCrPos = checkPos; + if (!lineIsComment) + { + commentStart = -1; + commentLineStart = -1; + } + lineIsComment = false; + lineHasNonComment = false; + } if (checkC.IsWhiteSpace) continue; let displayType = (SourceElementType)mData.mText[checkPos].mDisplayTypeId; if (displayType == .Comment) + { + if (!lineHasNonComment) + { + if (commentStart == -1) + commentStart = checkPos; + if (commentLineStart == -1) + commentLineStart = prevLineEnd + 1; + lineIsComment = true; + } continue; + } + + lineHasNonComment = true; + lineIsComment = false; + + if (innerBraceCount > 0) + { + if (checkC == '{') + { + innerBraceCount++; + braceCount++; + } + if (checkC == '}') + { + lastNonWS = checkPos; + innerBraceCount--; + braceCount--; + } + + if (innerBraceCount > 0) + continue; + } if (displayType == .Normal) { if (checkC == '(') + { parenCount++; + } else if (checkC == ')') parenCount--; } @@ -2182,40 +2255,116 @@ namespace IDE.ui if (parenCount != 0) continue; + if (displayType == .Method) + hadOuterMethodColoring = true; + if ((displayType == .Normal) && ((checkC == '{') || (checkC == '}') || (checkC == ';'))) { - if ((checkPos >= checkIdx) && (!expectingStatement)) + if (checkC == '{') { + lastOpenBrace = checkPos; + braceCount++; + } + if (checkC == '}') + { + lastCloseBrace = checkPos; + braceCount--; + } + + if ((checkPos >= startCheckPos) && (!expectingStatement)) + { + if (checkC == '{') + { + if (hadOuterMethodColoring) + statementKind = .Method; + + if ((flags.HasFlag(.IncludeBlock)) || (statementKind == .Method)) + { + innerBraceCount++; + continue; + } + } + endChar = checkC; - if (lastNonWS < checkIdx) - return false; + if (lastNonWS < startCheckPos) + return .None; startIdx = stmtStart; - endIdx = lastNonWS + 1; - return true; + if ((stmtCommentStart != -1) && (stmtCommentStart < startIdx)) + startIdx = stmtCommentStart; + + if (checkC == '{') + endIdx = lastNonWS + 1; + else + endIdx = checkPos + 1; + return statementKind; } expectingStatement = true; + hadOuterMethodColoring = false; } else if (expectingStatement) { - if (lastCrPos >= checkIdx) + if (lastCrPos >= startCheckPos) { - return false; + if ((commentLineStart == -1) || (commentLineStart > startCheckPos)) + break; } expectingStatement = false; stmtStart = checkPos; + stmtCommentStart = commentStart; } lastNonWS = checkPos; + commentStart = -1; } - return false; + if (!flags.HasFlag(.AllowInnerMethodSelect)) + return .None; + + // If out starting pos is within a method then select the whole thing + if ((startCheckPos >= lastOpenBrace) && (startCheckPos <= lastCloseBrace) && (braceCount != 0)) + { + let checkStmtKind = GetStatementRange(Math.Max(lastOpenBrace - 1, 0), .None, var checkStartIdx, var checkEndIdx, var checkEndChar); + //if ((checkStmtKind == .Method) && (startCheckPos >= checkStartIdx) && (startCheckPos <= checkEndIdx)) + if (checkStmtKind == .Method) + { + startIdx = checkStartIdx; + endIdx = checkEndIdx; + endChar = checkEndChar; + return checkStmtKind; + } + } + return .None; } - void MoveSelection(int toLinePos) + bool LineIsComment(int lineNum) + { + if (lineNum >= GetLineCount()) + return false; + + GetLinePosition(lineNum, var lineStart, var lineEnd); + + bool lineIsComment = false; + for (int checkPos = lineStart; checkPos < lineEnd; checkPos++) + { + char8 checkC = mData.mText[checkPos].mChar; + if (checkC.IsWhiteSpace) + continue; + + let displayType = (SourceElementType)mData.mText[checkPos].mDisplayTypeId; + if (displayType != .Comment) + return false; + + lineIsComment = true; + } + + return lineIsComment; + } + + void MoveSelection(int toLinePos, bool isStatementAware) { /*if (GetStatementRange(CursorTextPos, var startIdx, var endIdx)) { @@ -2246,62 +2395,58 @@ namespace IDE.ui String offsetText = scope .(); ExtractString(offsetLineStart, offsetLineEnd - offsetLineStart, offsetText); - if (movingDown) + if (isStatementAware) { - GetLinePosition(Math.Max(offsetLinePos - 1, 0), var toLineStart, var toLineEnd); - String txt = scope .(); - ExtractString(toLineStart, toLineEnd - toLineStart, txt); - if (GetStatementRange(toLineStart, var stmtStartIdx, var stmtEndIdx, var stmtEndChar)) + if (movingDown) { - String stmt = scope .(); - ExtractString(stmtStartIdx, stmtEndIdx - stmtStartIdx, stmt); - GetLineCharAtIdx(stmtEndIdx - 1, var stmtLine, var stmtLineChar); - offsetLinePos = stmtLine + 1; - GetLinePosition(Math.Max(offsetLinePos, 0), out offsetLineStart, out offsetLineEnd); - - if (stmtEndChar == '{') - offsetLinePos++; - } - } - else - { - /*for (int checkPos = offsetLineStart; checkPos < offsetLineEnd; checkPos++) - { - if (mData.mText[checkPos].mDisplayTypeId != 0) - continue; - char8 checkC = mData.mText[checkPos].mChar; - if (checkC.IsWhiteSpace) - continue; - if (checkC == '{') + GetLinePosition(Math.Max(offsetLinePos - 1, 0), var toLineStart, var toLineEnd); + String txt = scope .(); + ExtractString(toLineStart, toLineEnd - toLineStart, txt); + if (GetStatementRange(toLineStart, .None, var stmtStartIdx, var stmtEndIdx, var stmtEndChar) != .None) { - if (offsetLinePos > prevCursorLineAndColumn.mLine) - offsetLinePos--; + String stmt = scope .(); + ExtractString(stmtStartIdx, stmtEndIdx - stmtStartIdx, stmt); + GetLineCharAtIdx(stmtEndIdx - 1, var stmtLine, var stmtLineChar); + offsetLinePos = stmtLine + 1; + GetLinePosition(Math.Max(offsetLinePos, 0), out offsetLineStart, out offsetLineEnd); + + if (stmtEndChar == '{') + offsetLinePos++; + } + else if (GetStatementRange(toLineStart, .AllowInnerMethodSelect, var stmtStartIdx, var stmtEndIdx, var stmtEndChar) == .Method) + { + if (stmtStartIdx <= toLineStart) + { + GetLineCharAtIdx(stmtEndIdx, var endLine, var endChar); + //if (offsetLinePos) + offsetLinePos = endLine + 1; + } } - break; - }*/ - - GetLinePosition(Math.Max(offsetLinePos, 0), var toLineStart, var toLineEnd); - String txt = scope .(); - ExtractString(toLineStart, toLineEnd - toLineStart, txt); - - if (!GetStatementRange(toLineStart, var stmtStartIdx, var stmtEndIdx, var stmtEndChar)) - { - if (stmtEndChar == '{') - GetLinePosition(Math.Max(offsetLinePos - 1, 0), out toLineStart, out toLineEnd); } - - if (GetStatementRange(toLineStart, var stmtStartIdx, var stmtEndIdx, var stmtEndChar)) + else { - String stmt = scope .(); - ExtractString(stmtStartIdx, stmtEndIdx - stmtStartIdx, stmt); - GetLineCharAtIdx(stmtStartIdx, var stmtLine, var stmtLineChar); - offsetLinePos = stmtLine; - GetLinePosition(Math.Max(offsetLinePos, 0), out offsetLineStart, out offsetLineEnd); + GetLinePosition(Math.Max(offsetLinePos, 0), var toLineStart, var toLineEnd); + String txt = scope .(); + ExtractString(toLineStart, toLineEnd - toLineStart, txt); + + if (GetStatementRange(toLineStart, .None, var stmtStartIdx, var stmtEndIdx, var stmtEndChar) == .None) + { + if (stmtEndChar == '{') + GetLinePosition(Math.Max(offsetLinePos - 1, 0), out toLineStart, out toLineEnd); + } + + if (GetStatementRange(toLineStart, .AllowInnerMethodSelect, var stmtStartIdx, var stmtEndIdx, var stmtEndChar) != .None) + { + String stmt = scope .(); + ExtractString(stmtStartIdx, stmtEndIdx - stmtStartIdx, stmt); + GetLineCharAtIdx(stmtStartIdx, var stmtLine, var stmtLineChar); + offsetLinePos = stmtLine; + GetLinePosition(Math.Max(offsetLinePos, 0), out offsetLineStart, out offsetLineEnd); + } } } - - let wantCursorPos = LineAndColumn(Math.Max(offsetLinePos, 0), 0); + var wantCursorPos = LineAndColumn(Math.Max(offsetLinePos, 0), 0); if (wantCursorPos.mLine >= GetLineCount()) { CursorTextPos = mData.mTextLength; @@ -2309,6 +2454,19 @@ namespace IDE.ui } CursorLineAndColumn = wantCursorPos; + + String txt = scope .(); + ExtractLine(offsetLinePos, txt); + + if ((isStatementAware) && (LineIsComment(offsetLinePos - 1))) + { + if (movingDown) + { + InsertAtCursor("\n"); + wantCursorPos.mLine++; + } + } + PasteText(str, "line"); CursorLineAndColumn = LineAndColumn(wantCursorPos.mLine, prevCursorLineAndColumn.mColumn); @@ -2320,61 +2478,26 @@ namespace IDE.ui int lineNum = CursorLineAndColumn.mLine; GetLinePosition(lineNum, var lineStart, var lineEnd); mSelection = .(lineStart, Math.Min(lineEnd + 1, mData.mTextLength)); - MoveSelection(lineNum + (int)dir); + MoveSelection(lineNum + (int)dir, false); } public void MoveStatement(VertDir dir) { int lineNum = CursorLineAndColumn.mLine; + int origLineNum = lineNum; GetLinePosition(lineNum, var lineStart, var lineEnd); - int selStart = -1; - int selEnd = -1; - int parenDepth = 0; - - int checkPos = lineStart; - for ( ; checkPos < mData.mTextLength - 1; checkPos++) - { - char8 checkC = mData.mText[checkPos].mChar; - - if (selStart == -1) - { - if (checkC == '\n') - break; // Don't support moving empty lines - if (!checkC.IsWhiteSpace) - { - selStart = checkPos; - } - } - - if (mData.mText[checkPos].mDisplayTypeId != 0) - continue; - - if ((checkC == ';') && (parenDepth == 0)) - { - selEnd = checkPos + 1; - break; - } - - if (checkC == '{') - { - parenDepth++; - } - - if (checkC == '}') - { - parenDepth--; - if (parenDepth <= 0) - { - selEnd = checkPos + 1; - break; - } - } - } - - if (selEnd == -1) + var stmtKind = GetStatementRange(lineStart, .IncludeBlock, var stmtStart, var stmtEnd, var endChar); + if (stmtKind == .None) return; + GetLineCharAtIdx(stmtStart, var startLineNum, var startLineChar); + GetLinePosition(startLineNum, out lineStart, out lineEnd); + lineNum = startLineNum; + + int checkPos = stmtEnd; + int selEnd = stmtEnd; + for ( ; checkPos < mData.mTextLength - 1; checkPos++) { char8 checkC = mData.mText[checkPos].mChar; @@ -2396,7 +2519,9 @@ namespace IDE.ui GetLineCharAtIdx(selEnd, var line, var lineChar); toLine = line; } - MoveSelection(toLine); + MoveSelection(toLine, true); + + CursorLineAndColumn = .(Math.Min(CursorLineAndColumn.mLine + (origLineNum - startLineNum), Math.Max(GetLineCount() - 1, 0)), CursorLineAndColumn.mColumn); mData.mUndoManager.Add(undoBatchStart.mBatchEnd); }