We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
:yank markdown (and asciidoc and html)
I love the [v.1.6.0](https://github.com/qutebrowser/qutebrowser/releases/tag/v1.6.0) `:yank markdown` feature - thank you for that. For those of us who write in asciidoc and html, it would be great to also be able to yank in those formats, i.e., have this:

* :yank markdown
  * `[DuckDuckGo — Privacy, simplified.](https://start.duckduckgo.com/)`
* :yank asciidoc
  * `https://start.duckduckgo.com/[DuckDuckGo — Privacy, simplified.]`
* :yank html
  * `<a href="https://start.duckduckgo.com/">DuckDuckGo — Privacy, simplified.</a>`

Thank you for considering this!

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `qutebrowser/commands/runners.py`
Content:
```
1 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
2 
3 # Copyright 2014-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
4 #
5 # This file is part of qutebrowser.
6 #
7 # qutebrowser is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # qutebrowser is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with qutebrowser.  If not, see <http://www.gnu.org/licenses/>.
19 
20 """Module containing command managers (SearchRunner and CommandRunner)."""
21 
22 import traceback
23 import re
24 
25 import attr
26 from PyQt5.QtCore import pyqtSlot, QUrl, QObject
27 
28 from qutebrowser.api import cmdutils
29 from qutebrowser.config import config
30 from qutebrowser.commands import cmdexc
31 from qutebrowser.utils import message, objreg, qtutils, usertypes, utils
32 from qutebrowser.misc import split, objects
33 
34 
35 last_command = {}
36 
37 
38 @attr.s
39 class ParseResult:
40 
41     """The result of parsing a commandline."""
42 
43     cmd = attr.ib()
44     args = attr.ib()
45     cmdline = attr.ib()
46 
47 
48 def _current_url(tabbed_browser):
49     """Convenience method to get the current url."""
50     try:
51         return tabbed_browser.current_url()
52     except qtutils.QtValueError as e:
53         msg = "Current URL is invalid"
54         if e.reason:
55             msg += " ({})".format(e.reason)
56         msg += "!"
57         raise cmdutils.CommandError(msg)
58 
59 
60 def replace_variables(win_id, arglist):
61     """Utility function to replace variables like {url} in a list of args."""
62     tabbed_browser = objreg.get('tabbed-browser', scope='window',
63                                 window=win_id)
64 
65     variables = {
66         'url': lambda: _current_url(tabbed_browser).toString(
67             QUrl.FullyEncoded | QUrl.RemovePassword),
68         'url:pretty': lambda: _current_url(tabbed_browser).toString(
69             QUrl.DecodeReserved | QUrl.RemovePassword),
70         'url:host': lambda: _current_url(tabbed_browser).host(),
71         'clipboard': utils.get_clipboard,
72         'primary': lambda: utils.get_clipboard(selection=True),
73     }
74 
75     for key in list(variables):
76         modified_key = '{' + key + '}'
77         variables[modified_key] = lambda x=modified_key: x
78 
79     values = {}
80     args = []
81 
82     def repl_cb(matchobj):
83         """Return replacement for given match."""
84         var = matchobj.group("var")
85         if var not in values:
86             values[var] = variables[var]()
87         return values[var]
88     repl_pattern = re.compile("{(?P<var>" + "|".join(variables.keys()) + ")}")
89 
90     try:
91         for arg in arglist:
92             # using re.sub with callback function replaces all variables in a
93             # single pass and avoids expansion of nested variables (e.g.
94             # "{url}" from clipboard is not expanded)
95             args.append(repl_pattern.sub(repl_cb, arg))
96     except utils.ClipboardError as e:
97         raise cmdutils.CommandError(e)
98     return args
99 
100 
101 class CommandParser:
102 
103     """Parse qutebrowser commandline commands.
104 
105     Attributes:
106         _partial_match: Whether to allow partial command matches.
107     """
108 
109     def __init__(self, partial_match=False):
110         self._partial_match = partial_match
111 
112     def _get_alias(self, text, default=None):
113         """Get an alias from the config.
114 
115         Args:
116             text: The text to parse.
117             default : Default value to return when alias was not found.
118 
119         Return:
120             The new command string if an alias was found. Default value
121             otherwise.
122         """
123         parts = text.strip().split(maxsplit=1)
124         try:
125             alias = config.val.aliases[parts[0]]
126         except KeyError:
127             return default
128 
129         try:
130             new_cmd = '{} {}'.format(alias, parts[1])
131         except IndexError:
132             new_cmd = alias
133         if text.endswith(' '):
134             new_cmd += ' '
135         return new_cmd
136 
137     def _parse_all_gen(self, text, *args, aliases=True, **kwargs):
138         """Split a command on ;; and parse all parts.
139 
140         If the first command in the commandline is a non-split one, it only
141         returns that.
142 
143         Args:
144             text: Text to parse.
145             aliases: Whether to handle aliases.
146             *args/**kwargs: Passed to parse().
147 
148         Yields:
149             ParseResult tuples.
150         """
151         text = text.strip().lstrip(':').strip()
152         if not text:
153             raise cmdexc.NoSuchCommandError("No command given")
154 
155         if aliases:
156             text = self._get_alias(text, text)
157 
158         if ';;' in text:
159             # Get the first command and check if it doesn't want to have ;;
160             # split.
161             first = text.split(';;')[0]
162             result = self.parse(first, *args, **kwargs)
163             if result.cmd.no_cmd_split:
164                 sub_texts = [text]
165             else:
166                 sub_texts = [e.strip() for e in text.split(';;')]
167         else:
168             sub_texts = [text]
169         for sub in sub_texts:
170             yield self.parse(sub, *args, **kwargs)
171 
172     def parse_all(self, *args, **kwargs):
173         """Wrapper over _parse_all_gen."""
174         return list(self._parse_all_gen(*args, **kwargs))
175 
176     def parse(self, text, *, fallback=False, keep=False):
177         """Split the commandline text into command and arguments.
178 
179         Args:
180             text: Text to parse.
181             fallback: Whether to do a fallback splitting when the command was
182                       unknown.
183             keep: Whether to keep special chars and whitespace
184 
185         Return:
186             A ParseResult tuple.
187         """
188         cmdstr, sep, argstr = text.partition(' ')
189 
190         if not cmdstr and not fallback:
191             raise cmdexc.NoSuchCommandError("No command given")
192 
193         if self._partial_match:
194             cmdstr = self._completion_match(cmdstr)
195 
196         try:
197             cmd = objects.commands[cmdstr]
198         except KeyError:
199             if not fallback:
200                 raise cmdexc.NoSuchCommandError(
201                     '{}: no such command'.format(cmdstr))
202             cmdline = split.split(text, keep=keep)
203             return ParseResult(cmd=None, args=None, cmdline=cmdline)
204 
205         args = self._split_args(cmd, argstr, keep)
206         if keep and args:
207             cmdline = [cmdstr, sep + args[0]] + args[1:]
208         elif keep:
209             cmdline = [cmdstr, sep]
210         else:
211             cmdline = [cmdstr] + args[:]
212 
213         return ParseResult(cmd=cmd, args=args, cmdline=cmdline)
214 
215     def _completion_match(self, cmdstr):
216         """Replace cmdstr with a matching completion if there's only one match.
217 
218         Args:
219             cmdstr: The string representing the entered command so far
220 
221         Return:
222             cmdstr modified to the matching completion or unmodified
223         """
224         matches = [cmd for cmd in sorted(objects.commands, key=len)
225                    if cmdstr in cmd]
226         if len(matches) == 1:
227             cmdstr = matches[0]
228         elif len(matches) > 1 and config.val.completion.use_best_match:
229             cmdstr = matches[0]
230         return cmdstr
231 
232     def _split_args(self, cmd, argstr, keep):
233         """Split the arguments from an arg string.
234 
235         Args:
236             cmd: The command we're currently handling.
237             argstr: An argument string.
238             keep: Whether to keep special chars and whitespace
239 
240         Return:
241             A list containing the split strings.
242         """
243         if not argstr:
244             return []
245         elif cmd.maxsplit is None:
246             return split.split(argstr, keep=keep)
247         else:
248             # If split=False, we still want to split the flags, but not
249             # everything after that.
250             # We first split the arg string and check the index of the first
251             # non-flag args, then we re-split again properly.
252             # example:
253             #
254             # input: "--foo -v bar baz"
255             # first split: ['--foo', '-v', 'bar', 'baz']
256             #                0        1     2      3
257             # second split: ['--foo', '-v', 'bar baz']
258             # (maxsplit=2)
259             split_args = split.simple_split(argstr, keep=keep)
260             flag_arg_count = 0
261             for i, arg in enumerate(split_args):
262                 arg = arg.strip()
263                 if arg.startswith('-'):
264                     if arg in cmd.flags_with_args:
265                         flag_arg_count += 1
266                 else:
267                     maxsplit = i + cmd.maxsplit + flag_arg_count
268                     return split.simple_split(argstr, keep=keep,
269                                               maxsplit=maxsplit)
270 
271             # If there are only flags, we got it right on the first try
272             # already.
273             return split_args
274 
275 
276 class CommandRunner(QObject):
277 
278     """Parse and run qutebrowser commandline commands.
279 
280     Attributes:
281         _win_id: The window this CommandRunner is associated with.
282     """
283 
284     def __init__(self, win_id, partial_match=False, parent=None):
285         super().__init__(parent)
286         self._parser = CommandParser(partial_match=partial_match)
287         self._win_id = win_id
288 
289     def run(self, text, count=None):
290         """Parse a command from a line of text and run it.
291 
292         Args:
293             text: The text to parse.
294             count: The count to pass to the command.
295         """
296         record_last_command = True
297         record_macro = True
298 
299         mode_manager = objreg.get('mode-manager', scope='window',
300                                   window=self._win_id)
301         cur_mode = mode_manager.mode
302 
303         for result in self._parser.parse_all(text):
304             if result.cmd.no_replace_variables:
305                 args = result.args
306             else:
307                 args = replace_variables(self._win_id, result.args)
308             result.cmd.run(self._win_id, args, count=count)
309 
310             if result.cmdline[0] == 'repeat-command':
311                 record_last_command = False
312 
313             if result.cmdline[0] in ['record-macro', 'run-macro',
314                                      'set-cmd-text']:
315                 record_macro = False
316 
317         if record_last_command:
318             last_command[cur_mode] = (text, count)
319 
320         if record_macro and cur_mode == usertypes.KeyMode.normal:
321             macro_recorder = objreg.get('macro-recorder')
322             macro_recorder.record_command(text, count)
323 
324     @pyqtSlot(str, int)
325     @pyqtSlot(str)
326     def run_safely(self, text, count=None):
327         """Run a command and display exceptions in the statusbar."""
328         try:
329             self.run(text, count)
330         except cmdexc.Error as e:
331             message.error(str(e), stack=traceback.format_exc())
332
```
--- 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()

```

diff --git a/qutebrowser/commands/runners.py b/qutebrowser/commands/runners.py
--- a/qutebrowser/commands/runners.py
+++ b/qutebrowser/commands/runners.py
@@ -61,13 +61,29 @@
     """Utility function to replace variables like {url} in a list of args."""
     tabbed_browser = objreg.get('tabbed-browser', scope='window',
                                 window=win_id)
+    url = lambda: _current_url(tabbed_browser)
 
     variables = {
-        'url': lambda: _current_url(tabbed_browser).toString(
+        'url': lambda: url().toString(
             QUrl.FullyEncoded | QUrl.RemovePassword),
-        'url:pretty': lambda: _current_url(tabbed_browser).toString(
+        'url:pretty': lambda: url().toString(
             QUrl.DecodeReserved | QUrl.RemovePassword),
-        'url:host': lambda: _current_url(tabbed_browser).host(),
+        'url:domain': lambda: "{}://{}{}".format(
+            url().scheme(),
+            url().host(),
+            ":" + str(url().port()) if url().port() != -1 else ""),
+        'url:auth': lambda: "{}:{}@".format(
+            url().userName(),
+            url().password()) if url().userName() else "",
+        'url:scheme': lambda: url().scheme(),
+        'url:username': lambda: url().userName(),
+        'url:password': lambda: url().password(),
+        'url:host': lambda: url().host(),
+        'url:port': lambda: str(url().port()) if url().port() != -1 else "",
+        'url:path': lambda: url().path(),
+        'url:query': lambda: url().query(),
+        'title': lambda: tabbed_browser.widget.page_title(
+            tabbed_browser.widget.currentIndex()),
         'clipboard': utils.get_clipboard,
         'primary': lambda: utils.get_clipboard(selection=True),
     }

{"golden_diff": "diff --git a/qutebrowser/commands/runners.py b/qutebrowser/commands/runners.py\n--- a/qutebrowser/commands/runners.py\n+++ b/qutebrowser/commands/runners.py\n@@ -61,13 +61,29 @@\n     \"\"\"Utility function to replace variables like {url} in a list of args.\"\"\"\n     tabbed_browser = objreg.get('tabbed-browser', scope='window',\n                                 window=win_id)\n+    url = lambda: _current_url(tabbed_browser)\n \n     variables = {\n-        'url': lambda: _current_url(tabbed_browser).toString(\n+        'url': lambda: url().toString(\n             QUrl.FullyEncoded | QUrl.RemovePassword),\n-        'url:pretty': lambda: _current_url(tabbed_browser).toString(\n+        'url:pretty': lambda: url().toString(\n             QUrl.DecodeReserved | QUrl.RemovePassword),\n-        'url:host': lambda: _current_url(tabbed_browser).host(),\n+        'url:domain': lambda: \"{}://{}{}\".format(\n+            url().scheme(),\n+            url().host(),\n+            \":\" + str(url().port()) if url().port() != -1 else \"\"),\n+        'url:auth': lambda: \"{}:{}@\".format(\n+            url().userName(),\n+            url().password()) if url().userName() else \"\",\n+        'url:scheme': lambda: url().scheme(),\n+        'url:username': lambda: url().userName(),\n+        'url:password': lambda: url().password(),\n+        'url:host': lambda: url().host(),\n+        'url:port': lambda: str(url().port()) if url().port() != -1 else \"\",\n+        'url:path': lambda: url().path(),\n+        'url:query': lambda: url().query(),\n+        'title': lambda: tabbed_browser.widget.page_title(\n+            tabbed_browser.widget.currentIndex()),\n         'clipboard': utils.get_clipboard,\n         'primary': lambda: utils.get_clipboard(selection=True),\n     }\n", "issue": ":yank markdown (and asciidoc and html)\nI love the [v.1.6.0](https://github.com/qutebrowser/qutebrowser/releases/tag/v1.6.0) `:yank markdown` feature - thank you for that. For those of us who write in asciidoc and html, it would be great to also be able to yank in those formats, i.e., have this:\r\n\r\n* :yank markdown\r\n  * `[DuckDuckGo \u2014 Privacy, simplified.](https://start.duckduckgo.com/)`\r\n* :yank asciidoc\r\n  * `https://start.duckduckgo.com/[DuckDuckGo \u2014 Privacy, simplified.]`\r\n* :yank html\r\n  * `<a href=\"https://start.duckduckgo.com/\">DuckDuckGo \u2014 Privacy, simplified.</a>`\r\n\r\nThank you for considering this!\n", "before_files": [{"content": "# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:\n\n# Copyright 2014-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>\n#\n# This file is part of qutebrowser.\n#\n# qutebrowser is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# qutebrowser is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with qutebrowser.  If not, see <http://www.gnu.org/licenses/>.\n\n\"\"\"Module containing command managers (SearchRunner and CommandRunner).\"\"\"\n\nimport traceback\nimport re\n\nimport attr\nfrom PyQt5.QtCore import pyqtSlot, QUrl, QObject\n\nfrom qutebrowser.api import cmdutils\nfrom qutebrowser.config import config\nfrom qutebrowser.commands import cmdexc\nfrom qutebrowser.utils import message, objreg, qtutils, usertypes, utils\nfrom qutebrowser.misc import split, objects\n\n\nlast_command = {}\n\n\n@attr.s\nclass ParseResult:\n\n    \"\"\"The result of parsing a commandline.\"\"\"\n\n    cmd = attr.ib()\n    args = attr.ib()\n    cmdline = attr.ib()\n\n\ndef _current_url(tabbed_browser):\n    \"\"\"Convenience method to get the current url.\"\"\"\n    try:\n        return tabbed_browser.current_url()\n    except qtutils.QtValueError as e:\n        msg = \"Current URL is invalid\"\n        if e.reason:\n            msg += \" ({})\".format(e.reason)\n        msg += \"!\"\n        raise cmdutils.CommandError(msg)\n\n\ndef replace_variables(win_id, arglist):\n    \"\"\"Utility function to replace variables like {url} in a list of args.\"\"\"\n    tabbed_browser = objreg.get('tabbed-browser', scope='window',\n                                window=win_id)\n\n    variables = {\n        'url': lambda: _current_url(tabbed_browser).toString(\n            QUrl.FullyEncoded | QUrl.RemovePassword),\n        'url:pretty': lambda: _current_url(tabbed_browser).toString(\n            QUrl.DecodeReserved | QUrl.RemovePassword),\n        'url:host': lambda: _current_url(tabbed_browser).host(),\n        'clipboard': utils.get_clipboard,\n        'primary': lambda: utils.get_clipboard(selection=True),\n    }\n\n    for key in list(variables):\n        modified_key = '{' + key + '}'\n        variables[modified_key] = lambda x=modified_key: x\n\n    values = {}\n    args = []\n\n    def repl_cb(matchobj):\n        \"\"\"Return replacement for given match.\"\"\"\n        var = matchobj.group(\"var\")\n        if var not in values:\n            values[var] = variables[var]()\n        return values[var]\n    repl_pattern = re.compile(\"{(?P<var>\" + \"|\".join(variables.keys()) + \")}\")\n\n    try:\n        for arg in arglist:\n            # using re.sub with callback function replaces all variables in a\n            # single pass and avoids expansion of nested variables (e.g.\n            # \"{url}\" from clipboard is not expanded)\n            args.append(repl_pattern.sub(repl_cb, arg))\n    except utils.ClipboardError as e:\n        raise cmdutils.CommandError(e)\n    return args\n\n\nclass CommandParser:\n\n    \"\"\"Parse qutebrowser commandline commands.\n\n    Attributes:\n        _partial_match: Whether to allow partial command matches.\n    \"\"\"\n\n    def __init__(self, partial_match=False):\n        self._partial_match = partial_match\n\n    def _get_alias(self, text, default=None):\n        \"\"\"Get an alias from the config.\n\n        Args:\n            text: The text to parse.\n            default : Default value to return when alias was not found.\n\n        Return:\n            The new command string if an alias was found. Default value\n            otherwise.\n        \"\"\"\n        parts = text.strip().split(maxsplit=1)\n        try:\n            alias = config.val.aliases[parts[0]]\n        except KeyError:\n            return default\n\n        try:\n            new_cmd = '{} {}'.format(alias, parts[1])\n        except IndexError:\n            new_cmd = alias\n        if text.endswith(' '):\n            new_cmd += ' '\n        return new_cmd\n\n    def _parse_all_gen(self, text, *args, aliases=True, **kwargs):\n        \"\"\"Split a command on ;; and parse all parts.\n\n        If the first command in the commandline is a non-split one, it only\n        returns that.\n\n        Args:\n            text: Text to parse.\n            aliases: Whether to handle aliases.\n            *args/**kwargs: Passed to parse().\n\n        Yields:\n            ParseResult tuples.\n        \"\"\"\n        text = text.strip().lstrip(':').strip()\n        if not text:\n            raise cmdexc.NoSuchCommandError(\"No command given\")\n\n        if aliases:\n            text = self._get_alias(text, text)\n\n        if ';;' in text:\n            # Get the first command and check if it doesn't want to have ;;\n            # split.\n            first = text.split(';;')[0]\n            result = self.parse(first, *args, **kwargs)\n            if result.cmd.no_cmd_split:\n                sub_texts = [text]\n            else:\n                sub_texts = [e.strip() for e in text.split(';;')]\n        else:\n            sub_texts = [text]\n        for sub in sub_texts:\n            yield self.parse(sub, *args, **kwargs)\n\n    def parse_all(self, *args, **kwargs):\n        \"\"\"Wrapper over _parse_all_gen.\"\"\"\n        return list(self._parse_all_gen(*args, **kwargs))\n\n    def parse(self, text, *, fallback=False, keep=False):\n        \"\"\"Split the commandline text into command and arguments.\n\n        Args:\n            text: Text to parse.\n            fallback: Whether to do a fallback splitting when the command was\n                      unknown.\n            keep: Whether to keep special chars and whitespace\n\n        Return:\n            A ParseResult tuple.\n        \"\"\"\n        cmdstr, sep, argstr = text.partition(' ')\n\n        if not cmdstr and not fallback:\n            raise cmdexc.NoSuchCommandError(\"No command given\")\n\n        if self._partial_match:\n            cmdstr = self._completion_match(cmdstr)\n\n        try:\n            cmd = objects.commands[cmdstr]\n        except KeyError:\n            if not fallback:\n                raise cmdexc.NoSuchCommandError(\n                    '{}: no such command'.format(cmdstr))\n            cmdline = split.split(text, keep=keep)\n            return ParseResult(cmd=None, args=None, cmdline=cmdline)\n\n        args = self._split_args(cmd, argstr, keep)\n        if keep and args:\n            cmdline = [cmdstr, sep + args[0]] + args[1:]\n        elif keep:\n            cmdline = [cmdstr, sep]\n        else:\n            cmdline = [cmdstr] + args[:]\n\n        return ParseResult(cmd=cmd, args=args, cmdline=cmdline)\n\n    def _completion_match(self, cmdstr):\n        \"\"\"Replace cmdstr with a matching completion if there's only one match.\n\n        Args:\n            cmdstr: The string representing the entered command so far\n\n        Return:\n            cmdstr modified to the matching completion or unmodified\n        \"\"\"\n        matches = [cmd for cmd in sorted(objects.commands, key=len)\n                   if cmdstr in cmd]\n        if len(matches) == 1:\n            cmdstr = matches[0]\n        elif len(matches) > 1 and config.val.completion.use_best_match:\n            cmdstr = matches[0]\n        return cmdstr\n\n    def _split_args(self, cmd, argstr, keep):\n        \"\"\"Split the arguments from an arg string.\n\n        Args:\n            cmd: The command we're currently handling.\n            argstr: An argument string.\n            keep: Whether to keep special chars and whitespace\n\n        Return:\n            A list containing the split strings.\n        \"\"\"\n        if not argstr:\n            return []\n        elif cmd.maxsplit is None:\n            return split.split(argstr, keep=keep)\n        else:\n            # If split=False, we still want to split the flags, but not\n            # everything after that.\n            # We first split the arg string and check the index of the first\n            # non-flag args, then we re-split again properly.\n            # example:\n            #\n            # input: \"--foo -v bar baz\"\n            # first split: ['--foo', '-v', 'bar', 'baz']\n            #                0        1     2      3\n            # second split: ['--foo', '-v', 'bar baz']\n            # (maxsplit=2)\n            split_args = split.simple_split(argstr, keep=keep)\n            flag_arg_count = 0\n            for i, arg in enumerate(split_args):\n                arg = arg.strip()\n                if arg.startswith('-'):\n                    if arg in cmd.flags_with_args:\n                        flag_arg_count += 1\n                else:\n                    maxsplit = i + cmd.maxsplit + flag_arg_count\n                    return split.simple_split(argstr, keep=keep,\n                                              maxsplit=maxsplit)\n\n            # If there are only flags, we got it right on the first try\n            # already.\n            return split_args\n\n\nclass CommandRunner(QObject):\n\n    \"\"\"Parse and run qutebrowser commandline commands.\n\n    Attributes:\n        _win_id: The window this CommandRunner is associated with.\n    \"\"\"\n\n    def __init__(self, win_id, partial_match=False, parent=None):\n        super().__init__(parent)\n        self._parser = CommandParser(partial_match=partial_match)\n        self._win_id = win_id\n\n    def run(self, text, count=None):\n        \"\"\"Parse a command from a line of text and run it.\n\n        Args:\n            text: The text to parse.\n            count: The count to pass to the command.\n        \"\"\"\n        record_last_command = True\n        record_macro = True\n\n        mode_manager = objreg.get('mode-manager', scope='window',\n                                  window=self._win_id)\n        cur_mode = mode_manager.mode\n\n        for result in self._parser.parse_all(text):\n            if result.cmd.no_replace_variables:\n                args = result.args\n            else:\n                args = replace_variables(self._win_id, result.args)\n            result.cmd.run(self._win_id, args, count=count)\n\n            if result.cmdline[0] == 'repeat-command':\n                record_last_command = False\n\n            if result.cmdline[0] in ['record-macro', 'run-macro',\n                                     'set-cmd-text']:\n                record_macro = False\n\n        if record_last_command:\n            last_command[cur_mode] = (text, count)\n\n        if record_macro and cur_mode == usertypes.KeyMode.normal:\n            macro_recorder = objreg.get('macro-recorder')\n            macro_recorder.record_command(text, count)\n\n    @pyqtSlot(str, int)\n    @pyqtSlot(str)\n    def run_safely(self, text, count=None):\n        \"\"\"Run a command and display exceptions in the statusbar.\"\"\"\n        try:\n            self.run(text, count)\n        except cmdexc.Error as e:\n            message.error(str(e), stack=traceback.format_exc())\n", "path": "qutebrowser/commands/runners.py"}], "after_files": [{"content": "# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:\n\n# Copyright 2014-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>\n#\n# This file is part of qutebrowser.\n#\n# qutebrowser is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# qutebrowser is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with qutebrowser.  If not, see <http://www.gnu.org/licenses/>.\n\n\"\"\"Module containing command managers (SearchRunner and CommandRunner).\"\"\"\n\nimport traceback\nimport re\n\nimport attr\nfrom PyQt5.QtCore import pyqtSlot, QUrl, QObject\n\nfrom qutebrowser.api import cmdutils\nfrom qutebrowser.config import config\nfrom qutebrowser.commands import cmdexc\nfrom qutebrowser.utils import message, objreg, qtutils, usertypes, utils\nfrom qutebrowser.misc import split, objects\n\n\nlast_command = {}\n\n\n@attr.s\nclass ParseResult:\n\n    \"\"\"The result of parsing a commandline.\"\"\"\n\n    cmd = attr.ib()\n    args = attr.ib()\n    cmdline = attr.ib()\n\n\ndef _current_url(tabbed_browser):\n    \"\"\"Convenience method to get the current url.\"\"\"\n    try:\n        return tabbed_browser.current_url()\n    except qtutils.QtValueError as e:\n        msg = \"Current URL is invalid\"\n        if e.reason:\n            msg += \" ({})\".format(e.reason)\n        msg += \"!\"\n        raise cmdutils.CommandError(msg)\n\n\ndef replace_variables(win_id, arglist):\n    \"\"\"Utility function to replace variables like {url} in a list of args.\"\"\"\n    tabbed_browser = objreg.get('tabbed-browser', scope='window',\n                                window=win_id)\n    url = lambda: _current_url(tabbed_browser)\n\n    variables = {\n        'url': lambda: url().toString(\n            QUrl.FullyEncoded | QUrl.RemovePassword),\n        'url:pretty': lambda: url().toString(\n            QUrl.DecodeReserved | QUrl.RemovePassword),\n        'url:domain': lambda: \"{}://{}{}\".format(\n            url().scheme(),\n            url().host(),\n            \":\" + str(url().port()) if url().port() != -1 else \"\"),\n        'url:auth': lambda: \"{}:{}@\".format(\n            url().userName(),\n            url().password()) if url().userName() else \"\",\n        'url:scheme': lambda: url().scheme(),\n        'url:username': lambda: url().userName(),\n        'url:password': lambda: url().password(),\n        'url:host': lambda: url().host(),\n        'url:port': lambda: str(url().port()) if url().port() != -1 else \"\",\n        'url:path': lambda: url().path(),\n        'url:query': lambda: url().query(),\n        'title': lambda: tabbed_browser.widget.page_title(\n            tabbed_browser.widget.currentIndex()),\n        'clipboard': utils.get_clipboard,\n        'primary': lambda: utils.get_clipboard(selection=True),\n    }\n\n    for key in list(variables):\n        modified_key = '{' + key + '}'\n        variables[modified_key] = lambda x=modified_key: x\n\n    values = {}\n    args = []\n\n    def repl_cb(matchobj):\n        \"\"\"Return replacement for given match.\"\"\"\n        var = matchobj.group(\"var\")\n        if var not in values:\n            values[var] = variables[var]()\n        return values[var]\n    repl_pattern = re.compile(\"{(?P<var>\" + \"|\".join(variables.keys()) + \")}\")\n\n    try:\n        for arg in arglist:\n            # using re.sub with callback function replaces all variables in a\n            # single pass and avoids expansion of nested variables (e.g.\n            # \"{url}\" from clipboard is not expanded)\n            args.append(repl_pattern.sub(repl_cb, arg))\n    except utils.ClipboardError as e:\n        raise cmdutils.CommandError(e)\n    return args\n\n\nclass CommandParser:\n\n    \"\"\"Parse qutebrowser commandline commands.\n\n    Attributes:\n        _partial_match: Whether to allow partial command matches.\n    \"\"\"\n\n    def __init__(self, partial_match=False):\n        self._partial_match = partial_match\n\n    def _get_alias(self, text, default=None):\n        \"\"\"Get an alias from the config.\n\n        Args:\n            text: The text to parse.\n            default : Default value to return when alias was not found.\n\n        Return:\n            The new command string if an alias was found. Default value\n            otherwise.\n        \"\"\"\n        parts = text.strip().split(maxsplit=1)\n        try:\n            alias = config.val.aliases[parts[0]]\n        except KeyError:\n            return default\n\n        try:\n            new_cmd = '{} {}'.format(alias, parts[1])\n        except IndexError:\n            new_cmd = alias\n        if text.endswith(' '):\n            new_cmd += ' '\n        return new_cmd\n\n    def _parse_all_gen(self, text, *args, aliases=True, **kwargs):\n        \"\"\"Split a command on ;; and parse all parts.\n\n        If the first command in the commandline is a non-split one, it only\n        returns that.\n\n        Args:\n            text: Text to parse.\n            aliases: Whether to handle aliases.\n            *args/**kwargs: Passed to parse().\n\n        Yields:\n            ParseResult tuples.\n        \"\"\"\n        text = text.strip().lstrip(':').strip()\n        if not text:\n            raise cmdexc.NoSuchCommandError(\"No command given\")\n\n        if aliases:\n            text = self._get_alias(text, text)\n\n        if ';;' in text:\n            # Get the first command and check if it doesn't want to have ;;\n            # split.\n            first = text.split(';;')[0]\n            result = self.parse(first, *args, **kwargs)\n            if result.cmd.no_cmd_split:\n                sub_texts = [text]\n            else:\n                sub_texts = [e.strip() for e in text.split(';;')]\n        else:\n            sub_texts = [text]\n        for sub in sub_texts:\n            yield self.parse(sub, *args, **kwargs)\n\n    def parse_all(self, *args, **kwargs):\n        \"\"\"Wrapper over _parse_all_gen.\"\"\"\n        return list(self._parse_all_gen(*args, **kwargs))\n\n    def parse(self, text, *, fallback=False, keep=False):\n        \"\"\"Split the commandline text into command and arguments.\n\n        Args:\n            text: Text to parse.\n            fallback: Whether to do a fallback splitting when the command was\n                      unknown.\n            keep: Whether to keep special chars and whitespace\n\n        Return:\n            A ParseResult tuple.\n        \"\"\"\n        cmdstr, sep, argstr = text.partition(' ')\n\n        if not cmdstr and not fallback:\n            raise cmdexc.NoSuchCommandError(\"No command given\")\n\n        if self._partial_match:\n            cmdstr = self._completion_match(cmdstr)\n\n        try:\n            cmd = objects.commands[cmdstr]\n        except KeyError:\n            if not fallback:\n                raise cmdexc.NoSuchCommandError(\n                    '{}: no such command'.format(cmdstr))\n            cmdline = split.split(text, keep=keep)\n            return ParseResult(cmd=None, args=None, cmdline=cmdline)\n\n        args = self._split_args(cmd, argstr, keep)\n        if keep and args:\n            cmdline = [cmdstr, sep + args[0]] + args[1:]\n        elif keep:\n            cmdline = [cmdstr, sep]\n        else:\n            cmdline = [cmdstr] + args[:]\n\n        return ParseResult(cmd=cmd, args=args, cmdline=cmdline)\n\n    def _completion_match(self, cmdstr):\n        \"\"\"Replace cmdstr with a matching completion if there's only one match.\n\n        Args:\n            cmdstr: The string representing the entered command so far\n\n        Return:\n            cmdstr modified to the matching completion or unmodified\n        \"\"\"\n        matches = [cmd for cmd in sorted(objects.commands, key=len)\n                   if cmdstr in cmd]\n        if len(matches) == 1:\n            cmdstr = matches[0]\n        elif len(matches) > 1 and config.val.completion.use_best_match:\n            cmdstr = matches[0]\n        return cmdstr\n\n    def _split_args(self, cmd, argstr, keep):\n        \"\"\"Split the arguments from an arg string.\n\n        Args:\n            cmd: The command we're currently handling.\n            argstr: An argument string.\n            keep: Whether to keep special chars and whitespace\n\n        Return:\n            A list containing the split strings.\n        \"\"\"\n        if not argstr:\n            return []\n        elif cmd.maxsplit is None:\n            return split.split(argstr, keep=keep)\n        else:\n            # If split=False, we still want to split the flags, but not\n            # everything after that.\n            # We first split the arg string and check the index of the first\n            # non-flag args, then we re-split again properly.\n            # example:\n            #\n            # input: \"--foo -v bar baz\"\n            # first split: ['--foo', '-v', 'bar', 'baz']\n            #                0        1     2      3\n            # second split: ['--foo', '-v', 'bar baz']\n            # (maxsplit=2)\n            split_args = split.simple_split(argstr, keep=keep)\n            flag_arg_count = 0\n            for i, arg in enumerate(split_args):\n                arg = arg.strip()\n                if arg.startswith('-'):\n                    if arg in cmd.flags_with_args:\n                        flag_arg_count += 1\n                else:\n                    maxsplit = i + cmd.maxsplit + flag_arg_count\n                    return split.simple_split(argstr, keep=keep,\n                                              maxsplit=maxsplit)\n\n            # If there are only flags, we got it right on the first try\n            # already.\n            return split_args\n\n\nclass CommandRunner(QObject):\n\n    \"\"\"Parse and run qutebrowser commandline commands.\n\n    Attributes:\n        _win_id: The window this CommandRunner is associated with.\n    \"\"\"\n\n    def __init__(self, win_id, partial_match=False, parent=None):\n        super().__init__(parent)\n        self._parser = CommandParser(partial_match=partial_match)\n        self._win_id = win_id\n\n    def run(self, text, count=None):\n        \"\"\"Parse a command from a line of text and run it.\n\n        Args:\n            text: The text to parse.\n            count: The count to pass to the command.\n        \"\"\"\n        record_last_command = True\n        record_macro = True\n\n        mode_manager = objreg.get('mode-manager', scope='window',\n                                  window=self._win_id)\n        cur_mode = mode_manager.mode\n\n        for result in self._parser.parse_all(text):\n            if result.cmd.no_replace_variables:\n                args = result.args\n            else:\n                args = replace_variables(self._win_id, result.args)\n            result.cmd.run(self._win_id, args, count=count)\n\n            if result.cmdline[0] == 'repeat-command':\n                record_last_command = False\n\n            if result.cmdline[0] in ['record-macro', 'run-macro',\n                                     'set-cmd-text']:\n                record_macro = False\n\n        if record_last_command:\n            last_command[cur_mode] = (text, count)\n\n        if record_macro and cur_mode == usertypes.KeyMode.normal:\n            macro_recorder = objreg.get('macro-recorder')\n            macro_recorder.record_command(text, count)\n\n    @pyqtSlot(str, int)\n    @pyqtSlot(str)\n    def run_safely(self, text, count=None):\n        \"\"\"Run a command and display exceptions in the statusbar.\"\"\"\n        try:\n            self.run(text, count)\n        except cmdexc.Error as e:\n            message.error(str(e), stack=traceback.format_exc())\n", "path": "qutebrowser/commands/runners.py"}]}