local tArgs = { ... } if #tArgs == 0 then print( "Usage: nano " ) return end local sPath = shell.resolve( tArgs[1] ) local bReadOnly = fs.isReadOnly( sPath ) if fs.exists( sPath ) and fs.isDir( sPath ) then print( "Cannot edit a directory" ) return end local w,h = term.getSize() local x,y = 1,1 local scrollX, scrollY = 0,0 local bMenu = false local nMenuItem = 1 local sStatus = "" local tLines = {} local tabWidth = 2 function explode(div,str) if (div=='') then return false end local pos,arr = 0,{} for st,sp in function() return string.find(str,div,pos,true) end do table.insert(arr,string.sub(str,pos,st-1)) pos = sp + 1 end table.insert(arr,string.sub(str,pos)) -- Attach chars right of last divider return arr end local function load() tLines = {} if fs.exists( sPath ) then local file = io.open( sPath, "r" ) local sLine = file:read() while sLine do table.insert( tLines, sLine ) sLine = file:read() end file:close() else table.insert( tLines, "" ) end end local function save( path ) if not path then path = sPath end local file = io.open( path, "w" ) if file then for n, sLine in ipairs( tLines ) do file:write( sLine .. "\n" ) end file:close() return true end return false end local function redrawText() local chrn = 1 local chr for y=1,h-1 do term.setCursorPos( 1 - scrollX, y ) term.clearLine() local sLine = tLines[ y + scrollY ] if sLine ~= nil then term.write( sLine ) end end term.setCursorPos( x - scrollX, y - scrollY ) end local function redrawLine() local sLine = tLines[y] term.setCursorPos( 1 - scrollX, y - scrollY ) term.clearLine() term.write( sLine ) term.setCursorPos( x - scrollX, y - scrollY ) end local tMenuItems = { "Save", "Save&Exit", "Save Copy As", "Exit" } local function redrawMenu() term.setCursorPos( 1, h ) term.clearLine() if not bMenu then term.write( sStatus ) else for n,sItem in ipairs( tMenuItems ) do if n == nMenuItem then term.write( "["..sItem.."]" ) else term.write( " "..sItem.." " ) end end end term.setCursorPos( x - scrollX, y - scrollY ) end local function setStatus( _sText ) sStatus = _sText .. " ["..x..":"..y.."]" redrawMenu() end local function setCursor( x, y ) local screenX = x - scrollX local screenY = y - scrollY local bRedraw = false if screenX < 1 then scrollX = x - 1 screenX = 1 bRedraw = true elseif screenX > w then scrollX = x - w screenX = w bRedraw = true end if screenY < 1 then scrollY = y - 1 screenY = 1 bRedraw = true elseif screenY > h-1 then scrollY = y - (h-1) screenY = h-1 bRedraw = true end if bRedraw then redrawText() end term.setCursorPos( screenX, screenY ) end load() term.clear() term.setCursorPos(x,y) term.setCursorBlink( true ) redrawText() setStatus( "Press CTRL for menu" ) local menux, menuprompt, menuinput, promptcallback = nil, "", "", function() end local function redrawMenuPrompt() menux = 1 + #menuprompt + #menuinput term.setCursorPos(1,h) term.clearLine() local minput = menuinput if #minput + #menuprompt > w then minput = string.sub(minput,#minput-w+#menuprompt+1) end term.write(menuprompt..minput) term.setCursorBlink(true) term.setCursorPos(menux, h) end local function setupMenuPrompt( prompt, defaultval ) if not defaultval then defaultval = "" end menuinput = defaultval menuprompt = prompt redrawMenuPrompt() end local function saveAs( prompt ) term.setCursorPos(1,h-1) term.clearLine() term.write( prompt ) setupMenuPrompt("File: ", "/"..sPath) promptcallback = function( fname ) if #fname == 0 then setStatus( "error: invalid filename" ) term.setCursorPos(1,h-1) term.clearLine() term.write( "error: invalid filename" ) return false end if string.sub( fname, 1, 1 ) ~= "/" then fname = shell.resove(fname) end if save(fname) then return true else setStatus( "error: couldn't write to file" ) term.setCursorPos(1,h-1) term.clearLine() term.write( "error: couldn't write to file" ) return false end end end local bRunning = true local tMenuFunctions = { ["Exit"] = function() bRunning = false end, ["Save&Exit"] = function() if save() then bRunning = false else while true do if saveAs( "error: Access denied" ) then bRunning = false break end end end end, ["Save"] = function() if save() then setStatus( "Saved to "..sPath ) else while true do if saveAs( "error: Access denied" ) then bRunning = false break end end end end, ["Save Copy As"] = function() saveAs( "Choose new location: " ) end, } local function doMenuItem( _n ) local fnMenu = tMenuFunctions[ tMenuItems[_n] ] if fnMenu then fnMenu() end bMenu = false term.setCursorBlink( true ) redrawMenu() end -- KEYS: -- up 200; down 208; left 203; right 205; -- bspace 14; enter 28; Tab 15; Ctrl 29; -- Del 211; PgUp 201; PgDn 209; Ins 210 while bRunning do local sEvent, param = os.pullEvent() if sEvent == "key" then if param == 200 then -- Up if not bMenu then -- Move cursor up if y > 1 then y = y - 1 x = math.min( x, string.len( tLines[y] ) + 1 ) setCursor( x, y ) end end elseif param == 208 then -- Down if not bMenu then -- Move cursor down if y < #tLines then y = y + 1 x = math.min( x, string.len( tLines[y] ) + 1 ) setCursor( x, y ) end end elseif param == 203 then -- Left if not bMenu then -- Move cursor left if x > 1 then x = x - 1 setCursor( x, y ) end else -- Move menu left nMenuItem = nMenuItem - 1 if nMenuItem < 1 then nMenuItem = #tMenuItems end redrawMenu() end elseif param == 205 then -- Right if not bMenu then -- Move cursor right if x < string.len( tLines[y] ) + 1 then x = x + 1 setCursor( x, y ) end else -- Move menu right nMenuItem = nMenuItem + 1 if nMenuItem > #tMenuItems then nMenuItem = 1 end redrawMenu() end elseif param == 211 then if not bMenu then local sLine = tLines[y] if x < #sLine then tLines[y] = string.sub(sLine,1,x-1) .. string.sub(sLine,x+1) redrawLine() end end elseif param == 14 then -- Backspace if menux then menuinput = string.sub( menuinput, 1, #menuinput - 1 ) elseif not bMenu then if x > 1 then -- Remove character local sLine = tLines[y] tLines[y] = string.sub(sLine,1,x-2) .. string.sub(sLine,x) redrawLine() x = x - 1 setCursor( x, y ) elseif y > 1 then -- Remove newline local sPrevLen = string.len( tLines[y-1] ) tLines[y-1] = tLines[y-1] .. tLines[y] table.remove( tLines, y ) redrawText() x = sPrevLen + 1 y = y - 1 setCursor( x, y ) end end elseif param == 28 then -- Enter if menux then menux = nil promptcallback( menuinput ) elseif not bMenu then -- Newline local sLine = tLines[y] local sLineStart = string.sub(sLine,1,x-1) tLines[y] = sLineStart local tx = 1 local spaces = -1 while tx <= #sLineStart do if string.sub(sLineStart,tx,tx) ~= " " then spaces = tx - 1 break end tx = tx + 1 end if spaces == -1 then spaces = #sLineStart end local blocks = { ["function"] = "end", ["then"] = "end", ["do"] = "end", ["--[["] = "]]" } sLineStart = sLineStart:gsub("[,.:(){}]"," ") local ids = explode(" ", sLineStart) local tobeclosed = {} for k,v in pairs(ids) do if blocks[v] then spaces = spaces + tabWidth tobeclosed[#tobeclosed] = blocks[v] end end for k,v in pairs(ids) do for k2,v2 in pairs(tobeclosed) do if v2 == v then spaces = spaces - tabWidth tobeclosed[k2] = nil break end end end if string.sub( sLineStart, #sLineStart-2 ) == "end" then local c = 1 while c < #sLineStart-2 do if string.sub( sLineStart, c, c ) ~= " " then c = -1 break end c = c + 1 end if #sLineStart-2>tabWidth then tLines[y] = string.rep(" ",#sLineStart-3-tabWidth).."end" spaces = spaces - tabWidth end end table.insert( tLines, y+1, string.rep(" ",spaces) .. string.sub(sLine,x) ) redrawText() x = 1+spaces y = y + 1 setCursor( x, y ) else -- Menu selection doMenuItem( nMenuItem ) end elseif param == 15 then -- Tab if not bMenu then local sLine = tLines[y] tLines[y] = string.sub(sLine,1,x-1) .. string.rep( " ", tabWidth - (x-1)%tabWidth ) .. string.sub(sLine,x) redrawLine() x = x + tabWidth - (x-1)%tabWidth setCursor( x, y ) end elseif param == 29 then -- Menu toggle bMenu = not bMenu if bMenu then term.setCursorBlink( false ) nMenuItem = 1 else term.setCursorBlink( true ) end redrawMenu() end setStatus("Press CTRL for menu") elseif sEvent == "char" then if not bMenu then -- Input text local sLine = tLines[y] tLines[y] = string.sub(sLine,1,x-1) .. param .. string.sub(sLine,x) redrawLine() x = x + string.len( param ) setCursor( x, y ) setStatus("Press CTRL for menu") else if menux then menuinput = menuinput .. param redrawMenuPrompt() else if(tostring(tonumber(param)) == param) and tMenuItems[tonumber(param)] then doMenuItem(tonumber(param)) end -- Select menu items for n,sMenuItem in ipairs( tMenuItems ) do if string.lower(string.sub(sMenuItem,1,1)) == string.lower(param) then doMenuItem( n ) break end end end end end end term.clear() term.setCursorBlink( false ) term.setCursorPos( 1, 1 )