Compare commits
No commits in common. "b2fdabea715fd91c9cf1f5b8e256dd50b7c0f0ae" and "f3a3a3603996b6569dc3a9e50ed4e0ccf12ddab9" have entirely different histories.
b2fdabea71
...
f3a3a36039
|
@ -4,6 +4,7 @@ import curses
|
||||||
import curses.ascii
|
import curses.ascii
|
||||||
import curses.textpad
|
import curses.textpad
|
||||||
import os
|
import os
|
||||||
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
from math import inf
|
from math import inf
|
||||||
|
|
||||||
|
@ -12,7 +13,6 @@ from bebop.bookmarks import (
|
||||||
)
|
)
|
||||||
from bebop.colors import ColorPair, init_colors
|
from bebop.colors import ColorPair, init_colors
|
||||||
from bebop.command_line import CommandLine
|
from bebop.command_line import CommandLine
|
||||||
from bebop.external import open_external_program
|
|
||||||
from bebop.history import History
|
from bebop.history import History
|
||||||
from bebop.links import Links
|
from bebop.links import Links
|
||||||
from bebop.mouse import ButtonState
|
from bebop.mouse import ButtonState
|
||||||
|
@ -102,7 +102,7 @@ class Browser:
|
||||||
elif char == ord("h"):
|
elif char == ord("h"):
|
||||||
self.scroll_page_horizontally(-3)
|
self.scroll_page_horizontally(-3)
|
||||||
elif char == ord("H"):
|
elif char == ord("H"):
|
||||||
self.scroll_whole_page_left()
|
pass # TODO h-scroll whole page left
|
||||||
elif char == ord("j"):
|
elif char == ord("j"):
|
||||||
self.scroll_page_vertically(3)
|
self.scroll_page_vertically(3)
|
||||||
elif char == ord("J"):
|
elif char == ord("J"):
|
||||||
|
@ -114,9 +114,9 @@ class Browser:
|
||||||
elif char == ord("l"):
|
elif char == ord("l"):
|
||||||
self.scroll_page_horizontally(3)
|
self.scroll_page_horizontally(3)
|
||||||
elif char == ord("L"):
|
elif char == ord("L"):
|
||||||
self.scroll_whole_page_right()
|
pass # TODO h-scroll whole page right
|
||||||
elif char == ord("^"):
|
elif char == ord("^"):
|
||||||
self.scroll_page_horizontally(-inf)
|
pass # TODO reset horizontal scrolling
|
||||||
elif char == ord("g"):
|
elif char == ord("g"):
|
||||||
char = self.screen.getch()
|
char = self.screen.getch()
|
||||||
if char == ord("g"):
|
if char == ord("g"):
|
||||||
|
@ -382,27 +382,10 @@ class Browser:
|
||||||
self.scroll_page_vertically(-self.page_pad_size[0])
|
self.scroll_page_vertically(-self.page_pad_size[0])
|
||||||
|
|
||||||
def scroll_page_horizontally(self, by_columns):
|
def scroll_page_horizontally(self, by_columns):
|
||||||
"""Scroll page horizontally.
|
"""Scroll page horizontally."""
|
||||||
|
if self.page_pad.scroll_h(by_columns, self.w):
|
||||||
If `by_lines` is an integer (positive or negative), scroll the page by
|
|
||||||
this amount of columns. If `by_lines` is -inf, scroll back to the first
|
|
||||||
column. Scrolling to the right-most column is not supported.
|
|
||||||
"""
|
|
||||||
if by_columns == -inf:
|
|
||||||
require_refresh = self.page_pad.go_to_first_column()
|
|
||||||
else:
|
|
||||||
require_refresh = self.page_pad.scroll_h(by_columns, self.w)
|
|
||||||
if require_refresh:
|
|
||||||
self.refresh_page()
|
self.refresh_page()
|
||||||
|
|
||||||
def scroll_whole_page_left(self):
|
|
||||||
"""Scroll left by a whole page."""
|
|
||||||
self.scroll_page_horizontally(-self.page_pad_size[1])
|
|
||||||
|
|
||||||
def scroll_whole_page_right(self):
|
|
||||||
"""Scroll right by a whole page."""
|
|
||||||
self.scroll_page_horizontally(self.page_pad_size[1])
|
|
||||||
|
|
||||||
def reload_page(self):
|
def reload_page(self):
|
||||||
"""Reload the page, if one has been previously loaded."""
|
"""Reload the page, if one has been previously loaded."""
|
||||||
if self.current_url:
|
if self.current_url:
|
||||||
|
@ -445,6 +428,15 @@ class Browser:
|
||||||
save_bookmark(self.current_url, title)
|
save_bookmark(self.current_url, title)
|
||||||
self.reset_status()
|
self.reset_status()
|
||||||
|
|
||||||
|
def open_external_program(self, command):
|
||||||
|
"""Pauses the curses modes to open an external program."""
|
||||||
|
curses.nocbreak()
|
||||||
|
curses.echo()
|
||||||
|
subprocess.run(command)
|
||||||
|
curses.noecho()
|
||||||
|
curses.cbreak()
|
||||||
|
self.refresh_windows()
|
||||||
|
|
||||||
def edit_page(self):
|
def edit_page(self):
|
||||||
"""Open a text editor to edit the page source.
|
"""Open a text editor to edit the page source.
|
||||||
|
|
||||||
|
@ -471,7 +463,6 @@ class Browser:
|
||||||
delete_source_after = True
|
delete_source_after = True
|
||||||
|
|
||||||
command.append(source_filename)
|
command.append(source_filename)
|
||||||
open_external_program(command)
|
self.open_external_program(command)
|
||||||
if delete_source_after:
|
if delete_source_after:
|
||||||
os.unlink(source_filename)
|
os.unlink(source_filename)
|
||||||
self.refresh_windows()
|
|
||||||
|
|
|
@ -3,21 +3,12 @@
|
||||||
import curses
|
import curses
|
||||||
import curses.ascii
|
import curses.ascii
|
||||||
import curses.textpad
|
import curses.textpad
|
||||||
import os
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
from bebop.external import open_external_program
|
|
||||||
from bebop.links import Links
|
from bebop.links import Links
|
||||||
|
|
||||||
|
|
||||||
class CommandLine:
|
class CommandLine:
|
||||||
"""Basic and flaky command-line à la Vim, using curses module's Textbox.
|
"""Basic and flaky command-line à la Vim, using curses module's Textbox."""
|
||||||
|
|
||||||
I don't understand how to get proper pad-like behaviour, e.g. to scroll past
|
|
||||||
the window's right border when writing more content than the width allows.
|
|
||||||
Therefore I just added the M-e keybind to call an external editor and use
|
|
||||||
its content as result.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, window):
|
def __init__(self, window):
|
||||||
self.window = window
|
self.window = window
|
||||||
|
@ -87,9 +78,6 @@ class CommandLine:
|
||||||
ch = self.window.getch()
|
ch = self.window.getch()
|
||||||
if ch == -1:
|
if ch == -1:
|
||||||
raise EscapeCommandInterrupt()
|
raise EscapeCommandInterrupt()
|
||||||
else: # ALT keybinds.
|
|
||||||
if ch == ord("e"):
|
|
||||||
self.open_editor(self.gather())
|
|
||||||
self.window.nodelay(False)
|
self.window.nodelay(False)
|
||||||
return ch
|
return ch
|
||||||
|
|
||||||
|
@ -163,27 +151,6 @@ class CommandLine:
|
||||||
# Everything else could be a control character and should be processed.
|
# Everything else could be a control character and should be processed.
|
||||||
return ch
|
return ch
|
||||||
|
|
||||||
def open_editor(self, existing_content=None):
|
|
||||||
"""Open an external editor and raise termination interrupt."""
|
|
||||||
try:
|
|
||||||
with tempfile.NamedTemporaryFile("w+t", delete=False) as temp_file:
|
|
||||||
if existing_content:
|
|
||||||
temp_file.write(existing_content)
|
|
||||||
temp_filepath = temp_file.name
|
|
||||||
except OSError:
|
|
||||||
return
|
|
||||||
|
|
||||||
command = ["vi", temp_filepath]
|
|
||||||
open_external_program(command)
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open(temp_filepath, "rt") as temp_file:
|
|
||||||
content = temp_file.read()
|
|
||||||
os.unlink(temp_filepath)
|
|
||||||
except OSError:
|
|
||||||
return
|
|
||||||
raise TerminateCommandInterrupt(content)
|
|
||||||
|
|
||||||
|
|
||||||
class EscapeCommandInterrupt(Exception):
|
class EscapeCommandInterrupt(Exception):
|
||||||
"""Signal that ESC has been pressed during command line."""
|
"""Signal that ESC has been pressed during command line."""
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
"""Call external commands."""
|
|
||||||
|
|
||||||
import curses
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
|
|
||||||
def open_external_program(command):
|
|
||||||
"""Call command as a subprocess, suspending curses rendering.
|
|
||||||
|
|
||||||
The caller has to refresh whatever windows it manages after calling this
|
|
||||||
method or garbage may be left on the screen.
|
|
||||||
"""
|
|
||||||
curses.nocbreak()
|
|
||||||
curses.echo()
|
|
||||||
subprocess.run(command)
|
|
||||||
curses.noecho()
|
|
||||||
curses.cbreak()
|
|
|
@ -1,5 +1,7 @@
|
||||||
"""Links manager."""
|
"""Links manager."""
|
||||||
|
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
class Links(dict):
|
class Links(dict):
|
||||||
|
|
||||||
|
|
|
@ -53,11 +53,11 @@ def set_parameter(url: str, user_input: str):
|
||||||
|
|
||||||
def get_parent_url(url: str) -> str:
|
def get_parent_url(url: str) -> str:
|
||||||
"""Return the parent URL (one level up)."""
|
"""Return the parent URL (one level up)."""
|
||||||
scheme, netloc, path, _, _, _ = parse_url(url)
|
scheme, netloc, path, params, query, frag = parse_url(url)
|
||||||
last_slash = path.rstrip("/").rfind("/")
|
last_slash = path.rstrip("/").rfind("/")
|
||||||
if last_slash > -1:
|
if last_slash > -1:
|
||||||
path = path[:last_slash + 1]
|
path = path[:last_slash + 1]
|
||||||
return urllib.parse.urlunparse((scheme, netloc, path, "", "", ""))
|
return urllib.parse.urlunparse((scheme, netloc, path, params, query, frag))
|
||||||
|
|
||||||
|
|
||||||
def get_root_url(url: str) -> str:
|
def get_root_url(url: str) -> str:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
from bebop.gemtext import parse_gemtext
|
from bebop.gemtext import parse_gemtext, Title
|
||||||
from bebop.metalines import generate_dumb_metalines, generate_metalines
|
from bebop.metalines import generate_dumb_metalines, generate_metalines
|
||||||
from bebop.links import Links
|
from bebop.links import Links
|
||||||
|
|
||||||
|
|
|
@ -98,10 +98,3 @@ class PagePad:
|
||||||
self.current_line = max_line
|
self.current_line = max_line
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def go_to_first_column(self):
|
|
||||||
"""Move to the first column; return True if a refresh is needed."""
|
|
||||||
if self.current_column:
|
|
||||||
self.current_column = 0
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
Reference in a new issue