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 };