Closed samth closed 3 weeks ago
Thanks for the report.
Although I'm 99% sure configure-runtime
is being called, I'll double check.
The bug seems to require that the Emacs customization variable racket-pretty-print
is the default t
, which causes the back end to use pretty-print
. I can reproduce with that, but not with plain print
, which IIUC is what command-line racket
would do.
Although that narrows it down a little, I'm not yet sure exactly where/what the problem is, and who needs to solve it, how. But I'll look...
What I've been able to figure out so far:
rhombus' configure-runtime sets a special value for global-port-print-handler
parameter. This is happening in Racket Mode. (Amusingly I can see it affecting log-debug
~v
values, from Racket code.)
However that's N/A -- the current-print
parameter is what is used by (a) REPL printing and (b) #%module-begin
. The latter is applicable when you run your example program.
In addition to struct props for individual values, there is a small zoo of print-ish handlers:
current-print
per-thread parameterport-{print display write}-hander
per-port functionsglobal-port-print-handler
parameterI am not 100% confident which are intended to be used by (a) end user programs, (b) languages, and (c) tools.
I do know that Racket mode (re)sets these before each run, but doesn't touch values changed by the language, libs, and/or end user program (which AFAICT is the right thing to do?).
I am not sure but maybe rhombus ought to set current-print
and perhaps pretty-print-{print size}-hook
functions? Or at least change its #%module-begin
not to use current-print
?? Again I'm not 100% clear what's intended.
Just calling Racket's pretty-print
in Rhombus results in the same printing as I see in racket-mode:
#lang rhombus
import lib("racket/main.rkt") as r:
expose #{pretty-print} as pretty_print
class M(x)
pretty_print(M(1))
I'm not sure what exactly the right configuration should be here, maybe @mflatt would have a suggestion. Alternatively, maybe just drop automatic pretty-print for rhombus files?
Alternatively, maybe just drop automatic pretty-print for rhombus files?
Yes. In fact it occurs to me now that racket/pretty
is thoroughly s-expression oriented. I recall seeing code in rhombus suggesting it has its own concept of pretty printing, which makes sense.
I'd love that to be lang
-driven not ad hoc, if possible. That is, some way for a lang to say, "If a user prefers pretty-printing for module-begin and REPL, my global print handler already does that by default, just use that." For example.
p.s. I think it's also possible that racket/pretty
might have a bug where it's not using the global print handler, somehow? But that might be moot, here.
Thinking about this a little bit more, maybe I should use pretty-print
only if the user prefers that, and default-global-port-print-handler
is in effect:
(define (make-racket-mode-print-handler pretty? columns pixels/char)
(define (racket-mode-print-handler v)
(unless (void? v)
(define-values (in out) (make-value-pipe))
(parameterize ([current-output-port out]
[print-syntax-width +inf.0])
(cond
- [pretty?
+ [(and pretty?
+ (equal? (global-port-print-handler)
+ default-global-port-print-handler))
(parameterize ([pretty-print-columns columns]
[pretty-print-size-hook (make-pp-size-hook pixels/char)]
[pretty-print-print-hook (make-pp-print-hook)])
(pretty-print v))]
[else
(match (convert-image v)
[(cons path-name _pixel-width)
(write-special (cons 'image path-name))]
[_
(print v)])
(newline)]))
(drain-value-pipe in out)))
racket-mode-print-handler)
In a quick test, this solves the immediate issue.
The rationale seems... plausible? "If a language has changed the default global port print handler, we can't assume it's an s-expression language and pretty-print
is appropriate. In fact it's best not to assume anything at all -- just use print
and let the handler do its thing."
Anyway that's my least-worst idea, so far. I'll ponder it a bit more. Of course I'm also open to better ideas or advice.
Although I might hold off in case anyone has better ideas, I might go ahead and merge commit bceac44.
It does help with the reported problem.
Admittedly pretty printing would be disabled if something changed the global port print handler, for any reason -- even if the values could still be pretty-printed as s-expressions. So that's a downside. But the values would still be printed.
[I could also enhance the racket-pretty-print
variable from a boolean, to something more complicated, such as three values (always, never, only if default global print handler), or even a dictionary from module language names to such settings. But... I'd prefer to do that with a real-world use-case and user to confirm it works for them, as opposed to proactively.]
Just as an additional note, the racket repl uses pretty-print
and has this problem as well.
Just as an additional note, the racket repl uses
pretty-print
and has this problem as well.
Interesting. It looks like:
The default value of current-print
where it's defined in racket/src/io/run/main.rkt
is just print
+ newline
. And if you $ racket -l racket/base -i
, current-print
in the REPL is that plain, non-pretty print.
However racket/collects/racket/init.rkt
sets current-print
to a pretty-printer
procedure that calls pretty-print
. Which is what you get from the REPL for $ racket
, which opens in the full racket
lang.
I suppose one could say, "Well, you should use $ racket -l rhombus -i
".
On the other hand people do things like run $ racket
, then use xrepl's ,enter
command one or more times during a session, which could be a program using any module language. I think the expectation is this just "just work". (And that's closer to the model for things like Racket Mode, DrRacket, and other such tools.)
So it seems current-print
needs to be lang-driven, somehow. Or pretty-print
needs to be lang-driven. Or there's some new current-pretty-printer
parameter, which can be racket/pretty
's pretty-print
, or whatever else the lang decides. Or... something...
On the other hand people do things like run
$ racket
, then use xrepl's,enter
command one or more times during a session, which could be a program using any module language.
Well. You might use ,enter
to run a #lang rhombus
program... once. Thereafter, you're stuck. AFAICT. The xrepl command trick ("we can use ,
because a stand-alone quasiunquote
isn't legal") doesn't work anymore, understandably for rhombus syntax.
p.s. Also I couldn't ,exit
. (Not even C-c worked. I had to kill the terminal shell window.)
No real answers, but some possibly relevant information.
The Racket REPL uses pretty-print
because the default -I
is -I racket/init
, and it's racket/init
that installs the pretty printer. To work in Rhombus mode, I use -I rhombus
, which does not install the racket/pretty
printer, but does install a Rhombus pretty printer via configure-runtime
. Note that -I racket
would not set the printer, because its configure-runtime
doesn't do that.
DrRacket injects pretty printing by setting global-port-print-handler
, as opposed to current-print
. The rhombus
configure-runtime
submodule replaces that global port print handler with one that uses Rhombus printing. Meanwhile, note that racket/init
sets current-print
, but rhombus
(via configure-runtime
) sets global-port-print-handler
.
XREPL recognizes ,exit
and similar even when starting with -I rhombus
. That's because XREPL checks an input string for whitespace followed by ,
. The shrubbery syntax and reader cooperate slightly by agreeing that something like ,exit
is ready to evaluate (as opposed to ,(
, for example), even though it would read as an error.
Using ,enter
in XREPL uses enter!
, which does not attempt to load configure-runtime
or similar, and the configure-runtime
protocol is not really set up for going into different modules. The configure-expand
protocol is more set up for that with enter-parameterization
and exit-parameterization
, which is more compatible with the idea of switching contexts. But the protocol does that by swapping whole parameterizations; I'm not sure that's the right idea for run-time swapping, but maybe it would work. In short, to make a REPL adapt to different contexts, probably we'd want.a new submodule with a enter—exit protocol, and maybe the configure-expand
approach would be good for that.
Should racket/init
change to use global-port-print-handler
?
OK I've been working on using global-port-print-handler
instead of current-print
to use racket/pretty
, allowing things like rhombus to override. So far, "it works".
I've also been working on using a (non-global) port-print-handler
associated with the REPL's current-output-port, to handle images. (There is a dance where racket/convert
-ible images are written to temp files, then a write-special
of the filename. That way the Emacs front end can show the images in the REPL buffer.)
So far that works for lang racket. Can you help me test, with two minimal examples of image values, in rhombus?
#lang racket/base
(require 2htdp/image)
(circle 20 "solid" "green")
I'd like to verify that works for rhombus, too. (The image handling before was tangled with the pretty-printing. Speaking of which...)
#lang racket/base
(require 2htdp/image)
(for/list ([n 3]) (circle 20 "solid" "green"))
I'd like to see if that works for "nested" values with rhombus' notion of pretty-printing, like it does for racket/pretty
.
The 2htdp/image
part is incidental; anything that emits convertible image objects of whatever kind.
Considering both 1 and 2, I'd hope it would look something like the following (modulo how rhombus prints lists):
OK it looks like an example could be:
#lang rhombus
import pict
pict.circle(~size: 40, ~fill: "lightgreen")
[pict.circle(~size: 40, ~fill: "lightgreen"),
pict.circle(~size: 40, ~fill: "lightgreen")]
and that seems to work:
Yeah here's your example more literally:
#lang rhombus
import pict
pict.circle(~size: 20, ~fill: "green")
for List:
each x: 3
pict.circle(~size: 20, ~fill: "green")
Should
racket/init
change to useglobal-port-print-handler
?
Sounds like it should?
If a commit changed that, IMHO ideally the commit would also change the current-print
documentation?
Like if current-print
is deprecated, a sentence or two explaining why. Or if "deprecated" is too strong, then whatever the background story and rationale for why/when to prefer global-port-print-handler
.
(I plan to make the change, regardless. I'm just curious why. :smile: current-print
applies to two specific print situations... but is that a limitation or an advantage? global-port-print-handler
applies to all prints on all ports that don't set up a specific handler... but is that "covering the bases", or potentially causing surprises?)
Should
racket/init
change to useglobal-port-print-handler
?
I am not sure, but I think probably not.
I'm thinking that there are two potential dimensions to customize: the syntax used for values, and the style (insignificant whitespace and such) used to write that syntax. I think of global-port-print-handler
as a way to change syntax, while current-print
is about changing the style used in an interactive context such as a REPL.
But it's hard to detangle those, since they are not independent (as least as they exist now). Also, environments like DrRacket really were trying to change syntax in the sense of changing the way images print. Picts print as expected in Rhombus not because they are intercepted at the level of DrRacket's print handler, but because the Rhombus pretty printer lets a class render viawrite-special
, and Rhombus Pict
values print that way. A consequence of this shift, though, is that a Racket pict does not render as an image in DrRacket in a #lang rhombus
panel. I think Rhombus is headed more in the right direction here, but I'm not certain.
If current-print
could somehow control just the style, then that seems like more the right thing for an environment like DrRacket to configure. Currently, it works better for DrRacket to set global-port-print-handler
, but that's because it lets Rhombus replace it and ignore DrRacket's attempt. That is, DrRacket and other environments don't have a good way to customize the style of printing. Maybe that's ok; maybe the idea of changing the style outside the language doesn't really make sense. For that perspective, DrRacket's use of global-port-print-handler
is an accommodation specific to racket
and the way its printing configuration evolved (e.g., for picts).
Meanwhile, racket/init
is tied to racket
, and that's why it can usefully adjust the style of racket
value printing without trying to change its syntax. And that line of thought suggests that racket/init
should stay the way it is.
But that leaves the potential problem of printing Racket pict values in #lang rhombus
mode. Rhombus also doesn't support literal images pasted into the program, and I'd like to recover that functionality in some form, which seems related. So all my conclusions are especially tentative.
I guess my own mental inventory is:
Rendering an image value (either directly in a GUI, or via some marshaling tricks as in Racket Mode) is about a single, "atomic" value. So the way I think about it, that has nothing to do with syntax or pretty-printing or code-formatting. Images are just another individual value for those. What is relevant (I think) is whether the port can/should render image values specially. Seems like a job for a non-global port-print-handler
.
A nice feature of DrRacket's REPL (and within the last year or so, Racket Mode's), is that the hopeless mix of REPL output is instead structured and can be presented differently. So e.g. printed values can be a different color than "raw" displayed output to the port. Again, to me, this seems independent of how the value(s) are formatted, and more relevant to the port (can it even show color). [This is where current-print
seemed like the best abstraction, to me. Just color these top-level values and REPL evalaution results. But, I guess it's OK if any print
to the REPL port gets that color -- which is true since my commit.]
The pretty vs. plain distinction exists, but.. probably it's OK in 2024 to assume most people want pretty. Printing is intended for humans to read. (Secondarily, things like Emacs can struggle within enormous single lines, although recent versions have improved.) Plus of course some langs are indent and space sensitive, and "pretty" = "plain". Like Python and Rhombus IIUC. So part of me feels like this needn't even be configurable anymore, and should just be hardwired ("pretty" is the configure-runtime default). And this feels like what global-port-print-handler
can/should do.
Again that's just my own perspective, definitely not 100.0% convinced or passionate about any particular point.
p.s.
Rendering an image value (either directly in a GUI, or via some marshaling tricks as in Racket Mode) is about a single, "atomic" value. So the way I think about it, that has nothing to do with syntax or pretty-printing or code-formatting. Images are just another individual value for those.
I meant they're just another value with some width, for purposes of pretty-printing according to the usual rules for whatever the pretty layout is.
But knowing each width is of course relevant.
So something like the racket/pretty
idea of a size-hook and print-hook is relevant.
Please copy all of the following lines and paste them into your bug report at https://github.com/greghendershott/racket-mode/issues/.
Package
System values
Buffer values
Racket Mode values
Minor modes
Disabled minor modes
Steps to reproduce:
Write a rhombus file like this:
Run it in racket-mode.
It prints like this:
Run the file with racket at the command line.
It prints like this:
My theory is that racket-mode is not calling
configure-runtime
correctly but I have not investigated this at all.