I've been playing around with idea of using edbrowse (CLI web browser
with ed-like interface) together with acme.  Launching it using win(1)
in acme is fine, but far from usable.  I decided to improve on that.
This script is result of that - it needs to be launched in acme.  Tmux
is used for bridging two worlds.  Since readme is included in script
(just keep scrolling, you will find it), there is not much else left to
say.

This is early version, but usable enough so it can be shared I guess.
Here is screenshot [1], not sure how useful is that, since acme looks
utilitarian as always.

[1] http://i.imgur.com/w4Zh6qs.png

Here is script enclosed inside "--" marks:

--
#!/bin/rc

. 9.rc
. $PLAN9/lib/acme.rc

# Preserve tabs and newlines, when tokenizing readme
tmpifs = $ifs
ifs = ' '

# README in form of here document
readme =  `{cat <<EOF}
ACMEBROWSE(1)

NAME
        acmebrowse - mouse driven interface for edbrowse

DESCRIPTION
        This document can be accessed in acme by executing a window title
        in the tag: clicking a mouse button 2 on "acmebrowse".  Some
        familiarity with acme(1) and edbrowse(1) is assumed.

        First of all win(1) isn't used.  Instead events emitted by acme
        are parsed using acmeevent(1).  This allows to assign a new context
        to the mouse actions.  The result is an interface that can be used
        almost without a keyboard.

        The basic usage is very simple.  The text executed with a button two
        is send to edbrowse as a literal command with an additional lookup
        for custom commands in this script.  In a similar fashion button
        three is used for sending the selected text as a regular expression.

        Example.  After launching acmebrowse, an address can by typed in the
        tag.  Clicking "b http://the-brannons.com/edbrowse/"; with a button
        two will send text as a literal command, edbrowse will open this
        page (not much to see, beside a status information).  "Print" after
        a button two click will be translated to "," command, which will
        print a whole site.  The text in a window can be clicked with a
        button three to send a search query e.g. "{user's guide}" will be
        translated to "/{user's guide}/".  The first line with a phrase
        will be printed.  "Go" clicked with a button two will be send as
        "g1" and "," commands - edbrowse will follow a first link in the
        line and then print a whole page.

        This is pretty much it.  Commands with an exclamation mark need some
        explanation.  The way acme works, a button two click on "Quit!" will
        select only "Quit".  This quirk/feature is used to prevent an
        accidental execution of a command.  Whole phrase "Quit!" must be
        drag-selected with a button two.  In some cases it is used to
        provide an alternative version of command e.g. "Go!" will follow
        a link without printing.

        "Quit!" is used to clear things up, when exiting including closing
        of tmux session running in background.

IMPLEMENTATION NOTES
        The script is written in rc(1), since acme is needed anyway, this
        was hardly a choice.  Acme and edbrowse are bridged together using
        tmux.  Detached session of tmux is spawned, edbrowse is started
        inside.  Then flow of the script goes as follow:

        - capture the input from acme using acmeevent
        - parse and then send commands to edbrowse using tmux send-keys
        - block till edbrowse is done using tmux wait-for
        - select the whole text in acme window, so it will be overwritten
        - pipe the buffer from edbrowse to acme using tmux capture-pane
        - jump to the top/beginning of a text in acme
        - erase the buffer in tmux using clear-history

FILES
        ~/.eb/bookmarks

EXAMPLES
        Managing the bookmarks.  "URLs" command will print the HTML version
        of links on a specified line.  "Bookmarks!" will append the content
        of a buffer to a bookmark file.  Those commands combined will add
        a new entry in bookmarks.

SEE ALSO
        acme(1), acmeevent(1), edbrowse(1), tmux(1), rc(1), acme(4)

CAVEATS
        Not implemented yet:
        - support for multiple sessions (not the ones in edbrowse)
        - sanitizing regexps sent to edbrowse

BUGS
        "Paste" and "Edit" aren't working properly (sometimes?).

EOF
# End of README

# Set $ifs back to default value
ifs = $tmpifs

# Target_window for tmux
twindow = browse
target = (-t ed: ^ $twindow)

fn tsend {
                tmux send-keys $target $*
}

# man acmeevent(1)
fn event {
        # $1 - c1 origin of event
        # $2 - c2 type of action
        # $3 - q0 beginning of selection
        # $4 - q1 end of selection
        # $5 - eq0 beginning of expanded selection
        # $6 - eq1 end of expanded selection
        # $7 - flag
        # $8 - nr number of runes in $9
        # $9 - text
        # $10 - chorded argument
        # $11 - origin of chorded argument

        switch ($1$2) {
        case E*         # write to body or tag
        case F*         # generated by ourselves; ignore
        case K*         # type away we do not care
        case Mi         # mouse: text inserted in tag
        case MI         # mouse: text inserted in body
        case Md         # mouse: text deleted from tag
        case MD         # mouse: text deleted from body
                        # We don't care about those events
                winwriteevent $*

        case Mx MX      # button 2 in tag or body
                if (~$9 Cut Look Paste Snarf)
                        winwriteevent $*
                if not if (~$9 acmebrowse)
                        echo -n $readme
                if not {
                        parsecmd $9
                        pipetowin
                }

        case Ml ML      # button 3 in tag or body
                        # Send selection as regexp to edbrowse
                tmux send-keys -l $target /$9/ ';' \
                        send-keys $target Enter
                pipetowin
        }
}

fn parsecmd {
        switch ($1) {
        case Back       # Go back one level
                tsend '^' Enter

        case Bookmarks  # Open bookmark file and print
                tsend 'b ~/.eb/bookmarks' Enter , Enter

        case Bookmarks! # Add bookmark, used together with URLs command.
                tsend 'w+ ~/.eb/bookmarks' Enter

        case DDG*       # Searching in duckduckgo.com
                ddg = `{echo $1}
                ddg =  $ddg(2-)
                ddg = 'b http://ddg.gg/lite?q=' ^ $"ddg
                tmux send-keys -l $target $ddg ';' \
                        send-keys $target Enter , Enter

        case Go         # Follow 1st link in the line and print
                tsend g1 Enter , Enter

        case Go2        # Follow 2nd link and...
                tsend g2 Enter , Enter

        case Go!        # Follow without print - for binary files etc
                tsend g1 Enter

        case Go2!
                tsend g2 Enter

        case Info       # Show title and address of current page
                tsend ft Enter f Enter

        case Interrupt  # Send Ctrl-C to edbrowse
                tsend C-c

        case Javascript # Toggle off/on javascript
                tsend js Enter

        case Print      # Print whole file
                tsend , Enter

        case Quit!      # With exclamation mark, must be drag-selected.
                tsend qt Enter
                windel sure
                exit

        case Refresh    # Refresh page - can be useful for JS
                tsend rf Enter

        case URLs       # Show addresses behind links in selected line.
                tsend A Enter , Enter

        case Write!     # Save (binary) file to disk.
                tsend w/ Enter

        case *          # Send selection as plain command to edbrowse
                tmux send-keys -l $target $1 ';' \
                        send-keys $target Enter
        }

}

fn pipetowin {
        # Block till edbrowse opens web page.
        tmux send-keys $target '!tmux wait-for -S ' ^ $twindow Enter ';' \
                wait-for $twindow

        # Select content of window in acme, so writing to data will erase it.
        echo -n , | winwrite addr
        winctl 'dot=addr'

        # Pipe output from tmux pane to acme window
        tmux capture-pane -p -S -10000 $target | winwrite data

        # Jump to the top/beginning of text in acme
        echo -n 0 | winwrite addr
        winctl 'dot=addr'
        winctl show

        # Erase visible part (pane) and scrollback in tmux
        tmux send-keys -R $target ';' clear-history $target
}

fn tmuxinit {
        # Unset $TMUX (in case it's running) and create detached session.
        TMUX=() tmux new-session -d -s ed -n dummy

        # Set history-limit to 10k lines - for long web pages.
        tmux set-option -q -t ed history-limit 10000

        # Postpone starting of edbrowse, otherwise history-limit won't work.
        tmux new-window -a -t ed:dummy -n $twindow edbrowse

        # Close dummy window
        tmux send-keys -t ed:dummy exit Enter
}

fn acmeinit {
        # Create new window in acme, change name
        newwindow
        winname acmebrowse

        # Add commands to tag in acme
        echo '| i? i* i= | DDG  | b http://' | winwrite tag
        echo -n 'Back Refresh | Print Go! Go2! | Info URLs Bookmarks!' \
                ' Write! | Javascript Interrupt | Quit!' | winwrite tag
}

# Initialize tmux, acme and start loop
tmuxinit
acmeinit
pipetowin
wineventloop

-- 
Paul Onyschuk

Reply via email to