## handling malformed programs container environment [ recipe-errors:text ] # copy code from recipe editor, persist to disk, load, save any errors def! update-recipes env:&:environment, resources:&:resources, screen:&:screen -> errors-found?:bool, env:&:environment, resources:&:resources, screen:&:screen [ local-scope load-inputs recipes:&:editor <- get *env, recipes:offset in:text <- editor-contents recipes resources <- dump resources, [lesson/recipes.mu], in recipe-errors:text <- reload in *env <- put *env, recipe-errors:offset, recipe-errors # if recipe editor has errors, stop { break-unless recipe-errors update-status screen, [errors found ], 1/red errors-found? <- copy true return } errors-found? <- copy false ] after [ old-recipe-errors:text <- get *env, recipe-errors:offset ] before [ # if there were recipe errors before, check if we can clear them { break-unless old-recipe-errors screen <- render-recipes screen, env, render } render-recipe-errors env, screen ] before [ row <- render-recipe-errors env, screen ] def render-recipe-errors env:&:environment, screen:&:screen -> row:num, screen:&:screen [ local-scope load-inputs recipe-errors:text <- get *env, recipe-errors:offset recipes:&:editor <- get *env, recipes:offset row:num <- get *recipes, bottom:offset row <- add row, 1 return-unless recipe-errors left:num <- get *recipes, left:offset right:num <- get *recipes, right:offset row, screen <- render-text screen, recipe-errors, left, right, 1/red, row # draw dotted line after recipes draw-horizontal screen, row, left, right, 9480/horizontal-dotted row <- add row, 1 clear-screen-from screen, row, left, left, right ] container environment [ error-index:num # index of first sandbox with an error (or -1 if none) ] after [ *result <- put *result, error-index:offset, -1 ] after [ *env <- put *env, error-index:offset, -1 ] before [ { error-index:num <- get *env, error-index:offset sandboxes-completed-successfully?:bool <- equal error-index, -1 break-if sandboxes-completed-successfully? errors-found? <- copy true } ] before [ { break-unless error? recipe-errors:text <- get *env, recipe-errors:offset break-if recipe-errors error-index:num <- get *env, error-index:offset sandboxes-completed-successfully?:bool <- equal error-index, -1 break-if sandboxes-completed-successfully? error-index-text:text <- to-text error-index status:text <- interpolate [errors found (_) ], error-index-text update-status screen, status, 1/red } ] container sandbox [ errors:text ] def! update-sandbox sandbox:&:sandbox, env:&:environment, idx:num -> sandbox:&:sandbox, env:&:environment [ local-scope load-inputs data:text <- get *sandbox, data:offset response:text, errors:text, fake-screen:&:screen, trace:text, completed?:bool <- run-sandboxed data *sandbox <- put *sandbox, response:offset, response *sandbox <- put *sandbox, errors:offset, errors *sandbox <- put *sandbox, screen:offset, fake-screen *sandbox <- put *sandbox, trace:offset, trace { break-if errors break-if completed? errors <- new [took too long! ] *sandbox <- put *sandbox, errors:offset, errors } { break-unless errors error-index:num <- get *env, error-index:offset error-not-set?:bool <- equal error-index, -1 break-unless error-not-set? *env <- put *env, error-index:offset, idx } ] # make sure we render any trace after [ { sandbox-errors:text <- get *sandbox, errors:offset break-unless sandbox-errors *sandbox <- put *sandbox, response-starting-row-on-screen:offset, 0 # no response row, screen <- render-text screen, sandbox-errors, left, right, 1/red, row # don't try to print anything more for this sandbox jump +render-sandbox-end } ] scenario run-shows-errors-in-get [ local-scope trace-until 100/app # trace too long assume-screen 100/width, 15/height assume-resources [ [lesson/recipes.mu] <- [ |recipe foo [| | get 123:num, foo:offset| |]| ] ] env:&:environment <- new-programming-environment resources, screen, [foo] render-all screen, env, render screen-should-contain [ . run (F4) . .recipe foo [ ┊foo . . get 123:num, foo:offset ┊─────────────────────────────────────────────────. .] ┊ . . ┊ . .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊ . . ┊ . ] assume-console [ press F4 ] run [ event-loop screen, console, env, resources ] screen-should-contain [ . errors found run (F4) . .recipe foo [ ┊foo . . get 123:num, foo:offset ┊─────────────────────────────────────────────────. .] ┊ . . ┊ . .foo: unknown element 'foo' in container 'number' ┊ . .foo: first ingredient of 'get' should be a contai↩┊ . .ner, but got '123:num' ┊ . .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊ . . ┊ . ] screen-should-contain-in-color 1/red, [ . errors found . . . . . . . . . .foo: unknown element 'foo' in container 'number' . .foo: first ingredient of 'get' should be a contai . .ner, but got '123:num' . . . ] ] scenario run-shows-errors-without-final-newline-in-recipe-side [ local-scope trace-until 100/app # trace too long assume-screen 100/width, 15/height assume-resources [ ] env:&:environment <- new-programming-environment resources, screen render-all screen, env, render assume-console [ type [recipe foo x [ ]] press F4 ] run [ event-loop screen, console, env, resources ] screen-should-contain [ . errors found run (F4) . .recipe foo x [ ┊ . .] ┊─────────────────────────────────────────────────. .foo: ingredient 'x' has no type ┊ . .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊ . . ┊ . ] ] scenario run-updates-status-with-first-erroneous-sandbox [ local-scope trace-until 100/app # trace too long assume-screen 100/width, 15/height assume-resources [ ] env:&:environment <- new-programming-environment resources, screen, [] render-all screen, env, render assume-console [ left-click 3, 80 # create invalid sandbox 1 type [get foo, x:offset] press F4 # create invalid sandbox 0 type [get foo, x:offset] press F4 ] run [ event-loop screen, console, env, resources ] # status line shows that error is in first sandbox screen-should-contain [ . errors found (0) run (F4) . ] ] scenario run-updates-status-with-first-erroneous-sandbox-2 [ local-scope trace-until 100/app # trace too long assume-screen 100/width, 15/height assume-resources [ ] env:&:environment <- new-programming-environment resources, screen, [] render-all screen, env, render assume-console [ left-click 3, 80 # create invalid sandbox 2 type [get foo, x:offset] press F4 # create invalid sandbox 1 type [get foo, x:offset] press F4 # create valid sandbox 0 type [add 2, 2] press F4 ] run [ event-loop screen, console, env, resources ] # status line shows that error is in second sandbox screen-should-contain [ . errors found (1) run (F4) . ] ] scenario run-hides-errors-from-past-sandboxes [ local-scope trace-until 100/app # trace too long assume-screen 100/width, 15/height assume-resources [ ] env:&:environment <- new-programming-environment resources, screen, [get foo, x:offset] # invalid render-all screen, env, render assume-console [ press F4 # generate error ] event-loop screen, console, env, resources assume-console [ left-click 3, 58 press ctrl-k type [add 2, 2] # valid code press F4 # update sandbox ] run [ event-loop screen, console, env, resources ] # error should disappear screen-should-contain [ . run (F4) . . ┊ . .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────. . ┊0 edit copy to recipe delete . . ┊add 2, 2 . . ┊4 . . ┊─────────────────────────────────â
/* (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
 * See LICENSE file for license details.
 */

#include "dwm.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <X11/cursorfont.h>
#include <X11/keysym.h>
#include <X11/Xatom.h>
#include <X11/Xproto.h>

/* extern */

char stext[1024];
Bool *seltag;
int bx, by, bw, bh, bmw, masterd, screen, sx, sy, sw, sh;
unsigned int master, ntags, numlockmask;
Atom wmatom[WMLast], netatom[NetLast];
Bool running = True;
Bool issel = True;
Client *clients = NULL;
Client *sel = NULL;
Client *stack = NULL;
Cursor cursor[CurLast];
Display *dpy;
DC dc = {0};
Window root, barwin;

/* static */

static int (*xerrorxlib)(Display *, XErrorEvent *);
static Bool otherwm, readin;

static void
cleanup(void) {
	close(STDIN_FILENO);
	while(sel) {
		resize(sel, True, TopLeft);
		unmanage(sel);
	}
	if(dc.font.set)
		XFreeFontSet(dpy, dc.font.set);
	else
		XFreeFont(dpy, dc.font.xfont);
	XUngrabKey(dpy, AnyKey, AnyModifier, root);
	XFreePixmap(dpy, dc.drawable);
	XFreeGC(dpy, dc.gc);
	XDestroyWindow(dpy, barwin);
	XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
	XSync(dpy, False);
	free(seltag);
}

static void
scan(void) {
	unsigned int i, num;
	Window *wins, d1, d2;
	XWindowAttributes wa;

	wins = NULL;
	if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
		for(i = 0; i < num; i++) {
			if(!XGetWindowAttributes(dpy, wins[i], &wa))
				continue;
			if(wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
				continue;
			if(wa.map_state == IsViewable)
				manage(wins[i], &wa);
		}
	}
	if(wins)
		XFree(wins);
}

static void
setup(void) {
	int i, j;
	unsigned int mask;
	Window w;
	XModifierKeymap *modmap;
	XSetWindowAttributes wa;

	/* init atoms */
	wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
	wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
	netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
	netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
	XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
			PropModeReplace, (unsigned char *) netatom, NetLast);
	/* init cursors */
	cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr);
	cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing);
	cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur);
	/* init modifier map */
	modmap = XGetModifierMapping(dpy);
	for (i = 0; i < 8; i++) {
		for (j = 0; j < modmap->max_keypermod; j++) {
			if(modmap->modifiermap[i * modmap->max_keypermod + j] == XKeysymToKeycode(dpy, XK_Num_Lock))
				numlockmask = (1 << i);
		}
	}
	XFree(modmap);
	/* select for events */
	wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask
		| EnterWindowMask | LeaveWindowMask;
	wa.cursor = cursor[CurNormal];
	XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa);
	grabkeys();
	initrregs();
	for(ntags = 0; tags[ntags]; ntags++);
	seltag = emallocz(sizeof(Bool) * ntags);
	seltag[0] = True;
	/* style */
	dc.norm[ColBG] = getcolor(NORMBGCOLOR);
	dc.norm[ColFG] = getcolor(NORMFGCOLOR);
	dc.sel[ColBG] = getcolor(SELBGCOLOR);
	dc.sel[ColFG] = getcolor(SELFGCOLOR);
	dc.status[ColBG] = getcolor(STATUSBGCOLOR);
	dc.status[ColFG] = getcolor(STATUSFGCOLOR);
	setfont(FONT);
	/* geometry */
	bmw = textw(TILESYMBOL) > textw(FLOATSYMBOL) ?  textw(TILESYMBOL) : textw(FLOATSYMBOL);
	sx = sy = 0;
	sw = DisplayWidth(dpy, screen);
	sh = DisplayHeight(dpy, screen);
	master = MASTER;
	/* bar */
	bx = by = 0;
	bw = sw;
	dc.h = bh = dc.font.height + 2;
	wa.override_redirect = 1;
	wa