1 //: Extend 'new' to handle a unicode string literal argument or 'text'.
  2 
  3 //: A Mu text is an address to an array of characters.
  4 :(before "End Mu Types Initialization")
  5 put(Type_abbreviations, "text", new_type_tree("&:@:character"));
  6 
  7 :(scenario new_string)
  8 def main [
  9   10:text <- new [abc def]
 10   20:char <- index *10:text, 5
 11 ]
 12 # number code for 'e'
 13 +mem: storing 101 in location 20
 14 
 15 :(scenario new_string_handles_unicode)
 16 def main [
 17   10:text <- new [a«c]
 18   20:num <- length *10:text
 19   21:char <- index *10:text, 1
 20 ]
 21 +mem: storing 3 in location 20
 22 # unicode for
package msgview

import (
	"errors"
	"fmt"
	"strconv"

	"git.sr.ht/~sircmpwn/aerc/widgets"
)

type NextPrevPart struct{}

func init() {
	register(NextPrevPart{})
}

func (_ NextPrevPart) Aliases() []string {
	return []string{"next-part", "prev-part"}
}

func (_ NextPrevPart) Complete(aerc *widgets.Aerc, args []string) []string {
	return nil
}

func (_ NextPrevPart) Execute(aerc *widgets.Aerc, args []string) error {
	if len(args) > 2 {
		return nextPrevPartUsage(args[0])
	}
	var (
		n   int = 1
		err error
	)
	if len(args) > 1 {
		n, err = strconv.Atoi(args[1])
		if err != nil {
			return nextPrevPartUsage(args[0])
		}
	}
	mv, _ := aerc.SelectedTab().(*widgets.MessageViewer)
	for ; n > 0; n-- {
		if args[0] == "prev-part" {
			mv.PreviousPart()
		} else {
			mv.NextPart()
		}
	}
	return nil
}

func nextPrevPartUsage(cmd string) error {
	return errors.New(fmt.Sprintf("Usage: %s [n]", cmd))
}
r">104 ] 105 +app: abc foo 106 107 :(scenario stash_text_as_array) 108 def main [ 109 1:text <- new [abc] 110 stash *1:text 111 ] 112 +app: 3 97 98 99 113 114 //: fixes way more than just stash 115 :(before "End Preprocess is_mu_text(reagent x)") 116 if (!canonize_type(x)) return false; 117 118 //: Allocate more to routine when initializing a literal text 119 :(scenario new_text_overflow) 120 % Initial_memory_per_routine = 3; 121 def main [ 122 10:&:num/raw <- new number:type 123 20:text/raw <- new [a] # not enough room in initial page, if you take the array length into account 124 ] 125 +new: routine allocated memory from 1000 to 1003 126 +new: routine allocated memory from 1003 to 1006 127 128 //: helpers 129 :(code) 130 int unicode_length(const string& s) { 131 const char* in = s.c_str(); 132 int result = 0; 133 int curr = 0; 134 while (curr < SIZE(s)) { // carefully bounds-check on the string 135 // before accessing its raw pointer 136 ++result; 137 curr += tb_utf8_char_length(in[curr]); 138 } 139 return result; 140 } 141 142 string read_mu_text(int address) { 143 if (address == 0) return ""; 144 int length = get_or_insert(Memory, address+/*alloc id*/1); 145 if (length == 0) return ""; 146 return read_mu_characters(address+/*alloc id*/1+/*length*/1, length); 147 } 148 149 string read_mu_characters(int start, int length) { 150 ostringstream tmp; 151 for (int curr = start; curr < start+length; ++curr) 152 tmp << to_unicode(static_cast<uint32_t>(get_or_insert(Memory, curr))); 153 return tmp.str(); 154 } 155 156 //:: some miscellaneous helpers now that we have text 157 158 //: assert: perform sanity checks at runtime 159 160 :(scenario assert_literal) 161 % Hide_errors = true; // '%' lines insert arbitrary C code into tests before calling 'run' with the lines below. Must be immediately after :(scenario) line. 162 def main [ 163 assert 0, [this is an assert in Mu] 164 ] 165 +error: this is an assert in Mu 166 167 :(scenario assert) 168 % Hide_errors = true; // '%' lines insert arbitrary C code into tests before calling 'run' with the lines below. Must be immediately after :(scenario) line. 169 def main [ 170 1:text <- new [this is an assert in Mu] 171 assert 0, 1:text 172 ] 173 +error: this is an assert in Mu 174 175 :(before "End Primitive Recipe Declarations") 176 ASSERT, 177 :(before "End Primitive Recipe Numbers") 178 put(Recipe_ordinal, "assert", ASSERT); 179 :(before "End Primitive Recipe Checks") 180 case ASSERT: { 181 if (SIZE(inst.ingredients) != 2) { 182 raise << maybe(get(Recipe, r).name) << "'assert' takes exactly two ingredients rather than '" << to_original_string(inst) << "'\n" << end(); 183 break; 184 } 185 if (!is_mu_address(inst.ingredients.at(0)) && !is_mu_scalar(inst.ingredients.at(0))) { 186 raise << maybe(get(Recipe, r).name) << "'assert' requires a scalar or address for its first ingredient, but got '" << inst.ingredients.at(0).original_string << "'\n" << end(); 187 break; 188 } 189 if (!is_literal_text(inst.ingredients.at(1)) && !is_mu_text(inst.ingredients.at(1))) { 190 raise << maybe(get(Recipe, r).name) << "'assert' requires a text as its second ingredient, but got '" << inst.ingredients.at(1).original_string << "'\n" << end(); 191 break; 192 } 193 break; 194 } 195 :(before "End Primitive Recipe Implementations") 196 case ASSERT: { 197 if (!scalar_ingredient(ingredients, 0)) { 198 if (is_literal_text(current_instruction().ingredients.at(1))) 199 raise << current_instruction().ingredients.at(1).name << '\n' << end(); 200 else 201 raise << read_mu_text(ingredients.at(1).at(/*skip alloc id*/1)) << '\n' << end(); 202 if (!Hide_errors) exit(1); 203 } 204 break; 205 } 206 207 //: 'cheating' by using the host system 208 209 :(before "End Primitive Recipe Declarations") 210 _READ, 211 :(before "End Primitive Recipe Numbers") 212 put(Recipe_ordinal, "$read", _READ); 213 :(before "End Primitive Recipe Checks") 214 case _READ: { 215 break; 216 } 217 :(before "End Primitive Recipe Implementations") 218 case _READ: { 219 skip_whitespace(cin); 220 string result; 221 if (has_data(cin)) 222 cin >> result; 223 products.resize(1); 224 products.at(0).push_back(new_mu_text(result)); 225 break; 226 } 227 228 :(code) 229 void skip_whitespace(istream& in) { 230 while (true) { 231 if (!has_data(in)) break; 232 if (isspace(in.peek())) in.get(); 233 else break; 234 } 235 }