on main help sign in
perf: attempt to improve permissions dialog performance
commitab7c7e6fe60fc4c120acc5ea867373c4b0e3a9b6 authorAndrey Nering <andreynering@users.noreply.github.com> dateTue 24 Jun 2025, 17:22 (1 year ago) parent8291df8
1 file changed+114−55
internal/tui/components/dialogs/permissions/permissions.go+114−55
internal/tui/components/dialogs/permissions/permissions.go+114−55
@@ -56,6 +56,11 @@ type permissionDialogCmp struct {
5656 diffXOffset int // horizontal scroll offset
5757 diffYOffset int // vertical scroll offset
5858
59+ // Caching
60+ cachedContent string
61+ cachedContentKey string
62+ contentDirty bool
63+
5964 keyMap KeyMap
6065}
6166
@@ -67,6 +72,7 @@ func NewPermissionDialogCmp(permission permission.PermissionRequest) PermissionD
6772 selectedOption: 0, // Default to "Allow"
6873 permission: permission,
6974 keyMap: DefaultKeyMap(),
75+ contentDirty: true, // Mark as dirty initially
7076 }
7177}
7278
@@ -74,6 +80,10 @@ func (p *permissionDialogCmp) Init() tea.Cmd {
7480 return p.contentViewPort.Init()
7581}
7682
83+func (p *permissionDialogCmp) supportsDiffView() bool {
84+ return p.permission.ToolName == tools.EditToolName || p.permission.ToolName == tools.WriteToolName
85+}
86+
7787func (p *permissionDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
7888 var cmds []tea.Cmd
7989
@@ -81,6 +91,7 @@ func (p *permissionDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
8191 case tea.WindowSizeMsg:
8292 p.wWidth = msg.Width
8393 p.wHeight = msg.Height
94+ p.contentDirty = true // Mark content as dirty on window resize
8495 cmd := p.SetSize()
8596 cmds = append(cmds, cmd)
8697 case tea.KeyPressMsg:
@@ -108,20 +119,35 @@ func (p *permissionDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
108119 util.CmdHandler(PermissionResponseMsg{Action: PermissionDeny, Permission: p.permission}),
109120 )
110121 case key.Matches(msg, p.keyMap.ToggleDiffMode):
111− p.diffSplitMode = !p.diffSplitMode
112− return p, nil
122+ if p.supportsDiffView() {
123+ p.diffSplitMode = !p.diffSplitMode
124+ p.contentDirty = true // Mark content as dirty when diff mode changes
125+ return p, nil
126+ }
113127 case key.Matches(msg, p.keyMap.ScrollDown):
114− p.diffYOffset += 1
115− return p, nil
128+ if p.supportsDiffView() {
129+ p.diffYOffset += 1
130+ p.contentDirty = true // Mark content as dirty when scrolling
131+ return p, nil
132+ }
116133 case key.Matches(msg, p.keyMap.ScrollUp):
117− p.diffYOffset = max(0, p.diffYOffset-1)
118− return p, nil
134+ if p.supportsDiffView() {
135+ p.diffYOffset = max(0, p.diffYOffset-1)
136+ p.contentDirty = true // Mark content as dirty when scrolling
137+ return p, nil
138+ }
119139 case key.Matches(msg, p.keyMap.ScrollLeft):
120− p.diffXOffset = max(0, p.diffXOffset-5)
121− return p, nil
140+ if p.supportsDiffView() {
141+ p.diffXOffset = max(0, p.diffXOffset-5)
142+ p.contentDirty = true // Mark content as dirty when scrolling
143+ return p, nil
144+ }
122145 case key.Matches(msg, p.keyMap.ScrollRight):
123− p.diffXOffset += 5
124− return p, nil
146+ if p.supportsDiffView() {
147+ p.diffXOffset += 5
148+ p.contentDirty = true // Mark content as dirty when scrolling
149+ return p, nil
150+ }
125151 default:
126152 // Pass other keys to viewport
127153 viewPort, cmd := p.contentViewPort.Update(msg)
@@ -247,7 +273,49 @@ func (p *permissionDialogCmp) renderHeader() string {
247273 return baseStyle.Render(lipgloss.JoinVertical(lipgloss.Left, headerParts...))
248274}
249275
250−func (p *permissionDialogCmp) renderBashContent() string {
276+func (p *permissionDialogCmp) generateContentKey() string {
277+ return fmt.Sprintf("%s_%s_%t_%d_%d_%d_%d",
278+ p.permission.ID,
279+ p.permission.ToolName,
280+ p.diffSplitMode,
281+ p.diffXOffset,
282+ p.diffYOffset,
283+ p.contentViewPort.Width(),
284+ p.contentViewPort.Height())
285+}
286+
287+func (p *permissionDialogCmp) getOrGenerateContent() string {
288+ currentKey := p.generateContentKey()
289+
290+ // Return cached content if available and not dirty
291+ if !p.contentDirty && p.cachedContentKey == currentKey && p.cachedContent != "" {
292+ return p.cachedContent
293+ }
294+
295+ // Generate new content
296+ var content string
297+ switch p.permission.ToolName {
298+ case tools.BashToolName:
299+ content = p.generateBashContent()
300+ case tools.EditToolName:
301+ content = p.generateEditContent()
302+ case tools.WriteToolName:
303+ content = p.generateWriteContent()
304+ case tools.FetchToolName:
305+ content = p.generateFetchContent()
306+ default:
307+ content = p.generateDefaultContent()
308+ }
309+
310+ // Cache the result
311+ p.cachedContent = content
312+ p.cachedContentKey = currentKey
313+ p.contentDirty = false
314+
315+ return content
316+}
317+
318+func (p *permissionDialogCmp) generateBashContent() string {
251319 t := styles.CurrentTheme()
252320 baseStyle := t.S().Base.Background(t.BgSubtle)
253321 if pr, ok := p.permission.Params.(tools.BashPermissionsParams); ok {
@@ -277,15 +345,12 @@ func (p *permissionDialogCmp) renderBashContent() string {
277345 Width(p.contentViewPort.Width()).
278346 Render(renderedContent)
279347
280− contentHeight := min(p.height-9, lipgloss.Height(finalContent))
281− p.contentViewPort.SetHeight(contentHeight)
282− p.contentViewPort.SetContent(finalContent)
283− return p.styleViewport()
348+ return finalContent
284349 }
285350 return ""
286351}
287352
288−func (p *permissionDialogCmp) renderEditContent() string {
353+func (p *permissionDialogCmp) generateEditContent() string {
289354 if pr, ok := p.permission.Params.(tools.EditPermissionsParams); ok {
290355 formatter := core.DiffFormatter().
291356 Before(fsext.PrettyPath(pr.FilePath), pr.OldContent).
@@ -301,33 +366,34 @@ func (p *permissionDialogCmp) renderEditContent() string {
301366 }
302367
303368 diff := formatter.String()
304− contentHeight := min(p.height-9, lipgloss.Height(diff))
305− p.contentViewPort.SetHeight(contentHeight)
306− p.contentViewPort.SetContent(diff)
307− return p.styleViewport()
369+ return diff
308370 }
309371 return ""
310372}
311373
312−func (p *permissionDialogCmp) renderWriteContent() string {
374+func (p *permissionDialogCmp) generateWriteContent() string {
313375 if pr, ok := p.permission.Params.(tools.WritePermissionsParams); ok {
314376 // Use the cache for diff rendering
315377 formatter := core.DiffFormatter().
316378 Before(fsext.PrettyPath(pr.FilePath), pr.OldContent).
317379 After(fsext.PrettyPath(pr.FilePath), pr.NewContent).
380+ Height(p.contentViewPort.Height()).
318381 Width(p.contentViewPort.Width()).
319− Split()
382+ XOffset(p.diffXOffset).
383+ YOffset(p.diffYOffset)
384+ if p.diffSplitMode {
385+ formatter = formatter.Split()
386+ } else {
387+ formatter = formatter.Unified()
388+ }
320389
321390 diff := formatter.String()
322− contentHeight := min(p.height-9, lipgloss.Height(diff))
323− p.contentViewPort.SetHeight(contentHeight)
324− p.contentViewPort.SetContent(diff)
325− return p.styleViewport()
391+ return diff
326392 }
327393 return ""
328394}
329395
330−func (p *permissionDialogCmp) renderFetchContent() string {
396+func (p *permissionDialogCmp) generateFetchContent() string {
331397 t := styles.CurrentTheme()
332398 baseStyle := t.S().Base.Background(t.BgSubtle)
333399 if pr, ok := p.permission.Params.(tools.FetchPermissionsParams); ok {
@@ -344,15 +410,12 @@ func (p *permissionDialogCmp) renderFetchContent() string {
344410 Width(p.contentViewPort.Width()).
345411 Render(renderedContent)
346412
347− contentHeight := min(p.height-9, lipgloss.Height(finalContent))
348− p.contentViewPort.SetHeight(contentHeight)
349− p.contentViewPort.SetContent(finalContent)
350− return p.styleViewport()
413+ return finalContent
351414 }
352415 return ""
353416}
354417
355−func (p *permissionDialogCmp) renderDefaultContent() string {
418+func (p *permissionDialogCmp) generateDefaultContent() string {
356419 t := styles.CurrentTheme()
357420 baseStyle := t.S().Base.Background(t.BgSubtle)
358421
@@ -368,13 +431,12 @@ func (p *permissionDialogCmp) renderDefaultContent() string {
368431 finalContent := baseStyle.
369432 Width(p.contentViewPort.Width()).
370433 Render(renderedContent)
371− p.contentViewPort.SetContent(finalContent)
372434
373435 if renderedContent == "" {
374436 return ""
375437 }
376438
377− return p.styleViewport()
439+ return finalContent
378440}
379441
380442func (p *permissionDialogCmp) styleViewport() string {
@@ -393,21 +455,17 @@ func (p *permissionDialogCmp) render() string {
393455
394456 p.contentViewPort.SetWidth(p.width - 4)
395457
396− // Render content based on tool type
397− var contentFinal string
458+ // Get cached or generate content
459+ contentFinal := p.getOrGenerateContent()
460+
461+ // Always set viewport content (the caching is handled in getOrGenerateContent)
462+ contentHeight := min(p.height-9, lipgloss.Height(contentFinal))
463+ p.contentViewPort.SetHeight(contentHeight)
464+ p.contentViewPort.SetContent(contentFinal)
465+
398466 var contentHelp string
399− switch p.permission.ToolName {
400− case tools.BashToolName:
401− contentFinal = p.renderBashContent()
402− case tools.EditToolName:
403− contentFinal = p.renderEditContent()
467+ if p.supportsDiffView() {
404468 contentHelp = help.New().View(p.keyMap)
405− case tools.WriteToolName:
406− contentFinal = p.renderWriteContent()
407− case tools.FetchToolName:
408− contentFinal = p.renderFetchContent()
409− default:
410− contentFinal = p.renderDefaultContent()
411469 }
412470 // Calculate content height dynamically based on window size
413471
@@ -415,7 +473,7 @@ func (p *permissionDialogCmp) render() string {
415473 title,
416474 "",
417475 headerContent,
418− contentFinal,
476+ p.styleViewport(),
419477 "",
420478 buttons,
421479 "",
@@ -443,6 +501,9 @@ func (p *permissionDialogCmp) SetSize() tea.Cmd {
443501 if p.permission.ID == "" {
444502 return nil
445503 }
504+
505+ oldWidth, oldHeight := p.width, p.height
506+
446507 switch p.permission.ToolName {
447508 case tools.BashToolName:
448509 p.width = int(float64(p.wWidth) * 0.4)
@@ -460,15 +521,13 @@ func (p *permissionDialogCmp) SetSize() tea.Cmd {
460521 p.width = int(float64(p.wWidth) * 0.7)
461522 p.height = int(float64(p.wHeight) * 0.5)
462523 }
463− return nil
464−}
465524
466−func (c *permissionDialogCmp) GetOrSetDiff(key string, generator func() (string, error)) string {
467− content, err := generator()
468− if err != nil {
469− return fmt.Sprintf("Error formatting diff: %v", err)
525+ // Mark content as dirty if size changed
526+ if oldWidth != p.width || oldHeight != p.height {
527+ p.contentDirty = true
470528 }
471− return content
529+
530+ return nil
472531}
473532
474533func (c *permissionDialogCmp) GetOrSetMarkdown(key string, generator func() (string, error)) string {