Compare commits

...

5 Commits

Author SHA1 Message Date
24613ea424 hook up the history logger thing 2023-01-02 22:03:03 +00:00
3cbe77a55e sqlite-based browsing history api
not hooked up to actual browsing actions yet
2023-01-02 21:25:36 +00:00
cc0f881fed fix tests for rename 2023-01-02 18:22:46 +00:00
d39d7059b9 Revert "inline temps and whitespace, improve update-widget-state"
This reverts commit 9775e00545.
2023-01-02 18:20:19 +00:00
561c717609 rename on-activate as on-input-finished
it's just too easily confused with "active" as in commander state.active
2023-01-01 22:32:50 +00:00
8 changed files with 154 additions and 31 deletions

View File

@ -24,15 +24,21 @@
(define-command (define-command
"switch-to-buffer" "switch-to-buffer"
[[:buffer Buffer.match #(. (Buffer.next $1.buffer) :name)]] [[:buffer
(fn [{: frame : buffer}] Buffer.match
#(. (Buffer.next $1.buffer) :name)]
]
(fn [{:frame frame :buffer buffer}]
(frame:show-buffer buffer))) (frame:show-buffer buffer)))
(define-command (define-command
"visit-location" "visit-location"
[[:buffer Buffer.match #$1.buffer.name] [[:buffer
[:url #{$1 $1} #($1.buffer:location)]] Buffer.match
(fn [{: url : buffer}] #$1.buffer.name]
[:url #{$1 $1} #($1.buffer:location)]
]
(fn [{:url url :buffer buffer}]
(buffer:visit url))) (buffer:visit url)))
(define-command (define-command
@ -87,7 +93,8 @@
{:command c :this-param k :collected-params p} {:command c :this-param k :collected-params p}
(let [{ : completer} (. c.params k) (let [{ : completer} (. c.params k)
value (. (completer input-string) input-string)] vals (completer input-string)
value (. vals input-string)]
(tset p k value) (tset p k value)
(state-for-next-param c p)) (state-for-next-param c p))
@ -98,25 +105,26 @@
state) state)
))) )))
(fn on-activate [self str] (fn on-input-finished [self str]
(let [s (next-action self str) (let [s (next-action self str)
param (if s.active (. (. s.command :params) s.this-param))] param (if s.active (. (. s.command :params) s.this-param))]
(set self.state s) (set self.state s)
{ {
:active s.active
:error s.error :error s.error
:prompt (if s.active (or s.this-param "Command?" "") "") :prompt (if s.active (or s.this-param "Command?" "") "")
:default (and param (param.default self.frame)) :default (and param (param.default self.frame))
})) }))
(fn update-widget-state [{ : state : widget : entry : completions-widget : prompt} result] (fn update-widget-state [{ : widget : entry : completions-widget : prompt} result]
(set prompt.label (or result.prompt "")) (set prompt.label (or result.prompt ""))
(set entry.sensitive state.active) (set entry.sensitive result.active)
(if (not state.active) (if (not result.active)
(completions-widget:hide)) (completions-widget:hide))
(set entry.text (or result.default result.error "")) (set entry.text (or result.default result.error ""))
(match widget.parent (when widget.parent
p (p:set_visible_child_name (widget.parent:set_visible_child_name
(if state.active "commander" "echo-area")))) (if result.active "commander" "echo-area"))))
(fn on-input [self str] (fn on-input [self str]
(match self.state (match self.state
@ -126,11 +134,12 @@
completions (completer str)] completions (completer str)]
(parent:foreach #(parent:remove $1)) (parent:foreach #(parent:remove $1))
(each [text _w (pairs completions)] (each [text _w (pairs completions)]
(parent:add (Gtk.Button { (parent:add
:label text (Gtk.Button {
:on_clicked :label text
#(update-widget-state self (self:on-activate text)) :on_clicked
}))) #(update-widget-state self (self:on-input-finished text))
})))
(parent:show_all) (parent:show_all)
))) )))
@ -139,16 +148,17 @@
(tset state :active true) (tset state :active true)
(update-widget-state (update-widget-state
self self
{ {:active true
:prompt (or state.this-param "Command" "") :prompt (or state.this-param "Command" "")
}) })
(entry:grab_focus)) (entry:grab_focus)
state)
(fn deactivate [{: state : entry : prompt &as self}] (fn deactivate [{: state : entry : prompt &as self}]
(doto state (doto state
(lume.clear) (lume.clear)
(tset :active false)) (tset :active false))
(update-widget-state self {})) (update-widget-state self {:active false}))
(fn invoke-interactively [self name params] (fn invoke-interactively [self name params]
(let [c (find-command name) (let [c (find-command name)
@ -160,7 +170,7 @@
:collected-params supplied-params :collected-params supplied-params
}] }]
(set self.state s) (set self.state s)
(let [r (self:on-activate nil)] (let [r (self:on-input-finished nil)]
(update-widget-state self r) (update-widget-state self r)
(self.entry:grab_focus) (self.entry:grab_focus)
r))) r)))
@ -181,7 +191,7 @@
: deactivate : deactivate
:active? (fn [self] self.state.active) :active? (fn [self] self.state.active)
: on-input : on-input
: on-activate : on-input-finished
: invoke-interactively : invoke-interactively
: entry : entry
:widget box :widget box
@ -198,7 +208,7 @@
(self:on-input event.text))) (self:on-input event.text)))
(tset entry :on_activate (tset entry :on_activate
(fn [event] (fn [event]
(let [result (self:on-activate event.text)] (let [result (self:on-input-finished event.text)]
(update-widget-state self result)))) (update-widget-state self result))))
self)) self))

View File

@ -33,12 +33,16 @@ let pname = "dunlin";
cp lume.lua "$out/share/lua/${lua.luaversion}" cp lume.lua "$out/share/lua/${lua.luaversion}"
''; '';
}; };
lsqlite3 = callPackage ./lsqlite3.nix { lua = lua5_3; };
lua = lua5_3.withPackages (ps: with ps; [ lua = lua5_3.withPackages (ps: with ps; [
lgi lgi
luafilesystem luafilesystem
luaposix luaposix
readline readline
lume lume
lsqlite3
]); ]);
fennel_ = lua.pkgs.fennel; fennel_ = lua.pkgs.fennel;
glib_networking_gio = "${glib-networking}/lib/gio/modules"; glib_networking_gio = "${glib-networking}/lib/gio/modules";

View File

@ -18,6 +18,10 @@
} }
}) })
(local history (require :history))
(tset _G :history (history.open
(.. (os.getenv "HOME") "/.dunlin-history.db")))
(let [f (Frame.new my-keymap) (let [f (Frame.new my-keymap)
b (Buffer.new "main")] b (Buffer.new "main")]
(f:show-buffer b) (f:show-buffer b)

View File

@ -38,12 +38,15 @@
contentwidget (Gtk.Box { contentwidget (Gtk.Box {
:orientation Gtk.Orientation.VERTICAL :orientation Gtk.Orientation.VERTICAL
}) })
update-prop (fn [name value] update-prop (fn [props name value]
(match name (match name
:estimated-load-progress :estimated-load-progress
(tset progress-bar :fraction value) (tset progress-bar :fraction value)
:uri :uri
(tset echo-area :label value) (do (tset echo-area :label value)
(_G.history:visit value (os.time)))
:title
(_G.history:title self.buffer.properties.uri value)
n n
(comment (print "prop change" n value))))] (comment (print "prop change" n value))))]
@ -91,7 +94,7 @@
(b:subscribe-property-changes (b:subscribe-property-changes
(fn [name val] (fn [name val]
(if (= b self.buffer) (if (= b self.buffer)
(update-prop name val) (update-prop self name val)
(print "ignore props from background" b)))) (print "ignore props from background" b))))
(b.webview:show)) (b.webview:show))
}] }]

56
history.fnl Normal file
View File

@ -0,0 +1,56 @@
(local sqlite (require :lsqlite3))
(local lume (require :lume))
(local { : view} (require :fennel))
(fn table-exists? [db]
(accumulate [ret false
r (db:nrows "select name from sqlite_master where type = 'table' and name= 'db_version'")]
(or ret true)))
(fn migrate [db serial statement]
(if (= (db:exec statement) 0)
(db:exec (.. "update db_version set serial = " serial))
(assert false (.. "db failed: " statement))))
(fn migrate-all [db]
(when (not (table-exists? db "db_version"))
(db:exec "create table db_version (serial integer)")
(db:exec "insert into db_version values (1)"))
(let [version (accumulate [v 0
r (db:nrows "select serial from db_version")]
r.serial)]
(when (< version 2)
(migrate db 2 "create table visits (url text, timestamp integer)"))
(when (< version 3)
(migrate db 3 "create table page_titles (url text primary key, title text)"))))
(fn visit [self url timestamp]
(let [s (self.db:prepare "insert into visits (url, timestamp) values (?,?)")]
(assert (= 0 (s:bind_values url timestamp)) (view [url timestamp]))
(s:step)
(s:reset)))
(fn title [self url title]
(let [s (self.db:prepare "insert into page_titles (url, title) values (?,?) on conflict(url) do update set title = excluded.title")]
(assert (= 0 (s:bind_values url title)) url)
(s:step)
(s:reset)))
(fn find [self term]
(let [s (self.db:prepare "select v.url,pt.title,v.timestamp from visits v left join page_titles pt on v.url = pt.url where instr(v.url, ?) is not null")]
(assert (= 0 (s:bind_values term)))
(icollect [r (s:nrows)] r)))
(fn open [pathname]
(let [db (if pathname (sqlite.open pathname) (sqlite.open_memory))]
(migrate-all db)
{
: db
: visit
: title
: find
}))
{
: open
}

21
lsqlite3.nix Normal file
View File

@ -0,0 +1,21 @@
{
fetchurl
, lua
, lib
, sqlite
}:
let
in lua.pkgs.buildLuarocksPackage {
pname = "lsqlite3";
version = "0.9.5-1";
buildInputs = [ sqlite ] ;
src = fetchurl {
url = "https://luarocks.org/manifests/dougcurrie/lsqlite3-0.9.5-1.src.rock";
sha256 = "sha256-/a48AzkKtOS32zoT0Jt5/+GTGHObdS0XkUrc2z0u+f8=";
};
disabled = (lua.pkgs.luaOlder "5.1");
propagatedBuildInputs = [ lua ];
preBuild = ''
cd lsqlite3_fsl09y
'';
}

View File

@ -20,7 +20,7 @@
(let [commander (Command.commander) (let [commander (Command.commander)
(ok err) (ok err)
(match-try (commander:activate) (match-try (commander:activate)
{:active true} (commander:on-activate "not-a-command") {:active true} (commander:on-input-finished "not-a-command")
(where {:error e :active false} (e:match "can't find command")) true (where {:error e :active false} (e:match "can't find command")) true
(catch (catch
x (values nil (view x))))] x (values nil (view x))))]
@ -30,9 +30,9 @@
(let [commander (Command.commander) (let [commander (Command.commander)
(ok err) (ok err)
(match-try (commander:activate) (match-try (commander:activate)
{:active true} (commander:on-activate "multiply") {:active true} (commander:on-input-finished "multiply")
{:active true :prompt p1} (commander:on-activate "2") {:active true :prompt p1} (commander:on-input-finished "2")
{:active true :prompt p2} (commander:on-activate "3") {:active true :prompt p2} (commander:on-input-finished "3")
(where {:active false} (= happened 6)) true (where {:active false} (= happened 6)) true
(catch (catch
x (values nil (view x))))] x (values nil (view x))))]

25
test/history.fnl Normal file
View File

@ -0,0 +1,25 @@
(local { : view } (require :fennel))
(local history (require :history))
(local fake-time
(let []
(var previous 1672682104)
(fn []
(doto previous
(+ 1)))))
(let [h (history.open)]
(h:visit "http://example.com" (fake-time))
(let [actual (h:find "example.com")]
(match actual
(where [{:url "http://example.com" :timestamp t}] (> t 0)) true
?fail (assert false (view ?fail)))))
(let [h (history.open)]
(h:visit "http://example.com" (fake-time))
(h:title "http://example.com" "Page title")
(let [actual (h:find "example.com")]
(match actual
(where [{:url "http://example.com" :title "Page title" :timestamp t}] (> t 0)) true
?fail (assert false (view ?fail)))))