diff --git a/README.asciidoc b/README.asciidoc index 3f29dcb1c..fca65a516 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -18,9 +18,10 @@ It was inspired by other browsers/addons like dwb and Vimperator/Pentadactyl. Documentation ------------- -In addition to the topics mentioned in that README, the following documents are +In addition to the topics mentioned in this README, the following documents are available: +* link:doc/quickstart.asciidoc[Quick start guide] * link:doc/FAQ.asciidoc[Frequently asked questions] * link:doc/HACKING.asciidoc[HACKING] * link:doc/readme.asciidoc[Reporting segfaults] @@ -165,6 +166,22 @@ As soon as v0.1 is out, a standalone .exe (built with http://cx-freeze.sourceforge.net/[cx_Freeze]) will be provided. In the meantime, you can simply ask in IRC if you need one. +Donating +-------- + +Working on qutebrowser is a very rewarding hobby, but like (nearly) all hobbies +it also costs some money. Namely I have to pay for the server and domain, and +do occasional hardware upgrades footnote:[It turned out a 160 GB SSD is rather +small - the VMs and custom Qt builds I use for testing/developing qutebrowser +need about 100 GB of space]. + +If you want to give me a beer or a pizza back, I'm trying to make it as easy as +possible for you to do so. If some other way would be easier for you, please +get in touch! + +* PayPal: me@the-compiler.org +* Bitcoin: link:bitcoin:1PMzbcetAHfpxoXww8Bj5XqquHtVvMjJtE[1PMzbcetAHfpxoXww8Bj5XqquHtVvMjJtE] + Authors ------- diff --git a/doc/HACKING.asciidoc b/doc/HACKING.asciidoc index e33ac21ec..c531350b5 100644 --- a/doc/HACKING.asciidoc +++ b/doc/HACKING.asciidoc @@ -421,7 +421,8 @@ displaying it to the user. * Mention in the docstring whether your function needs a URL string or a `QUrl`. * Call `ensure_valid` from `utils.qtutils` whenever getting or creating a -`QUrl` and take appropriate action if not. +`QUrl` and take appropriate action if not. Note the URL of the current page +always could be an invalid QUrl (if nothing is loaded yet). Style conventions diff --git a/doc/help/index.asciidoc b/doc/help/index.asciidoc index b81ea1d5e..5925e615d 100644 --- a/doc/help/index.asciidoc +++ b/doc/help/index.asciidoc @@ -6,6 +6,7 @@ Documentation The following help pages are currently available: +* link:quickstart.html[Quick start guide] * link:FAQ.html[Frequently asked questions] * link:commands.html[Documentation of commands] * link:settings.html[Documentation of settings] diff --git a/doc/quickstart.asciidoc b/doc/quickstart.asciidoc index 437479f72..ddb418cd2 100644 --- a/doc/quickstart.asciidoc +++ b/doc/quickstart.asciidoc @@ -40,7 +40,7 @@ What to do now * Press `:` to get the commandline. Press `o`/`O` to open a new page (with `O` in a new tab). Use `H` and `L` to go back/forward and `J`/`K` to focus the next/previous tab. See `~/.config/qutebrowser/keys.conf` for all mapped keys. -* Subscribe to +* Subscribe to https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[the mailinglist] where there are weekly "what's new in qutebrowser" posts. * Let me know what features you are missing or things that need (even small!) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 0d876c8a1..31bedbb21 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -263,6 +263,21 @@ class Application(QApplication): else: tabbed_browser.tabopen(url) + # Open quickstart if it's the first start + state_config = objreg.get('state-config') + try: + quickstart_done = state_config['general']['quickstart-done'] == '1' + except KeyError: + quickstart_done = False + if not quickstart_done: + tabbed_browser.tabopen( + QUrl('http://www.qutebrowser.org/quickstart.html')) + try: + state_config.add_section('general') + except configparser.DuplicateSectionError: + pass + state_config['general']['quickstart-done'] = '1' + def _python_hacks(self): """Get around some PyQt-oddities by evil hacks. diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 86226af04..49855d79f 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -92,6 +92,11 @@ class CommandDispatcher: tab: Whether to open in a new tab. background: Whether to open in the background. """ + if not url.isValid(): + errstr = "Invalid URL {}" + if url.errorString(): + errstr += " - {}".format(url.errorString()) + raise cmdexc.CommandError(errstr) tabbed_browser = objreg.get('tabbed-browser') if tab and background: raise cmdexc.CommandError("Only one of -t/-b can be given!") @@ -715,9 +720,6 @@ class CommandDispatcher: """ urlstr = quickmarks.get(name) url = QUrl(urlstr) - if not url.isValid(): - raise cmdexc.CommandError("Invalid URL {} ({})".format( - urlstr, url.errorString())) self._open(url, tab, bg) @cmdutils.register(instance='command-dispatcher', name='inspector') diff --git a/qutebrowser/browser/downloads.py b/qutebrowser/browser/downloads.py index 46c3ea7db..b8584a64e 100644 --- a/qutebrowser/browser/downloads.py +++ b/qutebrowser/browser/downloads.py @@ -359,7 +359,9 @@ class DownloadManager(QObject): url: The URL to get, as QUrl page: The QWebPage to get the download from. """ - qtutils.ensure_valid(url) + if not url.isValid(): + urlutils.invalid_url_error(url, "start download") + return req = QNetworkRequest(url) reply = page.networkAccessManager().get(req) self.fetch(reply) diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py index ae010d21f..3e6fc8a7b 100644 --- a/qutebrowser/browser/hints.py +++ b/qutebrowser/browser/hints.py @@ -327,7 +327,6 @@ class HintManager(QObject): Args: url: The URL to open as a QURL. """ - qtutils.ensure_valid(url) sel = self._context.target == Target.yank_primary mode = QClipboard.Selection if sel else QClipboard.Clipboard urlstr = url.toString(QUrl.FullyEncoded | QUrl.RemovePassword) @@ -341,7 +340,6 @@ class HintManager(QObject): Args: url: The URL to open as a QUrl. """ - qtutils.ensure_valid(url) urlstr = url.toDisplayString(QUrl.FullyEncoded) args = self._context.get_args(urlstr) message.set_cmd_text(' '.join(args)) @@ -357,19 +355,16 @@ class HintManager(QObject): message.error("No suitable link found for this element.", immediately=True) return - qtutils.ensure_valid(url) objreg.get('download-manager').get(url, elem.webFrame().page()) def _call_userscript(self, url): """Call an userscript from a hint.""" - qtutils.ensure_valid(url) cmd = self._context.args[0] args = self._context.args[1:] userscripts.run(cmd, *args, url=url) def _spawn(self, url): """Spawn a simple command from a hint.""" - qtutils.ensure_valid(url) urlstr = url.toString(QUrl.FullyEncoded | QUrl.RemovePassword) args = self._context.get_args(urlstr) subprocess.Popen(args) @@ -396,6 +391,7 @@ class HintManager(QObject): if baseurl is None: baseurl = self._context.baseurl url = baseurl.resolved(url) + qtutils.ensure_valid(url) return url def _find_prevnext(self, frame, prev=False): @@ -511,7 +507,6 @@ class HintManager(QObject): if url is None: raise cmdexc.CommandError("No {} links found!".format( "prev" if prev else "forward")) - qtutils.ensure_valid(url) if newtab: objreg.get('tabbed-browser').tabopen(url, background=False) else: diff --git a/qutebrowser/browser/quickmarks.py b/qutebrowser/browser/quickmarks.py index 4c91438f8..af357f228 100644 --- a/qutebrowser/browser/quickmarks.py +++ b/qutebrowser/browser/quickmarks.py @@ -64,7 +64,9 @@ def prompt_save(url): Args: url: The quickmark url as a QUrl. """ - qtutils.ensure_valid(url) + if not url.isValid(): + urlutils.invalid_url_error(url, "save quickmark") + return urlstr = url.toString(QUrl.RemovePassword | QUrl.FullyEncoded) message.ask_async("Add quickmark:", usertypes.PromptMode.text, functools.partial(quickmark_add, urlstr)) diff --git a/qutebrowser/utils/urlutils.py b/qutebrowser/utils/urlutils.py index a7cb9661c..f95ac789b 100644 --- a/qutebrowser/utils/urlutils.py +++ b/qutebrowser/utils/urlutils.py @@ -28,7 +28,7 @@ from PyQt5.QtCore import QUrl from PyQt5.QtNetwork import QHostInfo from qutebrowser.config import config -from qutebrowser.utils import log, qtutils +from qutebrowser.utils import log, qtutils, message # FIXME: we probably could raise some exceptions on invalid URLs @@ -262,6 +262,17 @@ def qurl_from_user_input(urlstr): return QUrl('http://[{}]{}'.format(ipstr, rest)) +def invalid_url_error(url, action): + """Display an error message for an URL.""" + if url.isValid(): + raise ValueError("Calling invalid_url_error with valid URL {}".format( + url.toDisplayString())) + errstring = "Trying to {} with invalid URL".format(action) + if url.errorString(): + errstring += " - {}".format(url.errorString()) + message.error(errstring) + + class FuzzyUrlError(Exception): """Exception raised by fuzzy_url on problems.""" diff --git a/qutebrowser/widgets/console.py b/qutebrowser/widgets/console.py index d433c705d..b7fe928d2 100644 --- a/qutebrowser/widgets/console.py +++ b/qutebrowser/widgets/console.py @@ -86,7 +86,7 @@ class ConsoleLineEdit(misc.CommandLineEdit): """Push a line to the interpreter.""" self._buffer.append(line) source = '\n'.join(self._buffer) - self.write.emit(self._curprompt() + line) + self.write.emit(self._curprompt() + line + '\n') # We do two special things with the contextmanagers here: # - We replace stdout/stderr to capture output. Even if we could # override InteractiveInterpreter's write method, most things are @@ -152,11 +152,12 @@ class ConsoleLineEdit(misc.CommandLineEdit): class ConsoleTextEdit(QTextEdit): - """Custom QTextEdit for console input.""" + """Custom QTextEdit for console output.""" def __init__(self, parent=None): super().__init__(parent) self.setAcceptRichText(False) + self.setLineWrapMode(QTextEdit.NoWrap) self.setReadOnly(True) config.on_change(self.update_font, 'fonts', 'debug-console') self.update_font() @@ -184,7 +185,7 @@ class ConsoleWidget(QWidget): super().__init__(parent) self._lineedit = ConsoleLineEdit(self) self._output = ConsoleTextEdit() - self._lineedit.write.connect(self._output.append) + self._lineedit.write.connect(self._output.insertPlainText) self._vbox = QVBoxLayout() self._vbox.setSpacing(0) self._vbox.addWidget(self._output) diff --git a/qutebrowser/widgets/tabbedbrowser.py b/qutebrowser/widgets/tabbedbrowser.py index cdeab74ff..4833a4aef 100644 --- a/qutebrowser/widgets/tabbedbrowser.py +++ b/qutebrowser/widgets/tabbedbrowser.py @@ -247,11 +247,13 @@ class TabbedBrowser(tabwidget.TabWidget): self._now_focused = None if tab is objreg.get('last-focused-tab', None): objreg.delete('last-focused-tab') - if not tab.cur_url.isEmpty(): - qtutils.ensure_valid(tab.cur_url) + if tab.cur_url.isValid(): history_data = qtutils.serialize(tab.history()) entry = UndoEntry(tab.cur_url, history_data) self._undo_stack.append(entry) + else: + urlutils.invalid_url_error(url, "saving tab") + return tab.shutdown() self._tabs.remove(tab) self.removeTab(idx) diff --git a/qutebrowser/widgets/webview.py b/qutebrowser/widgets/webview.py index 791483f9a..b7c712885 100644 --- a/qutebrowser/widgets/webview.py +++ b/qutebrowser/widgets/webview.py @@ -335,10 +335,13 @@ class WebView(QWebView): @pyqtSlot('QUrl') def on_url_changed(self, url): - """Update cur_url when URL has changed.""" - qtutils.ensure_valid(url) - self.cur_url = url - self.url_text_changed.emit(url.toDisplayString()) + """Update cur_url when URL has changed. + + If the URL is invalid, we just ignore it here. + """ + if url.isValid(): + self.cur_url = url + self.url_text_changed.emit(url.toDisplayString()) @pyqtSlot('QMouseEvent') def on_mouse_event(self, evt): diff --git a/scripts/asciidoc2html.py b/scripts/asciidoc2html.py index bac8dba4c..8a0bad30d 100755 --- a/scripts/asciidoc2html.py +++ b/scripts/asciidoc2html.py @@ -65,6 +65,7 @@ def main(colors=False): utils.use_color = colors asciidoc_files = [ ('doc/FAQ.asciidoc', 'qutebrowser/html/doc/FAQ.html'), + ('doc/quickstart.asciidoc', 'qutebrowser/html/doc/quickstart.html'), ] try: os.mkdir('qutebrowser/html/doc')