Browse Source

added mdbrowse and mdparse.

Izaya 1 year ago
parent
commit
b54cf24294
3 changed files with 308 additions and 1 deletions
  1. 13
    1
      README.md
  2. 245
    0
      mdbrowse.lua
  3. 50
    0
      mdparse.lua

+ 13
- 1
README.md View File

@@ -1,3 +1,15 @@
1 1
 # mdgopher
2 2
 
3
-Gopher + Markdown. Kinda defeats the purpose but hey whatever.
3
+Gopher + Markdown. Kinda defeats the purpose but hey whatever.
4
+
5
+## mdbrowse
6
+A terrible lua-curses and luasocket based markdown browser.
7
+
8
+Gopher menu support is experimental but markdown support should work fairly well.
9
+
10
+### keybinds
11
+- j and k for scrolling
12
+- f to follow a link (marked by a [number])
13
+- o to open a URL
14
+- s for settings (terminal size and etc.)
15
+- q to quit

+ 245
- 0
mdbrowse.lua View File

@@ -0,0 +1,245 @@
1
+local curses = require "curses"
2
+local md = require "mdparse"
3
+local socket = require "socket"
4
+local surl = require "socket.url"
5
+
6
+local sx,sy,pc = 80, 25, "@"
7
+
8
+local function pgopher(gm)
9
+ local s = ""
10
+ for line in gm:gmatch("[^\n]+") do
11
+  local t = line:sub(1,1)
12
+  if t == "i" then
13
+   if line:find("\t") then
14
+    line=line:sub(1,line:find("\t"))
15
+   end
16
+   s=s..line:sub(2).."\n"
17
+  elseif t == "0" or t == "1" then
18
+   line=line:gsub("(.)(.+)\t/?([%w%./]+)\t([%w%.]+)\t(%d+)","%1    [%2](gopher://%4:%5/%3")..")" -- we gsub now
19
+   line=line:gsub("\n","")
20
+   s=s..line.."\n"
21
+  else
22
+   s=s..line.."\n"
23
+  end
24
+ end
25
+ return s
26
+end
27
+
28
+local function geturl(url)
29
+ local ds = {}
30
+ ds.scheme = "gopher"
31
+ ds.port = "70"
32
+ ds.path = ""
33
+ local c = ""
34
+ local ourl = url
35
+ tURL = surl.parse(url,ds)
36
+ if not tURL.host then tURL.host = tURL.path tURL.path = "" end
37
+ local dn, po, pt = tURL.host, tURL.port, tURL.path
38
+ print(dn,po,pt)
39
+  if dn and po then
40
+   local s=socket.tcp()
41
+   s:connect(dn,po)
42
+   s:send(pt.."\n")
43
+   c=s:receive("*a")
44
+   s:close()
45
+   local w,tpg = pcall(pgopher,c)
46
+   if w then
47
+    print(tpg)
48
+    c=tpg
49
+   end
50
+  else
51
+   c="Unable to load URL "..ourl
52
+  end
53
+--[[--
54
+ else
55
+  local f=io.open(url,"rb")
56
+  if not f then
57
+   return "\r\nPage could not be loaded."
58
+  end
59
+  c=f:read("*a")
60
+  print(c)
61
+  f:close()
62
+ end
63
+--]]--
64
+ return c
65
+end
66
+
67
+local function settings()
68
+ while true do
69
+  local scr = curses.initscr ()
70
+ 
71
+  curses.cbreak ()
72
+  curses.echo (false)	-- not noecho !
73
+  curses.nl (false)	-- not nonl !
74
+  local selection = 1
75
+  while true do
76
+   local menu = {"Screen width: "..tostring(sx),"Screen height: "..tostring(sy),"Back"}
77
+   scr:clear()
78
+   scr:refresh()
79
+   for k,v in ipairs(menu) do
80
+    if k == selection then
81
+     io.write(" * ")
82
+    end
83
+    print("\t"..v.."\r")
84
+   end
85
+   local ch = scr:getch()
86
+   if ch == 66 then
87
+    selection = selection + 1
88
+   elseif ch == 65 then
89
+    selection = selection - 1
90
+   elseif ch == 13 then break
91
+   end
92
+   if selection > #menu then
93
+    selection = #menu
94
+   elseif selection < 1 then
95
+    selection = 1
96
+   end
97
+  end
98
+  if selection == 1 then
99
+   scr:clear()
100
+   scr:refresh()
101
+   scr:addstr("New screen width: ")
102
+   curses.echo (true)
103
+   scr:refresh()
104
+   sx = tonumber(scr:getstr()) or 80
105
+   curses.echo (false)
106
+  elseif selection == 2 then
107
+   scr:clear()
108
+   scr:refresh()
109
+   scr:addstr("New screen height: ")
110
+   curses.echo (true)
111
+   scr:refresh()
112
+   sy = tonumber(scr:getstr()) or 80
113
+   curses.echo (false)
114
+  else
115
+   scr:close()
116
+   break
117
+  end
118
+ end
119
+end
120
+
121
+local function main ()
122
+  local scr = curses.initscr ()
123
+  local mode = 1
124
+  local p = false
125
+  local sc = "#"
126
+  local history = {}
127
+
128
+  curses.cbreak ()
129
+  curses.echo(false)	-- not noecho !
130
+  curses.nl(false)	-- not nonl !
131
+
132
+  cy = 1
133
+  local ts = [[
134
+**SKS Markdown Gopher Client**
135
+
136
+See the [SKS gopher hole](gopher://shadowkat.net) for more info.
137
+
138
+Use *f* to follow a link, *o* to open a URL directly, *j* and *k* to scroll, and *s* for settings.
139
+
140
+Scrolling test:
141
+a
142
+b
143
+c
144
+d
145
+e
146
+f
147
+g
148
+h
149
+i
150
+j
151
+k
152
+l
153
+m
154
+n
155
+o
156
+p
157
+q
158
+r
159
+s
160
+t
161
+u
162
+v
163
+w
164
+x
165
+y
166
+z]]
167
+  local url = "homepage"
168
+  local lt = {}
169
+  while true do
170
+   scr:clear()
171
+   scr:refresh()
172
+   local ds = ""
173
+   local dt = {}
174
+   lt = md.parse(ts).l
175
+   for k,v in ipairs(md.parse(ts)) do
176
+--[[--
177
+    if v.bold then -- note to self: fix bold
178
+     v.content = "\27[n1"..v.content.."\27[n0"
179
+    end
180
+--]]--
181
+    if v.addrid then
182
+     ds=ds.."["..tostring(v.addrid).."]"..v.content
183
+    else
184
+     ds=ds..v.content
185
+    end
186
+   end
187
+   for line in ds:gmatch("[^\n]*") do
188
+    while line:len() > 0 do
189
+     dt[#dt+1] = line:sub(1,sx)
190
+     line = line:sub(sx+1)
191
+    end
192
+   end
193
+   if sy < #dt then
194
+    ms = sy
195
+   else
196
+    ms = #dt
197
+   end
198
+   for i = cy, cy+ms do
199
+    io.write((dt[i] or "").."\r\n")
200
+   end
201
+   print("\r\n["..url.." "..tostring(cy).."/"..tostring(#dt).." "..tostring(ts:len()).."]\r")
202
+   ch = scr:getch() or 0
203
+   if ch < 256 then
204
+    if string.char(ch) == "q" then
205
+     break
206
+    elseif string.char(ch) == "s" then
207
+     settings()
208
+    elseif string.char(ch) == "o" then
209
+     scr:addstr("\rURL: ")
210
+     curses.echo(true)
211
+     scr:refresh()
212
+     local nurl = scr:getstr()
213
+     local w,nts=pcall(geturl,nurl)
214
+     --print(w,nts)
215
+     if w then ts = nts url=nurl end
216
+     local nurl = scr:getstr()
217
+    elseif string.char(ch) == "f" then
218
+     scr:addstr("\rLink: ")
219
+     curses.echo(true)
220
+     scr:refresh()
221
+     local nurl = lt[tonumber(scr:getstr())]
222
+     local w,nts=pcall(geturl,nurl)
223
+     if w then ts = nts url=nurl end
224
+     scr:refresh()
225
+    elseif string.char(ch) == "j" then
226
+     if cy < #dt then
227
+      cy = cy + 1
228
+     end
229
+    elseif string.char(ch) == "k" then
230
+     if cy > 1 then
231
+      cy = cy - 1
232
+     end
233
+    end
234
+   end
235
+  end
236
+end
237
+
238
+local function err (err)
239
+  curses.endwin ()
240
+  print "Caught an error:"
241
+  print (debug.traceback (err, 2))
242
+  os.exit (2)
243
+end
244
+
245
+xpcall (main, err)

+ 50
- 0
mdparse.lua View File

@@ -0,0 +1,50 @@
1
+local md = {}
2
+function md.parse(md)
3
+ local it = {}
4
+ it.l = {}
5
+ it[#it+1] = {["content"]="",["bold"]=false,["italic"]=false}
6
+ local lc,llc = "",""
7
+ local function newpart()
8
+  it[#it+1] = {["content"]="",["bold"]=it[#it].bold,["italic"]=it[#it].italic}
9
+ end
10
+ newpart()
11
+ local lm = false
12
+ for c in md:gmatch(".") do
13
+  if c == "*" then
14
+   if lc == "*" then
15
+    it[#it].italic = false
16
+    it[#it].italic = it[#it-1].italic
17
+    it[#it].bold = not it[#it].bold
18
+   else
19
+    newpart()
20
+    it[#it].italic = not it[#it].italic
21
+   end
22
+  elseif c == "[" then
23
+   newpart()
24
+  elseif c == "(" and lc == "]" then
25
+   lm = true
26
+   it[#it].content = it[#it].content:sub(1,-2)
27
+   it[#it].address = ""
28
+  elseif c == ")" and lm then
29
+   lm = false
30
+   it.l[#it.l+1] = it[#it].address
31
+   it[#it].addrid = #it.l
32
+   newpart()
33
+  else
34
+   if not lm then
35
+    it[#it].content = it[#it].content .. c
36
+   else
37
+    it[#it].address = it[#it].address .. c
38
+   end
39
+  end
40
+  llc = lc
41
+  lc = c
42
+ end
43
+ for k,v in pairs(it) do
44
+  if v.content then
45
+   v.content = v.content:gsub("\n","\r\n")
46
+  end
47
+ end
48
+ return it
49
+end
50
+return md