Best way to cut a text down to a particular number of characters (6M62)

Unless I’ve missed a function of Inform that does exactly that, I’m finding this to be tricky to do without lag.

What I’m doing: I measure the width of the status window. I calculate a number of characters I have available to print up there a certain variable-length text I have. If the text will fit as is, I print it. If not, I want to chop it off at (screen width minus sixteen characters) – then I find the last unchopped off word, add elipses after it, and print this shortened version up there instead.

I’ve tried three ways to cut a text off after N characters.

Cutting characters off the right end one at a time, replacing with “” until only N are left, is slow.

Cutting words off the right end one at a time is a bit less slow.

It seems like regex should be able to do this most efficiently, but I don’t seem to have it right, or be able to plug N into the expression as a variable.

if TEST matches the regular expression "^(.{N})(.*)":
	replace the regular expression "^(.{N})(.*)" in TEST with "\1";

This matches if I replace N with an integer. e.g. 30 in place of N will collect the first 30 characters just fine. But using N isn’t putting the value of N in there – I don’t know what it’s doing. N gives a string two characters shorter than arbitrarily using P, though.

-Wade

Regexes can do this with only a little bit of hackery:

To decide what text is first (N - number) chars of (T - text):
	let regex be the substituted form of "^.{[N]}";
	if T matches the regular expression regex: 
		decide on text matching regular expression;
	else:
		decide on T;

When play begins:
	let T be the first 10 chars of "abcdefghijklmnopqrstuvwyxz";
	say "T is '[T]'.";
	let T be the first 10 chars of "abcde";
	say "T is '[T]'.";
3 Likes

Rather than truncate and then look for the word boundary, I’d be inclined toward just looking at words to begin with:

To say (t - sayable value) up to (limit - number):
let count be number of unpunctuated words in t;
let i be 1;
let chars be 0;
while i <= count begin;
  let word be unpunctuated word number i in t;
  if chars + the number of characters in word > limit begin;
    say "...";
    break;
  end if;
  unless i is 1, now word is " [word]";
  say "[word][no line break]";
  increase chars by the number of characters in word;
  increment i;
end while;

(Not thoroughly tested.)

3 Likes

Thank you both.

I definitely wanted to know how to chop lines down. I also tried Zed’s approach.

After adding the bit of code that tidies the line end to Zarf’s regex version, I tested both approaches for speed. I mean, they were obviously so fast that we’re in the realm where it doesn’t really matter, but my guess was the regex version would go faster as it doesn’t involve a loop, and that was the case.

For an 80 character line,
the regex chop and tidy approach averaged 16 ms in Lectrote.
the building up prose in a loop approach averaged 26 ms in Lectrote.

-Wade

2 Likes