screen: handle input request (code 10)

This commit is contained in:
dece 2021-02-16 19:10:11 +01:00
parent 5ef9a0430a
commit b23e4a8d6a
3 changed files with 35 additions and 9 deletions

View file

@ -1,7 +1,7 @@
import urllib.parse import urllib.parse
def parse_url(url, absolute=False): def parse_url(url: str, absolute: bool =False):
"""Return URL parts from this URL. """Return URL parts from this URL.
This uses urllib.parse.urlparse to not reinvent the wheel, with a few This uses urllib.parse.urlparse to not reinvent the wheel, with a few
@ -25,14 +25,22 @@ def parse_url(url, absolute=False):
return parts return parts
def sanitize_url(url): def sanitize_url(url: str):
"""Parse and unparse an URL to ensure it has been properly formatted.""" """Parse and unparse an URL to ensure it has been properly formatted."""
return urllib.parse.urlunparse(parse_url(url)) return urllib.parse.urlunparse(parse_url(url))
def join_url(base_url, url): def join_url(base_url: str, url: str):
"""Join a base URL with a relative url.""" """Join a base URL with a relative url."""
if base_url.startswith("gemini://"): if base_url.startswith("gemini://"):
base_url = base_url[7:] base_url = base_url[7:]
parts = parse_url(urllib.parse.urljoin(base_url, url)) parts = parse_url(urllib.parse.urljoin(base_url, url))
return urllib.parse.urlunparse(parts) return urllib.parse.urlunparse(parts)
def set_parameter(url: str, user_input: str):
"""Return a new URL with the user input escaped (RFC 3986) appended."""
quoted_input = urllib.parse.quote(user_input)
if "?" in url:
url = url.rsplit("?", maxsplit=1)[0]
return url + "?" + quoted_input

View file

@ -7,7 +7,7 @@ from bebop.colors import ColorPair, init_colors
from bebop.command_line import (CommandLine, EscapeCommandInterrupt, from bebop.command_line import (CommandLine, EscapeCommandInterrupt,
TerminateCommandInterrupt) TerminateCommandInterrupt)
from bebop.mouse import ButtonState from bebop.mouse import ButtonState
from bebop.navigation import join_url, parse_url, sanitize_url from bebop.navigation import join_url, parse_url, sanitize_url, set_parameter
from bebop.page import Page from bebop.page import Page
from bebop.protocol import Request, Response from bebop.protocol import Request, Response
@ -71,7 +71,7 @@ class Screen:
if char == ord("q"): if char == ord("q"):
running = False running = False
elif char == ord(":"): elif char == ord(":"):
command = self.input_common_command() command = self.take_user_input()
self.set_status(f"Command: {command}") self.set_status(f"Command: {command}")
elif char == ord("s"): elif char == ord("s"):
self.set_status(f"h {self.h} w {self.w}") self.set_status(f"h {self.h} w {self.w}")
@ -203,6 +203,8 @@ class Screen:
elif response.generic_code in (40, 50): elif response.generic_code in (40, 50):
error = f"Server error: {response.meta or Response.code.name}." error = f"Server error: {response.meta or Response.code.name}."
self.set_status_error(error) self.set_status_error(error)
elif response.generic_code == 10:
self.handle_input_request(url, response)
def load_page(self, gemtext: bytes): def load_page(self, gemtext: bytes):
"""Load Gemtext data as the current page.""" """Load Gemtext data as the current page."""
@ -215,9 +217,9 @@ class Screen:
else: else:
self.refresh_page() self.refresh_page()
def input_common_command(self): def take_user_input(self, type_char: str =":"):
"""Focus command line to type a regular command. Currently useless.""" """Focus command line to let the user type something"""
return self.command_line.focus(":", self.validate_common_char) return self.command_line.focus(type_char, self.validate_common_char)
def validate_common_char(self, ch: int): def validate_common_char(self, ch: int):
"""Generic input validator, handles a few more cases than default. """Generic input validator, handles a few more cases than default.
@ -321,6 +323,16 @@ class Screen:
return return
self.open_url(links[link_id]) self.open_url(links[link_id])
def handle_input_request(self, from_url: str, response: Response):
if response.meta:
self.set_status(f"Input needed: {response.meta}")
else:
self.set_status("Input needed:")
user_input = self.take_user_input("?")
if user_input:
url = set_parameter(from_url, user_input)
self.open_gemini_url(url)
def handle_mouse(self, mouse_id: int, x: int, y: int, z: int, bstate: int): def handle_mouse(self, mouse_id: int, x: int, y: int, z: int, bstate: int):
"""Handle mouse events. """Handle mouse events.

View file

@ -1,6 +1,6 @@
import unittest import unittest
from ..navigation import join_url, parse_url from ..navigation import join_url, parse_url, set_parameter
class TestNavigation(unittest.TestCase): class TestNavigation(unittest.TestCase):
@ -28,3 +28,9 @@ class TestNavigation(unittest.TestCase):
self.assertEqual(url, "gemini://dece.space/dir1/other-file.gmi") self.assertEqual(url, "gemini://dece.space/dir1/other-file.gmi")
url = join_url("gemini://dece.space/dir1/file.gmi", "../top-level.gmi") url = join_url("gemini://dece.space/dir1/file.gmi", "../top-level.gmi")
self.assertEqual(url, "gemini://dece.space/top-level.gmi") self.assertEqual(url, "gemini://dece.space/top-level.gmi")
def test_set_parameter(self):
url = set_parameter("gemini://gus.guru/search", "my search")
self.assertEqual(url, "gemini://gus.guru/search?my%20search")
url = set_parameter("gemini://gus.guru/search?old%20search", "new")
self.assertEqual(url, "gemini://gus.guru/search?new")