Re-printing text that was printed earlier

Thinking a bit more, is there a simple adjustment to the StartCapture code so that it would print both to the screen and to the buffer? That would allow formatting to appear during the first printing.

Ah, that’s a mistake on my part. You are correct, Text Capture doesn’t print to the screen while it’s printing to the buffer. So you need to say the captured text right after you stop capturing.

On Glulx, you could alternatively use an “echo stream” for this, which captures anything being printed to the window while still letting it be printed there. This is used for making transcripts, by default—and annoyingly, each window can only have a single echo stream. So if you use that, you can’t use transcripts.

Okay, I have a proof-of-concept of a solution.

First, we define formatting codes that print a special escape sequence if text capturing is active.

Include Text Capture by Eric Eve.

To say escape code: say "§".
To say ital:
	if text capturing is active, say "[escape code]i";
	otherwise say italic type.
To say rom:
	if text capturing is active, say "[escape code]r";
	otherwise say roman type.
Instead of waiting: say "This is [ital]very[rom] exciting!"

Then, we write a printing routine that watches for these escape codes.

To say (T - text) safely:
	let index be zero;
	while index is less than the number of characters in T:
		let C be character number index in T;
		if C exactly matches the text "[escape code]":
			increment index;
			let D be character number index in T;
			if D exactly matches the text "i":
				say ital;
			otherwise if D exactly matches the text "r":
				say rom;
		otherwise:
			say C;
		increment index.

And then we test it by printing everything twice:

First before doing anything except looking:
	start capturing text;
First every turn:
	stop capturing text;
	say "[captured text]" safely;
	say "[captured text]" safely.

This is, as you would expect, agonizingly slow. As in, several seconds per turn slow. But it shows that the concept works:

image

In other words, this would give us a way of preserving formatting through Text Capture. Meaning italics would no longer be lost during implicit actions, for example.

The big slowdown is in the printing routine, since it’s transmuting and untransmuting the text for every single character. Rewriting it in I6 should fix that.

2 Likes

And voilà, Inform 6 implementation. No noticeable delay any more!

The escape detection rules are a number based rulebook.
Escape detection for 105: say italic type.
Escape detection for 114: say roman type.

To say captured text safely: (- PrintCaptureSafe(); -).

Include (-
[ PrintCaptureSafe len i;
	len = captured_text-->0;
	i = 1;
	@push say__pc;
	say__pc = PARA_NORULEBOOKBREAKS;
	while(i <= len){
		if(captured_text-->i == 167){
			i++;
			FollowRulebook((+ escape detection rules +), captured_text-->i);
		}else{
			glk_put_char_uni(captured_text-->i);
		}
		i++;
	}
	@pull say__pc;
];
-).

And to test it:

First before doing anything except looking:
	start capturing text;
First every turn:
	stop capturing text;
	say captured text safely;
	say captured text safely.

image

I think the extra paragraph breaks come because I invoked a rulebook, but I set PARA_NORULEBOOKBREAKS, so…dunno. I’ll tinker and see if I can fix that.

3 Likes

Great! Thanks for working on this!

Here it is, all polished up as an extension. Compatible with both Z-machine and Glulx, though not with Glulx Text Effects; the only formatting it supports is Inform’s built-in “bold type”, “italic type”, “roman type”, “fixed letter spacing”, and “variable letter spacing”.

From the user’s end, this should work exactly the same as Text Capture.

Adding more formatting, if you need it, is straightforward: just make your formatting command print [escape code] followed by a single character, and write an “escape detection rule” that imposes the appropriate formatting when it sees that single character. So if someone needs more Glulx text styles (I know I sometimes use alert style for bold italics) it’s not hard to implement that.

The only thing missing is FyreVM support; Text Capture has a section with special FyreVM code, but I don’t know enough about that to feel confident modifying it (and certainly can’t test any modifications). So if you need FyreVM, stick to Eric Eve’s version.

Formatting Capture.i7x (5.4 KB)

4 Likes

Oh, I should also say, this is only compatible with version 9.3, not with version 10. Not much needs to change for version 10 (just specify that some replacements apply to Basic Inform rather than the Standard Rules) but a long-standing hatred between the IDE and my monitor makes it extremely difficult for me to test on different versions. So that’s going to have to wait a while longer.

That wouldn’t actually be too hard to support. You could intercept calls to glk_set_style/glk_set_style_stream and then record the style number.

I think your extension is an excellent start! But to suggest a few improvements, I don’t think 167 is a good escape character (even though it can be changed it can’t be lowered when defined that way.) It’s Ë in ZSCII and § in unicode, so those could be used in stories. What about using ESC itself (27)? It would definitely be fine to print in Glulx. I think it might be okay to use in Z-Code, though some interpreters (or perhaps Inform 6 itself) might complain because it’s defined as only an input code, if so something else less likely to be used than Ë could be chosen.

For Z-Code it would also be good to record reverse mode and colours. For Glulx it would in theory be possible to record inline images, though hardly anyone uses them, so it probably wouldn’t be worth adding unless someone requests it. The garglk formatting extension functions are a little more likely to be used but they could also be supported via intercepting the glk function calls. But if these all came into scope then it probably needs more than single character escape codes. That’s not an issue though, once an escape code is seen it can have a little parser that consumes as many characters as needed (the different codes don’t need to be all the same length.)

Lastly it would be good if these formatted texts could be used and sent like any other text (even written/read to files!) So rather than just having the phrase “say the captured text” it would be good to intercept printing texts at a lower level, perhaps indicating that the text has formatting by beginning with an escape code. Or perhaps a different I7 text kind. And if they can be written to files then it might be wise to write up a full versioned spec for this little formatting language.

That’s actually a lot of suggestions haha. Don’t feel you need to do any of it. But I love this idea, so I might work on it myself in the future.

1 Like

That was my first attempt, actually! But Borogove’s Glulx interpreter (Quixe I think?) complained about a non-printable character being sent to an output stream. Code 167 was chosen basically at random to get the proof-of-concept working, but I agree it’s not an ideal choice. (It could be improved slightly by printing any unrecognized escape sequences literally, but “§i” is still something that might theoretically appear in game text.)

Another thought was backslashes, but ASCII-art maps love drawing lines with those.

Absolutely; it shouldn’t be too hard to integrate this with Basic Screen Effects and Glulx Text Effects (are those still the standard “basic formatting tools” extensions?), all it would take is a bunch of escape detection rules.

That would also let formatted text be saved to transcripts, which would be nice for let’s plays. Or it could make it easier to hook up dumbfrotz to a pipeline that turned format codes into, say, Markdown; that would let bots like Floyd more easily format their output for platforms that support it, like Discord.

Unfortunately, digging into the representation of text in the block system is well beyond my current skills. But I would not be at all opposed to formalizing that a bit more and giving the escape codes a proper spec! The question is, if inline formatting codes start to become a standard part of text (passed around, written to files, modified with regexes), at which point does it become easier to just implement them on the interpreter side and bypass Glulx styles entirely?

By all means feel free! This thread made me realize that the very-handy Implicit Actions extension would remove all formatting from the results of implicit actions, since it captures the actions’ output and prints it again after it knows whether the action succeeded or failed.

Then this morning a bolt of inspiration hit and I threw this together on a whim to see if it would actually work at all. Turns out it would!

Huh. That surprises me. Clearly there are nuances that would need to be explored to find out what works reliably across all interpreters.

I’m not sure this kind of system would really suit transcripts. Interpreters could already output to HTML and RemGlk should be used for bots. Something like RTF would be more portable, but if we wanted it to be able to encode all of the Glk formatting options then I think we’d need something new. Which would mean it wouldn’t be useable in external word processors. I think it’s probably better to separate the use cases of transcripts etc from VM-internal text-capture-with-formatting. (When I mentioned writing to files I wasn’t thinking of it being user-readable, just that it could be successfully serialised.)

1 Like

My first attempt worked! (Good sign!)

One other nice feature would be “say unformatted captured text”, which would strip the formatting like it used to.

For example, if you wanted to repeat captured text later as a “memory”, all-italics might feel appropriate, like:

say italic type;
say unformatted captured text;
say roman type;

1 Like

I think when you “say captured text” and no text has been captured, it prints a line break. (Is that intended?)

Also, in some cases the capturing process seems to add an extra line break:

instead of waiting:
    start capturing text;
    say "Hi[paragraph break]";
    stop capturing text;
    say the captured text;

results in an extra break:

> z
Hi

>

I know rules-related line breaks are quirky, so maybe this is hard to avoid. (And I believe this happens with the original Text Capture also.)

Yeah, this happens because the spacing mechanisms of I7 are deeply arcane and I barely understand them. I turned off the mechanism that puts paragraph breaks after a rulebook, so that the escape detection rules wouldn’t destroy the formatting, but there’s something else going on too and I have no idea what.

At some point I’ll attain the gnosis required to make I7 paragraph breaks work and then all will be right.

Also, printing without formatting is definitely a good feature! If you’re on a deadline, you can do that in the current version by disabling the escape detection rules under certain circumstances (“first escape detection rule for a number when Special Scene is happening: rule succeeds”). That will make it ignore all escape codes.

1 Like

I don’t know if this is helpful, but I’ve been playing around and trying to figure out when this “extra line break” appears.

As far as I can tell, it happens when the last rule followed (before “stop capturing text”) either

  1. ends with a paragraph break, or
  2. prints no text

(There’s no urgency to fixing this, but I’m definitely scratching my head about it. Thanks.)

1 Like

Nathanael’s Cookbook has a reference to line break behavior. Dunno if it’ll provide enlightenment on this particular issue.

1 Like

Aha! Okay, that actually pointed to exactly what I need.

The documentation says:

Not all printing is to the screen: sometimes the output is to a file, or to memory, and in that case we want to start the switched output at a clear paragraphing state and then go back to the screen afterwards without any sign of change. The correct way to do this is to push the say__p and say__pc variables onto the VM stack and call ClearParagraphing() before starting to print to the new stream, and then pull the variables back again before resuming printing to the old stream.

So I wasn’t saving and restoring these values properly, and I think neither was Eric Eve.

The main problem is, anything pushed to the VM stack is lost when the routine returns. Which means we can’t push these values in “start capturing text” and then pull them again in “stop capturing text”.

But, since text capture can’t be stacked, I can just save them in global variables.

In no other case should any code alter say__pc except via the routines below.

I was also fiddling with say__pc on my own to avoid line breaks between escape detection rules, but:

(2d) PARA_NORULEBOOKBREAKS suppresses divide paragraph points in between rules in rulebooks; it treats all rulebooks, and in particular action rulebooks, the way activity rulebooks are treated. (The flag is used for short periods only and never across turn boundaries, prompts and so on.)

There’s a single routine that uses this, and I can emulate it to hopefully remove the rule breaks without causing serious damage.

Maybe we should push Graham for an overhaul to the printing system. It seems like too many implementation details need to be handled. It would be better if there was a single variable we could set to turn off paragraph breaks.

2 Likes

Mostly I want it to not print anything except at say time.

If it kept track of the newline state until say time and then printed all the necessary newlines, then you could adjust the state before that moment and make it do anything you wanted.

4 Likes

Alright, here’s a new version. I’ll put it on Github once I’ve stress-tested it a bit more, but it appears to work on both Z-machine and Glulx. Paragraphing now appears to work properly, at least in a few test cases, including capturing absolutely nothing.

The one case where I think it may not work properly, based on how I coded it, is if you print something that should prompt a paragraph break, then print empty captured text, that paragraph break might be lost. But I haven’t been able to make this happen in practice, so some arcane quirk of Inform’s paragraphing code might be fixing this problem before it happens.

It also now provides the phrase “say the/-- captured text without formatting”. This does exactly what you’d expect. I was hoping I could use phrase options, but you can’t do that in say phrases.

Bonus: I finally found a reason to “use ineffectual”, the use-option that does nothing! This extension now replaces Text Capture completely, but a lot of other extensions specifically depend on Text Capture, so I needed to dike out each heading of Text Capture and replace it with nothingness. But Inform (9.3 at least) crashes if you replace a non-empty heading with an empty one. So I put “use ineffectual” into each heading to placate it.

Formatting Capture.i7x (7.3 KB)

Please do put it through its paces; I’ve ensured the basic effect works on both Z8 and Glulx, but I want a general utility extension like this to be as bug-free as possible.

2 Likes

So far, in all the cases I’ve tried,

(call rules that print stuff)

is showing the same behavior as

start capturing text;
(call rules that print stuff)
stop capturing text;
say the captured text;

(I’m using Glulx in version 6L38.)

Thanks.

1 Like