1 ! Copyright (C) 2021 John Benediktsson
2 ! See https://factorcode.org/license.txt for BSD license
4 USING: accessors arrays combinators.short-circuit command-loop
5 environment formatting gemini gemini.private io io.directories
6 io.encodings.string io.encodings.utf8 io.files io.files.temp
7 io.launcher io.pipes kernel math math.parser namespaces present
8 sequences splitting system urls webbrowser ;
12 CONSTANT: DEFAULT-URL "gemini://gemini.circumlunar.space"
14 CONSTANT: HISTORY V{ }
20 : find-url ( url items -- i item )
21 [ dup array? [ first ] when = ] with find ;
23 : nth-url ( i items -- url )
24 ?nth dup array? [ first ] when ;
26 : stack-url ( delta -- url )
27 URL ?first STACK find-url drop
28 [ + STACK nth-url ] [ drop f ] if* ;
30 : add-stack ( args -- )
31 dup dup array? [ first ] when
32 dup STACK find-url drop [
35 URL ?first STACK find-url drop [
36 over PAGE find-url drop [
37 1 + dup STACK nth-url rot = [
40 STACK [ length ] [ delete-slice ] bi
43 0 STACK remove-nth! drop
57 : add-history ( args -- )
58 HISTORY dup length 10 > [
60 ] when dupd remove! push ;
62 : print-links ( links verbose? -- )
63 LINKS delete-all over LINKS push-all
65 1 + swap [ dup array? [ first ] when URL ?first = [ drop "*" ] when ] keep
66 _ [ dup array? [ dup second empty? not ] [ f ] if ] [ f ] if [
67 first2 swap "[%s] %s (%s)\n" printf
69 dup array? [ first2 ] [ f ] if
70 dup empty? -rot ? "[%s] %s\n" printf
74 : gemini-history ( -- )
75 HISTORY t print-links ;
77 : gemini-print ( url body meta -- )
80 gemini-charset decode split-lines [
81 { [ pre get not ] [ "=>" ?head ] } 0&& [
82 swap gemini-link present over 2array PAGE push
83 PAGE length swap "[%s] %s\n" printf
88 LINKS delete-all PAGE LINKS push-all
91 : gemini-get ( args -- )
92 dup array? [ first ] when dup URL set-first
93 >url dup gemini [ drop ] 2dip swap "text/" ?head [
94 "gemini.txt" temp-file
95 [ utf8 [ gemini-print ] with-file-writer ]
96 [ utf8 file-contents print ] bi
98 "ERROR: Cannot display '" "'" surround print 2drop
101 : gemini-go ( args -- )
102 dup array? [ first ] when present [ DEFAULT-URL ] when-empty
103 { [ dup "://" subseq-of? ] [ "gemini://" head? ] } 1||
104 [ "gemini://" prepend ] unless
105 dup "gemini://" head? [
106 [ add-history ] [ add-stack ] [ gemini-get ] tri
109 : gemini-reload ( -- )
110 URL ?first gemini-go ;
113 -1 stack-url [ gemini-get ] when* ;
115 : gemini-forward ( -- )
116 1 stack-url [ gemini-get ] when* ;
120 >url f >>query f >>anchor
121 [ dup "/" tail? "./../" "./" ? url-append-path ] change-path
126 "gemini.txt" temp-file [
129 "PAGER" os-env [ "less" ] unless* >>command
130 input-stream get >>stdin
135 : gemini-ls ( args -- )
136 [ PAGE ] [ "-l" = ] bi* print-links ;
139 "gemini.txt" temp-file ?delete-file 0 exit ;
142 URL ?first [ print ] when* ;
145 URL ?first [ >url "/" >>path gemini-go ] when* ;
147 : gemini-shell ( args -- )
148 "|" split "gemini.txt" temp-file dup file-exists? [
149 "cat" swap 2array prefix run-pipeline drop
155 { quot [ drop gemini-back ] }
156 { help "Go back to the previous Gemini URL." }
157 { abbrevs { "b" } } }
160 { quot [ drop gemini-forward ] }
161 { help "Go forward to the next Gemini URL." }
162 { abbrevs { "f" } } }
165 { quot [ drop gemini-history ] }
166 { help "Display recently viewed Gemini URLs." }
167 { abbrevs { "h" "hist" } } }
170 { quot [ drop gemini-less ] }
171 { help "View the most recent Gemini URL in a pager." }
172 { abbrevs { "l" } } }
175 { quot [ gemini-ls ] }
176 { help "List the currently available links." }
180 { quot [ gemini-go ] }
181 { help "Go to a Gemini URL" }
182 { abbrevs { "g" } } }
185 { quot [ drop "gemini://gus.guru/search" gemini-go ] }
186 { help "Submit a query to the GUS search engine." }
190 { quot [ drop gemini-up ] }
191 { help "Go up one directory from the recent Gemini URL." }
192 { abbrevs { "u" } } }
195 { quot [ drop gemini-url ] }
196 { help "Print the most recent Gemini URL." }
200 { quot [ drop gemini-reload ] }
201 { help "Reload the most recent Gemini URL." }
202 { abbrevs { "r" } } }
205 { quot [ drop gemini-root ] }
206 { help "Navigate to the most recent Gemini URL's root." }
210 { quot [ gemini-shell ] }
211 { help "'cat' the most recent Gemini URL through a shell." }
212 { abbrevs { "!" } } }
215 { quot [ drop DEFAULT-URL gemini-go ] }
216 { help "Go to the default Gemini URL" }
220 { quot [ drop gemini-quit ] }
221 { help "Quit the program." }
222 { abbrevs { "q" "exit" } } }
225 TUPLE: gemini-command-loop < command-loop ;
227 M: gemini-command-loop missing-command
228 over string>number [ 1 - LINKS ?nth ] [ f ] if* [
235 "Welcome to Project Gemini!" "GEMINI>"
236 gemini-command-loop new-command-loop
237 COMMANDS [ over add-command ] each