bookmarks: basic bookmark management

exec
dece 3 years ago
parent d6bcd1f706
commit 5b3e91336f

@ -0,0 +1,60 @@
import io
from pathlib import Path
from bebop.fs import get_user_data_path
TEMPLATE = """\
# Bookmarks
Welcome to your bookmark page! This file has been created in "{original_path}" \
and you can edit it as you wish. New bookmarks will be added on a new \
line at the end. Always keep an empty line at the end!
"""
def get_bookmarks_path() -> Path:
"""Return the path to the bookmarks file."""
return get_user_data_path() / "bookmarks.gmi"
def init_bookmarks(filepath):
"""Create the bookmarks file and return its initial content.
Raises OSError if the file could not be written.
"""
content = TEMPLATE.format(original_path=filepath)
with open(filepath, "wt") as bookmark_file:
bookmark_file.write(content)
return content
def get_bookmarks_document():
"""Return the bookmarks content, or None or failure.
If no bookmarks file exist yet, it is created. If accessing or creating the
file fails, or if it is unreadable, return None.
"""
filepath = get_bookmarks_path()
try:
if not filepath.exists():
content = init_bookmarks(filepath)
else:
with open(filepath, "rt") as bookmark_file:
content = bookmark_file.read()
except OSError:
return None
return content
def save_bookmark(url, title):
"""Append this URL/title pair to the bookmarks, return True on success."""
filepath = get_bookmarks_path()
try:
if not filepath.exists():
init_bookmarks(filepath)
with open(filepath, "at") as bookmark_file:
bookmark_file.write(f"=> {url} {title}\n")
except OSError:
return False
return True

@ -7,6 +7,7 @@ import os
import webbrowser
from math import inf
from bebop.bookmarks import get_bookmarks_document, save_bookmark
from bebop.colors import ColorPair, init_colors
from bebop.command_line import CommandLine
from bebop.history import History
@ -30,9 +31,9 @@ class Browser:
self.command_line = None
self.running = True
self.status_data = ("", 0, 0)
self.current_url = ""
self.history = History()
self.cache = {}
self._current_url = ""
@property
def h(self):
@ -42,6 +43,17 @@ class Browser:
def w(self):
return self.dim[1]
@property
def current_url(self):
"""Return the current URL."""
return self._current_url
@current_url.setter
def current_url(self, url):
"""Set the current URL and show it in the status line."""
self._current_url = url
self.set_status(url)
def run(self, *args, **kwargs):
"""Use curses' wrapper around _run."""
os.environ.setdefault("ESCDELAY", "25")
@ -116,6 +128,10 @@ class Browser:
self.go_to_parent_page()
elif char == ord("U"):
self.go_to_root_page()
elif char == ord("b"):
self.open_bookmarks()
elif char == ord("B"):
self.add_bookmark()
elif curses.ascii.isdigit(char):
self.handle_digit_input(char)
elif char == curses.KEY_MOUSE:
@ -126,7 +142,7 @@ class Browser:
self.screen.nodelay(True)
char = self.screen.getch()
if char == -1:
self.set_status(self.current_url)
self.reset_status()
else: # ALT keybinds.
if char == ord("h"):
self.scroll_page_horizontally(-1)
@ -182,6 +198,10 @@ class Browser:
self.status_data = text, ColorPair.NORMAL, curses.A_ITALIC
self.refresh_status_line()
def reset_status(self):
"""Reset status line, e.g. after a cancelled action."""
self.set_status(self.current_url)
def set_status_error(self, text):
"""Set an error message in the status bar."""
self.status_data = text, ColorPair.ERROR, 0
@ -241,11 +261,15 @@ class Browser:
# If there is no netloc, this is a relative URL.
if join or base_url:
url = join_url(base_url or self.current_url, url)
self.open_gemini_url(sanitize_url(url), redirects)
self.open_gemini_url(sanitize_url(url), redirects=redirects,
history=history, use_cache=use_cache)
elif parts.scheme.startswith("http"):
self.open_web_url(url)
elif parts.scheme == "file":
self.open_file(parts.path)
self.open_file(parts.path, history=history)
elif parts.scheme == "bebop":
if parts.netloc == "bookmarks":
self.open_bookmarks()
else:
self.set_status_error(f"Protocol {parts.scheme} not supported.")
@ -464,7 +488,7 @@ class Browser:
def reload_page(self):
"""Reload the page, if one has been previously loaded."""
if self.current_url:
self.open_gemini_url(
self.open_url(
self.current_url,
history=False,
use_cache=False
@ -473,7 +497,7 @@ class Browser:
def go_back(self):
"""Go back in history if possible."""
if self.history.has_links():
self.open_gemini_url(self.history.pop(), history=False)
self.open_url(self.history.pop(), history=False)
def go_to_parent_page(self):
"""Go to the parent URL if possible."""
@ -490,7 +514,7 @@ class Browser:
self.set_status(f"Opening {url}")
webbrowser.open_new_tab(url)
def open_file(self, filepath, encoding="utf-8"):
def open_file(self, filepath, encoding="utf-8", history=True):
"""Open a file and render it.
This should be used only on Gemtext files or at least text files.
@ -505,3 +529,29 @@ class Browser:
self.set_status_error(f"Failed to open file: {exc}")
return
self.load_page(Page.from_gemtext(text))
file_url = "file://" + filepath
if history:
self.history.push(file_url)
self.current_url = file_url
def open_bookmarks(self):
"""Open bookmarks."""
content = get_bookmarks_document()
if content is None:
self.set_status_error("Failed to open bookmarks.")
return
self.load_page(Page.from_gemtext(content))
self.current_url = "bebop://bookmarks"
def add_bookmark(self):
"""Add the current URL as bookmark."""
if not self.current_url:
return
self.set_status("Title?")
current_title = self.page_pad.current_page.title or ""
title = self.command_line.focus(">", prefix=current_title)
if title:
title = title.strip()
if title:
save_bookmark(self.current_url, title)
self.reset_status()

Loading…
Cancel
Save