ed

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

global.ha (4117B)


      1 use bufio;
      2 use fmt;
      3 use io;
      4 use memio;
      5 use regex;
      6 use strings;
      7 use types;
      8 
      9 fn global(s: *Session, cmd: *Command, matched: bool) (void | Error) = {
     10 	s.warned = false;
     11 
     12 	const (a, b) = get_range(
     13 		s,
     14 		&cmd.linenums,
     15 		addr_linenum(s.buf, 1)?,
     16 		addr_lastline(s.buf),
     17 	)?;
     18 	assert_nonzero(s, a)?;
     19 
     20 	const re = newregex(s, cmd.arg1)?; defer regex::finish(&re);
     21 
     22 	for (let i = a; i <= b; i += 1) {
     23 		const line = s.buf.lines[i];
     24 		line.globalmark = false; // TODO: is this necessary?
     25 		if (matched == regex::test(&re, line.text))
     26 			line.globalmark = true;
     27 	};
     28 
     29 	let subcmds: []Command = [];
     30 
     31 	let inputtext = strings::join("\n", cmd.textinput...);
     32 	let inputbytes = strings::toutf8(inputtext);
     33 	let inputstream = memio::dynamic_from(inputbytes);
     34 		defer io::close(&inputstream)!;
     35 	let input = bufio::newscanner(&inputstream, types::SIZE_MAX);
     36 		defer bufio::finish(&input);
     37 	// TODO: could be less than types::SIZE_MAX ^ ?
     38 
     39 	for (true) {
     40 		// TODO: handle mem
     41 		// TODO: just use 'parse()?' when compiler allows
     42 		//let subcmd = parse(s.input)?;
     43 		let subcmd = match (parse(&input)) {
     44 		case let c: Command =>
     45 			yield c;
     46 		case io::EOF =>
     47 			break;
     48 		case let e: ParseError =>
     49 			return e;
     50 		case let e: InteractionError =>
     51 			return e;
     52 		};
     53 
     54 		debug("cmd_global() for subcmd.name=<{}>", subcmd.name);
     55 
     56 		// TODO: write new parse_global() function instead
     57 		// as to avoid reading multiple lines in the case of
     58 		// 'a', 'c', 'i' ?
     59 		switch (subcmd.name) {
     60 		case 'g', 'G', 'v', 'V', '!' =>
     61 			// TODO: undefined specification. implement?
     62 			errormsg(s, subcmd.name: InvalidGlobalSubCmd);
     63 			continue;
     64 		case '&' =>
     65 			errormsg(s, '&': UnknownCommand);
     66 			continue;
     67 		case 'u' =>
     68 			// TODO: undo behaviour
     69 			continue;
     70 		case =>
     71 			void;
     72 		};
     73 
     74 		append(subcmds, subcmd);
     75 	};
     76 
     77 	debug("cmd_global() len(subcmds)={}", len(subcmds));
     78 
     79 	if (len(subcmds) == 0)
     80 		append(subcmds, Command{ name = 'p', ... });
     81 
     82 	for :marks (true) {
     83 		// find next global-marked line
     84 		let n = 1z;
     85 		for :lines (true; n += 1) {
     86 			if (n >= len(s.buf.lines)) {
     87 				break :marks;
     88 			};
     89 			if (s.buf.lines[n].globalmark) {
     90 				break :lines;
     91 			};
     92 		};
     93 		s.buf.cursor = n;
     94 		let line = s.buf.lines[n];
     95 
     96 		for (let i = 0z; i < len(subcmds); i += 1) {
     97 			let subcmd = subcmds[i];
     98 			//debug("cmd_global() for :marks execute()ing");
     99 			execute(s, &subcmd)?;
    100 		};
    101 
    102 		line.globalmark = false;
    103 	};
    104 };
    105 
    106 fn global_ia(s: *Session, cmd: *Command, matched: bool) (void | Error) = {
    107 	s.warned = false;
    108 
    109 	const (a, b) = get_range(
    110 		s,
    111 		&cmd.linenums,
    112 		addr_linenum(s.buf, 1)?,
    113 		addr_lastline(s.buf),
    114 	)?;
    115 	assert_nonzero(s, a)?;
    116 
    117 	const re = newregex(s, cmd.arg1)?; defer regex::finish(&re);
    118 
    119 	for (let i = a; i <= b; i += 1) {
    120 		const line = s.buf.lines[i];
    121 		line.globalmark = false; // TODO: is this necessary?
    122 		if (matched == regex::test(&re, line.text))
    123 			line.globalmark = true;
    124 	};
    125 
    126 	let prevsubcmd: (void | Command) = void; // TODO: handle mem
    127 
    128 	for :marks (true) {
    129 		// find next global-marked line
    130 		let n = 1z;
    131 		for :lines (true; n += 1) {
    132 			if (n >= len(s.buf.lines)) {
    133 				break :marks;
    134 			};
    135 			if (s.buf.lines[n].globalmark) {
    136 				break :lines;
    137 			};
    138 		};
    139 		s.buf.cursor = n;
    140 		let line = s.buf.lines[n];
    141 
    142 		fmt::println(line.text)!;
    143 
    144 		// TODO: handle mem
    145 		// TODO: just use 'parse()?' when compiler allows
    146 		//let subcmd = parse(s.input)?;
    147 		let subcmd = match (parse(s.input)) {
    148 		case let c: Command =>
    149 			yield c;
    150 		case let e: ParseError =>
    151 			return e;
    152 		case let e: InteractionError =>
    153 			return e;
    154 		};
    155 
    156 		// TODO: write new parse_global_ia() function instead
    157 		// as to avoid reading multiple lines in the case of
    158 		// 'a', 'c', 'i' ?
    159 		switch (subcmd.name) {
    160 		case 'a', 'c', 'i', 'g', 'G', 'v', 'V' =>
    161 			errormsg(s, subcmd.name: InvalidGlobalSubCmd);
    162 			continue;
    163 		case NUL =>
    164 			line.globalmark = false;
    165 			continue;
    166 		case '&' =>
    167 			match (prevsubcmd) {
    168 			case void =>
    169 				return NoPrevGlobalSubCmd;
    170 			case let prevsubcmd: Command =>
    171 				subcmd = prevsubcmd;
    172 			};
    173 		case 'u' =>
    174 			// TODO: undo behaviour
    175 			continue;
    176 		case =>
    177 			prevsubcmd = subcmd;
    178 		};
    179 
    180 		execute(s, &subcmd)?;
    181 
    182 		line.globalmark = false;
    183 	};
    184 };