SHOW:
|
|
- or go back to the newest paste.
| 1 | if not fs.exists('lex') then
| |
| 2 | shell.run('pastebin get edyuQ5xY lex')
| |
| 3 | end | |
| 4 | ||
| 5 | local lexSuccess = os.loadAPI('lex')
| |
| 6 | ||
| 7 | if not lexSuccess or not lex or not lex.lex then | |
| 8 | print('A valid lexer is required (file `lex` seems to be broken or not a lexer)')
| |
| 9 | return | |
| 10 | end | |
| 11 | ||
| 12 | -- Get file to edit | |
| 13 | local tArgs = { ... }
| |
| 14 | if #tArgs == 0 then | |
| 15 | print( "Usage: shedit <path>" ) | |
| 16 | return | |
| 17 | end | |
| 18 | ||
| 19 | -- Error checking | |
| 20 | local sPath = shell.resolve( tArgs[1] ) | |
| 21 | local bReadOnly = fs.isReadOnly( sPath ) | |
| 22 | if fs.exists( sPath ) and fs.isDir( sPath ) then | |
| 23 | print( "Cannot edit a directory." ) | |
| 24 | return | |
| 25 | end | |
| 26 | ||
| 27 | local x,y = 1,1 | |
| 28 | local markX, markY = 1, 1 | |
| 29 | local w,h = term.getSize() | |
| 30 | local scrollX, scrollY = 0,0 | |
| 31 | ||
| 32 | local tLines = {}
| |
| 33 | local bRunning = true | |
| 34 | ||
| 35 | -- Colors | |
| 36 | local highlightColor, keywordColor, commentColor, textColor, bgColor, stringColor | |
| 37 | if term.isColor() then | |
| 38 | bgColor = colors.black | |
| 39 | textColor = colors.white | |
| 40 | highlightColor = colors.yellow | |
| 41 | keywordColor = colors.yellow | |
| 42 | commentColor = colors.green | |
| 43 | stringColor = colors.red | |
| 44 | else | |
| 45 | bgColor = colors.black | |
| 46 | textColor = colors.white | |
| 47 | highlightColor = colors.white | |
| 48 | keywordColor = colors.white | |
| 49 | commentColor = colors.white | |
| 50 | stringColor = colors.white | |
| 51 | end | |
| 52 | ||
| 53 | -- Menus | |
| 54 | local bMenu = false | |
| 55 | local nMenuItem = 1 | |
| 56 | local tMenuItems = {}
| |
| 57 | if not bReadOnly then | |
| 58 | table.insert( tMenuItems, "Save" ) | |
| 59 | end | |
| 60 | if shell.openTab then | |
| 61 | table.insert( tMenuItems, "Run" ) | |
| 62 | end | |
| 63 | if peripheral.find( "printer" ) then | |
| 64 | table.insert( tMenuItems, "Print" ) | |
| 65 | end | |
| 66 | table.insert( tMenuItems, "Exit" ) | |
| 67 | table.insert(tMenuItems, 'Tools') | |
| 68 | table.insert(tMenuItems, 'Find') | |
| 69 | table.insert(tMenuItems, 'Jump') | |
| 70 | table.insert(tMenuItems, 'Copy') | |
| 71 | table.insert(tMenuItems, 'VPaste') | |
| 72 | ||
| 73 | local sStatus = "Press Ctrl to access menu" | |
| 74 | if string.len( sStatus ) > w - 5 then | |
| 75 | sStatus = "Press Ctrl for menu" | |
| 76 | end | |
| 77 | ||
| 78 | local function load( _sPath ) | |
| 79 | tLines = {}
| |
| 80 | if fs.exists( _sPath ) then | |
| 81 | local file = io.open( _sPath, "r" ) | |
| 82 | local sLine = file:read() | |
| 83 | while sLine do | |
| 84 | table.insert( tLines, sLine ) | |
| 85 | sLine = file:read() | |
| 86 | end | |
| 87 | file:close() | |
| 88 | end | |
| 89 | ||
| 90 | if #tLines == 0 then | |
| 91 | table.insert( tLines, "" ) | |
| 92 | end | |
| 93 | end | |
| 94 | ||
| 95 | local function save( _sPath ) | |
| 96 | -- Create intervening folder | |
| 97 | local sDir = _sPath:sub(1, _sPath:len() - fs.getName(_sPath):len() ) | |
| 98 | if not fs.exists( sDir ) then | |
| 99 | fs.makeDir( sDir ) | |
| 100 | end | |
| 101 | ||
| 102 | -- Save | |
| 103 | local file = nil | |
| 104 | local function innerSave() | |
| 105 | file = fs.open( _sPath, "w" ) | |
| 106 | if file then | |
| 107 | for n, sLine in ipairs( tLines ) do | |
| 108 | file.write( sLine .. "\n" ) | |
| 109 | end | |
| 110 | else | |
| 111 | error( "Failed to open ".._sPath ) | |
| 112 | end | |
| 113 | end | |
| 114 | ||
| 115 | local ok, err = pcall( innerSave ) | |
| 116 | if file then | |
| 117 | file.close() | |
| 118 | end | |
| 119 | return ok, err | |
| 120 | end | |
| 121 | ||
| 122 | local tKeywords = {
| |
| 123 | ["and"] = true, | |
| 124 | ["break"] = true, | |
| 125 | ["do"] = true, | |
| 126 | ["else"] = true, | |
| 127 | ["elseif"] = true, | |
| 128 | ["end"] = true, | |
| 129 | ["false"] = true, | |
| 130 | ["for"] = true, | |
| 131 | ["function"] = true, | |
| 132 | ["if"] = true, | |
| 133 | ["in"] = true, | |
| 134 | ["local"] = true, | |
| 135 | ["nil"] = true, | |
| 136 | ["not"] = true, | |
| 137 | ["or"] = true, | |
| 138 | ["repeat"] = true, | |
| 139 | ["return"] = true, | |
| 140 | ["then"] = true, | |
| 141 | ["true"] = true, | |
| 142 | ["until"]= true, | |
| 143 | ["while"] = true, | |
| 144 | } | |
| 145 | ||
| 146 | local function tryWrite( sLine, regex, color ) | |
| 147 | local match = string.match( sLine, regex ) | |
| 148 | if match then | |
| 149 | if type(color) == "number" then | |
| 150 | term.setTextColor( color ) | |
| 151 | else | |
| 152 | term.setTextColor( color(match) ) | |
| 153 | end | |
| 154 | term.write( match ) | |
| 155 | term.setTextColor( textColor ) | |
| 156 | return string.sub( sLine, string.len(match) + 1 ) | |
| 157 | end | |
| 158 | return nil | |
| 159 | end | |
| 160 | ||
| 161 | --[[local function writeHighlighted( sLine ) | |
| 162 | while string.len(sLine) > 0 do | |
| 163 | sLine = | |
| 164 | tryWrite( sLine, "^%-%-%[%[.-%]%]", commentColor ) or | |
| 165 | tryWrite( sLine, "^%-%-.*", commentColor ) or | |
| 166 | tryWrite( sLine, "^\"\"", stringColor ) or | |
| 167 | tryWrite( sLine, "^\".-[^\\]\"", stringColor ) or | |
| 168 | tryWrite( sLine, "^\'\'", stringColor ) or | |
| 169 | tryWrite( sLine, "^\'.-[^\\]\'", stringColor ) or | |
| 170 | tryWrite( sLine, "^%[%[.-%]%]", stringColor ) or | |
| 171 | tryWrite( sLine, "^[%w_]+", function( match ) | |
| 172 | if tKeywords[ match ] then | |
| 173 | return keywordColor | |
| 174 | end | |
| 175 | return textColor | |
| 176 | end ) or | |
| 177 | tryWrite( sLine, "^[^%w_]", textColor ) | |
| 178 | end | |
| 179 | end]] | |
| 180 | ||
| 181 | local apis = {
| |
| 182 | -- tables | |
| 183 | bit = true, | |
| 184 | bit32 = true, | |
| 185 | bitop = true, | |
| 186 | colors = true, | |
| 187 | colours = true, | |
| 188 | coroutine = true, | |
| 189 | disk = true, | |
| 190 | fs = true, | |
| 191 | gps = true, | |
| 192 | help = true, | |
| 193 | http = true, | |
| 194 | io = true, | |
| 195 | keys = true, | |
| 196 | math = true, | |
| 197 | os = true, | |
| 198 | paintutils = true, | |
| 199 | parallel = true, | |
| 200 | peripheral = true, | |
| 201 | rednet = true, | |
| 202 | redstone = true, | |
| 203 | rs = true, | |
| 204 | settings = true, | |
| 205 | shell = true, | |
| 206 | socket = true, | |
| 207 | string = true, | |
| 208 | table = true, | |
| 209 | term = true, | |
| 210 | textutils = true, | |
| 211 | vector = true, | |
| 212 | window = true, | |
| 213 | ||
| 214 | -- functions | |
| 215 | assert = true, | |
| 216 | collectgarbage = true, | |
| 217 | dofile = true, | |
| 218 | error = true, | |
| 219 | getfenv = true, | |
| 220 | getmetatable = true, | |
| 221 | ipairs = true, | |
| 222 | loadfile = true, | |
| 223 | loadstring = true, | |
| 224 | module = true, | |
| 225 | next = true, | |
| 226 | pairs = true, | |
| 227 | pcall = true, | |
| 228 | print = true, | |
| 229 | rawequal = true, | |
| 230 | rawget = true, | |
| 231 | rawset = true, | |
| 232 | require = true, | |
| 233 | select = true, | |
| 234 | setfenv = true, | |
| 235 | setmetatable = true, | |
| 236 | tonumber = true, | |
| 237 | tostring = true, | |
| 238 | type = true, | |
| 239 | unpack = true, | |
| 240 | xpcall = true, | |
| 241 | printError = true, | |
| 242 | write = true | |
| 243 | } | |
| 244 | ||
| 245 | local function onUpdate() | |
| 246 | tokenized = lex.lex(table.concat(tLines, '\n')) | |
| 247 | end | |
| 248 | ||
| 249 | local function markExists() | |
| 250 | if markX ~= x or markY ~= y then | |
| 251 | return true | |
| 252 | else | |
| 253 | return false | |
| 254 | end | |
| 255 | end | |
| 256 | ||
| 257 | local function isMarked(newX, newY) | |
| 258 | if (newY > markY and newY < y) or (newY > y and newY < markY) then | |
| 259 | return true | |
| 260 | end | |
| 261 | ||
| 262 | if newY == markY and newY == y then | |
| 263 | if markX > x and newX >= x and newX < markX then | |
| 264 | return true | |
| 265 | elseif markX < x and newX >= markX and newX < x then | |
| 266 | return true | |
| 267 | end | |
| 268 | elseif newY == markY then | |
| 269 | if newX < markX and y < markY then | |
| 270 | return true | |
| 271 | elseif newX >= markX and y > markY then | |
| 272 | return true | |
| 273 | end | |
| 274 | elseif newY == y then | |
| 275 | if newX < x and y > markY then | |
| 276 | return true | |
| 277 | elseif newX >= x and y < markY then | |
| 278 | return true | |
| 279 | end | |
| 280 | end | |
| 281 | ||
| 282 | return false | |
| 283 | end | |
| 284 | ||
| 285 | local function getMarks() | |
| 286 | local msx, msy | |
| 287 | local mex, mey | |
| 288 | ||
| 289 | if markY == y then | |
| 290 | if markX > x then | |
| 291 | msx = x | |
| 292 | msy = y | |
| 293 | mex = markX | |
| 294 | mey = markY | |
| 295 | else | |
| 296 | msx = markX | |
| 297 | msy = markY | |
| 298 | mex = x | |
| 299 | mey = y | |
| 300 | end | |
| 301 | else | |
| 302 | if markY > y then | |
| 303 | msx = x | |
| 304 | msy = y | |
| 305 | mex = markX | |
| 306 | mey = markY | |
| 307 | else | |
| 308 | msx = markX | |
| 309 | msy = markY | |
| 310 | mex = x | |
| 311 | mey = y | |
| 312 | end | |
| 313 | end | |
| 314 | ||
| 315 | return msx, msy, mex, mey | |
| 316 | end | |
| 317 | ||
| 318 | local function getCharDisplay(char) | |
| 319 | if char == '\t' then | |
| 320 | return ' ' | |
| 321 | else | |
| 322 | return char | |
| 323 | end | |
| 324 | end | |
| 325 | ||
| 326 | local function getCharWidth(char) | |
| 327 | return getCharDisplay(char):len() | |
| 328 | end | |
| 329 | ||
| 330 | local function writeHighlighted(lineNr) | |
| 331 | local tokens = tokenized[lineNr] or {}
| |
| 332 | local textColor = term.getTextColor() | |
| 333 | local bgColor = term.getBackgroundColor() | |
| 334 | local setX, setY = term.getCursorPos() | |
| 335 | local msx, msy, mex, mey = getMarks() | |
| 336 | ||
| 337 | if lineNr < mey and lineNr >= msy then | |
| 338 | term.setBackgroundColor(colors.white) | |
| 339 | end | |
| 340 | ||
| 341 | term.clearLine() | |
| 342 | term.setCursorPos(setX, setY) | |
| 343 | ||
| 344 | for t = 1, #tokens do | |
| 345 | local token = tokens[t] | |
| 346 | local color = colors.white | |
| 347 | ||
| 348 | if token.type == 'keyword' or token.type == 'operator' then | |
| 349 | color = colors.yellow | |
| 350 | elseif token.type == 'number' or token.type == 'value' then | |
| 351 | color = colors.purple | |
| 352 | elseif token.type == 'comment' then | |
| 353 | color = colors.green | |
| 354 | elseif token.type == 'ident' and apis[token.data] then | |
| 355 | color = colors.lightGray | |
| 356 | elseif token.type == 'string' then | |
| 357 | color = colors.cyan | |
| 358 | elseif token.type == 'escape' then | |
| 359 | color = colors.purple | |
| 360 | elseif token.type == 'unidentified' then | |
| 361 | color = colors.red | |
| 362 | end | |
| 363 | ||
| 364 | for p = 1, #token.data do | |
| 365 | local sub = getCharDisplay(token.data:sub(p, p)) | |
| 366 | ||
| 367 | local cX, cY = p + token.posFirst - 1, lineNr | |
| 368 | ||
| 369 | if (cY == msy and cY == mey and cX >= msx and cX < mex) or (cY == msy and cY ~= mey and cX >= msx) or (cY == mey and cY ~= msy and cX < mex) or (cY > msy and cY < mey) then | |
| 370 | term.setTextColor(colors.black) | |
| 371 | term.setBackgroundColor(color) | |
| 372 | else | |
| 373 | term.setBackgroundColor(colors.black) | |
| 374 | term.setTextColor(color) | |
| 375 | end | |
| 376 | ||
| 377 | term.write(sub) | |
| 378 | end | |
| 379 | end | |
| 380 | ||
| 381 | term.setTextColor(textColor) | |
| 382 | term.setBackgroundColor(bgColor) | |
| 383 | end | |
| 384 | ||
| 385 | local tCompletions | |
| 386 | local nCompletion | |
| 387 | ||
| 388 | local tCompleteEnv = _ENV | |
| 389 | local function complete( sLine ) | |
| 390 | if settings.get( "edit.autocomplete" ) then | |
| 391 | local nStartPos = string.find( sLine, "[a-zA-Z0-9_%.]+$" ) | |
| 392 | if nStartPos then | |
| 393 | sLine = string.sub( sLine, nStartPos ) | |
| 394 | end | |
| 395 | if #sLine > 0 then | |
| 396 | return textutils.complete( sLine, tCompleteEnv ) | |
| 397 | end | |
| 398 | end | |
| 399 | return nil | |
| 400 | end | |
| 401 | ||
| 402 | local function recomplete() | |
| 403 | local sLine = tLines[y] | |
| 404 | if not bMenu and not bReadOnly and x == string.len(sLine) + 1 then | |
| 405 | tCompletions = complete( sLine ) | |
| 406 | if tCompletions and #tCompletions > 0 then | |
| 407 | nCompletion = 1 | |
| 408 | else | |
| 409 | nCompletion = nil | |
| 410 | end | |
| 411 | else | |
| 412 | tCompletions = nil | |
| 413 | nCompletion = nil | |
| 414 | end | |
| 415 | end | |
| 416 | ||
| 417 | local function writeCompletion( sLine ) | |
| 418 | if nCompletion then | |
| 419 | local sCompletion = tCompletions[ nCompletion ] | |
| 420 | term.setTextColor( colors.white ) | |
| 421 | term.setBackgroundColor( colors.gray ) | |
| 422 | term.write( sCompletion ) | |
| 423 | term.setTextColor( textColor ) | |
| 424 | term.setBackgroundColor( bgColor ) | |
| 425 | end | |
| 426 | end | |
| 427 | ||
| 428 | local function getCursorX(lineNr) | |
| 429 | local line = tLines[lineNr] | |
| 430 | local cx = 1 | |
| 431 | ||
| 432 | for i = 1, x - 1 do | |
| 433 | cx = cx + getCharWidth(line:sub(i, i)) | |
| 434 | end | |
| 435 | ||
| 436 | return cx | |
| 437 | end | |
| 438 | ||
| 439 | local function cxToPos(lineNr, cx) | |
| 440 | local line = tLines[lineNr] | |
| 441 | local num = 1 | |
| 442 | local pos = 1 | |
| 443 | ||
| 444 | for i = 1, #line do | |
| 445 | amt = getCharWidth(line:sub(i, i)) | |
| 446 | ||
| 447 | if num + amt > cx then | |
| 448 | return pos | |
| 449 | else | |
| 450 | num = num + amt | |
| 451 | pos = pos + 1 | |
| 452 | end | |
| 453 | end | |
| 454 | ||
| 455 | return pos | |
| 456 | end | |
| 457 | ||
| 458 | local function redrawText() | |
| 459 | local cursorX, cursorY = x, y | |
| 460 | for y=1,h-1 do | |
| 461 | term.setCursorPos( 1 - scrollX, y ) | |
| 462 | term.clearLine() | |
| 463 | ||
| 464 | local sLine = tLines[ y + scrollY ] | |
| 465 | if sLine ~= nil then | |
| 466 | --writeHighlighted( sLine ) | |
| 467 | writeHighlighted(y + scrollY) | |
| 468 | if cursorY == y and cursorX == #sLine + 1 then | |
| 469 | writeCompletion() | |
| 470 | end | |
| 471 | end | |
| 472 | end | |
| 473 | ||
| 474 | local cx = getCursorX(y) | |
| 475 | ||
| 476 | term.setCursorPos( cx - scrollX, y - scrollY ) | |
| 477 | end | |
| 478 | ||
| 479 | --[[local function redrawLine(_nY) | |
| 480 | local sLine = tLines[_nY] | |
| 481 | if sLine then | |
| 482 | term.setCursorPos( 1 - scrollX, _nY - scrollY ) | |
| 483 | term.clearLine() | |
| 484 | --writeHighlighted( sLine ) | |
| 485 | writeHighlighted(_nY) | |
| 486 | if _nY == y and x == #sLine + 1 then | |
| 487 | writeCompletion() | |
| 488 | end | |
| 489 | term.setCursorPos( x - scrollX, _nY - scrollY ) | |
| 490 | end | |
| 491 | end]] | |
| 492 | ||
| 493 | local redrawLine = redrawText | |
| 494 | ||
| 495 | local function redrawMenu() | |
| 496 | -- Clear line | |
| 497 | term.setCursorPos( 1, h ) | |
| 498 | term.clearLine() | |
| 499 | ||
| 500 | -- Draw line numbers | |
| 501 | --[[term.setCursorPos( w - string.len( "Ln "..y ) + 1, h ) | |
| 502 | term.setTextColor( highlightColor ) | |
| 503 | term.write( "Ln " ) | |
| 504 | term.setTextColor( textColor ) | |
| 505 | term.write( y )]] | |
| 506 | ||
| 507 | if not bMenu then | |
| 508 | -- Draw status | |
| 509 | term.setTextColor( highlightColor ) | |
| 510 | term.write( sStatus ) | |
| 511 | term.setTextColor( textColor ) | |
| 512 | ||
| 513 | term.setCursorPos(w - ('[' .. x .. ':' .. y .. ']:[' .. markX .. ':' .. markY .. ']'):len() + 1, h)
| |
| 514 | term.setTextColor(highlightColor) | |
| 515 | term.write('[')
| |
| 516 | term.setTextColor(textColor) | |
| 517 | term.write(x) | |
| 518 | term.setTextColor(highlightColor) | |
| 519 | term.write(':')
| |
| 520 | term.setTextColor(textColor) | |
| 521 | term.write(y) | |
| 522 | term.setTextColor(highlightColor) | |
| 523 | term.write(']:[')
| |
| 524 | term.setTextColor(textColor) | |
| 525 | term.write(markX) | |
| 526 | term.setTextColor(highlightColor) | |
| 527 | term.write(':')
| |
| 528 | term.setTextColor(textColor) | |
| 529 | term.write(markY) | |
| 530 | term.setTextColor(highlightColor) | |
| 531 | term.write(']')
| |
| 532 | term.setTextColor(textColor) | |
| 533 | else | |
| 534 | -- Draw menu | |
| 535 | term.setTextColor( textColor ) | |
| 536 | for nItem,sItem in pairs( tMenuItems ) do | |
| 537 | if nItem == nMenuItem then | |
| 538 | term.setTextColor( highlightColor ) | |
| 539 | term.write( "[" ) | |
| 540 | term.setTextColor( textColor ) | |
| 541 | term.write( sItem ) | |
| 542 | term.setTextColor( highlightColor ) | |
| 543 | term.write( "]" ) | |
| 544 | term.setTextColor( textColor ) | |
| 545 | else | |
| 546 | term.write( " "..sItem.." " ) | |
| 547 | end | |
| 548 | end | |
| 549 | end | |
| 550 | ||
| 551 | -- Reset cursor | |
| 552 | term.setCursorPos( getCursorX(y) - scrollX, y - scrollY ) | |
| 553 | end | |
| 554 | ||
| 555 | local function setCursor( newX, newY ) | |
| 556 | local oldX, oldY = x, y | |
| 557 | x, y = newX, newY | |
| 558 | local screenX = getCursorX(y) - scrollX | |
| 559 | local screenY = y - scrollY | |
| 560 | ||
| 561 | local bRedraw = false | |
| 562 | if screenX < 1 then | |
| 563 | scrollX = getCursorX(y) - 1 | |
| 564 | screenX = 1 | |
| 565 | bRedraw = true | |
| 566 | elseif screenX > w then | |
| 567 | scrollX = scrollX + (screenX - w) | |
| 568 | screenX = w | |
| 569 | bRedraw = true | |
| 570 | end | |
| 571 | ||
| 572 | if screenY < 1 then | |
| 573 | scrollY = y - 1 | |
| 574 | screenY = 1 | |
| 575 | bRedraw = true | |
| 576 | elseif screenY > h-1 then | |
| 577 | scrollY = y - (h-1) | |
| 578 | screenY = h-1 | |
| 579 | bRedraw = true | |
| 580 | end | |
| 581 | ||
| 582 | recomplete() | |
| 583 | if bRedraw then | |
| 584 | redrawText() | |
| 585 | elseif y ~= oldY then | |
| 586 | redrawLine( oldY ) | |
| 587 | redrawLine( y ) | |
| 588 | else | |
| 589 | redrawLine( y ) | |
| 590 | end | |
| 591 | term.setCursorPos( screenX, screenY ) | |
| 592 | ||
| 593 | redrawMenu() | |
| 594 | end | |
| 595 | ||
| 596 | local function setCursorMark(newX, newY) | |
| 597 | markX, markY = newX, newY | |
| 598 | setCursor(newX, newY) | |
| 599 | end | |
| 600 | ||
| 601 | local function deleteMarked() | |
| 602 | if markExists() then | |
| 603 | local msx, msy, mex, mey = getMarks() | |
| 604 | ||
| 605 | if msy == mey then | |
| 606 | local line = tLines[mey] | |
| 607 | ||
| 608 | tLines[mey] = line:sub(1, msx - 1) .. line:sub(mex) | |
| 609 | else | |
| 610 | if mey - msy > 1 then | |
| 611 | for i = 1, mey - msy - 1 do | |
| 612 | table.remove(tLines, msy + 1) | |
| 613 | end | |
| 614 | end | |
| 615 | ||
| 616 | local line = tLines[msy] | |
| 617 | local line2 = tLines[msy + 1] | |
| 618 | ||
| 619 | tLines[msy] = line:sub(1, msx - 1) .. line2:sub(mex) | |
| 620 | table.remove(tLines, msy + 1) | |
| 621 | end | |
| 622 | ||
| 623 | setCursorMark(msx, msy) | |
| 624 | end | |
| 625 | end | |
| 626 | ||
| 627 | local function getMarked() | |
| 628 | if markExists() then | |
| 629 | local msx, msy, mex, mey = getMarks() | |
| 630 | ||
| 631 | if msy == mey then | |
| 632 | return tLines[msy]:sub(msx, mex - 1) | |
| 633 | else | |
| 634 | local marked = tLines[msy]:sub(msx) | |
| 635 | ||
| 636 | if mey - msy > 1 then | |
| 637 | for i = msy + 1, mey - 1 do | |
| 638 | marked = marked .. '\n' .. tLines[i] | |
| 639 | end | |
| 640 | end | |
| 641 | ||
| 642 | marked = marked .. '\n' .. tLines[mey]:sub(1, mex - 1) | |
| 643 | ||
| 644 | return marked | |
| 645 | end | |
| 646 | end | |
| 647 | end | |
| 648 | ||
| 649 | local toolsFuncs = {
| |
| 650 | {
| |
| 651 | 'Convert 2 spaces to tabs', | |
| 652 | function() | |
| 653 | for i = 1, #tLines do | |
| 654 | local line = tLines[i] | |
| 655 | local count = 0 | |
| 656 | ||
| 657 | while true do | |
| 658 | if line:sub(count * 2 + 1, count * 2 + 2) == ' ' then | |
| 659 | count = count + 1 | |
| 660 | else | |
| 661 | break | |
| 662 | end | |
| 663 | end | |
| 664 | ||
| 665 | tLines[i] = ('\t'):rep(count) .. line:sub(count * 2 + 1)
| |
| 666 | end | |
| 667 | end | |
| 668 | }, {
| |
| 669 | 'Convert tabs to 2 spaces', | |
| 670 | function() | |
| 671 | for i = 1, #tLines do | |
| 672 | local line = tLines[i] | |
| 673 | local count = 0 | |
| 674 | ||
| 675 | while true do | |
| 676 | if line:sub(count + 1, count + 1) == '\t' then | |
| 677 | count = count + 1 | |
| 678 | else | |
| 679 | break | |
| 680 | end | |
| 681 | end | |
| 682 | ||
| 683 | tLines[i] = (' '):rep(count) .. line:sub(count + 1)
| |
| 684 | end | |
| 685 | end | |
| 686 | } | |
| 687 | } | |
| 688 | ||
| 689 | -- for testing scrolling, etc in the tools menu | |
| 690 | --[[for i = 1, 50 do | |
| 691 | table.insert(toolsFuncs, {'Do nothing ' .. tostring(i), function() end})
| |
| 692 | end]] | |
| 693 | ||
| 694 | ||
| 695 | local findHistory = {}
| |
| 696 | local jumpHistory = {}
| |
| 697 | local vClipboard = '' | |
| 698 | ||
| 699 | local tMenuFuncs = {
| |
| 700 | Save = function() | |
| 701 | if bReadOnly then | |
| 702 | sStatus = "Access denied" | |
| 703 | else | |
| 704 | local ok, err = save( sPath ) | |
| 705 | if ok then | |
| 706 | sStatus="Saved to "..sPath | |
| 707 | else | |
| 708 | sStatus="Error saving to "..sPath | |
| 709 | end | |
| 710 | end | |
| 711 | redrawMenu() | |
| 712 | end, | |
| 713 | Print = function() | |
| 714 | local printer = peripheral.find( "printer" ) | |
| 715 | if not printer then | |
| 716 | sStatus = "No printer attached" | |
| 717 | return | |
| 718 | end | |
| 719 | ||
| 720 | local nPage = 0 | |
| 721 | local sName = fs.getName( sPath ) | |
| 722 | if printer.getInkLevel() < 1 then | |
| 723 | sStatus = "Printer out of ink" | |
| 724 | return | |
| 725 | elseif printer.getPaperLevel() < 1 then | |
| 726 | sStatus = "Printer out of paper" | |
| 727 | return | |
| 728 | end | |
| 729 | ||
| 730 | local screenTerminal = term.current() | |
| 731 | local printerTerminal = {
| |
| 732 | getCursorPos = printer.getCursorPos, | |
| 733 | setCursorPos = printer.setCursorPos, | |
| 734 | getSize = printer.getPageSize, | |
| 735 | write = printer.write, | |
| 736 | } | |
| 737 | printerTerminal.scroll = function() | |
| 738 | if nPage == 1 then | |
| 739 | printer.setPageTitle( sName.." (page "..nPage..")" ) | |
| 740 | end | |
| 741 | ||
| 742 | while not printer.newPage() do | |
| 743 | if printer.getInkLevel() < 1 then | |
| 744 | sStatus = "Printer out of ink, please refill" | |
| 745 | elseif printer.getPaperLevel() < 1 then | |
| 746 | sStatus = "Printer out of paper, please refill" | |
| 747 | else | |
| 748 | sStatus = "Printer output tray full, please empty" | |
| 749 | end | |
| 750 | ||
| 751 | term.redirect( screenTerminal ) | |
| 752 | redrawMenu() | |
| 753 | term.redirect( printerTerminal ) | |
| 754 | ||
| 755 | local timer = os.startTimer(0.5) | |
| 756 | sleep(0.5) | |
| 757 | end | |
| 758 | ||
| 759 | nPage = nPage + 1 | |
| 760 | if nPage == 1 then | |
| 761 | printer.setPageTitle( sName ) | |
| 762 | else | |
| 763 | printer.setPageTitle( sName.." (page "..nPage..")" ) | |
| 764 | end | |
| 765 | end | |
| 766 | ||
| 767 | bMenu = false | |
| 768 | term.redirect( printerTerminal ) | |
| 769 | local ok, error = pcall( function() | |
| 770 | term.scroll() | |
| 771 | for n, sLine in ipairs( tLines ) do | |
| 772 | print( sLine ) | |
| 773 | end | |
| 774 | end ) | |
| 775 | term.redirect( screenTerminal ) | |
| 776 | if not ok then | |
| 777 | print( error ) | |
| 778 | end | |
| 779 | ||
| 780 | while not printer.endPage() do | |
| 781 | sStatus = "Printer output tray full, please empty" | |
| 782 | redrawMenu() | |
| 783 | sleep( 0.5 ) | |
| 784 | end | |
| 785 | bMenu = true | |
| 786 | ||
| 787 | if nPage > 1 then | |
| 788 | sStatus = "Printed "..nPage.." Pages" | |
| 789 | else | |
| 790 | sStatus = "Printed 1 Page" | |
| 791 | end | |
| 792 | redrawMenu() | |
| 793 | end, | |
| 794 | Exit = function() | |
| 795 | bRunning = false | |
| 796 | end, | |
| 797 | Run = function() | |
| 798 | local sTempPath = "/.temp" | |
| 799 | local ok, err = save( sTempPath ) | |
| 800 | if ok then | |
| 801 | local nTask = shell.openTab( sTempPath ) | |
| 802 | if nTask then | |
| 803 | shell.switchTab( nTask ) | |
| 804 | else | |
| 805 | sStatus="Error starting Task" | |
| 806 | end | |
| 807 | fs.delete( sTempPath ) | |
| 808 | else | |
| 809 | sStatus="Error saving to "..sTempPath | |
| 810 | end | |
| 811 | redrawMenu() | |
| 812 | end, | |
| 813 | Tools = function() | |
| 814 | bMenu = false | |
| 815 | redrawText() | |
| 816 | redrawMenu() | |
| 817 | ||
| 818 | local bgColor = term.getBackgroundColor() | |
| 819 | paintutils.drawFilledBox(2, 2, w - 1, h - 1, colors.gray) | |
| 820 | term.setCursorPos(3, 3) | |
| 821 | term.write('ShEdit Tools')
| |
| 822 | ||
| 823 | local scrollY = 0 | |
| 824 | local height = h - 8 | |
| 825 | local offset = 5 | |
| 826 | local selected = 1 | |
| 827 | local run = false | |
| 828 | ||
| 829 | term.setTextColor(colors.lightGray) | |
| 830 | term.setCursorPos(3, offset + height + 1) | |
| 831 | term.write('Arrows to select. Enter to run. Ctrl to cancel.')
| |
| 832 | ||
| 833 | while true do | |
| 834 | term.setBackgroundColor(colors.gray) | |
| 835 | term.setTextColor(colors.lightGray) | |
| 836 | local toWrite = '(' .. tostring(selected) .. '/' .. tostring(#toolsFuncs) .. ')'
| |
| 837 | term.setCursorPos(w - toWrite:len() - 1, 3) | |
| 838 | term.write(toWrite) | |
| 839 | term.setTextColor(colors.white) | |
| 840 | ||
| 841 | for i = 1, height do | |
| 842 | local index = i + scrollY | |
| 843 | term.setTextColor(colors.white) | |
| 844 | ||
| 845 | if selected == index then | |
| 846 | term.setTextColor(keywordColor) | |
| 847 | end | |
| 848 | ||
| 849 | if index <= #toolsFuncs then | |
| 850 | paintutils.drawLine(2, offset + i - 1, w - 1, offset + i - 1, color) | |
| 851 | term.setCursorPos(3, offset + index - scrollY - 1) | |
| 852 | ||
| 853 | if selected == index then | |
| 854 | term.write('> ')
| |
| 855 | else | |
| 856 | term.write(' ')
| |
| 857 | end | |
| 858 | ||
| 859 | term.write(toolsFuncs[index][1]) | |
| 860 | end | |
| 861 | end | |
| 862 | ||
| 863 | local evt, arg1, arg2, arg3 = os.pullEvent() | |
| 864 | local clampScroll = false | |
| 865 | ||
| 866 | if evt == 'key' then | |
| 867 | clampScroll = true | |
| 868 | ||
| 869 | if arg1 == keys.up then | |
| 870 | if selected == 1 then | |
| 871 | selected = #toolsFuncs | |
| 872 | else | |
| 873 | selected = selected - 1 | |
| 874 | end | |
| 875 | elseif arg1 == keys.down then | |
| 876 | if selected == #toolsFuncs then | |
| 877 | selected = 1 | |
| 878 | else | |
| 879 | selected = selected + 1 | |
| 880 | end | |
| 881 | elseif arg1 == keys.pageUp then | |
| 882 | selected = math.max(1, math.min(#toolsFuncs, selected - height)) | |
| 883 | elseif arg1 == keys.pageDown then | |
| 884 | selected = math.max(1, math.min(#toolsFuncs, selected + height)) | |
| 885 | elseif arg1 == keys.enter then | |
| 886 | run = true | |
| 887 | ||
| 888 | break | |
| 889 | elseif arg1 == keys.leftCtrl then | |
| 890 | break | |
| 891 | else | |
| 892 | clampScroll = false | |
| 893 | end | |
| 894 | elseif evt == 'mouse_click' then | |
| 895 | if arg1 == 1 then | |
| 896 | if arg2 > 1 and arg2 < w then | |
| 897 | if arg3 >= offset and arg3 < offset + height then | |
| 898 | local index = arg3 - offset + scrollY + 1 | |
| 899 | ||
| 900 | if index <= #toolsFuncs then | |
| 901 | selected = index | |
| 902 | run = true | |
| 903 | ||
| 904 | break | |
| 905 | end | |
| 906 | end | |
| 907 | end | |
| 908 | end | |
| 909 | elseif evt == 'mouse_scroll' then | |
| 910 | if arg2 > 1 and arg2 < w then | |
| 911 | if arg3 >= offset and arg3 < offset + height then | |
| 912 | scrollY = math.max(0, math.min(#toolsFuncs - height, scrollY + arg1)) | |
| 913 | end | |
| 914 | end | |
| 915 | end | |
| 916 | ||
| 917 | if clampScroll == true then | |
| 918 | if selected > height + scrollY then | |
| 919 | scrollY = selected - height | |
| 920 | elseif selected <= scrollY then | |
| 921 | scrollY = selected - 1 | |
| 922 | end | |
| 923 | end | |
| 924 | end | |
| 925 | ||
| 926 | term.setBackgroundColor(bgColor) | |
| 927 | redrawText() | |
| 928 | bMenu = true | |
| 929 | ||
| 930 | if run then | |
| 931 | if not bReadOnly then | |
| 932 | bMenu = false | |
| 933 | redrawMenu() | |
| 934 | toolsFuncs[selected][2]() | |
| 935 | onUpdate() | |
| 936 | redrawText() | |
| 937 | bMenu = true | |
| 938 | else | |
| 939 | sStatus = 'File is read-only' | |
| 940 | end | |
| 941 | end | |
| 942 | end, | |
| 943 | Find = function() | |
| 944 | term.setCursorPos(1, h) | |
| 945 | term.clearLine() | |
| 946 | ||
| 947 | term.setTextColor(highlightColor) | |
| 948 | term.write('Find: ')
| |
| 949 | term.setTextColor(textColor) | |
| 950 | ||
| 951 | local text = read(nil, findHistory) | |
| 952 | table.insert(findHistory, text) | |
| 953 | ||
| 954 | local fLine, fPos | |
| 955 | local found = false | |
| 956 | ||
| 957 | for i = y, #tLines do | |
| 958 | local searchText = tLines[i] | |
| 959 | ||
| 960 | if i == y then | |
| 961 | searchText = searchText:sub(x + 1) | |
| 962 | end | |
| 963 | ||
| 964 | local searchPat = '' | |
| 965 | ||
| 966 | local escapedChars = {
| |
| 967 | ['%'] = true, ['.'] = true, ['('] = true, [')'] = true,
| |
| 968 | ['%'] = true, ['+'] = true, ['-'] = true, ['*'] = true, | |
| 969 | ['?'] = true, ['['] = true, ['^'] = true, ['$'] = true | |
| 970 | } | |
| 971 | ||
| 972 | for i = 1, #text do | |
| 973 | local char = text:sub(i, i) | |
| 974 | ||
| 975 | if escapedChars[char] == true then | |
| 976 | searchPat = searchPat .. '%' | |
| 977 | end | |
| 978 | ||
| 979 | searchPat = searchPat .. char | |
| 980 | end | |
| 981 | ||
| 982 | local pos = searchText:find(searchPat) | |
| 983 | ||
| 984 | if pos then | |
| 985 | fLine = i | |
| 986 | fPos = pos | |
| 987 | ||
| 988 | if i == y then | |
| 989 | fPos = fPos + x | |
| 990 | end | |
| 991 | ||
| 992 | found = true | |
| 993 | ||
| 994 | break | |
| 995 | end | |
| 996 | end | |
| 997 | ||
| 998 | if not found then | |
| 999 | sStatus = 'Found no matches' | |
| 1000 | else | |
| 1001 | setCursorMark(fPos, fLine) | |
| 1002 | setCursor(fPos + text:len(), fLine) | |
| 1003 | sStatus = 'Found a match on line ' .. tostring(fLine) | |
| 1004 | end | |
| 1005 | ||
| 1006 | redrawText() | |
| 1007 | end, | |
| 1008 | Jump = function() | |
| 1009 | term.setCursorPos(1, h) | |
| 1010 | term.clearLine() | |
| 1011 | ||
| 1012 | term.setTextColor(highlightColor) | |
| 1013 | term.write('Jump: ')
| |
| 1014 | term.setTextColor(textColor) | |
| 1015 | ||
| 1016 | local toJump = read(nil, jumpHistory) | |
| 1017 | table.insert(jumpHistory, toJump) | |
| 1018 | ||
| 1019 | local num = tonumber(toJump) | |
| 1020 | ||
| 1021 | if not num then | |
| 1022 | sStatus = 'Invalid line' | |
| 1023 | else | |
| 1024 | if num % 1 == 0 then | |
| 1025 | if num >= 1 and num <= #tLines then | |
| 1026 | setCursorMark(1, num) | |
| 1027 | sStatus = 'Successfully jumped to line ' .. toJump | |
| 1028 | else | |
| 1029 | sStatus = 'Line is not in the range 1 - ' .. tostring(#tLines) | |
| 1030 | end | |
| 1031 | else | |
| 1032 | sStatus = 'Line must be an integer' | |
| 1033 | end | |
| 1034 | end | |
| 1035 | ||
| 1036 | redrawText() | |
| 1037 | end, | |
| 1038 | Copy = function() | |
| 1039 | vClipboard = getMarked() | |
| 1040 | ||
| 1041 | local lines = 1 | |
| 1042 | ||
| 1043 | for i = 1, #vClipboard do | |
| 1044 | if vClipboard:sub(i, i) == '\n' then | |
| 1045 | lines = lines + 1 | |
| 1046 | end | |
| 1047 | end | |
| 1048 | ||
| 1049 | sStatus = 'Copied ' .. tostring(lines) .. ' lines' | |
| 1050 | end, | |
| 1051 | VPaste = function() | |
| 1052 | if not bReadOnly then | |
| 1053 | deleteMarked() | |
| 1054 | local lines = 1 | |
| 1055 | ||
| 1056 | for i = 1, #vClipboard do | |
| 1057 | local char = vClipboard:sub(i, i) | |
| 1058 | ||
| 1059 | if char == '\n' then | |
| 1060 | lines = lines + 1 | |
| 1061 | local sLine = tLines[y] | |
| 1062 | tLines[y] = string.sub(sLine,1,x-1) | |
| 1063 | table.insert( tLines, y+1, string.sub(sLine,x) ) | |
| 1064 | x = 1 | |
| 1065 | y = y + 1 | |
| 1066 | else | |
| 1067 | tLines[y] = tLines[y]:sub(1, x - 1) .. char .. tLines[y]:sub(x) | |
| 1068 | x = x + 1 | |
| 1069 | end | |
| 1070 | end | |
| 1071 | ||
| 1072 | setCursorMark(x, y) | |
| 1073 | onUpdate() | |
| 1074 | redrawText() | |
| 1075 | ||
| 1076 | sStatus = 'Pasted ' .. tostring(lines) .. ' lines' | |
| 1077 | else | |
| 1078 | sStatus = 'File is read-only' | |
| 1079 | end | |
| 1080 | end | |
| 1081 | } | |
| 1082 | ||
| 1083 | local function doMenuItem( _n ) | |
| 1084 | tMenuFuncs[tMenuItems[_n]]() | |
| 1085 | if bMenu then | |
| 1086 | bMenu = false | |
| 1087 | term.setCursorBlink( true ) | |
| 1088 | end | |
| 1089 | redrawMenu() | |
| 1090 | end | |
| 1091 | ||
| 1092 | -- Actual program functionality begins | |
| 1093 | load(sPath) | |
| 1094 | ||
| 1095 | term.setBackgroundColor( bgColor ) | |
| 1096 | term.clear() | |
| 1097 | term.setCursorPos(getCursorX(y), y) | |
| 1098 | term.setCursorBlink( true ) | |
| 1099 | ||
| 1100 | recomplete() | |
| 1101 | onUpdate() | |
| 1102 | redrawText() | |
| 1103 | redrawMenu() | |
| 1104 | ||
| 1105 | local function acceptCompletion() | |
| 1106 | if nCompletion then | |
| 1107 | -- Append the completion | |
| 1108 | local sCompletion = tCompletions[ nCompletion ] | |
| 1109 | tLines[y] = tLines[y] .. sCompletion | |
| 1110 | onUpdate() | |
| 1111 | setCursorMark( x + string.len( sCompletion ), y ) | |
| 1112 | end | |
| 1113 | end | |
| 1114 | ||
| 1115 | local holdingShift = false | |
| 1116 | ||
| 1117 | -- Handle input | |
| 1118 | while bRunning do | |
| 1119 | local sEvent, param, param2, param3 = os.pullEvent() | |
| 1120 | if sEvent == "key" then | |
| 1121 | local oldX, oldY = x, y | |
| 1122 | if param == keys.up then | |
| 1123 | local cursorSet = setCursorMark | |
| 1124 | ||
| 1125 | if holdingShift then | |
| 1126 | cursorSet = setCursor | |
| 1127 | end | |
| 1128 | ||
| 1129 | -- Up | |
| 1130 | if not bMenu then | |
| 1131 | if nCompletion then | |
| 1132 | -- Cycle completions | |
| 1133 | nCompletion = nCompletion - 1 | |
| 1134 | if nCompletion < 1 then | |
| 1135 | nCompletion = #tCompletions | |
| 1136 | end | |
| 1137 | redrawLine(y) | |
| 1138 | ||
| 1139 | elseif y > 1 then | |
| 1140 | -- Move cursor up | |
| 1141 | --[[cursorSet( | |
| 1142 | math.min( x, string.len( tLines[y - 1] ) + 1 ), | |
| 1143 | y - 1 | |
| 1144 | )]] | |
| 1145 | ||
| 1146 | local cx = getCursorX(y) | |
| 1147 | local pos = cxToPos(y - 1, cx) | |
| 1148 | ||
| 1149 | cursorSet(pos, y - 1) | |
| 1150 | else | |
| 1151 | cursorSet(1, y) | |
| 1152 | end | |
| 1153 | end | |
| 1154 | ||
| 1155 | elseif param == keys.down then | |
| 1156 | local cursorSet = setCursorMark | |
| 1157 | ||
| 1158 | if holdingShift then | |
| 1159 | cursorSet = setCursor | |
| 1160 | end | |
| 1161 | ||
| 1162 | -- Down | |
| 1163 | if not bMenu then | |
| 1164 | -- Move cursor down | |
| 1165 | if nCompletion then | |
| 1166 | -- Cycle completions | |
| 1167 | nCompletion = nCompletion + 1 | |
| 1168 | if nCompletion > #tCompletions then | |
| 1169 | nCompletion = 1 | |
| 1170 | end | |
| 1171 | redrawLine(y) | |
| 1172 | ||
| 1173 | elseif y < #tLines then | |
| 1174 | -- Move cursor down | |
| 1175 | --[[cursorSet( | |
| 1176 | math.min( x, string.len( tLines[y + 1] ) + 1 ), | |
| 1177 | y + 1 | |
| 1178 | )]] | |
| 1179 | ||
| 1180 | local cx = getCursorX(y) | |
| 1181 | local pos = cxToPos(y + 1, cx) | |
| 1182 | ||
| 1183 | cursorSet(pos, y + 1) | |
| 1184 | else | |
| 1185 | cursorSet(#tLines[y] + 1, y) | |
| 1186 | end | |
| 1187 | end | |
| 1188 | ||
| 1189 | elseif param == keys.tab then | |
| 1190 | -- Tab | |
| 1191 | if not bMenu and not bReadOnly then | |
| 1192 | if nCompletion and x == string.len(tLines[y]) + 1 then | |
| 1193 | -- Accept autocomplete | |
| 1194 | acceptCompletion() | |
| 1195 | else | |
| 1196 | local msx, msy, mex, mey = getMarks() | |
| 1197 | ||
| 1198 | for i = msy, mey do | |
| 1199 | local line = tLines[i] | |
| 1200 | ||
| 1201 | if holdingShift then | |
| 1202 | -- Unindent line | |
| 1203 | if line:sub(1, 1) == '\t' then | |
| 1204 | line = line:sub(2) | |
| 1205 | end | |
| 1206 | else | |
| 1207 | -- Indent line | |
| 1208 | if markExists() then | |
| 1209 | line = '\t' .. line | |
| 1210 | else | |
| 1211 | line = line:sub(1, x - 1) .. '\t' .. line:sub(x) | |
| 1212 | end | |
| 1213 | end | |
| 1214 | ||
| 1215 | tLines[i] = line | |
| 1216 | end | |
| 1217 | ||
| 1218 | if holdingShift then | |
| 1219 | markX = math.max(1, markX - 1) | |
| 1220 | setCursor(math.max(1, x - 1), y) | |
| 1221 | else | |
| 1222 | markX = markX + 1 | |
| 1223 | setCursor(x + 1, y) | |
| 1224 | end | |
| 1225 | ||
| 1226 | onUpdate() | |
| 1227 | redrawText() | |
| 1228 | end | |
| 1229 | end | |
| 1230 | ||
| 1231 | elseif param == keys.pageUp then | |
| 1232 | local cursorSet = setCursorMark | |
| 1233 | ||
| 1234 | if holdingShift then | |
| 1235 | cursorSet = setCursor | |
| 1236 | end | |
| 1237 | ||
| 1238 | -- Page Up | |
| 1239 | if not bMenu then | |
| 1240 | -- Move up a page | |
| 1241 | local newY | |
| 1242 | if y - (h - 1) >= 1 then | |
| 1243 | newY = y - (h - 1) | |
| 1244 | else | |
| 1245 | newY = 1 | |
| 1246 | end | |
| 1247 | cursorSet( | |
| 1248 | math.min( x, string.len( tLines[newY] ) + 1 ), | |
| 1249 | newY | |
| 1250 | ) | |
| 1251 | end | |
| 1252 | ||
| 1253 | elseif param == keys.pageDown then | |
| 1254 | local cursorSet = setCursorMark | |
| 1255 | ||
| 1256 | if holdingShift then | |
| 1257 | cursorSet = setCursor | |
| 1258 | end | |
| 1259 | ||
| 1260 | -- Page Down | |
| 1261 | if not bMenu then | |
| 1262 | -- Move down a page | |
| 1263 | local newY | |
| 1264 | if y + (h - 1) <= #tLines then | |
| 1265 | newY = y + (h - 1) | |
| 1266 | else | |
| 1267 | newY = #tLines | |
| 1268 | end | |
| 1269 | local newX = math.min( x, string.len( tLines[newY] ) + 1 ) | |
| 1270 | cursorSet( newX, newY ) | |
| 1271 | end | |
| 1272 | ||
| 1273 | elseif param == keys.home then | |
| 1274 | local cursorSet = setCursorMark | |
| 1275 | ||
| 1276 | if holdingShift then | |
| 1277 | cursorSet = setCursor | |
| 1278 | end | |
| 1279 | ||
| 1280 | -- Home | |
| 1281 | if not bMenu then | |
| 1282 | -- Move cursor to the beginning | |
| 1283 | if x > 1 then | |
| 1284 | cursorSet(1,y) | |
| 1285 | end | |
| 1286 | end | |
| 1287 | ||
| 1288 | elseif param == keys["end"] then | |
| 1289 | local cursorSet = setCursorMark | |
| 1290 | ||
| 1291 | if holdingShift then | |
| 1292 | cursorSet = setCursor | |
| 1293 | end | |
| 1294 | ||
| 1295 | -- End | |
| 1296 | if not bMenu then | |
| 1297 | -- Move cursor to the end | |
| 1298 | local nLimit = string.len( tLines[y] ) + 1 | |
| 1299 | if x < nLimit then | |
| 1300 | cursorSet( nLimit, y ) | |
| 1301 | end | |
| 1302 | end | |
| 1303 | ||
| 1304 | elseif param == keys.left then | |
| 1305 | local cursorSet = setCursorMark | |
| 1306 | ||
| 1307 | if holdingShift then | |
| 1308 | cursorSet = setCursor | |
| 1309 | end | |
| 1310 | ||
| 1311 | -- Left | |
| 1312 | if not bMenu then | |
| 1313 | if x > 1 then | |
| 1314 | -- Move cursor left | |
| 1315 | cursorSet( x - 1, y ) | |
| 1316 | elseif x==1 and y>1 then | |
| 1317 | cursorSet( string.len( tLines[y-1] ) + 1, y - 1 ) | |
| 1318 | end | |
| 1319 | else | |
| 1320 | -- Move menu left | |
| 1321 | nMenuItem = nMenuItem - 1 | |
| 1322 | if nMenuItem < 1 then | |
| 1323 | nMenuItem = #tMenuItems | |
| 1324 | end | |
| 1325 | redrawMenu() | |
| 1326 | end | |
| 1327 | ||
| 1328 | elseif param == keys.right then | |
| 1329 | local cursorSet = setCursorMark | |
| 1330 | ||
| 1331 | if holdingShift then | |
| 1332 | cursorSet = setCursor | |
| 1333 | end | |
| 1334 | ||
| 1335 | -- Right | |
| 1336 | if not bMenu then | |
| 1337 | local nLimit = string.len( tLines[y] ) + 1 | |
| 1338 | if x < nLimit then | |
| 1339 | -- Move cursor right | |
| 1340 | cursorSet( x + 1, y ) | |
| 1341 | elseif nCompletion and x == string.len(tLines[y]) + 1 then | |
| 1342 | -- Accept autocomplete | |
| 1343 | acceptCompletion() | |
| 1344 | elseif x==nLimit and y<#tLines then | |
| 1345 | -- Go to next line | |
| 1346 | cursorSet( 1, y + 1 ) | |
| 1347 | end | |
| 1348 | else | |
| 1349 | -- Move menu right | |
| 1350 | nMenuItem = nMenuItem + 1 | |
| 1351 | if nMenuItem > #tMenuItems then | |
| 1352 | nMenuItem = 1 | |
| 1353 | end | |
| 1354 | redrawMenu() | |
| 1355 | end | |
| 1356 | ||
| 1357 | elseif param == keys.delete then | |
| 1358 | -- Delete | |
| 1359 | if not bMenu and not bReadOnly then | |
| 1360 | if markExists() then | |
| 1361 | deleteMarked() | |
| 1362 | else | |
| 1363 | local nLimit = string.len( tLines[y] ) + 1 | |
| 1364 | if x < nLimit then | |
| 1365 | local sLine = tLines[y] | |
| 1366 | tLines[y] = string.sub(sLine,1,x-1) .. string.sub(sLine,x+1) | |
| 1367 | elseif y<#tLines then | |
| 1368 | tLines[y] = tLines[y] .. tLines[y+1] | |
| 1369 | table.remove( tLines, y+1 ) | |
| 1370 | end | |
| 1371 | end | |
| 1372 | ||
| 1373 | onUpdate() | |
| 1374 | recomplete() | |
| 1375 | redrawText() | |
| 1376 | end | |
| 1377 | ||
| 1378 | elseif param == keys.backspace then | |
| 1379 | -- Backspace | |
| 1380 | if not bMenu and not bReadOnly then | |
| 1381 | if markExists() then | |
| 1382 | deleteMarked() | |
| 1383 | else | |
| 1384 | if x > 1 then | |
| 1385 | -- Remove character | |
| 1386 | local sLine = tLines[y] | |
| 1387 | tLines[y] = string.sub(sLine,1,x-2) .. string.sub(sLine,x) | |
| 1388 | setCursorMark( x - 1, y ) | |
| 1389 | elseif y > 1 then | |
| 1390 | -- Remove newline | |
| 1391 | local sPrevLen = string.len( tLines[y-1] ) | |
| 1392 | tLines[y-1] = tLines[y-1] .. tLines[y] | |
| 1393 | table.remove( tLines, y ) | |
| 1394 | setCursorMark( sPrevLen + 1, y - 1 ) | |
| 1395 | end | |
| 1396 | end | |
| 1397 | ||
| 1398 | onUpdate() | |
| 1399 | recomplete() | |
| 1400 | redrawText() | |
| 1401 | end | |
| 1402 | ||
| 1403 | elseif param == keys.enter then | |
| 1404 | -- Enter | |
| 1405 | if not bMenu and not bReadOnly then | |
| 1406 | deleteMarked() | |
| 1407 | ||
| 1408 | -- Newline | |
| 1409 | local sLine = tLines[y] | |
| 1410 | local _,tabs=string.find(sLine,"^[\t]+") | |
| 1411 | if not tabs then | |
| 1412 | tabs=0 | |
| 1413 | end | |
| 1414 | tLines[y] = string.sub(sLine,1,x-1) | |
| 1415 | table.insert( tLines, y+1, string.rep('\t',tabs)..string.sub(sLine,x) )
| |
| 1416 | onUpdate() | |
| 1417 | setCursorMark( tabs + 1, y + 1 ) | |
| 1418 | redrawText() | |
| 1419 | ||
| 1420 | elseif bMenu then | |
| 1421 | -- Menu selection | |
| 1422 | doMenuItem( nMenuItem ) | |
| 1423 | ||
| 1424 | end | |
| 1425 | ||
| 1426 | elseif (param == keys.leftCtrl or param == keys.rightCtrl) and not param2 then | |
| 1427 | -- Menu toggle | |
| 1428 | bMenu = not bMenu | |
| 1429 | if bMenu then | |
| 1430 | term.setCursorBlink( false ) | |
| 1431 | else | |
| 1432 | term.setCursorBlink( true ) | |
| 1433 | end | |
| 1434 | redrawMenu() | |
| 1435 | ||
| 1436 | elseif (param == keys.rightAlt) then | |
| 1437 | if bMenu then | |
| 1438 | bMenu = false | |
| 1439 | term.setCursorBlink( true ) | |
| 1440 | redrawMenu() | |
| 1441 | end | |
| 1442 | ||
| 1443 | elseif param == 42 then | |
| 1444 | holdingShift = true | |
| 1445 | end | |
| 1446 | ||
| 1447 | elseif sEvent == 'key_up' then | |
| 1448 | if param == 42 then | |
| 1449 | holdingShift = false | |
| 1450 | end | |
| 1451 | ||
| 1452 | elseif sEvent == "char" then | |
| 1453 | if not bMenu and not bReadOnly then | |
| 1454 | deleteMarked() | |
| 1455 | ||
| 1456 | -- Input text | |
| 1457 | local sLine = tLines[y] | |
| 1458 | tLines[y] = string.sub(sLine,1,x-1) .. param .. string.sub(sLine,x) | |
| 1459 | onUpdate() | |
| 1460 | setCursorMark( x + 1, y ) | |
| 1461 | ||
| 1462 | elseif bMenu then | |
| 1463 | -- Select menu items | |
| 1464 | for n,sMenuItem in ipairs( tMenuItems ) do | |
| 1465 | if string.lower(string.sub(sMenuItem,1,1)) == string.lower(param) then | |
| 1466 | doMenuItem( n ) | |
| 1467 | break | |
| 1468 | end | |
| 1469 | end | |
| 1470 | end | |
| 1471 | ||
| 1472 | elseif sEvent == "paste" then | |
| 1473 | if not bMenu and not bReadOnly then | |
| 1474 | -- Input text | |
| 1475 | --[[local sLine = tLines[y] | |
| 1476 | tLines[y] = string.sub(sLine,1,x-1) .. param .. string.sub(sLine,x) | |
| 1477 | onUpdate() | |
| 1478 | setCursorMark( x + string.len( param ), y )]] | |
| 1479 | ||
| 1480 | --[[deleteMarked() | |
| 1481 | ||
| 1482 | local newX, newY = x, y | |
| 1483 | ||
| 1484 | for i = 1, #param do | |
| 1485 | local char = param:sub(i, i) | |
| 1486 | ||
| 1487 | if char == '\n' then | |
| 1488 | local rest = tLines[newY]:sub(newX) | |
| 1489 | tLines[newY] = tLines[newY]:sub(1, newX - 1) | |
| 1490 | ||
| 1491 | table.insert(tLines, rest, newY + 1) | |
| 1492 | ||
| 1493 | newY = newY + 1 | |
| 1494 | newX = 1 | |
| 1495 | else | |
| 1496 | local line = tLines[newY] | |
| 1497 | ||
| 1498 | line = line:sub(1, newX - 1) .. char .. line:sub(newX) | |
| 1499 | ||
| 1500 | tLines[newY] = line | |
| 1501 | newX = newX + 1 | |
| 1502 | end | |
| 1503 | end | |
| 1504 | ||
| 1505 | onUpdate() | |
| 1506 | setCursorMark(newX, newY)]] | |
| 1507 | ||
| 1508 | local oldvClipboard = vClipboard | |
| 1509 | vClipboard = param | |
| 1510 | tMenuFuncs.VPaste() | |
| 1511 | redrawMenu() | |
| 1512 | vClipboard = oldvClipboard | |
| 1513 | end | |
| 1514 | ||
| 1515 | elseif sEvent == "mouse_click" then | |
| 1516 | local cursorSet = setCursorMark | |
| 1517 | ||
| 1518 | if holdingShift then | |
| 1519 | cursorSet = setCursor | |
| 1520 | end | |
| 1521 | ||
| 1522 | if not bMenu then | |
| 1523 | if param == 1 then | |
| 1524 | -- Left click | |
| 1525 | local cx,cy = param2, param3 | |
| 1526 | if cy < h then | |
| 1527 | local tx, ty = cx + scrollX, cy + scrollY | |
| 1528 | local newX, newY | |
| 1529 | ||
| 1530 | if ty <= #tLines then | |
| 1531 | newY = math.min(#tLines, math.max(ty, 1)) | |
| 1532 | tx = cxToPos(ty, tx) | |
| 1533 | newX = math.min(tLines[newY]:len() + 1, math.max(1, tx)) | |
| 1534 | else | |
| 1535 | newY = #tLines | |
| 1536 | newX = #tLines[newY] + 1 | |
| 1537 | end | |
| 1538 | ||
| 1539 | cursorSet(newX, newY) | |
| 1540 | end | |
| 1541 | end | |
| 1542 | end | |
| 1543 | ||
| 1544 | elseif sEvent == "mouse_drag" then | |
| 1545 | if not bMenu then | |
| 1546 | if param == 1 then | |
| 1547 | -- Left click | |
| 1548 | local cx,cy = param2, param3 | |
| 1549 | if cy < h then | |
| 1550 | local tx, ty = cx + scrollX, cy + scrollY | |
| 1551 | local newX, newY | |
| 1552 | ||
| 1553 | if ty <= #tLines then | |
| 1554 | newY = math.min(#tLines, math.max(ty, 1)) | |
| 1555 | tx = cxToPos(ty, tx) | |
| 1556 | newX = math.min(tLines[newY]:len() + 1, math.max(1, tx)) | |
| 1557 | else | |
| 1558 | newY = #tLines | |
| 1559 | newX = #tLines[newY] + 1 | |
| 1560 | end | |
| 1561 | ||
| 1562 | setCursor(newX, newY) | |
| 1563 | end | |
| 1564 | end | |
| 1565 | end | |
| 1566 | ||
| 1567 | elseif sEvent == "mouse_scroll" then | |
| 1568 | if not bMenu then | |
| 1569 | if param == -1 then | |
| 1570 | -- Scroll up | |
| 1571 | if scrollY > 0 then | |
| 1572 | -- Move cursor up | |
| 1573 | scrollY = scrollY - 1 | |
| 1574 | redrawText() | |
| 1575 | end | |
| 1576 | ||
| 1577 | elseif param == 1 then | |
| 1578 | -- Scroll down | |
| 1579 | local nMaxScroll = #tLines - (h-1) | |
| 1580 | if scrollY < nMaxScroll then | |
| 1581 | -- Move cursor down | |
| 1582 | scrollY = scrollY + 1 | |
| 1583 | redrawText() | |
| 1584 | end | |
| 1585 | ||
| 1586 | end | |
| 1587 | end | |
| 1588 | ||
| 1589 | elseif sEvent == "term_resize" then | |
| 1590 | w,h = term.getSize() | |
| 1591 | setCursor( x, y ) | |
| 1592 | redrawMenu() | |
| 1593 | redrawText() | |
| 1594 | ||
| 1595 | end | |
| 1596 | end | |
| 1597 | ||
| 1598 | -- Cleanup | |
| 1599 | term.clear() | |
| 1600 | term.setCursorBlink( false ) | |
| 1601 | term.setCursorPos( 1, 1 ) |