new object $editor_session: $misc;
var $editor_session client_data = 0;
var $editor_session finisher = 0;
var $editor_session finisher_object = 0;
var $editor_session line = 0;
var $editor_session modified = 0;
var $editor_session sender = 0;
var $editor_session text = 0;
var $root created_on = 820684587;
var $root flags = ['methods, 'code, 'fertile, 'core, 'variables];
var $root inited = 1;
var $root managed = [$editor_session];
var $root manager = $editor_session;
public method ._parse_range() {
arg what, @allow;
var start, end, range;
if (what == "%")
what = "1-$";
what = strsub(what, "$", tostr(listlen(text)));
what = strsub(what, ".", tostr(line));
what = strsub(what, ",", "-");
allow = allow ? 1 : 0;
if (!what) {
start = (end = line);
} else {
range = (> $parse_lib.range(what) <);
start = range[1];
if (start == 'specific)
throw(~range, "Illegal range.");
end = ((range[2]) == 'single) ? start : (range[2]);
}
if ((start < 1) || ((end > (listlen(text) + allow)) || (end < start)))
throw(~range, "Illegal range.");
return [start, end];
};
public method .abort_cmd() {
arg @args;
(> .perms(caller(), 'command) <);
sender.quit_editor();
return "Aborted. Editor cleared.";
};
public method .after_cmd() {
arg cmd, tmpl, what;
var offset;
(> .perms(caller(), 'command) <);
if (line > listlen(text))
offset = line - listlen(text);
if (what || match_regexp(cmd, "^[^a-z]")) {
modified = 1;
text = insert(text, (line + 1) - offset, what);
line += 2 - offset;
return ("Line " + (line - 1)) + " added.";
} else {
return .read_text(1 - offset, sender());
}
};
public method .append_cmd() {
arg cmd, tmpl, what;
var offset;
(> .perms(caller(), 'command) <);
modified = 1;
if (line > listlen(text)) {
text += [what];
return ("Line " + listlen(text)) + " added.";
} else if (line == 1) {
text = [line] + text;
line = 2;
return "Line 1 added.";
} else {
text = replace(text, line, (text[line]) + what);
return ("Appended to line " + tostr(line)) + ".";
}
};
public method .cleanup_session() {
(> .perms(sender()) <);
if (type(text) == 'symbol)
return 1;
return !modified;
};
public method .compile_errors() {
arg @err;
var m;
if ((m = regexp(err[1], "Line ([0-9]+)"))) {
line = toint(m[1]);
return [.list_cmd("", "", m[1])] + err;
}
return err;
};
public method .copy_cmd() {
arg cmd, tmpl, args;
var m, range, toline, copy, start, end;
(> .perms(caller(), 'command) <);
if ((m = match_template(args, "* to *")))
[range, m, toline] = m;
else if (listlen((m = explode(args))) > 1)
[range, toline] = m;
else
toline = args;
if (!toline)
return "Copy to where?";
toline = toint(toline);
if ((toline < 1) || (toline > (listlen(text) + 1)))
return ((("Line " + toline) + " is outside the possible range (1 .. ") + (listlen(text) + 1)) + ").";
if (range) {
if (!(range = (| ._parse_range(args, 'allow) |)))
return "Illegal range, cannot copy.";
} else if (line > listlen(text)) {
return "Illegal range, cannot copy.";
} else {
range = [line, line];
}
[start, end] = range;
copy = sublist(text, start, (end - start) + 1);
text = listgraft(text, toline, copy);
line = toline + listlen(copy);
if (start == end)
return ((("Line " + start) + " copied to line ") + toline) + ".";
return ((((("Lines " + start) + " .. ") + end) + " copied to line ") + toline) + ".";
};
public method .delete_cmd() {
arg cmd, tmpl, what;
var start, end, gone, foo, len;
(> .perms(caller(), 'command) <);
catch any
start = ._parse_range(what);
with
return "Illegal range, can't delete.";
[start, end] = start;
len = end - start;
gone = sublist(text, start, (end - start) || 1);
text = sublist(text, 1, start - 1) + sublist(text, end + 1);
modified = 1;
line = start;
return ((((((("Deleted " + ((end - start) + 1)) + " line") + ((end == start) ? "" : "s")) + " (\"") + ((gone[1]).chop(20))) + "\"), current line is ") + line) + ".";
};
public method .fill_cmd() {
arg cmd, tmpl, what;
var start, end, width;
(> .perms(caller(), 'command) <);
what = what.explode();
catch any
start = ._parse_range(what[1]);
with
return "Illegal range, can't fill.";
end = start[2];
start = start[1];
width = ((what.length()) > 1) ? ((what[2]).to_number()) : 75;
text = ((text.subrange(1, start - 1)) + (((text.subrange(start, (end - start) + 1)).join(" ")).wrap_lines(width))) + (text.subrange(end + 1));
modified = 1;
line = start;
return ("Fill completed. Current set to " + tostr(start)) + ".";
};
public method .help_cmd() {
arg cmd, tmpl, what;
var parse;
what = what.trim();
if (!what)
return ["Editor commands (use 'help <cmd>' for detailed information):"] + (((($editor_parser.commands()).keys()).vcolumnize(3, (sender().linelen()) - 5)).prefix(" "));
parse = $editor_parser.parse(sender(), what, $null_parser);
if ((parse[1]) in ['ok, 'failed])
return ("Unable to find editor command '" + what) + "'.";
return ($editor_parser.command_help())[parse[3]];
};
public method .insert_cmd() {
arg cmd, tmpl, what;
(> .perms(caller(), 'command) <);
modified = 1;
if (what || match_regexp(cmd, "^[^a-z]")) {
text = insert(text, line, what);
line++;
return ("Line " + tostr(line - 1)) + " added.";
} else {
return .read_text(0, sender());
}
};
public method .is_resumable() {
return type(text) != 'symbol;
};
public method .line_cmd() {
arg cmd, tmpl, number;
(> .perms(caller(), 'command) <);
number = number.trim();
catch ~nonum
number = (number == "$") ? (text.length()) : (number.to_number());
with
return "You must set to a line number.";
if ((number < 1) || (number > ((text.length()) + 1)))
return "Out if range.";
else
line = number;
return ("Current line set to " + tostr(line)) + ".";
};
public method .list_cmd() {
arg cmd, tmpl, rangestr;
var start, end, out, i, str, l, lineno, rows, lines, diff;
(> .perms(caller(), $user, definer()) <);
if (text == [])
return "There is no text.";
rangestr = rangestr.trim();
l = text.length();
if (!rangestr) {
rows = sender().get_rows();
start = line - (rows / 2);
end = line + (rows / 2);
if (start < 1) {
end -= start;
start = 1;
}
} else {
catch any
[start, end] = ._parse_range(rangestr);
with
return "Illegal list range.";
}
out = [];
rows = (end - start) + 1;
for i in [start .. end] {
if (i > l)
break;
if (i == line)
lineno = (("=>" + i).right(5)) + ": ";
else
lineno = (tostr(i).right(5)) + ": ";
lines = (lineno + (text[i])).wrap_lines(sender.linelen(), " ");
if ((listlen(out) + listlen(lines)) > rows) {
if (listlen(out) >= rows)
break;
diff = rows - listlen(out);
diff = (listlen(out) + listlen(lines)) - rows;
out += sublist(lines, 1, (listlen(lines) - diff) - 1);
out += [((("[.." + (diff.to_english_text())) + " more row") + ((diff > 1) ? "s" : "")) + "]"];
break;
}
out += lines;
}
if (i >= l) {
if ((i == line) && (i == (l + 1)))
out += ["=>[End]"];
else
out += [" [End]"];
}
return out;
};
public method .mcp_upload() {
arg newtext;
var err;
(> .perms(sender()) <);
if ((> (err = sender.do_save(finisher_object, finisher, newtext, client_data)) <) == 'clear) {
sender.quit_editor();
return "Done. Editor cleared.";
}
if (type(err) != 'list)
modified = 0;
return err ? err : "Save completed.";
};
public method .move_cmd() {
arg cmd, tmpl, args;
var m, range, toline, move, start, end;
(> .perms(caller(), 'command) <);
if ((m = match_template(args, "* to *")))
[range, m, toline] = m;
else if (listlen((m = explode(args))) > 1)
[range, toline] = m;
else
toline = args;
if (!toline)
return "Copy to where?";
toline = (| ._parse_range(toline, 'allow) |);
if ((!toline) || ((toline[1]) != (toline[2])))
return "Invalid move destination reference.";
toline = toline[1];
if (range) {
if (!(range = (| ._parse_range(args, 'allow) |)))
return "Illegal range, cannot move.";
} else if (line > listlen(text)) {
return "Illegal range, cannot move.";
} else {
range = [line, line];
}
[start, end] = range;
if ((toline == start) || ((toline == end) || ((toline > start) && (toline < end))))
return "Source and destination lines conflict.";
move = sublist(text, start, (end - start) + 1);
if (toline > end) {
text = listgraft(text, toline, move);
text = sublist(text, 1, start - 1) + sublist(text, end + 1);
line = toline;
} else {
text = sublist(text, 1, start - 1) + sublist(text, end + 1);
text = listgraft(text, toline, move);
line = toline + listlen(move);
}
if (start == end)
return ((("Line " + start) + " moved to line ") + toline) + ".";
return ((((("Lines " + start) + " .. ") + end) + " moved to line ") + toline) + ".";
};
public method .quit_cmd() {
arg cmd, tmpl, @args;
var ans;
(> .perms(caller(), 'command) <);
if (modified) {
ans = sender.prompt("Discard changes? [yes] ");
if (!(ans.is_boolean()))
return "Ok. Editor not cleared.";
}
sender.quit_editor();
return "Done. Editor cleared.";
};
private method .read_text() {
arg offset, sender;
var newtext, start;
if ((> (newtext = sender.read()) <)) {
if (newtext == 'aborted)
return;
modified = 1;
text = listgraft(text, line + offset, newtext);
start = line + offset;
line = (line + (newtext.length())) + offset;
if (listlen(newtext) == 1)
return ("Line " + start) + " added.";
return ((("Lines " + start) + " to ") + (start + listlen(newtext))) + " added.";
} else {
return "Text unchanged.";
}
};
public method .save_cmd() {
arg cmd, tmpl, args;
var result, m;
(> .perms(caller(), $user, this()) <);
if ((m = match_template(args, "as *")))
return "Save as support is not yet completed.";
if ((> (result = sender.do_save(finisher_object, finisher, text, client_data)) <) == 'clear) {
sender.quit_editor();
return "Done. Editor cleared.";
}
if ((result[1]) == 'failure)
return .compile_errors(@result[2]);
modified = 0;
return (result[2]) + ["Save completed."];
};
public method .sed() {
arg start, end, search, replace, opts;
var x, line, changed;
for x in [start .. end] {
line = strsed(text[x], search, replace, opts);
if (strcmp(line, text[x])) {
text = replace(text, x, line);
changed++;
}
}
return changed;
};
public method .send_cmd() {
arg cmd, tmpl, args;
var err, m, list, recip, subj, mail, ismail, ret, cd, out;
(> .perms(caller(), $user, this()) <);
ismail = client_data && ((client_data[1]) == 'mail);
if ((m = match_template(args, "to *")))
args = m[2];
else
args = args.trim('left);
list = [];
for recip in (args.explode_english_list()) {
if (recip == "me") {
list = setadd(list, sender());
} else {
catch ~listnf
list = list.setadd((> $mail_lib.match_mail_recipient(recip) <));
with
sender().tell(("The recipient \"" + recip) + "\" is invalid.");
}
}
if (!list) {
if (ismail)
list = client_data[2];
else
return "No recipients specified.";
}
if (ismail) {
cd = client_data;
cd = replace(cd, 2, list);
} else {
cd = ['mail, list, ""];
}
ret = (> sender()._edit_mail_callback(text, cd) <);
if ((ret[1]) == 'success) {
if (ismail)
sender.quit_editor();
return [ret[2], "Done. Editor Cleared."];
} else {
return ret[2];
}
};
public method .session_name() {
var tmp;
// cruft this up at a later time
switch (finisher) {
case '_edit_method_callback:
return ((("Method " + (client_data[1])) + ".") + (client_data[2])) + "()";
case '_edit_messages_callback:
return "Messages on " + (finisher_object.namef('ref));
case '_edit_mail_callback:
return "Mail for " + (((client_data[2]).mmap('mail_name)).to_english());
default:
tmp = strsed(tostr(finisher), "_edit_", "");
tmp = strsed(tmp, "_callback", "");
return "%l/%l".format(finisher_object.name(), tmp);
}
};
public method .startup() {
arg fin_object, finish_method, initial_text, cdata;
(> .perms(sender()) <);
if (type(initial_text) == 'string)
initial_text = [initial_text];
sender = sender();
finisher = finish_method;
finisher_object = fin_object;
text = initial_text;
line = 1;
modified = 0;
client_data = cdata;
if ((| (sender().local_editor()) == 'mcp |)) {
// Next time, when you have a clever idea about edit name, check
// it for non-method editting sessions
sender.tell([(("#$# edit name: " + (.session_name())) + " upload: @mcp-upload-session ") + (.name()), @text, "."]);
text = 'mcp;
sender.store_editor();
return 0;
} else {
return 1;
}
};
public method .startup_sender(): nooverride {
return sender;
};
public method .store_cmd() {
arg cmd, tmpl, @args;
(> .perms(caller(), 'command) <);
return sender.store_editor();
};
public method .sub() {
arg start, end, search, replace, opts;
var x, line, changed, i;
for x in [start .. end] {
line = strsub(text[x], search, replace, opts);
if (strcmp(line, text[x])) {
text = replace(text, x, line);
changed++;
}
}
return changed;
};
public method .subst_cmd() {
arg cmd, tmpl, args;
var start, end, opts, changed, sep, h, range;
(> .perms(caller(), 'command) <);
h = ($editor_parser.command_help())['subst_cmd];
if (!args)
return h;
sep = args[1];
args = substr(args, 2);
args = explode(args, sep, 'keep_blanks);
if ((!args) || (listlen(args) < 2))
return h + ["", "! Invalid number of arguments."];
if (text == [])
return h + ["", "! There is no text to search and replace in."];
if (listlen(args) > 2) {
[opts, range] = (args[3]).regexp("([gsci]*)(.*)");
if ((!range) && (listlen(args) > 3))
range = args[3];
} else {
opts = "";
}
if (range) {
catch any
start = ._parse_range(range);
with
return h + ["", "! " + ((traceback()[1])[2])];
[start, end] = start;
} else {
start = line;
end = line;
}
if (cmd == "sed")
changed = .sed(start, end, args[1], args[2], opts);
else
changed = .sub(start, end, args[1], args[2], opts);
modified = changed;
if (changed == 1)
return changed + " line changed.";
else if (changed > 1)
return changed + " lines changed.";
else
return "No lines changed.";
};