1 ! Copyright (C) 2021 John Benediktsson
2 ! See http://factorcode.org/license.txt for BSD license
4 USING: accessors arrays assocs combinators
5 combinators.short-circuit command-loop formatting gemini
6 gemini.private io io.directories io.encodings.string
7 io.encodings.utf8 io.files io.files.temp io.launcher io.pipes
8 kernel math math.parser namespaces present sequences splitting
9 system urls webbrowser ;
13 CONSTANT: DEFAULT-URL "gemini://gemini.circumlunar.space"
15 CONSTANT: HISTORY V{ }
21 : add-stack ( args -- )
22 dup PAGE keys index [ STACK delete-all ] unless
23 URL ?first STACK index [
24 1 + dup STACK ?nth pick = [
27 STACK [ length ] [ delete-slice ] bi
30 0 STACK remove-nth! drop
37 : add-history ( args -- )
38 HISTORY dup length 10 > [
40 ] when dupd remove! push ;
42 : gemini-history ( -- )
43 HISTORY [ 1 + swap "[%d] %s\n" printf ] each-index
44 LINKS delete-all HISTORY LINKS push-all ;
46 : gemini-print ( url body meta -- )
49 gemini-charset decode string-lines [
50 { [ pre get not ] [ "=>" ?head ] } 0&& [
51 swap gemini-link present over 2array PAGE push
52 PAGE length swap "[%s] %s\n" printf
57 LINKS delete-all PAGE keys LINKS push-all
60 : gemini-get ( args -- )
62 >url dup gemini [ drop ] 2dip swap "text/" ?head [
63 "gemini.txt" temp-file
64 [ utf8 [ gemini-print ] with-file-writer ]
65 [ utf8 file-contents print ] bi
67 "ERROR: Cannot display '" "'" surround print 2drop
70 : gemini-go ( args -- )
71 present [ DEFAULT-URL ] when-empty
72 { [ "://" over subseq? ] [ "gemini://" head? ] } 1||
73 [ "gemini://" prepend ] unless
74 dup "gemini://" head? [
75 [ add-history ] [ add-stack ] [ gemini-get ] tri
78 : gemini-reload ( -- )
79 HISTORY ?last gemini-go ;
82 URL ?first STACK index [
83 1 - STACK ?nth [ gemini-get ] when*
86 : gemini-forward ( -- )
87 URL ?first STACK index [
88 1 + STACK ?nth [ gemini-get ] when*
93 >url f >>query f >>anchor
94 [ "/" ?tail drop "/" split1-last drop "/" append ] change-path
99 "gemini.txt" temp-file dup exists? [
100 "less" swap 2array try-process
103 : gemini-ls ( args -- )
106 _ [ " (" ")" surround ] [ drop f ] if
109 LINKS delete-all PAGE keys LINKS push-all ;
112 "gemini.txt" temp-file ?delete-file 0 exit ;
115 URL ?first [ print ] when* ;
118 URL ?first [ >url "/" >>path gemini-go ] when* ;
120 : gemini-shell ( args -- )
121 "|" split "gemini.txt" temp-file dup exists? [
122 "cat" swap 2array prefix run-pipeline drop
125 : gemini-stack ( -- )
127 1 + swap dup URL ?first = " (*)" f ?
130 LINKS delete-all STACK LINKS push-all ;
135 { quot [ drop gemini-back ] }
136 { help "Go back to the previous Gemini URL." }
137 { abbrevs { "b" } } }
140 { quot [ drop gemini-forward ] }
141 { help "Go forward to the next Gemini URL." }
142 { abbrevs { "f" } } }
145 { quot [ drop gemini-history ] }
146 { help "Display recently viewed Gemini URLs." }
147 { abbrevs { "h" "hist" } } }
150 { quot [ drop gemini-less ] }
151 { help "View the most recent Gemini URL in a pager." }
152 { abbrevs { "l" } } }
155 { quot [ gemini-ls ] }
156 { help "List the currently available links." }
160 { quot [ gemini-go ] }
161 { help "Go to a Gemini URL" }
162 { abbrevs { "g" } } }
165 { quot [ drop gemini-up ] }
166 { help "Go up one directory from the recent Gemini URL." }
167 { abbrevs { "u" } } }
170 { quot [ drop gemini-url ] }
171 { help "Print the most recent Gemini URL." }
175 { quot [ drop gemini-reload ] }
176 { help "Reload the most recent Gemini URL." }
177 { abbrevs { "r" } } }
180 { quot [ drop gemini-root ] }
181 { help "Navigate to the most recent Gemini URL's root." }
185 { quot [ gemini-shell ] }
186 { help "'cat' the most recent Gemini URL through a shell." }
187 { abbrevs { "!" } } }
190 { quot [ drop gemini-stack ] }
191 { help "Display the current navigation stack." }
195 { quot [ drop gemini-quit ] }
196 { help "Quit the program." }
197 { abbrevs { "q" "exit" } } }
200 TUPLE: gemini-command-loop < command-loop ;
202 M: gemini-command-loop missing-command
203 over string>number [ 1 - LINKS ?nth ] [ f ] if* [
210 "Welcome to Project Gemini!" "GEMINI>"
211 gemini-command-loop new-command-loop
212 COMMANDS [ over add-command ] each