Skip to content

Commit

Permalink
Fix explicit line breaks in comments.
Browse files Browse the repository at this point in the history
Given the bugs in the (quite ancient) C code, I wonder whether this ever
worked correctly even in classic pd-l2ork. But it was definitely broken
in Purr Data. The changes required to fix it are both in the C code and
on the GUI / JavaScript side.

Note that the implementation still uses \v (a.k.a. vertical tab a.k.a.
^K a.k.a. ASCII code 11) as a substitute for the \n newline character
(which the Pd binbuf eats for breakfast, as it is just whitespace,
whereas it leaves \v alone). These "fake" newline characters will only
ever occur in comments when using explicit line breaks. They *will* end
up in the patch file; you can verify this by looking at the .pd file in
any decent text editor capable of displaying control characters.

Vanilla Pd doesn't know how to handle those \v characters, so depending
on its canvas font it will render them as funny-looking glyphs instead.
Thus you better avoid explicit line breaks if you want to keep your
patches "vanilla-clean".
  • Loading branch information
agraef committed Sep 19, 2024
1 parent d7847bb commit f00fe78
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 52 deletions.
34 changes: 25 additions & 9 deletions pd/nw/pd_canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,9 @@ var canvas_events = (function() {
if (msg.length <= chunk_max) {
out_array.push([msg]);
} else {
in_array = msg.split(/[\s\n]/); // split on newlines or spaces
// ag: make sure to exclude \v below since we need these as
// newline substitutes which survive binbuf treatment
in_array = msg.split(/[ \t\n]/); // split on newlines or spaces
while (in_array.length) {
left = in_array.slice(); // make a copy of in_array
if (left.toString().length > chunk_max) {
Expand Down Expand Up @@ -1012,18 +1014,32 @@ var canvas_events = (function() {
// here. I want those newlines: although that isn't
// standard in Pd-Vanilla, Pd-l2ork uses and preserves
// them inside comments
var fudi_msg = text_to_fudi(textbox().innerText),
fudi_array = string_to_array_of_chunks(fudi_msg),
i;
for (i = 0; i < fudi_array.length; i++) {
pdgui.pdsend(name, "obj_addtobuf", fudi_array[i].join(" "));
var msg = textbox().innerText;
// GB: find obj class and remove word "selected"
let obj = document
.getElementById(textbox().getAttribute("tag")+"gobj");
let obj_class = obj.getAttribute("class")
.toString().split(" ").slice(0,1).toString();
if (obj_class == "comment") {
// ag: Visual comment formatting: We need to replace \n
// with \v to protect the newlines from the binbuf
// routines on the C side -- for them, \n is just
// whitespace which they eat for breakfast. However, we
// also need to preserve comma and semicolon atoms which
// get special treatment from the binfuf parser.
msg = msg.replace(/\n/g, "\v");
msg = msg.replace(/,[ \t]*\v/g, ", \v");
msg = msg.replace(/;[ \t]*\v/g, "; ");
}
var fudi_msg = text_to_fudi(msg),
fudi_array = string_to_array_of_chunks(fudi_msg);
for (var i = 0; i < fudi_array.length; i++) {
var chunk = fudi_array[i].join(" ");
pdgui.pdsend(name, "obj_addtobuf", chunk);
}
pdgui.pdsend(name, "obj_buftotext");

// GB: index created object
let obj = document.getElementById(textbox().getAttribute("tag")+"gobj");
// find obj class and remove word "selected"
let obj_class = obj.getAttribute("class").toString().split(" ").slice(0,1).toString();
if (pdgui.autocomplete_enabled()) {
pdgui.index_obj_completion(obj_class, fudi_msg);
// GB: save every 50 changes, so that we don't loose too
Expand Down
15 changes: 15 additions & 0 deletions pd/nw/pdgui.js
Original file line number Diff line number Diff line change
Expand Up @@ -3803,6 +3803,8 @@ function text_to_tspans(cid, svg_text, text) {
dy: i == 0 ? 0 : text_line_height_kludge(+fontsize, "gui") + "px",
x: 0
});
// tspan needs at least one char so that empty lines are preserved
if (lines[i] == "") lines[i] = " ";
// find a way to abstract away the canvas array and the DOM here
text_node = patchwin[cid].window.document
.createTextNode(lines[i]);
Expand Down Expand Up @@ -7697,6 +7699,19 @@ function gui_textarea(cid, tag, type, x, y, width_spec, height_spec, text,
gui(cid).get_nw_window(function(nw_win) {
zoom = nw_win.zoomLevel;
});
if (type === "obj") {
// ag: Replace \v -> \n in visually formatted comments so that we
// render them as newlines when editing a comment. We also remove any
// \n immediately preceding a \v, which may have been added
// automatically by the binbuf unparser after a semicolon.

// NOTE: \v (a.k.a. vertical tab a.k.a. ^K a.k.a. ASCII code 11) will
// only ever occur in comments when using explicit line breaks. These
// *will* end up in the patch file, and render as funny-looking glyphs
// in vanilla, so you better avoid explicit line breaks if you want to
// keep your patches "vanilla-clean".
text = text.replace(/\n?\v/g, "\n");
}
if (state !== 0) {
// Make sure we're in editmode
canvas_set_editmode(cid, 1);
Expand Down
4 changes: 2 additions & 2 deletions pd/src/g_canvas.c
Original file line number Diff line number Diff line change
Expand Up @@ -3173,7 +3173,7 @@ static void canvas_f(t_canvas *x, t_symbol *s, int argc, t_atom *argv)
}
// if we are part of a restore message
// of a subpatch in the form "#X restore..., f 123456789+;"
if (!x->gl_list || !strcmp(last_typedmess->s_name, "restore"))
if (!x->gl_list && !strcmp(last_typedmess->s_name, "restore"))
{
if (x->gl_owner && !x->gl_isgraph)
{
Expand All @@ -3193,7 +3193,7 @@ static void canvas_f(t_canvas *x, t_symbol *s, int argc, t_atom *argv)
}
else
{
for (g = x->gl_list; g2 = g->g_next; g = g2)
for (g = x->gl_list; g && (g2 = g->g_next); g = g2)
;
//fprintf(stderr,"same canvas .x%zx .x%zx\n", (t_uint)g, (t_uint)x);
}
Expand Down
6 changes: 4 additions & 2 deletions pd/src/g_rtext.c
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,10 @@ static void rtext_senditup(t_rtext *x, int action, int *widthp, int *heightp,
nlines++;
}
// append new line in case we end our input with an \n
if (x_bufsize_c > 0 && (x->x_buf[x_bufsize_c - 1] == '\n' || x->x_buf[x_bufsize_c - 1] == '\v'))
// suppressed at the end of a comment (backport from pd-l2ork)
int iscomment = pd_class(&x->x_text->te_pd) == text_class &&
x->x_text->te_type == T_TEXT;
if (!iscomment && x_bufsize_c > 0 && (x->x_buf[x->x_bufsize - 1] == '\n' || x->x_buf[x->x_bufsize - 1] == '\v'))
{
nlines++;
tempbuf[outchars_b++] = '\n';
Expand Down Expand Up @@ -357,7 +360,6 @@ static void rtext_senditup(t_rtext *x, int action, int *widthp, int *heightp,
rtext_senditup(x, 0, &newwidth, &newheight, &newindex);
if (newwidth/fontwidth != widthwas)
x->x_text->te_width = widthwas;
else x->x_text->te_width = 0;
}
if (action == SEND_FIRST)
{
Expand Down
39 changes: 0 additions & 39 deletions pd/src/g_text.c
Original file line number Diff line number Diff line change
Expand Up @@ -2606,33 +2606,6 @@ void text_save(t_gobj *z, t_binbuf *b)
}
else
{
//fprintf(stderr,"comment\n");
int natom = binbuf_getnatom(x->te_binbuf);
t_atom *a = binbuf_getvec(x->te_binbuf);
int i;
for (i = 0; i < natom; i++)
{
t_symbol *s;
if (a[i].a_type == A_SYMBOL)
{
//fprintf(stderr,"%d is a symbol\n", i);
s = a[i].a_w.w_symbol;
if (s != NULL && s->s_name != NULL)
{
//fprintf(stderr,"s != NULL\n");
char *c;
for(c = s->s_name; c != NULL && *c != '\0'; c++)
{
if (*c == '\n')
{
*c = '\v';
//fprintf(stderr,"n->v\n");
}
}
}
}
}

binbuf_addv(b, "ssii", gensym("#X"), gensym("text"),
(int)x->te_xpix, (int)x->te_ypix);
binbuf_addbinbuf(b, x->te_binbuf);
Expand Down Expand Up @@ -3143,18 +3116,6 @@ void text_setto(t_text *x, t_glist *glist, char *buf, int bufsize, int pos)
}
else
{ // T_MESSAGE, T_TEXT, T_ATOM
if (buf && x->te_type == T_TEXT)
{
char *c;
int n;
for(c = buf, n = 0; n < bufsize; n++, c++)
{
if(*c == '\n')
{
*c = '\v';
}
}
}
binbuf_gettext(x->te_binbuf, &c1, &i1);
t_binbuf *b = binbuf_new();
binbuf_text(b, buf, bufsize);
Expand Down

0 comments on commit f00fe78

Please sign in to comment.