prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  WebSocket view jumps to top on new message
  The WebSocket view keeps jumping to the top every time a new message arrives. This makes it basically impossible to work with while the connection is open. I hold down arrow -> it scrolls a bit -> message arrives -> I'm back at the top
  
  _Originally posted by @Prinzhorn in https://github.com/mitmproxy/mitmproxy/issues/4486#issuecomment-796578909_
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `mitmproxy/tools/console/searchable.py`
  Content:
  ```
  1 import urwid
  2 
  3 from mitmproxy.tools.console import signals
  4 
  5 
  6 class Highlight(urwid.AttrMap):
  7 
  8     def __init__(self, t):
  9         urwid.AttrMap.__init__(
  10             self,
  11             urwid.Text(t.text),
  12             "focusfield",
  13         )
  14         self.backup = t
  15 
  16 
  17 class Searchable(urwid.ListBox):
  18 
  19     def __init__(self, contents):
  20         self.walker = urwid.SimpleFocusListWalker(contents)
  21         urwid.ListBox.__init__(self, self.walker)
  22         self.search_offset = 0
  23         self.current_highlight = None
  24         self.search_term = None
  25         self.last_search = None
  26 
  27     def keypress(self, size, key):
  28         if key == "/":
  29             signals.status_prompt.send(
  30                 prompt = "Search for",
  31                 text = "",
  32                 callback = self.set_search
  33             )
  34         elif key == "n":
  35             self.find_next(False)
  36         elif key == "N":
  37             self.find_next(True)
  38         elif key == "m_start":
  39             self.set_focus(0)
  40             self.walker._modified()
  41         elif key == "m_end":
  42             self.set_focus(len(self.walker) - 1)
  43             self.walker._modified()
  44         else:
  45             return super().keypress(size, key)
  46 
  47     def set_search(self, text):
  48         self.last_search = text
  49         self.search_term = text or None
  50         self.find_next(False)
  51 
  52     def set_highlight(self, offset):
  53         if self.current_highlight is not None:
  54             old = self.body[self.current_highlight]
  55             self.body[self.current_highlight] = old.backup
  56         if offset is None:
  57             self.current_highlight = None
  58         else:
  59             self.body[offset] = Highlight(self.body[offset])
  60             self.current_highlight = offset
  61 
  62     def get_text(self, w):
  63         if isinstance(w, urwid.Text):
  64             return w.text
  65         elif isinstance(w, Highlight):
  66             return w.backup.text
  67         else:
  68             return None
  69 
  70     def find_next(self, backwards):
  71         if not self.search_term:
  72             if self.last_search:
  73                 self.search_term = self.last_search
  74             else:
  75                 self.set_highlight(None)
  76                 return
  77         # Start search at focus + 1
  78         if backwards:
  79             rng = range(len(self.body) - 1, -1, -1)
  80         else:
  81             rng = range(1, len(self.body) + 1)
  82         for i in rng:
  83             off = (self.focus_position + i) % len(self.body)
  84             w = self.body[off]
  85             txt = self.get_text(w)
  86             if txt and self.search_term in txt:
  87                 self.set_highlight(off)
  88                 self.set_focus(off, coming_from="above")
  89                 self.body._modified()
  90                 return
  91         else:
  92             self.set_highlight(None)
  93             signals.status_message.send(message="Search not found.", expire=1)
  94
  ```
  --- 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/mitmproxy/tools/console/searchable.py b/mitmproxy/tools/console/searchable.py
  --- a/mitmproxy/tools/console/searchable.py
  +++ b/mitmproxy/tools/console/searchable.py
  @@ -19,6 +19,7 @@
       def __init__(self, contents):
           self.walker = urwid.SimpleFocusListWalker(contents)
           urwid.ListBox.__init__(self, self.walker)
  +        self.set_focus(len(self.walker) - 1)
           self.search_offset = 0
           self.current_highlight = None
           self.search_term = None
  
verification_info: {"golden_diff": "diff --git a/mitmproxy/tools/console/searchable.py b/mitmproxy/tools/console/searchable.py\n--- a/mitmproxy/tools/console/searchable.py\n+++ b/mitmproxy/tools/console/searchable.py\n@@ -19,6 +19,7 @@\n     def __init__(self, contents):\n         self.walker = urwid.SimpleFocusListWalker(contents)\n         urwid.ListBox.__init__(self, self.walker)\n+        self.set_focus(len(self.walker) - 1)\n         self.search_offset = 0\n         self.current_highlight = None\n         self.search_term = None\n", "issue": "WebSocket view jumps to top on new message\nThe WebSocket view keeps jumping to the top every time a new message arrives. This makes it basically impossible to work with while the connection is open. I hold down arrow -> it scrolls a bit -> message arrives -> I'm back at the top\r\n\r\n_Originally posted by @Prinzhorn in https://github.com/mitmproxy/mitmproxy/issues/4486#issuecomment-796578909_\n", "before_files": [{"content": "import urwid\n\nfrom mitmproxy.tools.console import signals\n\n\nclass Highlight(urwid.AttrMap):\n\n    def __init__(self, t):\n        urwid.AttrMap.__init__(\n            self,\n            urwid.Text(t.text),\n            \"focusfield\",\n        )\n        self.backup = t\n\n\nclass Searchable(urwid.ListBox):\n\n    def __init__(self, contents):\n        self.walker = urwid.SimpleFocusListWalker(contents)\n        urwid.ListBox.__init__(self, self.walker)\n        self.search_offset = 0\n        self.current_highlight = None\n        self.search_term = None\n        self.last_search = None\n\n    def keypress(self, size, key):\n        if key == \"/\":\n            signals.status_prompt.send(\n                prompt = \"Search for\",\n                text = \"\",\n                callback = self.set_search\n            )\n        elif key == \"n\":\n            self.find_next(False)\n        elif key == \"N\":\n            self.find_next(True)\n        elif key == \"m_start\":\n            self.set_focus(0)\n            self.walker._modified()\n        elif key == \"m_end\":\n            self.set_focus(len(self.walker) - 1)\n            self.walker._modified()\n        else:\n            return super().keypress(size, key)\n\n    def set_search(self, text):\n        self.last_search = text\n        self.search_term = text or None\n        self.find_next(False)\n\n    def set_highlight(self, offset):\n        if self.current_highlight is not None:\n            old = self.body[self.current_highlight]\n            self.body[self.current_highlight] = old.backup\n        if offset is None:\n            self.current_highlight = None\n        else:\n            self.body[offset] = Highlight(self.body[offset])\n            self.current_highlight = offset\n\n    def get_text(self, w):\n        if isinstance(w, urwid.Text):\n            return w.text\n        elif isinstance(w, Highlight):\n            return w.backup.text\n        else:\n            return None\n\n    def find_next(self, backwards):\n        if not self.search_term:\n            if self.last_search:\n                self.search_term = self.last_search\n            else:\n                self.set_highlight(None)\n                return\n        # Start search at focus + 1\n        if backwards:\n            rng = range(len(self.body) - 1, -1, -1)\n        else:\n            rng = range(1, len(self.body) + 1)\n        for i in rng:\n            off = (self.focus_position + i) % len(self.body)\n            w = self.body[off]\n            txt = self.get_text(w)\n            if txt and self.search_term in txt:\n                self.set_highlight(off)\n                self.set_focus(off, coming_from=\"above\")\n                self.body._modified()\n                return\n        else:\n            self.set_highlight(None)\n            signals.status_message.send(message=\"Search not found.\", expire=1)\n", "path": "mitmproxy/tools/console/searchable.py"}], "after_files": [{"content": "import urwid\n\nfrom mitmproxy.tools.console import signals\n\n\nclass Highlight(urwid.AttrMap):\n\n    def __init__(self, t):\n        urwid.AttrMap.__init__(\n            self,\n            urwid.Text(t.text),\n            \"focusfield\",\n        )\n        self.backup = t\n\n\nclass Searchable(urwid.ListBox):\n\n    def __init__(self, contents):\n        self.walker = urwid.SimpleFocusListWalker(contents)\n        urwid.ListBox.__init__(self, self.walker)\n        self.set_focus(len(self.walker) - 1)\n        self.search_offset = 0\n        self.current_highlight = None\n        self.search_term = None\n        self.last_search = None\n\n    def keypress(self, size, key):\n        if key == \"/\":\n            signals.status_prompt.send(\n                prompt = \"Search for\",\n                text = \"\",\n                callback = self.set_search\n            )\n        elif key == \"n\":\n            self.find_next(False)\n        elif key == \"N\":\n            self.find_next(True)\n        elif key == \"m_start\":\n            self.set_focus(0)\n            self.walker._modified()\n        elif key == \"m_end\":\n            self.set_focus(len(self.walker) - 1)\n            self.walker._modified()\n        else:\n            return super().keypress(size, key)\n\n    def set_search(self, text):\n        self.last_search = text\n        self.search_term = text or None\n        self.find_next(False)\n\n    def set_highlight(self, offset):\n        if self.current_highlight is not None:\n            old = self.body[self.current_highlight]\n            self.body[self.current_highlight] = old.backup\n        if offset is None:\n            self.current_highlight = None\n        else:\n            self.body[offset] = Highlight(self.body[offset])\n            self.current_highlight = offset\n\n    def get_text(self, w):\n        if isinstance(w, urwid.Text):\n            return w.text\n        elif isinstance(w, Highlight):\n            return w.backup.text\n        else:\n            return None\n\n    def find_next(self, backwards):\n        if not self.search_term:\n            if self.last_search:\n                self.search_term = self.last_search\n            else:\n                self.set_highlight(None)\n                return\n        # Start search at focus + 1\n        if backwards:\n            rng = range(len(self.body) - 1, -1, -1)\n        else:\n            rng = range(1, len(self.body) + 1)\n        for i in rng:\n            off = (self.focus_position + i) % len(self.body)\n            w = self.body[off]\n            txt = self.get_text(w)\n            if txt and self.search_term in txt:\n                self.set_highlight(off)\n                self.set_focus(off, coming_from=\"above\")\n                self.body._modified()\n                return\n        else:\n            self.set_highlight(None)\n            signals.status_message.send(message=\"Search not found.\", expire=1)\n", "path": "mitmproxy/tools/console/searchable.py"}]}