prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  [undo develop] undoing a reload blanks the entire sheet
  Since v2.5 undo for reload has been removed, and replaced with quitguard+confirm! However, in that case an undo should not be set.
  
  Current behaviour is that it blanks the sheet.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `visidata/undo.py`
  Content:
  ```
  1 import itertools
  2 from copy import copy
  3 
  4 from visidata import vd, options, VisiData, BaseSheet, UNLOADED
  5 
  6 BaseSheet.init('undone', list)  # list of CommandLogRow for redo after undo
  7 
  8 vd.option('undo', True, 'enable undo/redo')
  9 
  10 nonUndo = '''commit open-file'''.split()
  11 
  12 def isUndoableCommand(longname):
  13     for n in nonUndo:
  14         if longname.startswith(n):
  15             return False
  16     return True
  17 
  18 @VisiData.api
  19 def addUndo(vd, undofunc, *args, **kwargs):
  20     'On undo of latest command, call ``undofunc(*args, **kwargs)``.'
  21     if options.undo:
  22         # occurs when VisiData is just starting up
  23         if getattr(vd, 'activeCommand', UNLOADED) is UNLOADED:
  24             return
  25         r = vd.modifyCommand
  26         # some special commands, like open-file, do not have an undofuncs set
  27         if not r or not isUndoableCommand(r.longname):
  28             return
  29         if not r.undofuncs:
  30             r.undofuncs = []
  31         r.undofuncs.append((undofunc, args, kwargs))
  32 
  33 
  34 @VisiData.api
  35 def undo(vd, sheet):
  36     if not options.undo:
  37         vd.fail("options.undo not enabled")
  38 
  39     # don't allow undo of first command on a sheet, which is always the command that created the sheet.
  40     for cmdlogrow in sheet.cmdlog_sheet.rows[:0:-1]:
  41         if cmdlogrow.undofuncs:
  42             for undofunc, args, kwargs, in cmdlogrow.undofuncs[::-1]:
  43                 undofunc(*args, **kwargs)
  44             sheet.undone.append(cmdlogrow)
  45             sheet.cmdlog_sheet.rows.remove(cmdlogrow)
  46 
  47             vd.clearCaches()  # undofunc can invalidate the drawcache
  48 
  49             vd.moveToReplayContext(cmdlogrow, sheet)
  50             vd.status("%s undone" % cmdlogrow.longname)
  51             return
  52 
  53     vd.fail("nothing to undo on current sheet")
  54 
  55 
  56 @VisiData.api
  57 def redo(vd, sheet):
  58     sheet.undone or vd.fail("nothing to redo")
  59     cmdlogrow = sheet.undone.pop()
  60     vd.replayOne(cmdlogrow)
  61     vd.status("%s redone" % cmdlogrow.longname)
  62 
  63 # undoers
  64 def undoAttrFunc(objs, attrname):
  65     'Return closure that sets attrname on each obj to its former value.'
  66     oldvals = [(o, getattr(o, attrname)) for o in objs]
  67     def _undofunc():
  68         for o, v in oldvals:
  69             setattr(o, attrname, v)
  70     return _undofunc
  71 
  72 
  73 class Fanout(list):
  74     'Fan out attribute changes to every element in a list.'
  75     def __getattr__(self, k):
  76         return Fanout([getattr(o, k) for o in self])
  77 
  78     def __setattr__(self, k, v):
  79         vd.addUndo(undoAttrFunc(self, k))
  80         for o in self:
  81             setattr(o, k, v)
  82 
  83     def __call__(self, *args, **kwargs):
  84         return Fanout([o(*args, **kwargs) for o in self])
  85 
  86 
  87 def undoAttrCopyFunc(objs, attrname):
  88     'Return closure that sets attrname on each obj to its former value.'
  89     oldvals = [(o, copy(getattr(o, attrname))) for o in objs]
  90     def _undofunc():
  91         for o, v in oldvals:
  92             setattr(o, attrname, v)
  93     return _undofunc
  94 
  95 
  96 @VisiData.api
  97 def addUndoSetValues(vd, cols, rows):
  98     'Add undo function to reset values for *rows* in *cols*.'
  99     oldvals = [(c, r, c.getValue(r)) for c,r in itertools.product(cols, vd.Progress(rows, gerund='doing'))]
  100     def _undo():
  101         for c, r, v in oldvals:
  102             c.setValue(r, v)
  103     vd.addUndo(_undo)
  104 
  105 @VisiData.api
  106 def addUndoColNames(vd, cols):
  107     oldnames = [(c, c.name) for c in cols]
  108     def _undo():
  109         for c, name in oldnames:
  110             c.name = name
  111     vd.addUndo(_undo)
  112 
  113 
  114 BaseSheet.addCommand('U', 'undo-last', 'vd.undo(sheet)', 'Undo the most recent change (options.undo must be enabled)')
  115 BaseSheet.addCommand('R', 'redo-last', 'vd.redo(sheet)', 'Redo the most recent undo (options.undo must be enabled)')
  116
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/visidata/undo.py b/visidata/undo.py
  --- a/visidata/undo.py
  +++ b/visidata/undo.py
  @@ -7,7 +7,7 @@
   
   vd.option('undo', True, 'enable undo/redo')
   
  -nonUndo = '''commit open-file'''.split()
  +nonUndo = '''commit open-file reload-sheet'''.split()
   
   def isUndoableCommand(longname):
       for n in nonUndo:
  
verification_info: {"golden_diff": "diff --git a/visidata/undo.py b/visidata/undo.py\n--- a/visidata/undo.py\n+++ b/visidata/undo.py\n@@ -7,7 +7,7 @@\n \n vd.option('undo', True, 'enable undo/redo')\n \n-nonUndo = '''commit open-file'''.split()\n+nonUndo = '''commit open-file reload-sheet'''.split()\n \n def isUndoableCommand(longname):\n     for n in nonUndo:\n", "issue": "[undo develop] undoing a reload blanks the entire sheet\nSince v2.5 undo for reload has been removed, and replaced with quitguard+confirm! However, in that case an undo should not be set.\r\n\r\nCurrent behaviour is that it blanks the sheet.\n", "before_files": [{"content": "import itertools\nfrom copy import copy\n\nfrom visidata import vd, options, VisiData, BaseSheet, UNLOADED\n\nBaseSheet.init('undone', list)  # list of CommandLogRow for redo after undo\n\nvd.option('undo', True, 'enable undo/redo')\n\nnonUndo = '''commit open-file'''.split()\n\ndef isUndoableCommand(longname):\n    for n in nonUndo:\n        if longname.startswith(n):\n            return False\n    return True\n\n@VisiData.api\ndef addUndo(vd, undofunc, *args, **kwargs):\n    'On undo of latest command, call ``undofunc(*args, **kwargs)``.'\n    if options.undo:\n        # occurs when VisiData is just starting up\n        if getattr(vd, 'activeCommand', UNLOADED) is UNLOADED:\n            return\n        r = vd.modifyCommand\n        # some special commands, like open-file, do not have an undofuncs set\n        if not r or not isUndoableCommand(r.longname):\n            return\n        if not r.undofuncs:\n            r.undofuncs = []\n        r.undofuncs.append((undofunc, args, kwargs))\n\n\n@VisiData.api\ndef undo(vd, sheet):\n    if not options.undo:\n        vd.fail(\"options.undo not enabled\")\n\n    # don't allow undo of first command on a sheet, which is always the command that created the sheet.\n    for cmdlogrow in sheet.cmdlog_sheet.rows[:0:-1]:\n        if cmdlogrow.undofuncs:\n            for undofunc, args, kwargs, in cmdlogrow.undofuncs[::-1]:\n                undofunc(*args, **kwargs)\n            sheet.undone.append(cmdlogrow)\n            sheet.cmdlog_sheet.rows.remove(cmdlogrow)\n\n            vd.clearCaches()  # undofunc can invalidate the drawcache\n\n            vd.moveToReplayContext(cmdlogrow, sheet)\n            vd.status(\"%s undone\" % cmdlogrow.longname)\n            return\n\n    vd.fail(\"nothing to undo on current sheet\")\n\n\n@VisiData.api\ndef redo(vd, sheet):\n    sheet.undone or vd.fail(\"nothing to redo\")\n    cmdlogrow = sheet.undone.pop()\n    vd.replayOne(cmdlogrow)\n    vd.status(\"%s redone\" % cmdlogrow.longname)\n\n# undoers\ndef undoAttrFunc(objs, attrname):\n    'Return closure that sets attrname on each obj to its former value.'\n    oldvals = [(o, getattr(o, attrname)) for o in objs]\n    def _undofunc():\n        for o, v in oldvals:\n            setattr(o, attrname, v)\n    return _undofunc\n\n\nclass Fanout(list):\n    'Fan out attribute changes to every element in a list.'\n    def __getattr__(self, k):\n        return Fanout([getattr(o, k) for o in self])\n\n    def __setattr__(self, k, v):\n        vd.addUndo(undoAttrFunc(self, k))\n        for o in self:\n            setattr(o, k, v)\n\n    def __call__(self, *args, **kwargs):\n        return Fanout([o(*args, **kwargs) for o in self])\n\n\ndef undoAttrCopyFunc(objs, attrname):\n    'Return closure that sets attrname on each obj to its former value.'\n    oldvals = [(o, copy(getattr(o, attrname))) for o in objs]\n    def _undofunc():\n        for o, v in oldvals:\n            setattr(o, attrname, v)\n    return _undofunc\n\n\n@VisiData.api\ndef addUndoSetValues(vd, cols, rows):\n    'Add undo function to reset values for *rows* in *cols*.'\n    oldvals = [(c, r, c.getValue(r)) for c,r in itertools.product(cols, vd.Progress(rows, gerund='doing'))]\n    def _undo():\n        for c, r, v in oldvals:\n            c.setValue(r, v)\n    vd.addUndo(_undo)\n\n@VisiData.api\ndef addUndoColNames(vd, cols):\n    oldnames = [(c, c.name) for c in cols]\n    def _undo():\n        for c, name in oldnames:\n            c.name = name\n    vd.addUndo(_undo)\n\n\nBaseSheet.addCommand('U', 'undo-last', 'vd.undo(sheet)', 'Undo the most recent change (options.undo must be enabled)')\nBaseSheet.addCommand('R', 'redo-last', 'vd.redo(sheet)', 'Redo the most recent undo (options.undo must be enabled)')\n", "path": "visidata/undo.py"}], "after_files": [{"content": "import itertools\nfrom copy import copy\n\nfrom visidata import vd, options, VisiData, BaseSheet, UNLOADED\n\nBaseSheet.init('undone', list)  # list of CommandLogRow for redo after undo\n\nvd.option('undo', True, 'enable undo/redo')\n\nnonUndo = '''commit open-file reload-sheet'''.split()\n\ndef isUndoableCommand(longname):\n    for n in nonUndo:\n        if longname.startswith(n):\n            return False\n    return True\n\n@VisiData.api\ndef addUndo(vd, undofunc, *args, **kwargs):\n    'On undo of latest command, call ``undofunc(*args, **kwargs)``.'\n    if options.undo:\n        # occurs when VisiData is just starting up\n        if getattr(vd, 'activeCommand', UNLOADED) is UNLOADED:\n            return\n        r = vd.modifyCommand\n        # some special commands, like open-file, do not have an undofuncs set\n        if not r or not isUndoableCommand(r.longname):\n            return\n        if not r.undofuncs:\n            r.undofuncs = []\n        r.undofuncs.append((undofunc, args, kwargs))\n\n\n@VisiData.api\ndef undo(vd, sheet):\n    if not options.undo:\n        vd.fail(\"options.undo not enabled\")\n\n    # don't allow undo of first command on a sheet, which is always the command that created the sheet.\n    for cmdlogrow in sheet.cmdlog_sheet.rows[:0:-1]:\n        if cmdlogrow.undofuncs:\n            for undofunc, args, kwargs, in cmdlogrow.undofuncs[::-1]:\n                undofunc(*args, **kwargs)\n            sheet.undone.append(cmdlogrow)\n            sheet.cmdlog_sheet.rows.remove(cmdlogrow)\n\n            vd.clearCaches()  # undofunc can invalidate the drawcache\n\n            vd.moveToReplayContext(cmdlogrow, sheet)\n            vd.status(\"%s undone\" % cmdlogrow.longname)\n            return\n\n    vd.fail(\"nothing to undo on current sheet\")\n\n\n@VisiData.api\ndef redo(vd, sheet):\n    sheet.undone or vd.fail(\"nothing to redo\")\n    cmdlogrow = sheet.undone.pop()\n    vd.replayOne(cmdlogrow)\n    vd.status(\"%s redone\" % cmdlogrow.longname)\n\n# undoers\ndef undoAttrFunc(objs, attrname):\n    'Return closure that sets attrname on each obj to its former value.'\n    oldvals = [(o, getattr(o, attrname)) for o in objs]\n    def _undofunc():\n        for o, v in oldvals:\n            setattr(o, attrname, v)\n    return _undofunc\n\n\nclass Fanout(list):\n    'Fan out attribute changes to every element in a list.'\n    def __getattr__(self, k):\n        return Fanout([getattr(o, k) for o in self])\n\n    def __setattr__(self, k, v):\n        vd.addUndo(undoAttrFunc(self, k))\n        for o in self:\n            setattr(o, k, v)\n\n    def __call__(self, *args, **kwargs):\n        return Fanout([o(*args, **kwargs) for o in self])\n\n\ndef undoAttrCopyFunc(objs, attrname):\n    'Return closure that sets attrname on each obj to its former value.'\n    oldvals = [(o, copy(getattr(o, attrname))) for o in objs]\n    def _undofunc():\n        for o, v in oldvals:\n            setattr(o, attrname, v)\n    return _undofunc\n\n\n@VisiData.api\ndef addUndoSetValues(vd, cols, rows):\n    'Add undo function to reset values for *rows* in *cols*.'\n    oldvals = [(c, r, c.getValue(r)) for c,r in itertools.product(cols, vd.Progress(rows, gerund='doing'))]\n    def _undo():\n        for c, r, v in oldvals:\n            c.setValue(r, v)\n    vd.addUndo(_undo)\n\n@VisiData.api\ndef addUndoColNames(vd, cols):\n    oldnames = [(c, c.name) for c in cols]\n    def _undo():\n        for c, name in oldnames:\n            c.name = name\n    vd.addUndo(_undo)\n\n\nBaseSheet.addCommand('U', 'undo-last', 'vd.undo(sheet)', 'Undo the most recent change (options.undo must be enabled)')\nBaseSheet.addCommand('R', 'redo-last', 'vd.redo(sheet)', 'Redo the most recent undo (options.undo must be enabled)')\n", "path": "visidata/undo.py"}]}