From fff1d3e87c8ed9bbb3b74faa78525b7f506b457d Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Tue, 15 Dec 2015 13:57:16 +0300 Subject: [PATCH] breakpoints, bookmarks, workspace; close #72 --- src/dlangide/ui/debuggerui.d | 3 + src/dlangide/ui/dsourceedit.d | 59 +++++++++++++++++ src/dlangide/ui/frame.d | 24 ++++++- src/dlangide/workspace/project.d | 16 +++++ src/dlangide/workspace/workspace.d | 31 ++++++++- src/dlangide/workspace/workspacesettings.d | 73 +++++++++++++++++++++- 6 files changed, 199 insertions(+), 7 deletions(-) diff --git a/src/dlangide/ui/debuggerui.d b/src/dlangide/ui/debuggerui.d index 57e3b2b..b002ae1 100644 --- a/src/dlangide/ui/debuggerui.d +++ b/src/dlangide/ui/debuggerui.d @@ -52,6 +52,9 @@ class DebuggerUIHandler : DebuggerCallback { } } + void onBreakpointListUpdated(Breakpoint[] breakpoints) { + } + void run() { _debugger.run(); } diff --git a/src/dlangide/ui/dsourceedit.d b/src/dlangide/ui/dsourceedit.d index 7ae9429..99877c4 100644 --- a/src/dlangide/ui/dsourceedit.d +++ b/src/dlangide/ui/dsourceedit.d @@ -26,6 +26,10 @@ interface BreakpointListChangeListener { void onBreakpointListChanged(ProjectSourceFile sourceFile, Breakpoint[] breakpoints); } +interface BookmarkListChangeListener { + void onBookmarkListChanged(ProjectSourceFile sourceFile, EditorBookmark[] bookmarks); +} + /// DIDE source file editor class DSourceEdit : SourceEdit, EditableContentMarksChangeListener { this(string ID) { @@ -50,6 +54,7 @@ class DSourceEdit : SourceEdit, EditableContentMarksChangeListener { } Signal!BreakpointListChangeListener breakpointListChanged; + Signal!BookmarkListChangeListener bookmarkListChanged; /// handle theme change: e.g. reload some themed resources override void onThemeChanged() { @@ -185,6 +190,10 @@ class DSourceEdit : SourceEdit, EditableContentMarksChangeListener { case IDEActions.DebugEnableBreakpoint: case IDEActions.DebugDisableBreakpoint: handleBreakpointAction(a); + return true; + case EditorActions.ToggleBookmark: + super.handleAction(a); + notifyBookmarkListChanged(); return true; default: break; @@ -234,8 +243,37 @@ class DSourceEdit : SourceEdit, EditableContentMarksChangeListener { return breakpoints; } + void setBookmarkList(EditorBookmark[] bookmarks) { + // remove all existing breakpoints + content.lineIcons.removeByType(LineIconType.bookmark); + // add new breakpoints + foreach(bp; bookmarks) { + LineIcon icon = new LineIcon(LineIconType.bookmark, bp.line - 1); + content.lineIcons.add(icon); + } + } + + EditorBookmark[] getBookmarkList() { + import std.path; + LineIcon[] icons = content.lineIcons.findByType(LineIconType.bookmark); + EditorBookmark[] bookmarks; + if (projectSourceFile) { + foreach(icon; icons) { + EditorBookmark bp = new EditorBookmark(); + bp.line = icon.line + 1; + bp.file = baseName(filename); + bp.projectName = projectSourceFile.project.name8; + bp.fullFilePath = filename; + bp.projectFilePath = projectSourceFile.project.absoluteToRelativePath(filename); + bookmarks ~= bp; + } + } + return bookmarks; + } + protected void onMarksChange(EditableContent content, LineIcon[] movedMarks, LineIcon[] removedMarks) { bool changed = false; + bool bookmarkChanged = false; foreach(moved; movedMarks) { if (moved.type == LineIconType.breakpoint) { Breakpoint bp = cast(Breakpoint)moved.objectParam; @@ -244,6 +282,13 @@ class DSourceEdit : SourceEdit, EditableContentMarksChangeListener { bp.line = moved.line + 1; changed = true; } + } else if (moved.type == LineIconType.bookmark) { + EditorBookmark bp = cast(EditorBookmark)moved.objectParam; + if (bp) { + // update Breakpoint line + bp.line = moved.line + 1; + bookmarkChanged = true; + } } } foreach(removed; removedMarks) { @@ -252,10 +297,17 @@ class DSourceEdit : SourceEdit, EditableContentMarksChangeListener { if (bp) { changed = true; } + } else if (removed.type == LineIconType.bookmark) { + EditorBookmark bp = cast(EditorBookmark)removed.objectParam; + if (bp) { + bookmarkChanged = true; + } } } if (changed) notifyBreakpointListChanged(); + if (bookmarkChanged) + notifyBookmarkListChanged(); } protected void notifyBreakpointListChanged() { @@ -265,6 +317,13 @@ class DSourceEdit : SourceEdit, EditableContentMarksChangeListener { } } + protected void notifyBookmarkListChanged() { + if (projectSourceFile) { + if (bookmarkListChanged.assigned) + bookmarkListChanged(projectSourceFile, getBookmarkList()); + } + } + protected void handleBreakpointAction(const Action a) { int line = a.longParam >= 0 ? cast(int)a.longParam : caretPos.line; LineIcon icon = content.lineIcons.findByLineAndType(line, LineIconType.breakpoint); diff --git a/src/dlangide/ui/frame.d b/src/dlangide/ui/frame.d index c4bb0d4..b9cf71f 100644 --- a/src/dlangide/ui/frame.d +++ b/src/dlangide/ui/frame.d @@ -67,7 +67,7 @@ class BackgroundOperationWatcherTest : BackgroundOperationWatcher { } /// DIDE app frame -class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeListener { +class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeListener, BookmarkListChangeListener { private ToolBarComboBox projectConfigurationCombo; @@ -194,6 +194,7 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL debuggerProxy.setDebuggerExecutable(debuggerExecutable); _execution = debuggerProxy; _debugHandler = new DebuggerUIHandler(this, debuggerProxy); + _debugHandler.onBreakpointListUpdated(currentWorkspace.getBreakpoints()); _debugHandler.run(); } @@ -332,8 +333,12 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL TabItem tab = _tabs.tab(filename); tab.objectParam = file; editor.modifiedStateChange = &onModifiedStateChange; - editor.breakpointListChanged = this; //onBreakpointListChanged - editor.setBreakpointList(currentWorkspace.getSourceFileBreakpoints(file)); + if (file) { + editor.breakpointListChanged = this; //onBreakpointListChanged + editor.bookmarkListChanged = this; //onBreakpointListChanged + editor.setBreakpointList(currentWorkspace.getSourceFileBreakpoints(file)); + editor.setBookmarkList(currentWorkspace.getSourceFileBookmarks(file)); + } applySettings(editor, settings); _tabs.selectTab(index, true); if( filename.endsWith(".d") ) @@ -874,6 +879,8 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL } void closeWorkspace() { + if (currentWorkspace) + currentWorkspace.save(); askForUnsavedEdits(delegate() { setWorkspace(null); showHomeScreen(); @@ -886,6 +893,15 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL if (sourcefile) { currentWorkspace.setSourceFileBreakpoints(sourcefile, breakpoints); } + if (_debugHandler) + _debugHandler.onBreakpointListUpdated(currentWorkspace.getBreakpoints()); + } + + void onBookmarkListChanged(ProjectSourceFile sourcefile, EditorBookmark[] bookmarks) { + if (!currentWorkspace) + return; + if (sourcefile) + currentWorkspace.setSourceFileBookmarks(sourcefile, bookmarks); } void refreshProjectItem(const Object obj) { @@ -1220,6 +1236,8 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL /// return false to prevent closing bool onCanClose() { askForUnsavedEdits(delegate() { + if (currentWorkspace) + currentWorkspace.save(); window.close(); }); return false; diff --git a/src/dlangide/workspace/project.d b/src/dlangide/workspace/project.d index 12634aa..3979bc8 100644 --- a/src/dlangide/workspace/project.d +++ b/src/dlangide/workspace/project.d @@ -63,6 +63,10 @@ class ProjectItem { return _name; } + @property string name8() { + return _name.toUTF8; + } + /// returns true if item is folder @property const bool isFolder() { return false; @@ -234,6 +238,10 @@ class WorkspaceItem { return _name; } + @property string name8() { + return _name.toUTF8; + } + /// name @property void name(dstring s) { _name = s; @@ -722,3 +730,11 @@ bool isValidFileName(string s) { } return true; } + +class EditorBookmark { + string file; + string fullFilePath; + string projectFilePath; + int line; + string projectName; +} diff --git a/src/dlangide/workspace/workspace.d b/src/dlangide/workspace/workspace.d index 852f899..9c6a773 100644 --- a/src/dlangide/workspace/workspace.d +++ b/src/dlangide/workspace/workspace.d @@ -92,7 +92,7 @@ class Workspace : WorkspaceItem { } } - void updateBreakpointFiles(Breakpoint[] breakpoints) { + private void updateBreakpointFiles(Breakpoint[] breakpoints) { foreach(bp; breakpoints) { Project project = findProjectByName(bp.projectName); if (project) @@ -100,6 +100,14 @@ class Workspace : WorkspaceItem { } } + private void updateBookmarkFiles(EditorBookmark[] bookmarks) { + foreach(bp; bookmarks) { + Project project = findProjectByName(bp.projectName); + if (project) + bp.fullFilePath = project.relativeToAbsolutePath(bp.projectFilePath); + } + } + Breakpoint[] getSourceFileBreakpoints(ProjectSourceFile file) { Breakpoint[] res = _settings.getProjectBreakpoints(toUTF8(file.project.name), file.projectFilePath); updateBreakpointFiles(res); @@ -109,6 +117,23 @@ class Workspace : WorkspaceItem { void setSourceFileBreakpoints(ProjectSourceFile file, Breakpoint[] breakpoints) { _settings.setProjectBreakpoints(toUTF8(file.project.name), file.projectFilePath, breakpoints); } + + EditorBookmark[] getSourceFileBookmarks(ProjectSourceFile file) { + EditorBookmark[] res = _settings.getProjectBookmarks(toUTF8(file.project.name), file.projectFilePath); + updateBookmarkFiles(res); + return res; + } + + void setSourceFileBookmarks(ProjectSourceFile file, EditorBookmark[] bookmarks) { + _settings.setProjectBookmarks(toUTF8(file.project.name), file.projectFilePath, bookmarks); + } + + /// returns all workspace breakpoints + Breakpoint[] getBreakpoints() { + Breakpoint[] res = _settings.getBreakpoints(); + updateBreakpointFiles(res); + return res; + } protected void fillStartupProject() { string s = _settings.startupProjectName; @@ -178,9 +203,9 @@ class Workspace : WorkspaceItem { override bool save(string fname = null) { if (fname.length > 0) filename = fname; - if (!filename) // no file name specified + if (_filename.empty) // no file name specified return false; - _settings.save(filename ~ WORKSPACE_SETTINGS_EXTENSION); + _settings.save(_filename ~ WORKSPACE_SETTINGS_EXTENSION); _workspaceFile.setString("name", toUTF8(_name)); _workspaceFile.setString("description", toUTF8(_description)); Setting projects = _workspaceFile.objectByPath("projects", true); diff --git a/src/dlangide/workspace/workspacesettings.d b/src/dlangide/workspace/workspacesettings.d index 135a066..c8bd580 100644 --- a/src/dlangide/workspace/workspacesettings.d +++ b/src/dlangide/workspace/workspacesettings.d @@ -3,6 +3,7 @@ module dlangide.workspace.workspacesettings; import dlangui.core.settings; import dlangui.core.i18n; import ddebug.common.debugger; +import dlangide.workspace.project; import std.array; @@ -14,6 +15,7 @@ class WorkspaceSettings : SettingsFile { } private Breakpoint[] _breakpoints; + private EditorBookmark[] _bookmarks; private string _startupProjectName; @property string startupProjectName() { @@ -79,13 +81,71 @@ class WorkspaceSettings : SettingsFile { obj[index++] = bpObj; } _breakpoints = bps; - save(); + //save(); + } + + /// get all bookmarks for project (for specified source file only, if specified) + EditorBookmark[] getProjectBookmarks(string projectName, string projectFilePath) { + EditorBookmark[] res; + for (int i = cast(int)_bookmarks.length - 1; i >= 0; i--) { + EditorBookmark bp = _bookmarks[i]; + if (!bp.projectName.equal(projectName)) + continue; + if (!projectFilePath.empty && !bp.projectFilePath.equal(projectFilePath)) + continue; + res ~= bp; + } + return res; + } + + /// get all bookmarks for project (for specified source file only, if specified) + void setProjectBookmarks(string projectName, string projectFilePath, EditorBookmark[] bps) { + bool changed = false; + for (int i = cast(int)_bookmarks.length - 1; i >= 0; i--) { + EditorBookmark bp = _bookmarks[i]; + if (!bp.projectName.equal(projectName)) + continue; + if (!projectFilePath.empty && !bp.projectFilePath.equal(projectFilePath)) + continue; + for (auto j = i; j < _bookmarks.length - 1; j++) + _bookmarks[j] = _bookmarks[j + 1]; + _bookmarks.length--; + changed = true; + } + if (bps.length) { + changed = true; + foreach(bp; bps) + _bookmarks ~= bp; + } + if (changed) { + setBookmarks(_bookmarks); + } + } + + void setBookmarks(EditorBookmark[] bps) { + Setting obj = _setting.settingByPath("bookmarks", SettingType.ARRAY); + obj.clear(SettingType.ARRAY); + int index = 0; + foreach(bp; bps) { + Setting bpObj = new Setting(); + bpObj.setString("file", bp.file); + bpObj.setInteger("line", bp.line); + bpObj.setString("projectName", bp.projectName); + bpObj.setString("projectFilePath", bp.projectFilePath); + obj[index++] = bpObj; + } + _bookmarks = bps; + //save(); } Breakpoint[] getBreakpoints() { return _breakpoints; } + EditorBookmark[] getBookmarks() { + return _bookmarks; + } + /// override to do something after loading - e.g. set defaults override void afterLoad() { _startupProjectName = _setting.getString("startupProject"); @@ -102,6 +162,17 @@ class WorkspaceSettings : SettingsFile { bp.enabled = item.getBoolean("enabled"); _breakpoints ~= bp; } + obj = _setting.settingByPath("bookmarks", SettingType.ARRAY); + _bookmarks = null; + for (int i = 0; i < obj.length; i++) { + EditorBookmark bp = new EditorBookmark(); + Setting item = obj[i]; + bp.file = item.getString("file"); + bp.projectName = item.getString("projectName"); + bp.projectFilePath = item.getString("projectFilePath"); + bp.line = cast(int)item.getInteger("line"); + _bookmarks ~= bp; + } } override void updateDefaults() {