diff options
-rw-r--r-- | Makefile | 8 | ||||
-rw-r--r-- | adapter/format/uri2html | 3 | ||||
-rw-r--r-- | doc/config.md | 28 | ||||
-rw-r--r-- | res/config.toml | 7 | ||||
-rw-r--r-- | res/mailcap | 1 | ||||
-rw-r--r-- | res/mime.types | 1 | ||||
-rw-r--r-- | src/config/config.nim | 4 | ||||
-rw-r--r-- | src/config/history.nim | 127 | ||||
-rw-r--r-- | src/local/container.nim | 3 | ||||
-rw-r--r-- | src/local/lineedit.nim | 44 | ||||
-rw-r--r-- | src/local/pager.nim | 69 | ||||
-rw-r--r-- | src/main.nim | 6 | ||||
-rw-r--r-- | src/utils/twtstr.nim | 4 | ||||
-rw-r--r-- | todo | 5 |
14 files changed, 260 insertions, 50 deletions
diff --git a/Makefile b/Makefile index 29df1f15..51d41295 100644 --- a/Makefile +++ b/Makefile @@ -58,7 +58,7 @@ all: $(OUTDIR_BIN)/cha $(OUTDIR_BIN)/mancha $(OUTDIR_CGI_BIN)/http \ $(OUTDIR_CGI_BIN)/gopher $(OUTDIR_LIBEXEC)/gopher2html \ $(OUTDIR_CGI_BIN)/finger $(OUTDIR_CGI_BIN)/about \ $(OUTDIR_CGI_BIN)/file $(OUTDIR_CGI_BIN)/ftp $(OUTDIR_CGI_BIN)/sftp \ - $(OUTDIR_LIBEXEC)/dirlist2html \ + $(OUTDIR_LIBEXEC)/dirlist2html $(OUTDIR_LIBEXEC)/uri2html \ $(OUTDIR_CGI_BIN)/man $(OUTDIR_CGI_BIN)/spartan \ $(OUTDIR_CGI_BIN)/stbi $(OUTDIR_CGI_BIN)/jebp $(OUTDIR_CGI_BIN)/canvas \ $(OUTDIR_CGI_BIN)/nanosvg $(OUTDIR_CGI_BIN)/sixel $(OUTDIR_CGI_BIN)/resize \ @@ -142,6 +142,10 @@ $(OUTDIR_CGI_BIN)/%: adapter/protocol/% @mkdir -p "$(OUTDIR_CGI_BIN)" install -m755 $< "$(OUTDIR_CGI_BIN)" +$(OUTDIR_LIBEXEC)/%: adapter/format/% + @mkdir -p "$(OUTDIR_LIBEXEC)" + install -m755 $< "$(OUTDIR_LIBEXEC)" + $(OUTDIR_CGI_BIN)/%: adapter/img/%.nim @mkdir -p "$(OUTDIR_CGI_BIN)" $(NIMC) $(FLAGS) --nimcache:"$(OBJDIR)/$(TARGET)/$(subst $(OUTDIR_CGI_BIN)/,,$@)" \ @@ -188,7 +192,7 @@ manpage: $(manpages:%=doc/%) protocols = http about file ftp sftp gopher gemini finger man spartan stbi \ jebp sixel canvas resize chabookmark nanosvg -converters = gopher2html md2html ansi2html gmi2html dirlist2html +converters = gopher2html md2html ansi2html gmi2html dirlist2html uri2html tools = urlenc nc .PHONY: install diff --git a/adapter/format/uri2html b/adapter/format/uri2html new file mode 100644 index 00000000..20ae5670 --- /dev/null +++ b/adapter/format/uri2html @@ -0,0 +1,3 @@ +#!/bin/sh +echo "<!DOCTYPE html>${1+"<h1>$1</h1><hr>"}<ol>" +sed -e '/^#/d' -e 's@.*@<li><a href="&">&</a>@' diff --git a/doc/config.md b/doc/config.md index c501c934..3c337194 100644 --- a/doc/config.md +++ b/doc/config.md @@ -195,6 +195,14 @@ Defaults to "ask". </td> </tr> +<tr> +<td>history</td> +<td>true / false</td> +<td>Whether or not browsing history should be saved to the disk.<br> +Defaults to true. +</td> +</tr> + </table> ## Search @@ -377,6 +385,18 @@ defaults to `xsel -bo`.</td> .md extension, so that its type can be correctly deduced.)</td> </tr> +<tr> +<td>history-file</td> +<td>path</td> +<td>Path to the history file. Defaults to "history.uri".</td> +</tr> + +<tr> +<td>history-size</td> +<td>number</td> +<td>Maximum length of the history file. Defaults to 100.</td> +</tr> + </table> ## Input @@ -896,6 +916,14 @@ Overrides `buffer.meta-refresh`. </td> </tr> +<tr> +<td>history</td> +<td>true / false</td> +<td>Whether or not browsing history should be saved to the disk.<br> +Overrides `buffer.history`. +</td> +</tr> + </table> ## Stylesheets diff --git a/res/config.toml b/res/config.toml index 3dca5fc3..fa38d6c6 100644 --- a/res/config.toml +++ b/res/config.toml @@ -20,6 +20,7 @@ scripting = false referer-from = false cookie = false meta-refresh = "ask" +history = true [search] wrap = true @@ -50,6 +51,8 @@ urimethodmap = [ "/usr/local/etc/w3m/urimethodmap" ] bookmark = "bookmark.md" +history-file = "history.uri" +history-size = 100 tmpdir = "${TMPDIR:-/tmp}/cha-tmp-$LOGNAME" sockdir = "${TMPDIR:-/tmp}/cha-sock-$LOGNAME" editor = "${EDITOR:-vi}" @@ -211,6 +214,7 @@ C-l = 'cmd.pager.load' C-k = 'cmd.pager.webSearch' M-a = 'cmd.pager.addBookmark' M-b = 'cmd.pager.openBookmarks' +C-h = 'cmd.pager.openHistory' M-u = 'cmd.pager.dupeBuffer' U = 'cmd.pager.reloadBuffer' C-g = 'cmd.pager.lineInfo' @@ -323,7 +327,8 @@ dupeBuffer = '() => pager.dupeBuffer()' load = '() => pager.load()' webSearch = '() => pager.load("go:")' addBookmark = '() => pager.gotoURL(`cgi-bin:chabookmark?url=${encodeURIComponent(pager.url)}&title=${encodeURIComponent(pager.title)}`)' -openBookmarks = '() => pager.gotoURL(`cgi-bin:chabookmark?action=view`)' +openBookmarks = '() => pager.gotoURL(`cgi-bin:chabookmark?action=view`, {history: false})' +openHistory = '() => pager.gotoURL(pager.getHistoryURL(), {contentType: `text/uri-list;title="History page"`, history: false})' reloadBuffer = '() => pager.reload()' lineInfo = '() => pager.lineInfo()' toggleSource = '() => pager.toggleSource()' diff --git a/res/mailcap b/res/mailcap index ba4989ec..e0e2b503 100644 --- a/res/mailcap +++ b/res/mailcap @@ -5,3 +5,4 @@ text/gemini; "$CHA_LIBEXEC_DIR"/gmi2html; x-htmloutput; x-needsstyle text/markdown; "$CHA_LIBEXEC_DIR"/md2html; x-htmloutput text/x-ansi; "$CHA_LIBEXEC_DIR"/ansi2html -st '%{title}'; x-htmloutput; x-needsstyle text/x-dirlist; "$CHA_LIBEXEC_DIR"/dirlist2html -t '%{title}'; x-htmloutput +text/uri-list; "$CHA_LIBEXEC_DIR"/uri2html '%{title}'; x-htmloutput diff --git a/res/mime.types b/res/mime.types index 9feed14e..533b2f01 100644 --- a/res/mime.types +++ b/res/mime.types @@ -16,3 +16,4 @@ image/svg+xml svg text/markdown md text/gemini gmi text/x-ansi ans asc +text/uri-list uri diff --git a/src/config/config.nim b/src/config/config.nim index 2e0eaacf..a55c807f 100644 --- a/src/config/config.nim +++ b/src/config/config.nim @@ -73,6 +73,7 @@ type insecure_ssl_no_verify*: Option[bool] autofocus*: Option[bool] meta_refresh*: Option[MetaRefresh] + history*: Option[bool] OmniRule* = ref object match*: Regex @@ -110,6 +111,8 @@ type cgi_dir* {.jsgetset.}: seq[ChaPathResolved] urimethodmap*: URIMethodMap bookmark* {.jsgetset.}: ChaPathResolved + history_file*: ChaPathResolved + history_size* {.jsgetset.}: int32 download_dir* {.jsgetset.}: ChaPathResolved w3m_cgi_compat* {.jsgetset.}: bool copy_cmd* {.jsgetset.}: string @@ -162,6 +165,7 @@ type referer_from* {.jsgetset.}: bool autofocus* {.jsgetset.}: bool meta_refresh* {.jsgetset.}: MetaRefresh + history* {.jsgetset.}: bool Config* = ref object jsctx*: JSContext diff --git a/src/config/history.nim b/src/config/history.nim new file mode 100644 index 00000000..879f0b77 --- /dev/null +++ b/src/config/history.nim @@ -0,0 +1,127 @@ +# Generic object for line editing and browsing hist. +import std/posix +import std/tables + +import io/dynstream +import utils/twtstr + +type + History* = ref object + first*: HistoryEntry + last*: HistoryEntry + mtime*: int64 + map: Table[string, HistoryEntry] + len: int + maxLen: int + + HistoryEntry* = ref object + s*: string + prev* {.cursor.}: HistoryEntry + next*: HistoryEntry + +func newHistoryEntry(s: string): HistoryEntry = + return HistoryEntry(s: s) + +proc add(hist: History; entry: HistoryEntry) = + let old = hist.map.getOrDefault(entry.s) + if old != nil: + if hist.first == old: + hist.first = old.next + if hist.last == old: + hist.last = old.prev + let prev = old.prev + if prev != nil: + prev.next = old.next + if old.next != nil: + old.next.prev = prev + dec hist.len + if hist.first == nil: + hist.first = entry + else: + entry.prev = hist.last + hist.last.next = entry + hist.map[entry.s] = entry + hist.last = entry + inc hist.len + if hist.len > hist.maxLen: + hist.first = hist.first.next + if hist.first == nil: + hist.last = nil + dec hist.len + +func newHistory*(maxLen: int; mtime = 0i64): History = + return History(maxLen: maxLen, mtime: mtime) + +proc add*(hist: History; s: string) = + hist.add(newHistoryEntry(s)) + +proc parse(hist: History; iq: openArray[char]) = + var i = 0 + while i < iq.len: + if iq[i] == '#': # text/uri-list :P + while i < iq.len and iq[i] != '\n': + inc i + else: + let entry = newHistoryEntry(iq.until('\n', i)) + hist.add(entry) + i += entry.s.len + inc i + +# Consumes `ps'. +proc parse*(hist: History; ps: PosixStream; mtime: int64): bool = + try: + let src = ps.recvAllOrMmap() + hist.parse(src.toOpenArray()) + hist.mtime = mtime + deallocMem(src) + except IOError: + return false + finally: + ps.sclose() + return true + +proc c_rename(oldname, newname: cstring): cint {.importc: "rename", + header: "<stdio.h>".} + +# Consumes `ps'. +proc write*(hist: History; ps: PosixStream; reverse = false): bool = + try: + var buf = "" + var entry = if reverse: hist.last else: hist.first + while entry != nil: + buf &= entry.s + buf &= '\n' + if buf.len >= 4096: + ps.sendDataLoop(buf) + buf = "" + if reverse: + entry = entry.prev + else: + entry = entry.next + if buf.len > 0: + ps.sendDataLoop(buf) + except IOError: + return false + finally: + ps.sclose() + return true + +proc write*(hist: History; file: string): bool = + let ps = newPosixStream(file) + if ps != nil: + var stats: Stat + if fstat(ps.fd, stats) != -1 and S_ISREG(stats.st_mode): + let mtime = int64(stats.st_mtime) + if mtime > hist.mtime: + if not hist.parse(ps, mtime): + return false + if hist.first == nil: + return true + block write: + # Can't just use getTempFile, because the temp directory may be in + # another filesystem. + let tmp = file & '~' + let ps = newPosixStream(tmp, O_WRONLY or O_CREAT, 0o600) + if ps != nil and hist.write(ps): + return c_rename(cstring(tmp), file) == 0 + return false diff --git a/src/local/container.nim b/src/local/container.nim index ab69a0ed..7e43f296 100644 --- a/src/local/container.nim +++ b/src/local/container.nim @@ -94,7 +94,8 @@ type lsLoading, lsCanceled, lsLoaded ContainerFlag* = enum - cfCloned, cfUserRequested, cfHasStart, cfCanReinterpret, cfSave, cfIsHTML + cfCloned, cfUserRequested, cfHasStart, cfCanReinterpret, cfSave, cfIsHTML, + cfHistory CachedImageState* = enum cisLoading, cisCanceled, cisLoaded diff --git a/src/local/lineedit.nim b/src/local/lineedit.nim index 3ae3f4ef..30c672d9 100644 --- a/src/local/lineedit.nim +++ b/src/local/lineedit.nim @@ -2,6 +2,7 @@ import std/strutils import chagashi/charset import chagashi/decoder +import config/history import monoucha/javascript import monoucha/quickjs import types/cell @@ -16,9 +17,6 @@ type LineEditState* = enum lesEdit, lesFinish, lesCancel - LineHistory* = ref object - lines*: seq[string] - LineEdit* = ref object news*: string prompt: string @@ -33,17 +31,14 @@ type maxwidth: int disallowed: set[char] hide: bool - hist: LineHistory - histindex: int + hist: History + currHist: HistoryEntry histtmp: string luctx: LUContext redraw*: bool jsDestructor(LineEdit) -func newLineHistory*(): LineHistory = - return LineHistory() - # Note: capped at edit.maxwidth. func getDisplayWidth(edit: LineEdit): int = var dispw = 0 @@ -133,8 +128,8 @@ proc cancel(edit: LineEdit) {.jsfunc.} = edit.state = lesCancel proc submit(edit: LineEdit) {.jsfunc.} = - if edit.hist.lines.len == 0 or edit.news != edit.hist.lines[^1]: - edit.hist.lines.add(edit.news) + if edit.hist.mtime == 0: + edit.hist.add(edit.news) edit.state = lesFinish proc backspace(edit: LineEdit) {.jsfunc.} = @@ -274,11 +269,14 @@ proc `end`(edit: LineEdit) {.jsfunc.} = edit.redraw = true proc prevHist(edit: LineEdit) {.jsfunc.} = - if edit.histindex > 0: - if edit.news.len > 0: + if edit.currHist == nil: + if edit.hist.last != nil and edit.news.len > 0: edit.histtmp = $edit.news - dec edit.histindex - edit.news = edit.hist.lines[edit.histindex] + edit.currHist = edit.hist.last + elif edit.currHist.prev != nil: + edit.currHist = edit.currHist.prev + if edit.currHist != nil: + edit.news = edit.currHist.s # The begin call is needed so the cursor doesn't get lost outside # the string. edit.begin() @@ -286,25 +284,25 @@ proc prevHist(edit: LineEdit) {.jsfunc.} = edit.redraw = true proc nextHist(edit: LineEdit) {.jsfunc.} = - if edit.histindex + 1 < edit.hist.lines.len: - inc edit.histindex - edit.news = edit.hist.lines[edit.histindex] + if edit.currHist != nil and edit.currHist != edit.hist.last: + edit.currHist = edit.currHist.next + edit.news = edit.currHist.s edit.begin() edit.end() edit.redraw = true - elif edit.histindex < edit.hist.lines.len: - inc edit.histindex - edit.news = edit.histtmp + elif edit.currHist == edit.hist.last: + edit.currHist = edit.currHist.next + edit.news = move(edit.histtmp) + edit.histtmp = "" edit.begin() edit.end() - edit.histtmp = "" edit.redraw = true proc windowChange*(edit: LineEdit; attrs: WindowAttributes) = edit.maxwidth = attrs.width - edit.promptw - 1 proc readLine*(prompt, current: string; termwidth: int; disallowed: set[char]; - hide: bool; hist: LineHistory; luctx: LUContext): LineEdit = + hide: bool; hist: History; luctx: LUContext): LineEdit = let promptw = prompt.width() return LineEdit( prompt: prompt, @@ -318,7 +316,7 @@ proc readLine*(prompt, current: string; termwidth: int; disallowed: set[char]; # - 1, so that the cursor always has place maxwidth: termwidth - promptw - 1, hist: hist, - histindex: hist.lines.len, + currHist: nil, luctx: luctx ) diff --git a/src/local/pager.nim b/src/local/pager.nim index 72945745..dfd73b05 100644 --- a/src/local/pager.nim +++ b/src/local/pager.nim @@ -7,11 +7,13 @@ import std/posix import std/sets import std/strutils import std/tables +import std/times import chagashi/charset import chagashi/decoder import config/chapath import config/config +import config/history import config/mailcap import config/mimetypes import css/render @@ -156,7 +158,7 @@ type jsrt: JSRuntime lastAlert: string # last alert seen by the user lineData: LineData - lineHist: array[LineMode, LineHistory] + lineHist: array[LineMode, History] lineedit*: LineEdit linemode: LineMode loader*: FileLoader @@ -210,13 +212,13 @@ proc dumpBuffers(pager: Pager) proc evalJS(pager: Pager; src, filename: string; module = false): JSValue proc fulfillAsk(pager: Pager; y: bool) proc fulfillCharAsk(pager: Pager; s: string) -proc getLineHist(pager: Pager; mode: LineMode): LineHistory +proc getHist(pager: Pager; mode: LineMode): History proc handleEvents(pager: Pager) proc handleRead(pager: Pager; fd: int) proc headlessLoop(pager: Pager) proc inputLoop(pager: Pager) proc loadURL(pager: Pager; url: string; ctype = none(string); - cs = CHARSET_UNKNOWN) + cs = CHARSET_UNKNOWN; history = true) proc openMenu(pager: Pager; x = -1; y = -1) proc readPipe(pager: Pager; contentType: string; cs: Charset; ps: PosixStream; title: string) @@ -343,14 +345,14 @@ proc searchPrev(pager: Pager; n = 1) {.jsfunc.} = else: pager.alert("No previous regular expression") -proc getLineHist(pager: Pager; mode: LineMode): LineHistory = +proc getHist(pager: Pager; mode: LineMode): History = if pager.lineHist[mode] == nil: - pager.lineHist[mode] = newLineHistory() + pager.lineHist[mode] = newHistory(100) return pager.lineHist[mode] proc setLineEdit(pager: Pager; mode: LineMode; current = ""; hide = false; extraPrompt = "") = - let hist = pager.getLineHist(mode) + let hist = pager.getHist(mode) if pager.term.isatty() and pager.config.input.use_mouse: pager.term.disableMouse() pager.lineedit = readLine($mode & extraPrompt, current, pager.attrs.width, @@ -485,12 +487,22 @@ proc newPager*(config: Config; forkserver: ForkServer; ctx: JSContext; proxy: pager.config.network.proxy, filter: newURLFilter(default = true), )) + let hist = newHistory(pager.config.external.history_size, getTime().toUnix()) + let ps = newPosixStream(pager.config.external.history_file) + if ps != nil: + var stat: Stat + if fstat(ps.fd, stat) != -1: + discard hist.parse(ps, int64(stat.st_mtime)) + pager.lineHist[lmLocation] = hist return pager proc cleanup(pager: Pager) = if pager.alive: pager.alive = false pager.term.quit() + let hist = pager.lineHist[lmLocation] + if not hist.write(pager.config.external.history_file): + pager.alert("failed to save history") for msg in pager.alerts: stderr.write("cha: " & msg & '\n') for val in pager.config.cmd.map.values: @@ -809,7 +821,7 @@ proc addLoaderClient(pager: Pager; pid: int; config: LoaderClientConfig; return key proc run*(pager: Pager; pages: openArray[string]; contentType: Option[string]; - cs: Charset; dump: bool) = + cs: Charset; dump, history: bool) = var istream: PosixStream = nil var dump = dump if not dump: @@ -847,8 +859,9 @@ proc run*(pager: Pager; pages: openArray[string]; contentType: Option[string]; let contentType = contentType.get("text/x-ansi") let ps = newPosixStream(STDIN_FILENO) pager.readPipe(contentType, cs, ps, "*stdin*") + let history = not dump and history # we don't want history for dump either for page in pages: - pager.loadURL(page, ctype = contentType, cs = cs) + pager.loadURL(page, ctype = contentType, cs = cs, history = history) pager.showAlerts() pager.acceptBuffers() if not dump: @@ -912,11 +925,8 @@ proc refreshStatusMsg(pager: Pager) = discard pager.writeStatusMessage(pager.alerts[0]) # save to alert history if pager.lastAlert != "": - let hist = pager.getLineHist(lmAlert) - if hist.lines.len == 0 or hist.lines[^1] != pager.lastAlert: - if hist.lines.len > 19: - hist.lines.delete(0) - hist.lines.add(move(pager.lastAlert)) + let hist = pager.getHist(lmAlert) + hist.add(move(pager.lastAlert)) pager.lastAlert = move(pager.alerts[0]) pager.alerts.delete(0) else: @@ -1878,7 +1888,8 @@ proc applySiteconf(pager: Pager; url: URL; charsetOverride: Charset; proc gotoURL(pager: Pager; request: Request; prevurl = none(URL); contentType = none(string); cs = CHARSET_UNKNOWN; replace: Container = nil; replaceBackup: Container = nil; redirectDepth = 0; - referrer: Container = nil; save = false; url: URL = nil): Container = + referrer: Container = nil; save = false; history = true; + url: URL = nil): Container = pager.navDirection = ndNext if referrer != nil and referrer.config.refererFrom: request.referrer = referrer.url @@ -1904,6 +1915,8 @@ proc gotoURL(pager: Pager; request: Request; prevurl = none(URL); var flags = {cfCanReinterpret, cfUserRequested} if save: flags.incl(cfSave) + if history: + flags.incl(cfHistory) let container = pager.newContainer( bufferConfig, loaderConfig, @@ -1963,7 +1976,7 @@ proc omniRewrite(pager: Pager; s: string): string = # * https://<url> # So we attempt to load both, and see what works. proc loadURL(pager: Pager; url: string; ctype = none(string); - cs = CHARSET_UNKNOWN) = + cs = CHARSET_UNKNOWN; history = true) = let url0 = pager.omniRewrite(url) let url = expandPath(url0) if url.len == 0: @@ -1974,7 +1987,8 @@ proc loadURL(pager: Pager; url: string; ctype = none(string); some(pager.container.url) else: none(URL) - discard pager.gotoURL(newRequest(firstparse.get), prev, ctype, cs) + discard pager.gotoURL(newRequest(firstparse.get), prev, ctype, cs, + history = history) return var urls: seq[URL] = @[] if pager.config.network.prepend_https and @@ -1992,7 +2006,7 @@ proc loadURL(pager: Pager; url: string; ctype = none(string); pager.alert("Invalid URL " & url) else: let container = pager.gotoURL(newRequest(urls.pop()), contentType = ctype, - cs = cs) + cs = cs, history = history) if container != nil: container.retry = urls @@ -2029,6 +2043,18 @@ proc readPipe(pager: Pager; contentType: string; cs: Charset; ps: PosixStream; inc pager.numload pager.addContainer(container) +proc getHistoryURL(pager: Pager): URL {.jsfunc.} = + let (pins, pouts) = pager.createPipe() + if pins == nil: + return nil + let url = newURL("stream:history").get + pager.loader.passFd(url.pathname, pins.fd) + pins.sclose() + let hist = pager.lineHist[lmLocation] + if not hist.write(pouts, reverse = true): + pager.alert("failed to write history") + return url + const ConsoleTitle = "Browser Console" proc showConsole(pager: Pager) = @@ -2256,6 +2282,7 @@ type GotoURLDict = object of JSDict contentType {.jsdefault.}: Option[string] replace {.jsdefault.}: Container save {.jsdefault.}: bool + history {.jsdefault.}: bool proc jsGotoURL(pager: Pager; v: JSValue; t = GotoURLDict()): JSResult[void] {.jsfunc: "gotoURL".} = @@ -2271,7 +2298,7 @@ proc jsGotoURL(pager: Pager; v: JSValue; t = GotoURLDict()): JSResult[void] url = ?newURL(s) request = newRequest(url) discard pager.gotoURL(request, contentType = t.contentType, - replace = t.replace, save = t.save) + replace = t.replace, save = t.save, history = t.history) return ok() # Reload the page in a new buffer, then kill the previous buffer. @@ -2533,7 +2560,8 @@ proc redirectTo(pager: Pager; container: Container; request: Request) = container.find(ndAny) let nc = pager.gotoURL(request, some(container.url), replace = container, replaceBackup = replaceBackup, redirectDepth = container.redirectDepth + 1, - referrer = container) + referrer = container, save = cfSave in container.flags, + history = cfHistory in container.flags) nc.loadinfo = "Redirecting to " & $request.url pager.onSetLoadInfo(nc) dec pager.numload @@ -2743,6 +2771,8 @@ proc connected(pager: Pager; container: Container; response: Response) = # variable.) if cfUserRequested in container.flags: pager.hasload = true + if cfHistory in container.flags: + pager.lineHist[lmLocation].add($container.url) var contentType = if "Content-Type" in response.headers: response.headers["Content-Type"] else: @@ -2868,6 +2898,7 @@ const MenuMap = [ ("─────────────────────────", ""), ("Bookmark page (M-a)", "cmd.pager.addBookmark(1)"), ("Open bookmarks (M-b)", "cmd.pager.openBookmarks(1)"), + ("Open history (C-h)", "cmd.pager.openHistory(1)"), ] proc menuFinish(opaque: RootRef; select: Select; sr: SubmitResult) = diff --git a/src/main.nim b/src/main.nim index 1a9a583e..ec0f4f8a 100644 --- a/src/main.nim +++ b/src/main.nim @@ -250,13 +250,17 @@ proc main() = if (let res = ctx.initConfig(config, warnings); res.isNone): stderr.writeLine(res.error) quit(1) + var history = true if ctx.pages.len == 0 and stdin.isatty(): if ctx.visual: ctx.pages.add(config.start.visual_home) + history = false elif (let httpHome = getEnv("HTTP_HOME"); httpHome != ""): ctx.pages.add(httpHome) + history = false elif (let wwwHome = getEnv("WWW_HOME"); wwwHome != ""): ctx.pages.add(wwwHome) + history = false if ctx.pages.len == 0 and not config.start.headless: if stdin.isatty(): help(1) @@ -269,7 +273,7 @@ proc main() = let client = newClient(config, forkserver, loaderPid, jsctx, warnings, urandom) try: - client.pager.run(ctx.pages, ctx.contentType, ctx.charset, ctx.dump) + client.pager.run(ctx.pages, ctx.contentType, ctx.charset, ctx.dump, history) except CatchableError: client.flushConsole() raise diff --git a/src/utils/twtstr.nim b/src/utils/twtstr.nim index 0759cbae..18f7dd13 100644 --- a/src/utils/twtstr.nim +++ b/src/utils/twtstr.nim @@ -270,10 +270,10 @@ func untilLower*(s: openArray[char]; c: set[char]; starti = 0): string = break result.add(s[i].toLowerAscii()) -func until*(s: string; c: char; starti = 0): string = +func until*(s: openArray[char]; c: char; starti = 0): string = return s.until({c}, starti) -func untilLower*(s: string; c: char; starti = 0): string = +func untilLower*(s: openArray[char]; c: char; starti = 0): string = return s.untilLower({c}, starti) func after*(s: string; c: set[char]): string = diff --git a/todo b/todo index 0a6cfbc5..fb434b27 100644 --- a/todo +++ b/todo @@ -38,6 +38,10 @@ buffer: buffer * this also includes not crashing when the buffer dies while container is reading... +- color visited links + * needs some sort of conditional formatting in pager, e.g. give + all Formats an id in buffer and send a list of "if URL + visited, change Format" commands - configurable/better url filtering in loader - when the log buffer crashes, print its contents to stderr * easiest way seems to be to just dump its cache file @@ -46,7 +50,6 @@ buffer: pager: - better horizontal line handling: allow viewing content positioned before page start, handle long lines, etc -- history (what format?) - save/edit buffer output - alert on external command failure network: |