ed

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

global.ha (4213B)


      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 		// TODO: handle termination by SIGINT signal
     97 
     98 		for (let i = 0z; i < len(subcmds); i += 1) {
     99 			let subcmd = subcmds[i];
    100 			//debug("cmd_global() for :marks execute()ing");
    101 			execute(s, &subcmd)?;
    102 		};
    103 
    104 		line.globalmark = false;
    105 	};
    106 };
    107 
    108 fn global_ia(s: *Session, cmd: *Command, matched: bool) (void | Error) = {
    109 	s.warned = false;
    110 
    111 	const (a, b) = get_range(
    112 		s,
    113 		&cmd.linenums,
    114 		addr_linenum(s.buf, 1)?,
    115 		addr_lastline(s.buf),
    116 	)?;
    117 	assert_nonzero(s, a)?;
    118 
    119 	const re = newregex(s, cmd.arg1)?; defer regex::finish(&re);
    120 
    121 	for (let i = a; i <= b; i += 1) {
    122 		const line = s.buf.lines[i];
    123 		line.globalmark = false; // TODO: is this necessary?
    124 		if (matched == regex::test(&re, line.text))
    125 			line.globalmark = true;
    126 	};
    127 
    128 	let prevsubcmd: (void | Command) = void; // TODO: handle mem
    129 
    130 	for :marks (true) {
    131 		// find next global-marked line
    132 		let n = 1z;
    133 		for :lines (true; n += 1) {
    134 			if (n >= len(s.buf.lines)) {
    135 				break :marks;
    136 			};
    137 			if (s.buf.lines[n].globalmark) {
    138 				break :lines;
    139 			};
    140 		};
    141 		s.buf.cursor = n;
    142 		let line = s.buf.lines[n];
    143 
    144 		fmt::println(line.text)!;
    145 
    146 		// TODO: handle termination by SIGINT signal
    147 
    148 		// TODO: handle mem
    149 		// TODO: just use 'parse()?' when compiler allows
    150 		//let subcmd = parse(s.input)?;
    151 		let subcmd = match (parse(s.input)) {
    152 		case let c: Command =>
    153 			yield c;
    154 		case let e: ParseError =>
    155 			return e;
    156 		case let e: InteractionError =>
    157 			return e;
    158 		};
    159 
    160 		// TODO: write new parse_global_ia() function instead
    161 		// as to avoid reading multiple lines in the case of
    162 		// 'a', 'c', 'i' ?
    163 		switch (subcmd.name) {
    164 		case 'a', 'c', 'i', 'g', 'G', 'v', 'V' =>
    165 			errormsg(s, subcmd.name: InvalidGlobalSubCmd);
    166 			continue;
    167 		case NUL =>
    168 			line.globalmark = false;
    169 			continue;
    170 		case '&' =>
    171 			match (prevsubcmd) {
    172 			case void =>
    173 				return NoPrevGlobalSubCmd;
    174 			case let prevsubcmd: Command =>
    175 				subcmd = prevsubcmd;
    176 			};
    177 		case 'u' =>
    178 			// TODO: undo behaviour
    179 			continue;
    180 		case =>
    181 			prevsubcmd = subcmd;
    182 		};
    183 
    184 		execute(s, &subcmd)?;
    185 
    186 		line.globalmark = false;
    187 	};
    188 };