ed

[hare] The standard editor
Log | Files | Refs | README | LICENSE

address.ha (2132B)


      1 use regex;
      2 use strings;
      3 
      4 type Address = struct{
      5 	addrform: AddressForm,
      6 	lineoffset: int,
      7 	setcurrentline: bool,
      8 };
      9 
     10 type AddressForm = (size | CurrentLine | LastLine | rune | RegexAddr);
     11 
     12 // The dot '.' address.
     13 type CurrentLine = void;
     14 
     15 // The dollar '$' address.
     16 type LastLine = void;
     17 
     18 // Regular expression search. /forward/ is true, ?backward? is false.
     19 type RegexAddr = struct{
     20 	expr: str,
     21 	direction: bool,
     22 };
     23 
     24 // The address was invalid.
     25 type InvalidAddress = !void;
     26 
     27 // A regular expression search found no match.
     28 type NoMatch = !void;
     29 
     30 // There is no previously used regex search pattern.
     31 type NoPrevRegex = !void;
     32 
     33 fn addr_nextline(buf: *Buffer, n: size) size = {
     34 	if (len(buf.lines) - 1 == n)
     35 		return n;
     36 
     37 	return n + 1;
     38 };
     39 
     40 fn addr_prevline(buf: *Buffer, n: size) size = {
     41 	if (n == 0 || n == 1)
     42 		return n;
     43 
     44 	return n - 1;
     45 };
     46 
     47 fn addr_linenum(buf: *Buffer, n: size) (size | InvalidAddress) = {
     48 	if (n >= len(buf.lines))
     49 		return InvalidAddress;
     50 
     51 	return n;
     52 };
     53 
     54 fn addr_lastline(buf: *Buffer) size = {
     55 	return len(buf.lines) - 1z;
     56 };
     57 
     58 fn addr_mark(buf: *Buffer, mark: rune) (size | InvalidAddress) = {
     59 	for (let n = 1z; n < len(buf.lines); n += 1) {
     60 		const line = buf.lines[n];
     61 		if (line.mark == mark)
     62 			return n;
     63 	};
     64 
     65 	return InvalidAddress;
     66 };
     67 
     68 fn addr_regex(
     69 	buf: *Buffer,
     70 	rad: RegexAddr,
     71 	start: size,
     72 	s: *Session,
     73 ) (size | NoPrevRegex | NoMatch | regex::error) = {
     74 	const length = len(buf.lines);
     75 	const re = newregex(s, rad.expr)?; defer regex::finish(&re);
     76 
     77 	for (let x = 1z; x <= length; x += 1) {
     78 		const n =
     79 			if (rad.direction)
     80 				(start + x) % length
     81 			else
     82 				(start - x) % length;
     83 
     84 		if (n == 0)
     85 			continue;
     86 
     87 		if (regex::test(&re, buf.lines[n].text))
     88 			return n;
     89 	};
     90 
     91 	return NoMatch;
     92 };
     93 
     94 fn newregex(s: *Session, expr: str) (regex::regex | NoPrevRegex | regex::error) = {
     95 	//debug("expr=<{}>", expr);
     96 	//debug("s.lastregex=<{}>", s.lastregex);
     97 
     98 	const newexpr =
     99 		if (expr != "")
    100 			expr
    101 		else if (s.lastregex is str)
    102 			s.lastregex as str
    103 		else
    104 			return NoPrevRegex;
    105 
    106 	// TODO: use BRE, not ERE.
    107 	const re = regex::compile(newexpr)?;
    108 	s.lastregex = strings::dup(newexpr);
    109 	yield re;
    110 };