mirror of
https://codeberg.org/TicklishHoneyBee/CLIte.git
synced 2026-03-11 09:04:37 +00:00
409 lines
8 KiB
JavaScript
409 lines
8 KiB
JavaScript
clite.commands.data = function() {
|
|
|
|
clite.commands.load('vi',function(args,env,io) {
|
|
var stdio = io.include('stdio');
|
|
var clite = io.include('clite');
|
|
var curses = io.include('curses'); // definitely not dark magic
|
|
var term = io.include('term');
|
|
|
|
var cmd_buff = '';
|
|
var file = null;
|
|
var mode = 0; // 0 view mode, 1 command mode, 2 edit mode
|
|
var cursor_line = 0;
|
|
var cursor_col = 0;
|
|
var changed = false;
|
|
var num = false;
|
|
var message = '';
|
|
|
|
function help() {
|
|
stdio.printf(`
|
|
vi - Visual text file editor
|
|
Usage: vi [OPTION] <FILE>
|
|
|
|
Options:
|
|
-? Print this help information
|
|
`);
|
|
}
|
|
|
|
for (var i=1; i<args.length; i++) {
|
|
if (args[i][0] == '-') {
|
|
for (var j=1; j<args[i].length; j++) {
|
|
switch (args[i][j]) {
|
|
case '?':
|
|
help();
|
|
return 0;
|
|
break;
|
|
default:
|
|
stdio.fprintf(io.stderr,'unknown argument: -%c',args[i][j]);
|
|
}
|
|
}
|
|
}else if (file == null) {
|
|
file = clite.resolvePath(args[i],false);
|
|
}else{
|
|
stdio.fprintf(io.stderr,'unknown argument: %s',args[i]);
|
|
}
|
|
}
|
|
|
|
if (!stdio.isatty(io.stdin))
|
|
return 1;
|
|
|
|
var cols;
|
|
var rows;
|
|
var lines = [];
|
|
var topLine = 0;
|
|
var bottomLine = 0;
|
|
|
|
function calculateBottomLine() {
|
|
bottomLine = lines.length;
|
|
var r = rows;
|
|
var i = lines.length-1;
|
|
|
|
for (; i>-1; i--) {
|
|
r -= lines[i][1];
|
|
if (r<0)
|
|
i++;
|
|
if (r <= 0)
|
|
break;
|
|
}
|
|
|
|
bottomLine = i;
|
|
}
|
|
|
|
function showAt(line) {
|
|
if (line > lines.length)
|
|
line = lines.length;
|
|
if (line < 0)
|
|
line = 0;
|
|
var start = line;
|
|
var end = line+rows;
|
|
if (end > lines.length)
|
|
end = lines.length;
|
|
curses.clear();
|
|
if (num) {
|
|
for (var i=start; i<end; i++) {
|
|
if (i == cursor_line) {
|
|
var cursor = '█';
|
|
if (cursor_col == 0) {
|
|
curses.printw('% 4d%c%s\n',i+1,cursor,lines[i][0]);
|
|
}else{
|
|
if (cursor_col > lines[i][0].length)
|
|
cursor_col = lines[i][0].length;
|
|
var l = lines[i][0];
|
|
var b = l.substring(0,cursor_col);
|
|
var e = l.substring(cursor_col);
|
|
curses.printw('% 4d %s%c%s\n',i+1,b,cursor,e);
|
|
}
|
|
}else{
|
|
curses.printw('% 4d %s\n',i+1,lines[i][0]);
|
|
}
|
|
}
|
|
}else{
|
|
for (var i=start; i<end; i++) {
|
|
if (i == cursor_line) {
|
|
var cursor = '█';
|
|
if (cursor_col == 0) {
|
|
curses.printw('%c%s\n',cursor,lines[i][0]);
|
|
}else{
|
|
if (cursor_col > lines[i][0].length)
|
|
cursor_col = lines[i][0].length;
|
|
var l = lines[i][0];
|
|
var b = l.substring(0,cursor_col);
|
|
var e = l.substring(cursor_col);
|
|
curses.printw(' %s%c%s\n',b,cursor,e);
|
|
}
|
|
}else{
|
|
curses.printw(' %s\n',lines[i][0]);
|
|
}
|
|
}
|
|
}
|
|
curses.printw('%s\t\t\t\t\t\t%d,%d\n',message,cursor_line,cursor_col);
|
|
}
|
|
|
|
function prepFile(fd) {
|
|
if (!fd) {
|
|
stdio.write(io.stderr,'could not open file: '+file);
|
|
io.exit(1);
|
|
return;
|
|
}
|
|
var st = stdio.fstat(fd);
|
|
if ( !st || (st.type != stdio.types.FT_TEXT && st.type != stdio.types.FT_SCRIPT)) {
|
|
stdio.write(io.stderr,'can not read files of this type');
|
|
io.exit(1);
|
|
return;
|
|
}
|
|
var txt = stdio.readAll(fd);
|
|
stdio.close(fd);
|
|
|
|
var parts = txt.split('\n');
|
|
parts.forEach(function(line) {
|
|
if (line.length > cols) {
|
|
var count = Math.ceil(line.length/cols);
|
|
lines.push([line,count]);
|
|
return;
|
|
}
|
|
lines.push([line,1]);
|
|
});
|
|
|
|
if (lines.length < 1)
|
|
lines.push(['',0]);
|
|
|
|
cursor_line = lines.length-1;
|
|
|
|
calculateBottomLine();
|
|
topLine = bottomLine;
|
|
|
|
showAt(topLine);
|
|
}
|
|
|
|
function writeFile(force) {
|
|
var fd = stdio.open(file,stdio.flags.O_WRONLY|stdio.flags.O_TRUNC,stdio.flags.O_SYNC);
|
|
if (!fd)
|
|
return;
|
|
var data = '';
|
|
lines.forEach(function(l) {
|
|
data += l[0]+'\n';
|
|
});
|
|
stdio.write(fd,data);
|
|
stdio.close(fd);
|
|
}
|
|
|
|
function doCommand(str) {
|
|
// TODO: support search
|
|
if (str[0] != ':')
|
|
return true;
|
|
var quit = false;
|
|
var write = false;
|
|
var force = false;
|
|
|
|
for (var i=1; i<str.length; i++) {
|
|
switch (str[i]) {
|
|
case 'w':
|
|
write = true;
|
|
break;
|
|
case 'q':
|
|
quit = true;
|
|
break;
|
|
case '!':
|
|
force = true;
|
|
break;
|
|
case 'n':
|
|
num = !num;
|
|
showAt(topLine);
|
|
default:;
|
|
}
|
|
}
|
|
if (write) {
|
|
writeFile(force);
|
|
changed = false;
|
|
}
|
|
|
|
if (quit) {
|
|
if (!changed || force) {
|
|
curses.endwin();
|
|
io.exit(0);
|
|
return false;
|
|
}else{
|
|
message = 'no write since last change (add ! to override)';
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function cursorUp() {
|
|
if (cursor_line > 0) {
|
|
cursor_line--;
|
|
if (cursor_line <= topLine && topLine > 0)
|
|
topLine--;
|
|
showAt(topLine);
|
|
}
|
|
}
|
|
function cursorDn() {
|
|
if (cursor_line < lines.length-1) {
|
|
cursor_line++;
|
|
calculateBottomLine();
|
|
if (topLine < bottomLine) {
|
|
topLine++;
|
|
}
|
|
showAt(topLine);
|
|
}
|
|
}
|
|
function cursorLt() {
|
|
if (cursor_col > 0)
|
|
cursor_col--;
|
|
showAt(topLine);
|
|
}
|
|
function cursorRt() {
|
|
if (cursor_col < lines[cursor_line][0].length)
|
|
cursor_col++;
|
|
showAt(topLine);
|
|
}
|
|
|
|
function setLine(str) {
|
|
lines[cursor_line][0] = str;
|
|
var count = Math.ceil(lines[cursor_line][0].length/cols);
|
|
lines[cursor_line][1] = count;
|
|
calculateBottomLine();
|
|
changed = true;
|
|
}
|
|
function insertLine(str) {
|
|
var l = [str,str.length];
|
|
lines.splice(cursor_line+1,0,l);
|
|
cursor_col = 0;
|
|
cursorDn();
|
|
changed = true;
|
|
}
|
|
|
|
if (file == null) {
|
|
stdio.write(io.stderr,'no file specified');
|
|
return 1;
|
|
}
|
|
|
|
var fd = stdio.open(file,stdio.flags.O_RDONLY|stdio.flags.O_CREAT,prepFile);
|
|
if (!fd) {
|
|
stdio.write(io.stderr,'could not open file: '+file);
|
|
return 1;
|
|
}
|
|
|
|
curses.initscr();
|
|
cols = curses.getmaxx();
|
|
rows = curses.getmaxy()-2;
|
|
curses.cbreak();
|
|
curses.noecho();
|
|
curses.printw('Loading...');
|
|
|
|
function input(key) {
|
|
if (mode == 0) { // view mode
|
|
//message = '';
|
|
switch (key) {
|
|
case 'Up':
|
|
case 'ArrowUp':
|
|
cursorUp();
|
|
break;
|
|
case 'Down':
|
|
case 'ArrowDown':
|
|
cursorDn();
|
|
break;
|
|
case 'Left':
|
|
case 'ArrowLeft':
|
|
cursorLt();
|
|
break;
|
|
case 'Right':
|
|
case 'ArrowRight':
|
|
cursorRt();
|
|
break;
|
|
case 'i':
|
|
curses.noecho();
|
|
mode = 2;
|
|
message = 'INSERT';
|
|
showAt(topLine);
|
|
break;
|
|
case 'a':
|
|
cursor_col = lines[cursor_line][0].length;
|
|
curses.noecho();
|
|
mode = 2;
|
|
message = 'INSERT';
|
|
showAt(topLine);
|
|
break;
|
|
case ':':
|
|
curses.echo();
|
|
term.ttyctrl('iset',':');
|
|
cmd_buff = ':';
|
|
mode = 1;
|
|
showAt(topLine);
|
|
break;
|
|
}
|
|
}else if (mode == 1) { // command mode
|
|
//message = '';
|
|
switch (key) {
|
|
case 'Escape':
|
|
mode = 0;
|
|
cmd_buff = '';
|
|
curses.noecho();
|
|
showAt(topLine);
|
|
break;
|
|
case 'Enter':
|
|
if (!doCommand(cmd_buff))
|
|
return;
|
|
showAt(topLine);
|
|
cmd_buff = '';
|
|
term.ttyctrl('iset','');
|
|
break;
|
|
default:
|
|
if (key.length == 1)
|
|
cmd_buff+=key;
|
|
break;
|
|
}
|
|
}else{ // 2 edit mode
|
|
message = 'INSERT';
|
|
switch (key) {
|
|
case 'Escape':
|
|
mode = 0;
|
|
curses.noecho();
|
|
message = '';
|
|
showAt(topLine);
|
|
break;
|
|
case 'Up':
|
|
case 'ArrowUp':
|
|
cursorUp();
|
|
break;
|
|
case 'Down':
|
|
case 'ArrowDown':
|
|
cursorDn();
|
|
break;
|
|
case 'Left':
|
|
case 'ArrowLeft':
|
|
cursorLt();
|
|
break;
|
|
case 'Right':
|
|
case 'ArrowRight':
|
|
cursorRt();
|
|
break;
|
|
case 'Backspace':
|
|
if (cursor_col > 0) {
|
|
var l = lines[cursor_line][0];
|
|
var b = l.substring(0,cursor_col-1);
|
|
var e = l.substring(cursor_col);
|
|
setLine(b+e);
|
|
cursor_col--;
|
|
showAt(topLine);
|
|
}
|
|
break;
|
|
case 'Enter':
|
|
var l = lines[cursor_line][0];
|
|
var b = l.substring(0,cursor_col);
|
|
var e = l.substring(cursor_col);
|
|
setLine(b);
|
|
insertLine(e);
|
|
cursor_col = e.length;
|
|
showAt(topLine);
|
|
break;
|
|
case 'Delete':
|
|
if (cursor_col < lines[cursor_line][1]) {
|
|
var l = lines[cursor_line][0];
|
|
var b = l.substring(0,cursor_col);
|
|
var e = l.substring(cursor_col+1);
|
|
setLine(b+key+e);
|
|
showAt(topLine);
|
|
}
|
|
break;
|
|
default:
|
|
if (key.length == 1) {
|
|
var l = lines[cursor_line][0];
|
|
var b = l.substring(0,cursor_col);
|
|
var e = l.substring(cursor_col);
|
|
setLine(b+key+e);
|
|
cursor_col++;
|
|
showAt(topLine);
|
|
}
|
|
}
|
|
}
|
|
curses.getch(input);
|
|
}
|
|
|
|
curses.getch(input);
|
|
|
|
return null;
|
|
});
|
|
|
|
}
|