v0.11.0 dc50343

workspace_operations.go RawHistoryFolder

1// workspace_operations.go - Workspace operation methods with validation and safety
2package browser
3
4import (
5	"fmt"
6	"log"
7
8	"github.com/bnema/dumber/pkg/webkit"
9)
10
11// SplitPane performs a split operation with validation and safety checks
12func (wm *WorkspaceManager) SplitPane(target *paneNode, direction string) (*paneNode, error) {
13	if wm == nil {
14		return nil, fmt.Errorf("workspace manager is nil")
15	}
16
17	// Validate direction parameter
18	validDirections := map[string]bool{
19		"left":  true,
20		"right": true,
21		"up":    true,
22		"down":  true,
23	}
24	if !validDirections[direction] {
25		return nil, fmt.Errorf("invalid split direction '%s', expected one of: left, right, up, down", direction)
26	}
27
28	log.Printf("[workspace] Starting split operation: target=%p direction=%s", target, direction)
29
30	// Step 1: Validate geometry constraints (only in DebugBasic+)
31	if wm.debugLevel >= DebugBasic {
32		validation := wm.geometryValidator.ValidateSplit(target, direction)
33		if !validation.IsValid {
34			if wm.debugLevel == DebugFull {
35				return nil, fmt.Errorf("split validation failed: %s", validation.Reason)
36			}
37			log.Printf("[workspace] WARNING: Split validation failed but allowing operation: %s", validation.Reason)
38		}
39
40		// Log if re-validation will be needed due to pending widget allocation
41		if validation.RequiresRevalidation {
42			log.Printf("[workspace] Split validation passed with pending allocation - operation will proceed")
43		}
44
45		// Step 3: Validate tree invariants before operation
46		if err := wm.treeValidator.ValidateTree(wm.root, "before_split"); err != nil {
47			if wm.debugLevel == DebugFull {
48				return nil, fmt.Errorf("tree validation failed before split: %w", err)
49			}
50			log.Printf("[workspace] WARNING: Tree validation failed before split but allowing operation: %v", err)
51		}
52	}
53
54	// Step 2: Execute directly if we're already on the GTK main thread
55	if webkit.IsMainThread() {
56		log.Printf("[workspace] Already on main thread, executing split directly")
57		newNode, err := wm.splitNode(target, direction)
58		if err != nil {
59			return nil, err
60		}
61
62		if wm.debugLevel >= DebugBasic {
63			if err := wm.treeValidator.ValidateTree(wm.root, "after_split"); err != nil {
64				log.Printf("[workspace] Tree validation failed after split: %v", err)
65			}
66		}
67
68		// Tree rebalancing is only needed after close promotions. Splits have correct allocation from GTK.
69
70		log.Printf("[workspace] Split operation completed successfully (direct execution): newNode=%p", newNode)
71		return newNode, nil
72	}
73
74	// Step 3: Not on main thread, marshal via IdleAdd
75	log.Printf("[workspace] Not on main thread, marshalling split via IdleAdd")
76	var newNode *paneNode
77	var splitErr error
78	done := make(chan struct{})
79
80	_ = webkit.IdleAdd(func() bool {
81		newNode, splitErr = wm.splitNode(target, direction)
82		if splitErr == nil {
83			if wm.debugLevel >= DebugBasic {
84				if verr := wm.treeValidator.ValidateTree(wm.root, "after_split"); verr != nil {
85					log.Printf("[workspace] Tree validation failed after split: %v", verr)
86				}
87			}
88		}
89		close(done)
90		return false
91	})
92
93	<-done
94
95	if splitErr != nil {
96		return nil, splitErr
97	}
98
99	log.Printf("[workspace] Split operation completed successfully: newNode=%p", newNode)
100	return newNode, nil
101}
102
103// ClosePane performs a close operation with validation and safety checks
104func (wm *WorkspaceManager) ClosePane(node *paneNode) error {
105	if wm == nil {
106		return fmt.Errorf("workspace manager is nil")
107	}
108
109	log.Printf("[workspace] Starting close operation: node=%p", node)
110	wm.paneCloseLogf("start bulletproof close node=%p", node)
111	wm.dumpTreeState("before_close")
112
113	// Step 1: Validate tree invariants before operation (only in DebugBasic+)
114	if wm.debugLevel >= DebugBasic {
115		if err := wm.treeValidator.ValidateTree(wm.root, "before_close"); err != nil {
116			if wm.debugLevel == DebugFull {
117				return fmt.Errorf("tree validation failed before close: %w", err)
118			}
119			log.Printf("[workspace] WARNING: Tree validation failed before close but allowing operation: %v", err)
120		}
121	}
122
123	// Step 3: Check if this is a stacked pane and use enhanced lifecycle management
124	if node.parent != nil && node.parent.isStacked {
125		log.Printf("[workspace] Using enhanced stack lifecycle management for close")
126		return wm.stackLifecycleManager.CloseStackedPaneWithLifecycle(node)
127	}
128
129	// Step 4: Execute directly when already on the GTK main thread
130	if webkit.IsMainThread() {
131		log.Printf("[workspace] Already on main thread, executing close directly")
132
133		wm.paneCloseLogf("invoking closePane node=%p", node)
134		promoted, err := wm.closePane(node)
135		if err != nil {
136			wm.paneCloseLogf("closePane failed node=%p err=%v", node, err)
137			wm.dumpTreeState("after_close_error")
138			return err
139		}
140
141		if wm.debugLevel >= DebugBasic {
142			if err := wm.treeValidator.ValidateTree(wm.root, "after_close"); err != nil {
143				log.Printf("[workspace] Tree validation failed after close: %v", err)
144			}
145		}
146		wm.paneCloseLogf("closePane succeeded node=%p promoted=%p root=%p", node, promoted, wm.root)
147		wm.dumpTreeState("after_close_success")
148
149		if wm.treeRebalancer != nil {
150			if err := wm.treeRebalancer.RebalanceAfterClose(node, promoted); err != nil {
151				log.Printf("[workspace] Tree rebalancing failed after close: %v", err)
152			}
153		}
154
155		log.Printf("[workspace] Close operation completed successfully (direct execution)")
156		return nil
157	}
158
159	// Step 5: Not on main thread, marshal via IdleAdd
160	log.Printf("[workspace] Not on main thread, marshalling close via IdleAdd")
161	var promoted *paneNode
162	var closeErr error
163	done := make(chan struct{})
164
165	_ = webkit.IdleAdd(func() bool {
166		wm.paneCloseLogf("invoking closePane node=%p", node)
167		promoted, closeErr = wm.closePane(node)
168		if closeErr != nil {
169			wm.paneCloseLogf("closePane failed node=%p err=%v", node, closeErr)
170			wm.dumpTreeState("after_close_error")
171		} else {
172			if wm.debugLevel >= DebugBasic {
173				if verr := wm.treeValidator.ValidateTree(wm.root, "after_close"); verr != nil {
174					log.Printf("[workspace] Tree validation failed after close: %v", verr)
175				}
176			}
177			wm.paneCloseLogf("closePane succeeded node=%p promoted=%p root=%p", node, promoted, wm.root)
178			wm.dumpTreeState("after_close_success")
179
180			if wm.treeRebalancer != nil {
181				if rerr := wm.treeRebalancer.RebalanceAfterClose(node, promoted); rerr != nil {
182					log.Printf("[workspace] Tree rebalancing failed after close: %v", rerr)
183				}
184			}
185		}
186		close(done)
187		return false
188	})
189
190	<-done
191
192	if closeErr != nil {
193		return closeErr
194	}
195
196	log.Printf("[workspace] Close operation completed successfully")
197	return nil
198}
199
200// StackPane performs a stack operation with validation and safety checks
201func (wm *WorkspaceManager) StackPane(target *paneNode) (*paneNode, error) {
202	if wm == nil {
203		return nil, fmt.Errorf("workspace manager is nil")
204	}
205
206	log.Printf("[workspace] Starting stack operation: target=%p", target)
207
208	// Step 1: Validate stack operation constraints (only in DebugBasic+)
209	if wm.debugLevel >= DebugBasic {
210		validation := wm.geometryValidator.ValidateStackOperation(target)
211		if !validation.IsValid {
212			if wm.debugLevel == DebugFull {
213				return nil, fmt.Errorf("stack validation failed: %s", validation.Reason)
214			}
215			log.Printf("[workspace] WARNING: Stack validation failed but allowing operation: %s", validation.Reason)
216		}
217
218		// Log if re-validation will be needed due to pending widget allocation
219		if validation.RequiresRevalidation {
220			log.Printf("[workspace] Stack validation passed with pending allocation - operation will proceed")
221		}
222	}
223
224	// Step 2: Validate tree invariants before operation (only in DebugBasic+)
225	if wm.debugLevel >= DebugBasic {
226		if err := wm.treeValidator.ValidateTree(wm.root, "before_stack"); err != nil {
227			if wm.debugLevel == DebugFull {
228				return nil, fmt.Errorf("tree validation failed before stack: %w", err)
229			}
230			log.Printf("[workspace] WARNING: Tree validation failed before stack but allowing operation: %v", err)
231		}
232	}
233
234	// Step 3: Execute directly if already on the GTK main thread
235	if webkit.IsMainThread() {
236		log.Printf("[workspace] Already on main thread, executing stack directly")
237		newNode, err := wm.stackedPaneManager.StackPane(target)
238		if err != nil {
239			return nil, err
240		}
241
242		if wm.debugLevel >= DebugBasic {
243			if err := wm.treeValidator.ValidateTree(wm.root, "after_stack"); err != nil {
244				log.Printf("[workspace] Tree validation failed after stack: %v", err)
245			}
246		}
247
248		log.Printf("[workspace] Stack operation completed successfully (direct execution): newNode=%p", newNode)
249		return newNode, nil
250	}
251
252	// Step 4: Not on main thread, marshal via IdleAdd
253	log.Printf("[workspace] Not on main thread, marshalling stack via IdleAdd")
254	var newNode *paneNode
255	var stackErr error
256	done := make(chan struct{})
257
258	_ = webkit.IdleAdd(func() bool {
259		newNode, stackErr = wm.stackedPaneManager.StackPane(target)
260		if stackErr == nil {
261			if wm.debugLevel >= DebugBasic {
262				if verr := wm.treeValidator.ValidateTree(wm.root, "after_stack"); verr != nil {
263					log.Printf("[workspace] Tree validation failed after stack: %v", verr)
264				}
265			}
266		}
267		close(done)
268		return false
269	})
270
271	<-done
272
273	if stackErr != nil {
274		return nil, stackErr
275	}
276
277	log.Printf("[workspace] Stack operation completed successfully: newNode=%p", newNode)
278	return newNode, nil
279}
280
281// EnableEnhancedMode enables all enhanced validation features
282func (wm *WorkspaceManager) EnableEnhancedMode() {
283	if wm.treeValidator != nil {
284		wm.treeValidator.Enable()
285	}
286	if wm.treeRebalancer != nil {
287		wm.treeRebalancer.Enable()
288	}
289	log.Printf("[workspace] Enhanced validation mode enabled")
290}
291
292// DisableEnhancedMode disables enhanced features for performance
293func (wm *WorkspaceManager) DisableEnhancedMode() {
294	if wm.treeValidator != nil {
295		wm.treeValidator.Disable()
296	}
297	if wm.treeRebalancer != nil {
298		wm.treeRebalancer.Disable()
299	}
300	log.Printf("[workspace] Enhanced validation mode disabled")
301}
302
303// SetEnhancedDebugMode enables/disables debug mode for all enhanced validation components
304func (wm *WorkspaceManager) SetEnhancedDebugMode(debug bool) {
305	if wm.treeValidator != nil {
306		wm.treeValidator.SetDebugMode(debug)
307	}
308	if wm.geometryValidator != nil {
309		wm.geometryValidator.SetDebugMode(debug)
310	}
311	log.Printf("[workspace] Enhanced debug mode set to: %v", debug)
312}
313
314// GetEnhancedStats returns comprehensive statistics about all enhanced validation components
315func (wm *WorkspaceManager) GetEnhancedStats() map[string]interface{} {
316	stats := make(map[string]interface{})
317
318	if wm.treeValidator != nil {
319		stats["tree_validation"] = wm.treeValidator.GetValidationStats()
320	}
321
322	if wm.treeRebalancer != nil {
323		stats["tree_rebalancing"] = wm.treeRebalancer.GetRebalancingStats()
324	}
325
326	// Geometry validator no longer tracks stats - simplified
327
328	return stats
329}
330
331// ValidateWorkspaceIntegrity performs a comprehensive validation of the workspace
332func (wm *WorkspaceManager) ValidateWorkspaceIntegrity() error {
333	log.Printf("[workspace] Performing comprehensive workspace integrity check")
334
335	// Tree structure validation
336	if wm.treeValidator != nil {
337		if err := wm.treeValidator.ValidateTree(wm.root, "integrity_check"); err != nil {
338			return fmt.Errorf("tree validation failed: %w", err)
339		}
340	}
341
342	// Geometry validation simplified - ValidateWorkspaceLayout removed
343
344	log.Printf("[workspace] Workspace integrity check completed successfully")
345	return nil
346}
347
348// ShutdownEnhancedComponents gracefully shuts down all enhanced validation components
349func (wm *WorkspaceManager) ShutdownEnhancedComponents() {
350	log.Printf("[workspace] Shutting down enhanced validation components")
351
352	// Other components don't require explicit shutdown currently
353	log.Printf("[workspace] Enhanced validation components shutdown complete")
354}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close