1 #include "gtk_console.h"
2 
3 #include <math.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <time.h>
8 #include <gtksourceview/gtksource.h>
9 #include <gdk-pixbuf/gdk-pixbuf.h>
10 #include <girepository.h>
11 #include <gc/gc.h>
12 #include "minilang.h"
13 #include "ml_macros.h"
14 #include "stringmap.h"
15 #include "ml_compiler.h"
16 #include <sys/stat.h>
17 
18 #include "ml_gir.h"
19 #include "ml_runtime.h"
20 #include "ml_bytecode.h"
21 #include "ml_debugger.h"
22 
23 #define MAX_HISTORY 128
24 
25 struct console_t {
26 	ml_state_t Base;
27 	const char *Name;
28 	GtkWidget *Window, *LogScrolled, *LogView, *InputView, *SourceView;
29 	GtkWidget *DebugButtons, *Paned;
30 	GtkNotebook *Notebook;
31 	GtkSourceLanguage *Language;
32 	GtkSourceStyleScheme *StyleScheme;
33 	GtkLabel *MemoryBar;
34 	GtkTextTag *OutputTag, *ResultTag, *ErrorTag, *BinaryTag;
35 	GtkTextMark *EndMark;
36 	GtkSourceBuffer *SourceBuffer;
37 	ml_getter_t ParentGetter;
38 	void *ParentGlobals;
39 	interactive_debugger_t *Debugger;
40 	const char *ConfigPath;
41 	const char *FontName;
42 	GKeyFile *Config;
43 	PangoFontDescription *FontDescription;
44 	ml_parser_t *Parser;
45 	ml_compiler_t *Compiler;
46 	char *History[MAX_HISTORY];
47 	int HistoryIndex, HistoryEnd;
48 	stringmap_t SourceViews[1];
49 	stringmap_t Cycles[1];
50 	stringmap_t Combos[1];
51 	char Chars[32];
52 	int NumChars;
53 };
54 
55 #ifdef MINGW
stpcpy(char * Dest,const char * Source)56 static char *stpcpy(char *Dest, const char *Source) {
57 	while (*Source) *Dest++ = *Source++;
58 	return Dest;
59 }
60 
61 #define lstat stat
62 #endif
63 
console_global_get(console_t * Console,const char * Name)64 static ml_value_t *console_global_get(console_t *Console, const char *Name) {
65 	if (Console->Debugger) {
66 		ml_value_t *Value = interactive_debugger_get(Console->Debugger, Name);
67 		if (Value) return Value;
68 	}
69 	return (Console->ParentGetter)(Console->ParentGlobals, Name);
70 }
71 
console_log(console_t * Console,ml_value_t * Value)72 void console_log(console_t *Console, ml_value_t *Value) {
73 	GtkTextIter End[1];
74 	GtkTextBuffer *LogBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(Console->LogView));
75 	gtk_text_buffer_get_end_iter(LogBuffer, End);
76 	if (ml_is_error(Value)) {
77 		char *Buffer;
78 		int Length = asprintf(&Buffer, "%s: %s\n", ml_error_type(Value), ml_error_message(Value));
79 		gtk_text_buffer_insert_with_tags(LogBuffer, End, Buffer, Length, Console->ErrorTag, NULL);
80 		ml_source_t Source;
81 		int Level = 0;
82 		while (ml_error_source(Value, Level++, &Source)) {
83 			Length = asprintf(&Buffer, "\t%s:%d\n", Source.Name, Source.Line);
84 			gtk_text_buffer_insert_with_tags(LogBuffer, End, Buffer, Length, Console->ErrorTag, NULL);
85 		}
86 	} else {
87 		ml_value_t *String = ml_simple_inline(MLStringT, 1, Value);
88 		if (ml_is(String, MLStringT)) {
89 			const char *Buffer = ml_string_value(String);
90 			int Length = ml_string_length(String);
91 			if (Length > 10240) {
92 				char Text[32];
93 				int TextLength = sprintf(Text, "<%d bytes>", Length);
94 				gtk_text_buffer_insert_with_tags(LogBuffer, End, Text, TextLength, Console->ResultTag, NULL);
95 			} else if (g_utf8_validate(Buffer, Length, NULL)) {
96 				gtk_text_buffer_insert_with_tags(LogBuffer, End, Buffer, Length, Console->ResultTag, NULL);
97 			} else {
98 				gtk_text_buffer_insert_with_tags(LogBuffer, End, "<", 1, Console->BinaryTag, NULL);
99 				for (int I = 0; I < Length; ++I) {
100 					static char HexChars[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
101 					char Bytes[4] = " ??";
102 					unsigned char Byte = Buffer[I];
103 					Bytes[1] = HexChars[Byte >> 4];
104 					Bytes[2] = HexChars[Byte & 15];
105 					gtk_text_buffer_insert_with_tags(LogBuffer, End, Bytes, 3, Console->BinaryTag, NULL);
106 				}
107 				gtk_text_buffer_insert_with_tags(LogBuffer, End, " >", 2, Console->BinaryTag, NULL);
108 			}
109 			gtk_text_buffer_insert_with_tags(LogBuffer, End, "\n", 1, Console->ResultTag, NULL);
110 		} else {
111 			char *Buffer;
112 			int Length = asprintf(&Buffer, "<%s>\n", ml_typeof(Value)->Name);
113 			gtk_text_buffer_insert_with_tags(LogBuffer, End, Buffer, Length, Console->ResultTag, NULL);
114 		}
115 	}
116 	gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(Console->LogView), Console->EndMark);
117 }
118 
119 ML_TYPE(ConsoleT, (), "console");
120 
ml_console_repl_run(console_t * Console,ml_value_t * Result)121 static void ml_console_repl_run(console_t *Console, ml_value_t *Result) {
122 	if (Result == MLEndOfInput) {
123 		gtk_widget_grab_focus(Console->InputView);
124 		return;
125 	}
126 	console_log(Console, Result);
127 	GtkTextIter End[1];
128 	GtkTextBuffer *LogBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(Console->LogView));
129 	gtk_text_buffer_get_end_iter(LogBuffer, End);
130 	gtk_text_buffer_insert(LogBuffer, End, "\n", 1);
131 	if (ml_is_error(Result)) {
132 		gtk_widget_grab_focus(Console->InputView);
133 		return;
134 	}
135 	return ml_command_evaluate((ml_state_t *)Console, Console->Parser, Console->Compiler);
136 }
137 
console_step_in(GtkWidget * Button,console_t * Console)138 static void console_step_in(GtkWidget *Button, console_t *Console) {
139 	ml_parser_t *Parser = Console->Parser;
140 	ml_compiler_t *Compiler = Console->Compiler;
141 	ml_parser_reset(Parser);
142 	ml_parser_input(Parser, "step_in()");
143 	ml_command_evaluate((ml_state_t *)Console, Parser, Compiler);
144 }
145 
console_step_over(GtkWidget * Button,console_t * Console)146 static void console_step_over(GtkWidget *Button, console_t *Console) {
147 	ml_parser_t *Parser = Console->Parser;
148 	ml_compiler_t *Compiler = Console->Compiler;
149 	ml_parser_reset(Parser);
150 	ml_parser_input(Parser, "step_over()");
151 	ml_command_evaluate((ml_state_t *)Console, Parser, Compiler);
152 }
153 
console_step_out(GtkWidget * Button,console_t * Console)154 static void console_step_out(GtkWidget *Button, console_t *Console) {
155 	ml_parser_t *Parser = Console->Parser;
156 	ml_compiler_t *Compiler = Console->Compiler;
157 	ml_parser_reset(Parser);
158 	ml_parser_input(Parser, "step_out()");
159 	ml_command_evaluate((ml_state_t *)Console, Parser, Compiler);
160 }
161 
console_continue(GtkWidget * Button,console_t * Console)162 static void console_continue(GtkWidget *Button, console_t *Console) {
163 	ml_parser_t *Parser = Console->Parser;
164 	ml_compiler_t *Compiler = Console->Compiler;
165 	ml_parser_reset(Parser);
166 	ml_parser_input(Parser, "continue()");
167 	ml_command_evaluate((ml_state_t *)Console, Parser, Compiler);
168 }
169 
console_submit(GtkWidget * Button,console_t * Console)170 static void console_submit(GtkWidget *Button, console_t *Console) {
171 	GtkTextBuffer *InputBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(Console->InputView));
172 	GtkTextIter InputStart[1], InputEnd[1];
173 	gtk_source_buffer_set_highlight_matching_brackets(GTK_SOURCE_BUFFER(InputBuffer), FALSE);
174 	gtk_text_buffer_get_bounds(InputBuffer, InputStart, InputEnd);
175 	const char *Text = gtk_text_buffer_get_text(InputBuffer, InputStart, InputEnd, FALSE);
176 
177 	int HistoryEnd = Console->HistoryEnd;
178 	Console->History[HistoryEnd] = GC_strdup(Text);
179 	Console->HistoryIndex = Console->HistoryEnd = (HistoryEnd + 1) % MAX_HISTORY;
180 
181 	GtkTextIter End[1];
182 
183 	GtkTextBuffer *LogBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(Console->LogView));
184 	gtk_text_buffer_get_end_iter(LogBuffer, End);
185 	gtk_source_buffer_create_source_mark(GTK_SOURCE_BUFFER(LogBuffer), NULL, "result", End);
186 	gtk_text_buffer_insert_range(LogBuffer, End, InputStart, InputEnd);
187 	gtk_text_buffer_insert(LogBuffer, End, "\n", -1);
188 	gtk_text_buffer_set_text(InputBuffer, "", 0);
189 
190 	GtkTextBuffer *SourceBuffer = GTK_TEXT_BUFFER(Console->SourceBuffer);
191 	gtk_text_buffer_get_end_iter(SourceBuffer, End);
192 	gtk_text_buffer_insert(SourceBuffer, End, Text, -1);
193 	gtk_text_buffer_insert(SourceBuffer, End, "\n", -1);
194 gtk_source_buffer_set_highlight_matching_brackets(GTK_SOURCE_BUFFER(InputBuffer), TRUE);
195 
196 
197 	//GtkTextIter End[1];
198 	//GtkTextBuffer *LogBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(Console->LogView));
199 	gtk_text_buffer_get_end_iter(LogBuffer, End);
200 	gtk_text_buffer_insert(LogBuffer, End, "\n", 1);
201 
202 	ml_parser_t *Parser = Console->Parser;
203 	ml_compiler_t *Compiler = Console->Compiler;
204 	ml_parser_reset(Parser);
205 	ml_parser_input(Parser, Text);
206 	ml_command_evaluate((ml_state_t *)Console, Parser, Compiler);
207 }
208 
console_debug_enter(console_t * Console,interactive_debugger_t * Debugger,ml_source_t Source,int Index)209 static void console_debug_enter(console_t *Console, interactive_debugger_t *Debugger, ml_source_t Source, int Index) {
210 	gtk_widget_show(Console->DebugButtons);
211 	Console->Debugger = Debugger;
212 	console_printf(Console, "Debug break [%d]: %s:%d\n", Index, Source.Name, Source.Line);
213 	GtkWidget *SourceView;
214 	if (Source.Name == Console->Name) {
215 		SourceView = Console->SourceView;
216 	} else {
217 		GtkWidget **Slot = (GtkWidget **)stringmap_slot(Console->SourceViews, Source.Name);
218 		if (!Slot[0]) {
219 			GtkSourceBuffer *Buffer = gtk_source_buffer_new_with_language(Console->Language);
220 			gtk_source_buffer_set_style_scheme(Buffer, Console->StyleScheme);
221 			GtkTextIter End[1];
222 			gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(Buffer), End);
223 			FILE *File = fopen(Source.Name, "r");
224 			if (File) {
225 				char Text[128];
226 				size_t Length;
227 				do {
228 				 Length = fread(Text, 1, 128, File);
229 				 gtk_text_buffer_insert(GTK_TEXT_BUFFER(Buffer), End, Text, Length);
230 				} while (Length == 128);
231 				fclose(File);
232 			}
233 			GtkWidget *View = gtk_source_view_new_with_buffer(Buffer);
234 			GtkWidget *Scrolled = gtk_scrolled_window_new(NULL, NULL);
235 			gtk_container_add(GTK_CONTAINER(Scrolled), View);
236 			gtk_text_view_set_monospace(GTK_TEXT_VIEW(View), TRUE);
237 			gtk_widget_override_font(View, Console->FontDescription);
238 			gtk_source_view_set_tab_width(GTK_SOURCE_VIEW(View), 4);
239 			gtk_source_view_set_highlight_current_line(GTK_SOURCE_VIEW(View), TRUE);
240 			gtk_source_view_set_show_line_numbers(GTK_SOURCE_VIEW(View), TRUE);
241 			gtk_notebook_append_page(Console->Notebook, Scrolled, gtk_label_new(Source.Name));
242 			gtk_widget_show_all(GTK_WIDGET(Console->Notebook));
243 			Slot[0] = View;
244 		}
245 		SourceView = Slot[0];
246 	}
247 	int PageNum = gtk_notebook_page_num(Console->Notebook, gtk_widget_get_parent(SourceView));
248 	gtk_notebook_set_current_page(Console->Notebook, PageNum);
249 
250 	GtkTextBuffer *Buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(SourceView));
251 	GtkTextIter LineBeg[1], LineEnd[1];
252 	gtk_text_buffer_get_iter_at_line(Buffer, LineBeg, Source.Line - 1);
253 	gtk_text_buffer_get_iter_at_line(Buffer, LineEnd, Source.Line);
254 	//gtk_text_buffer_apply_tag(Buffer, PausedTag, LineBeg, LineEnd);
255 	gtk_text_buffer_place_cursor(Buffer, LineBeg);
256 	gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(SourceView), LineBeg, 0.0, FALSE, 0.0, 0.0);
257 }
258 
console_debug_exit(console_t * Console,interactive_debugger_t * Debugger,ml_state_t * Caller,int Index)259 static void console_debug_exit(console_t *Console, interactive_debugger_t *Debugger, ml_state_t *Caller, int Index) {
260 	gtk_widget_hide(Console->DebugButtons);
261 	return interactive_debugger_resume(Debugger);
262 }
263 
console_clear(GtkWidget * Button,console_t * Console)264 static void console_clear(GtkWidget *Button, console_t *Console) {
265 	GtkTextBuffer *LogBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(Console->LogView));
266 	GtkTextIter Start[1], End[1];
267 	gtk_text_buffer_get_start_iter(LogBuffer, Start);
268 	gtk_text_buffer_get_end_iter(LogBuffer, End);
269 	gtk_text_buffer_delete(LogBuffer, Start, End);
270 }
271 
toggle_layout(GtkWidget * Button,console_t * Console)272 static void toggle_layout(GtkWidget *Button, console_t *Console) {
273 	switch (gtk_orientable_get_orientation(GTK_ORIENTABLE(Console->Paned))) {
274 	case GTK_ORIENTATION_HORIZONTAL:
275 		gtk_orientable_set_orientation(GTK_ORIENTABLE(Console->Paned), GTK_ORIENTATION_VERTICAL);
276 		break;
277 	case GTK_ORIENTATION_VERTICAL:
278 		gtk_orientable_set_orientation(GTK_ORIENTABLE(Console->Paned), GTK_ORIENTATION_HORIZONTAL);
279 		break;
280 	}
281 }
282 
console_style_changed(GtkComboBoxText * Widget,console_t * Console)283 static void console_style_changed(GtkComboBoxText *Widget, console_t *Console) {
284 	const char *StyleId = gtk_combo_box_text_get_active_text(Widget);
285 	GtkSourceStyleSchemeManager *StyleManager = gtk_source_style_scheme_manager_get_default();
286 	Console->StyleScheme = gtk_source_style_scheme_manager_get_scheme(StyleManager, StyleId);
287 	gtk_source_buffer_set_style_scheme(GTK_SOURCE_BUFFER(gtk_text_view_get_buffer(GTK_TEXT_VIEW(Console->InputView))), Console->StyleScheme);
288 	gtk_source_buffer_set_style_scheme(GTK_SOURCE_BUFFER(gtk_text_view_get_buffer(GTK_TEXT_VIEW(Console->LogView))), Console->StyleScheme);
289 
290 	g_key_file_set_string(Console->Config, "gtk-console", "style", StyleId);
291 	g_key_file_save_to_file(Console->Config, Console->ConfigPath, NULL);
292 }
293 
console_font_changed(GtkFontChooser * Widget,console_t * Console)294 static void console_font_changed(GtkFontChooser *Widget, console_t *Console) {
295 	gchar *FontName = gtk_font_chooser_get_font(Widget);
296 	Console->FontName = FontName;
297 	Console->FontDescription = pango_font_description_from_string(FontName);
298 	gtk_widget_override_font(Console->InputView, Console->FontDescription);
299 	gtk_widget_override_font(Console->LogView, Console->FontDescription);
300 
301 	g_key_file_set_string(Console->Config, "gtk-console", "font", FontName);
302 	g_key_file_save_to_file(Console->Config, Console->ConfigPath, NULL);
303 }
304 
305 #ifdef __APPLE__
306 #define COMMAND_MASK GDK_META_MASK
307 #else
308 #define COMMAND_MASK GDK_CONTROL_MASK
309 #endif
310 
console_keypress(GtkWidget * Widget,GdkEventKey * Event,console_t * Console)311 static gboolean console_keypress(GtkWidget *Widget, GdkEventKey *Event, console_t *Console) {
312 	switch (Event->keyval) {
313 	case GDK_KEY_Return:
314 		Console->NumChars = 0;
315 		if (Event->state & COMMAND_MASK) {
316 			console_submit(NULL, Console);
317 			return TRUE;
318 		}
319 		break;
320 	case GDK_KEY_Up:
321 		Console->NumChars = 0;
322 		if (Event->state & COMMAND_MASK) {
323 			int HistoryIndex = (Console->HistoryIndex + MAX_HISTORY - 1) % MAX_HISTORY;
324 			if (Console->History[HistoryIndex]) {
325 				gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(Console->InputView)), Console->History[HistoryIndex], -1);
326 				Console->HistoryIndex = HistoryIndex;
327 			}
328 			return TRUE;
329 		}
330 		break;
331 	case GDK_KEY_Down:
332 		Console->NumChars = 0;
333 		if (Event->state & COMMAND_MASK) {
334 			int HistoryIndex = (Console->HistoryIndex + 1) % MAX_HISTORY;
335 			if (Console->History[HistoryIndex]) {
336 				gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(Console->InputView)), Console->History[HistoryIndex], -1);
337 				Console->HistoryIndex = HistoryIndex;
338 			} else {
339 				gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(Console->InputView)), "", 0);
340 				Console->HistoryIndex = Console->HistoryEnd;
341 			}
342 			return TRUE;
343 		}
344 		break;
345 	case GDK_KEY_Escape:
346 	case GDK_KEY_Left:
347 	case GDK_KEY_Right:
348 		Console->NumChars = 0;
349 		break;
350 	case GDK_KEY_BackSpace:
351 		if (Console->NumChars > 0) --Console->NumChars;
352 		break;
353 	case GDK_KEY_Tab: {
354 		Console->Chars[Console->NumChars] = 0;
355 		for (int I = 0; I < Console->NumChars; ++I) {
356 			const char *Cycle = stringmap_search(Console->Cycles, Console->Chars + I);
357 			if (Cycle) {
358 				GtkTextIter Start[1], End[1];
359 				GtkTextBuffer *InputBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(Console->InputView));
360 				GtkTextMark *Cursor = gtk_text_buffer_get_insert(InputBuffer);
361 				gtk_text_buffer_get_iter_at_mark(InputBuffer, Start, Cursor);
362 				gtk_text_buffer_get_iter_at_mark(InputBuffer, End, Cursor);
363 				gtk_text_iter_backward_chars(Start, g_utf8_strlen(Console->Chars + I, Console->NumChars - I));
364 				Console->NumChars = stpcpy(Console->Chars + I, Cycle) - Console->Chars;
365 				gtk_text_buffer_delete(InputBuffer, Start, End);
366 				gtk_text_buffer_insert(InputBuffer, Start, Console->Chars + I, Console->NumChars - I);
367 				return TRUE;
368 			}
369 		}
370 		break;
371 	}
372 	default: {
373 		guint32 Unichar = gdk_keyval_to_unicode(Event->keyval);
374 		if (!Unichar) return FALSE;
375 		if (Unichar <= 32) {
376 			Console->NumChars = 0;
377 			return FALSE;
378 		}
379 		Console->NumChars += g_unichar_to_utf8(Unichar, Console->Chars + Console->NumChars);
380 		if (Console->NumChars > 16) {
381 			memmove(Console->Chars, Console->Chars + Console->NumChars - 16, 16);
382 			Console->NumChars = 16;
383 		}
384 		Console->Chars[Console->NumChars] = 0;
385 		for (int I = 0; I < Console->NumChars; ++I) {
386 			const char *Combo = stringmap_search(Console->Combos, Console->Chars + I);
387 			if (Combo) {
388 				GtkTextIter Start[1], End[1];
389 				GtkTextBuffer *InputBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(Console->InputView));
390 				GtkTextMark *Cursor = gtk_text_buffer_get_insert(InputBuffer);
391 				gtk_text_buffer_get_iter_at_mark(InputBuffer, Start, Cursor);
392 				gtk_text_buffer_get_iter_at_mark(InputBuffer, End, Cursor);
393 				gtk_text_iter_backward_chars(Start, g_utf8_strlen(Console->Chars + I, Console->NumChars - I) - 1);
394 				Console->NumChars = stpcpy(Console->Chars + I, Combo) - Console->Chars;
395 				gtk_text_buffer_delete(InputBuffer, Start, End);
396 				gtk_text_buffer_insert(InputBuffer, Start, Console->Chars + I, Console->NumChars - I);
397 				return TRUE;
398 			}
399 		}
400 	}
401 	}
402 	return FALSE;
403 }
404 
console_show(console_t * Console,GtkWindow * Parent)405 void console_show(console_t *Console, GtkWindow *Parent) {
406 	gtk_window_set_transient_for(GTK_WINDOW(Console->Window), Parent);
407 	gtk_widget_show_all(Console->Window);
408 	gtk_widget_hide(Console->DebugButtons);
409 	gtk_widget_grab_focus(Console->InputView);
410 }
411 
console_append(console_t * Console,const char * Buffer,int Length)412 int console_append(console_t *Console, const char *Buffer, int Length) {
413 	GtkTextIter End[1];
414 	GtkTextBuffer *LogBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(Console->LogView));
415 	gtk_text_buffer_get_end_iter(LogBuffer, End);
416 
417 	if (g_utf8_validate(Buffer, Length, NULL)) {
418 		gtk_text_buffer_insert_with_tags(LogBuffer, End, Buffer, Length, Console->OutputTag, NULL);
419 	} else {
420 		gtk_text_buffer_insert_with_tags(LogBuffer, End, "<", 1, Console->BinaryTag, NULL);
421 		for (int I = 0; I < Length; ++I) {
422 			static char HexChars[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
423 			char Bytes[4] = " ??";
424 			unsigned char Byte = Buffer[I];
425 			Bytes[1] = HexChars[Byte >> 4];
426 			Bytes[2] = HexChars[Byte & 15];
427 			gtk_text_buffer_insert_with_tags(LogBuffer, End, Bytes, 3, Console->BinaryTag, NULL);
428 		}
429 		gtk_text_buffer_insert_with_tags(LogBuffer, End, " >", 2, Console->BinaryTag, NULL);
430 	}
431 	gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(Console->LogView), Console->EndMark);
432 	while (gtk_events_pending()) gtk_main_iteration();
433 	return 0;
434 }
435 
console_print(console_t * Console,int Count,ml_value_t ** Args)436 ml_value_t *console_print(console_t *Console, int Count, ml_value_t **Args) {
437 	GtkTextIter End[1];
438 	GtkTextBuffer *LogBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(Console->LogView));
439 	gtk_text_buffer_get_end_iter(LogBuffer, End);
440 	ml_stringbuffer_t Buffer[1] = {ML_STRINGBUFFER_INIT};
441 	for (int I = 0; I < Count; ++I) {
442 		ml_value_t *Result = ml_stringbuffer_append(Buffer, Args[I]);
443 		if (ml_is_error(Result)) return Result;
444 	}
445 	ml_stringbuffer_foreach(Buffer, Console, (void *)console_append);
446 	while (gtk_events_pending()) gtk_main_iteration();
447 	return MLNil;
448 }
449 
console_printf(console_t * Console,const char * Format,...)450 void console_printf(console_t *Console, const char *Format, ...) {
451 	char *Buffer;
452 	va_list Args;
453 	va_start(Args, Format);
454 	int Length = vasprintf(&Buffer, Format, Args);
455 	va_end(Args);
456 	console_append(Console, Buffer, Length);
457 	free(Buffer);
458 }
459 
console_set_font(console_t * Console,int Count,ml_value_t ** Args)460 static ml_value_t *console_set_font(console_t *Console, int Count, ml_value_t **Args) {
461 	ML_CHECK_ARG_COUNT(2);
462 	ML_CHECK_ARG_TYPE(0, MLStringT);
463 	ML_CHECK_ARG_TYPE(1, MLIntegerT);
464 	Console->FontDescription = pango_font_description_new();
465 	pango_font_description_set_family(Console->FontDescription, ml_string_value(Args[0]));
466 	pango_font_description_set_size(Console->FontDescription, PANGO_SCALE * ml_integer_value(Args[1]));
467 	gtk_widget_override_font(Console->InputView, Console->FontDescription);
468 	gtk_widget_override_font(Console->LogView, Console->FontDescription);
469 	return MLNil;
470 }
471 
console_set_style(console_t * Console,int Count,ml_value_t ** Args)472 static ml_value_t *console_set_style(console_t *Console, int Count, ml_value_t **Args) {
473 	ML_CHECK_ARG_COUNT(1);
474 	ML_CHECK_ARG_TYPE(0, MLStringT);
475 	GtkSourceStyleSchemeManager *StyleManager = gtk_source_style_scheme_manager_get_default();
476 	Console->StyleScheme = gtk_source_style_scheme_manager_get_scheme(StyleManager, ml_string_value(Args[0]));
477 	gtk_source_buffer_set_style_scheme(GTK_SOURCE_BUFFER(gtk_text_view_get_buffer(GTK_TEXT_VIEW(Console->InputView))), Console->StyleScheme);
478 	gtk_source_buffer_set_style_scheme(GTK_SOURCE_BUFFER(gtk_text_view_get_buffer(GTK_TEXT_VIEW(Console->LogView))), Console->StyleScheme);
479 	return MLNil;
480 }
481 
console_add_cycle(console_t * Console,int Count,ml_value_t ** Args)482 static ml_value_t *console_add_cycle(console_t *Console, int Count, ml_value_t **Args) {
483 	ML_CHECK_ARG_COUNT(1);
484 	ML_CHECK_ARG_TYPE(0, MLStringT);
485 	for (int I = 1; I < Count; ++I) {
486 		ML_CHECK_ARG_TYPE(I, MLStringT);
487 		stringmap_insert(Console->Cycles, ml_string_value(Args[I - 1]), (void *)ml_string_value(Args[I]));
488 	}
489 	stringmap_insert(Console->Cycles, ml_string_value(Args[Count - 1]), (void *)ml_string_value(Args[0]));
490 	return MLNil;
491 }
492 
console_add_combo(console_t * Console,int Count,ml_value_t ** Args)493 static ml_value_t *console_add_combo(console_t *Console, int Count, ml_value_t **Args) {
494 	ML_CHECK_ARG_COUNT(2);
495 	ML_CHECK_ARG_TYPE(0, MLStringT);
496 	ML_CHECK_ARG_TYPE(1, MLStringT);
497 	stringmap_insert(Console->Combos, ml_string_value(Args[0]), (void *)ml_string_value(Args[1]));
498 	stringmap_insert(Console->Cycles, ml_string_value(Args[1]), (void *)ml_string_value(Args[0]));
499 	return MLNil;
500 }
501 
console_included_run(ml_state_t * State,ml_value_t * Value)502 static void console_included_run(ml_state_t *State, ml_value_t *Value) {
503 	ml_state_t *Caller = State->Caller;
504 	if (ml_is_error(Value)) ML_RETURN(Value);
505 	return ml_call(Caller, Value, 0, NULL);
506 }
507 
console_include_fnx(ml_state_t * Caller,console_t * Console,int Count,ml_value_t ** Args)508 static void console_include_fnx(ml_state_t *Caller, console_t *Console, int Count, ml_value_t **Args) {
509 	ML_CHECKX_ARG_COUNT(1);
510 	ML_CHECKX_ARG_TYPE(0, MLStringT);
511 	ml_state_t *State = new(ml_state_t);
512 	State->Caller = Caller;
513 	State->Context = Caller->Context;
514 	State->run = console_included_run;
515 	return ml_load_file(State, (ml_getter_t)ml_compiler_lookup, Console->Compiler, ml_string_value(Args[0]), NULL);
516 }
517 
console_update_status(console_t * Console)518 static gboolean console_update_status(console_t *Console) {
519 	GC_word HeapSize, FreeBytes, UnmappedBytes, BytesSinceGC, TotalBytes;
520 	GC_get_heap_usage_safe(&HeapSize, &FreeBytes, &UnmappedBytes, &BytesSinceGC, &TotalBytes);
521 	GC_word UsedSize = HeapSize - FreeBytes;
522 	int UsedBase, HeapBase;
523 	char UsedUnits, HeapUnits;
524 	if (UsedSize < (1 << 10)) {
525 		UsedBase = UsedSize;
526 		UsedUnits = 'b';
527 	} else if (UsedSize < (1 << 20)) {
528 		UsedBase = UsedSize >> 10;
529 		UsedUnits = 'k';
530 	} else if (UsedSize < (1 << 30)) {
531 		UsedBase = UsedSize >> 20;
532 		UsedUnits = 'M';
533 	} else {
534 		UsedBase = UsedSize >> 30;
535 		UsedUnits = 'G';
536 	}
537 	if (HeapSize < (1 << 10)) {
538 		HeapBase = HeapSize;
539 		HeapUnits = 'b';
540 	} else if (HeapSize < (1 << 20)) {
541 		HeapBase = HeapSize >> 10;
542 		HeapUnits = 'k';
543 	} else if (HeapSize < (1 << 30)) {
544 		HeapBase = HeapSize >> 20;
545 		HeapUnits = 'M';
546 	} else {
547 		HeapBase = HeapSize >> 30;
548 		HeapUnits = 'G';
549 	}
550 
551 	char Text[48];
552 	sprintf(Text, "Memory: %d%c / %d%c", UsedBase, UsedUnits, HeapBase, HeapUnits);
553 	gtk_label_set_text(Console->MemoryBar, Text);
554 	/*printf("Memory Status:\n");
555 	printf("\tHeapSize = %ld\n", HeapSize);
556 	printf("\tFreeBytes = %ld\n", FreeBytes);
557 	printf("\tUnmappedBytes = %ld\n", UnmappedBytes);
558 	printf("\tBytesSinceGC = %ld\n", BytesSinceGC);
559 	printf("\tTotalBytes = %ld\n", TotalBytes);*/
560 	return G_SOURCE_CONTINUE;
561 }
562 
563 #ifdef ML_SCHEDULER
564 
565 static unsigned int Counter = 1000;
566 
queue_run(void * Data)567 static gboolean queue_run(void *Data) {
568 	ml_queued_state_t QueuedState = ml_scheduler_queue_next();
569 	if (!QueuedState.State) return FALSE;
570 	Counter = 1000;
571 	QueuedState.State->run(QueuedState.State, QueuedState.Value);
572 	return TRUE;
573 }
574 
console_swap_state(ml_state_t * State,ml_value_t * Value)575 static void console_swap_state(ml_state_t *State, ml_value_t *Value) {
576 	if (ml_scheduler_queue_add(State, Value) == 1) g_idle_add(queue_run, NULL);
577 }
578 
console_scheduler(ml_context_t * Context)579 static ml_schedule_t console_scheduler(ml_context_t *Context) {
580 	return (ml_schedule_t){&Counter, console_swap_state};
581 }
582 
console_schedule(ml_state_t * Caller,console_t * Console,int Count,ml_value_t ** Args)583 static void console_schedule(ml_state_t *Caller, console_t *Console, int Count, ml_value_t **Args) {
584 	ML_CHECKX_ARG_COUNT(1);
585 	ML_CHECKX_ARG_TYPE(0, MLFunctionT);
586 	ml_state_t *State = ml_state_new(Caller);
587 	ml_context_set(State->Context, ML_SCHEDULER_INDEX, console_scheduler);
588 	return ml_call(State, Args[0], Count - 1, Args + 1);
589 }
590 
sleep_run(void * Data)591 static gboolean sleep_run(void *Data) {
592 	console_swap_state((ml_state_t *)Data, MLNil);
593 	return FALSE;
594 }
595 
ML_FUNCTIONX(MLSleep)596 ML_FUNCTIONX(MLSleep) {
597 //@sleep
598 	ML_CHECKX_ARG_COUNT(1);
599 	ML_CHECKX_ARG_TYPE(0, MLNumberT);
600 	guint Interval = ml_real_value(Args[0]) * 1000;
601 	g_timeout_add(Interval, sleep_run, Caller);
602 }
603 
604 #endif
605 
console_new(ml_context_t * Context,ml_getter_t GlobalGet,void * Globals)606 console_t *console_new(ml_context_t *Context, ml_getter_t GlobalGet, void *Globals) {
607 	gtk_init(0, 0);
608 
609 	console_t *Console = new(console_t);
610 	Console->Base.Type = ConsoleT;
611 	Console->Base.run = (ml_state_fn)ml_console_repl_run;
612 	Console->Base.Context = Context;
613 	Console->Name = strdup("<console>");
614 	Console->ParentGetter = GlobalGet;
615 	Console->ParentGlobals = Globals;
616 	Console->HistoryIndex = 0;
617 	Console->HistoryEnd = 0;
618 	Console->Parser = ml_parser(NULL, NULL);
619 	Console->Compiler = ml_compiler((ml_getter_t)console_global_get, Console);
620 	ml_parser_source(Console->Parser, (ml_source_t){Console->Name, 0});
621 	Console->Notebook = GTK_NOTEBOOK(gtk_notebook_new());
622 
623 #ifdef ML_SCHEDULER
624 	ml_context_set(Console->Base.Context, ML_SCHEDULER_INDEX, console_scheduler);
625 #endif
626 
627 	asprintf((char **)&Console->ConfigPath, "%s/%s", g_get_user_config_dir(), "minilang.conf");
628 	Console->Config = g_key_file_new();
629 	g_key_file_load_from_file(Console->Config, Console->ConfigPath, G_KEY_FILE_NONE, NULL);
630 
631 	GtkSourceLanguageManager *LanguageManager = gtk_source_language_manager_get_default();
632 	Console->Language = gtk_source_language_manager_get_language(LanguageManager, "minilang");
633 
634 	GtkSourceBuffer *InputBuffer = gtk_source_buffer_new_with_language(Console->Language);
635 	Console->InputView = gtk_source_view_new_with_buffer(InputBuffer);
636 	GtkTextTagTable *TagTable = gtk_text_buffer_get_tag_table(GTK_TEXT_BUFFER(InputBuffer));
637 	Console->OutputTag = gtk_text_tag_new("log-output");
638 	Console->ResultTag = gtk_text_tag_new("log-result");
639 	Console->ErrorTag = gtk_text_tag_new("log-error");
640 	Console->BinaryTag = gtk_text_tag_new("log-binary");
641 	g_object_set(Console->OutputTag,
642 		"background", "#FFFFF0",
643 	NULL);
644 	g_object_set(Console->ResultTag,
645 		"background", "#FFF0F0",
646 		"foreground", "#303030",
647 		"indent", 10,
648 	NULL);
649 	g_object_set(Console->ErrorTag,
650 		"background", "#FFF0F0",
651 		"foreground", "#FF0000",
652 		"indent", 10,
653 	NULL);
654 	g_object_set(Console->BinaryTag,
655 		"background", "#F0F0FF",
656 		"foreground", "#FF8000",
657 	NULL);
658 	gtk_text_tag_table_add(TagTable, Console->OutputTag);
659 	gtk_text_tag_table_add(TagTable, Console->ResultTag);
660 	gtk_text_tag_table_add(TagTable, Console->ErrorTag);
661 	gtk_text_tag_table_add(TagTable, Console->BinaryTag);
662 	GtkSourceBuffer *LogBuffer = gtk_source_buffer_new(TagTable);
663 	Console->LogView = gtk_source_view_new_with_buffer(LogBuffer);
664 	gtk_text_view_set_editable(GTK_TEXT_VIEW(Console->LogView), FALSE);
665 	GtkSourceStyleSchemeManager *StyleManager = gtk_source_style_scheme_manager_get_default();
666 	Console->SourceBuffer = gtk_source_buffer_new_with_language(Console->Language);
667 
668 
669 
670 	GtkWidget *InputPanel = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
671 
672 	GtkWidget *DebugButtons = Console->DebugButtons = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
673 	GtkWidget *StepInButton = gtk_button_new();
674 	gtk_button_set_label(GTK_BUTTON(StepInButton), "In");
675 	gtk_box_pack_start(GTK_BOX(DebugButtons), StepInButton, FALSE, FALSE, 2);
676 	GtkWidget *StepOverButton = gtk_button_new();
677 	gtk_button_set_label(GTK_BUTTON(StepOverButton), "Over");
678 	gtk_box_pack_start(GTK_BOX(DebugButtons), StepOverButton, FALSE, FALSE, 2);
679 	GtkWidget *StepOutButton = gtk_button_new();
680 	gtk_button_set_label(GTK_BUTTON(StepOutButton), "Out");
681 	gtk_box_pack_start(GTK_BOX(DebugButtons), StepOutButton, FALSE, FALSE, 2);
682 	GtkWidget *ContinueButton = gtk_button_new();
683 	gtk_button_set_label(GTK_BUTTON(ContinueButton), "Run");
684 	gtk_box_pack_start(GTK_BOX(DebugButtons), ContinueButton, FALSE, FALSE, 2);
685 	g_signal_connect(G_OBJECT(StepInButton), "clicked", G_CALLBACK(console_step_in), Console);
686 	g_signal_connect(G_OBJECT(StepOverButton), "clicked", G_CALLBACK(console_step_over), Console);
687 	g_signal_connect(G_OBJECT(StepOutButton), "clicked", G_CALLBACK(console_step_out), Console);
688 	g_signal_connect(G_OBJECT(ContinueButton), "clicked", G_CALLBACK(console_continue), Console);
689 	gtk_box_pack_start(GTK_BOX(InputPanel), DebugButtons, FALSE, FALSE, 2);
690 
691 	GtkWidget *SubmitButton = gtk_button_new();
692 	gtk_button_set_image(GTK_BUTTON(SubmitButton), gtk_image_new_from_icon_name("go-jump-symbolic", GTK_ICON_SIZE_BUTTON));
693 	GtkWidget *ClearButton = gtk_button_new();
694 	gtk_button_set_image(GTK_BUTTON(ClearButton), gtk_image_new_from_icon_name("edit-delete-symbolic", GTK_ICON_SIZE_BUTTON));
695 	Console->LogScrolled = gtk_scrolled_window_new(NULL, NULL);
696 	gtk_container_add(GTK_CONTAINER(Console->LogScrolled), Console->LogView);
697 	gtk_box_pack_start(GTK_BOX(InputPanel), Console->InputView, TRUE, TRUE, 2);
698 	gtk_box_pack_start(GTK_BOX(InputPanel), SubmitButton, FALSE, FALSE, 2);
699 	gtk_box_pack_start(GTK_BOX(InputPanel), ClearButton, FALSE, FALSE, 2);
700 
701 	GtkWidget *StyleCombo = gtk_combo_box_text_new();
702 	for (const gchar * const * StyleId = gtk_source_style_scheme_manager_get_scheme_ids(StyleManager); StyleId[0]; ++StyleId) {
703 		gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(StyleCombo), StyleId[0], StyleId[0]);
704 	}
705 
706 	g_signal_connect(G_OBJECT(StyleCombo), "changed", G_CALLBACK(console_style_changed), Console);
707 
708 	GtkWidget *FontButton = gtk_font_button_new();
709 	g_signal_connect(G_OBJECT(FontButton), "font-set", G_CALLBACK(console_font_changed), Console);
710 
711 	Console->Paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
712 	gtk_paned_add1(GTK_PANED(Console->Paned), Console->LogScrolled);
713 	gtk_paned_add2(GTK_PANED(Console->Paned), GTK_WIDGET(Console->Notebook));
714 	gtk_paned_set_position(GTK_PANED(Console->Paned), 500);
715 
716 	GtkWidget *SourceView = Console->SourceView = gtk_source_view_new_with_buffer(Console->SourceBuffer);
717 	gtk_text_view_set_monospace(GTK_TEXT_VIEW(SourceView), TRUE);
718 	gtk_source_view_set_tab_width(GTK_SOURCE_VIEW(SourceView), 4);
719 	gtk_source_view_set_highlight_current_line(GTK_SOURCE_VIEW(SourceView), TRUE);
720 	gtk_source_view_set_show_line_numbers(GTK_SOURCE_VIEW(SourceView), TRUE);
721 	GtkWidget *SourceScrolled = gtk_scrolled_window_new(NULL, NULL);
722 	gtk_container_add(GTK_CONTAINER(SourceScrolled), SourceView);
723 	gtk_notebook_append_page(Console->Notebook, SourceScrolled, gtk_label_new("<console>"));
724 
725 	GtkWidget *Container = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
726 	gtk_box_pack_start(GTK_BOX(Container), Console->Paned, TRUE, TRUE, 2);
727 
728 	GtkWidget *InputFrame = gtk_frame_new(NULL);
729 	gtk_container_add(GTK_CONTAINER(InputFrame), InputPanel);
730 	gtk_box_pack_start(GTK_BOX(Container), InputFrame, FALSE, TRUE, 2);
731 	g_signal_connect(G_OBJECT(Console->InputView), "key-press-event", G_CALLBACK(console_keypress), Console);
732 	g_signal_connect(G_OBJECT(SubmitButton), "clicked", G_CALLBACK(console_submit), Console);
733 	g_signal_connect(G_OBJECT(ClearButton), "clicked", G_CALLBACK(console_clear), Console);
734 	Console->Window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
735 	gtk_window_set_icon_name(GTK_WINDOW(Console->Window), "face-smile");
736 
737 	GtkWidget *LayoutButton = gtk_button_new_with_label("Layout");
738 	g_signal_connect(G_OBJECT(LayoutButton), "clicked", G_CALLBACK(toggle_layout), Console);
739 
740 	GtkWidget *MenuButton = gtk_menu_button_new();
741 	GtkWidget *ActionsBox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
742 	gtk_box_pack_start(GTK_BOX(ActionsBox), StyleCombo, FALSE, TRUE, 0);
743 	gtk_box_pack_start(GTK_BOX(ActionsBox), FontButton, FALSE, TRUE, 0);
744 	gtk_box_pack_start(GTK_BOX(ActionsBox), LayoutButton, FALSE, TRUE, 0);
745 	GtkWidget *ActionsPopover = gtk_popover_new(MenuButton);
746 	gtk_container_add(GTK_CONTAINER(ActionsPopover), ActionsBox);
747 	gtk_menu_button_set_popover(GTK_MENU_BUTTON(MenuButton), ActionsPopover);
748 	gtk_widget_show_all(ActionsBox);
749 
750 
751 	GtkWidget *HeaderBar = gtk_header_bar_new();
752 	gtk_header_bar_set_title(GTK_HEADER_BAR(HeaderBar), "Minilang");
753 	gtk_header_bar_set_has_subtitle(GTK_HEADER_BAR(HeaderBar), FALSE);
754 	gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(HeaderBar), TRUE);
755 	gtk_header_bar_pack_start(GTK_HEADER_BAR(HeaderBar), MenuButton);
756 	gtk_window_set_titlebar(GTK_WINDOW(Console->Window), HeaderBar);
757 
758 	GtkWidget *MemoryBar = gtk_label_new("");
759 	gtk_header_bar_pack_end(GTK_HEADER_BAR(HeaderBar), MemoryBar);
760 
761 	Console->MemoryBar = GTK_LABEL(MemoryBar);
762 
763 	gtk_container_add(GTK_CONTAINER(Console->Window), Container);
764 	gtk_window_set_default_size(GTK_WINDOW(Console->Window), 640, 480);
765 	g_signal_connect(G_OBJECT(Console->Window), "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), Console);
766 
767 	ml_compiler_define(Console->Compiler, "set_font", ml_cfunction(Console, (ml_callback_t)console_set_font));
768 	ml_compiler_define(Console->Compiler, "set_style", ml_cfunction(Console, (ml_callback_t)console_set_style));
769 	ml_compiler_define(Console->Compiler, "add_cycle", ml_cfunction(Console, (ml_callback_t)console_add_cycle));
770 	ml_compiler_define(Console->Compiler, "add_combo", ml_cfunction(Console, (ml_callback_t)console_add_combo));
771 	ml_compiler_define(Console->Compiler, "include", ml_cfunctionx(Console, (ml_callbackx_t)console_include_fnx));
772 
773 #ifdef ML_SCHEDULER
774 	ml_compiler_define(Console->Compiler, "schedule", ml_cfunctionx(Console, (ml_callbackx_t)console_schedule));
775 	ml_compiler_define(Console->Compiler, "sleep", (ml_value_t *)MLSleep);
776 #endif
777 
778 	if (g_key_file_has_key(Console->Config, "gtk-console", "font", NULL)) {
779 		Console->FontName = g_key_file_get_string(Console->Config, "gtk-console", "font", NULL);
780 	} else {
781 		Console->FontName = "Monospace 10";
782 	}
783 	Console->FontDescription = pango_font_description_from_string(Console->FontName);
784 	gtk_widget_override_font(Console->InputView, Console->FontDescription);
785 	gtk_widget_override_font(Console->LogView, Console->FontDescription);
786 	gtk_widget_override_font(SourceView, Console->FontDescription);
787 	gtk_font_button_set_font_name(GTK_FONT_BUTTON(FontButton), Console->FontName);
788 
789 	if (g_key_file_has_key(Console->Config, "gtk-console", "style", NULL)) {
790 		const char *StyleId = g_key_file_get_string(Console->Config, "gtk-console", "style", NULL);
791 		Console->StyleScheme = gtk_source_style_scheme_manager_get_scheme(StyleManager, StyleId);
792 		GtkSourceBuffer *InputBuffer = GTK_SOURCE_BUFFER(gtk_text_view_get_buffer(GTK_TEXT_VIEW(Console->InputView)));
793 		gtk_source_buffer_set_style_scheme(InputBuffer, Console->StyleScheme);
794 		gtk_source_buffer_set_style_scheme(LogBuffer, Console->StyleScheme);
795 		gtk_source_buffer_set_style_scheme(Console->SourceBuffer, Console->StyleScheme);
796 		gtk_combo_box_set_active_id(GTK_COMBO_BOX(StyleCombo), StyleId);
797 	}
798 
799 	gtk_text_view_set_top_margin(GTK_TEXT_VIEW(Console->LogView), 4);
800 	gtk_text_view_set_bottom_margin(GTK_TEXT_VIEW(Console->LogView), 4);
801 	gtk_text_view_set_left_margin(GTK_TEXT_VIEW(Console->LogView), 4);
802 	gtk_text_view_set_right_margin(GTK_TEXT_VIEW(Console->LogView), 4);
803 	gtk_text_view_set_monospace(GTK_TEXT_VIEW(Console->LogView), TRUE);
804 	gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(Console->LogView), TRUE);
805 	gtk_source_view_set_tab_width(GTK_SOURCE_VIEW(Console->LogView), 4);
806 	GtkTextIter End[1];
807 	gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(LogBuffer), End);
808 	Console->EndMark = gtk_text_buffer_create_mark(GTK_TEXT_BUFFER(LogBuffer), "end", End, FALSE);
809 
810 	GtkSourceMarkAttributes *MarkAttributes = gtk_source_mark_attributes_new();
811 	GdkPixbuf *MarkPixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 32, 32);
812 	gdk_pixbuf_fill(MarkPixbuf, 0xFF8000FF);
813 	gtk_source_mark_attributes_set_pixbuf(MarkAttributes, MarkPixbuf);
814 	gtk_source_view_set_mark_attributes(GTK_SOURCE_VIEW(Console->LogView), "result", MarkAttributes, 10);
815 	gtk_source_view_set_show_line_marks(GTK_SOURCE_VIEW(Console->LogView), TRUE);
816 
817 	gtk_text_view_set_top_margin(GTK_TEXT_VIEW(Console->InputView), 4);
818 	gtk_text_view_set_bottom_margin(GTK_TEXT_VIEW(Console->InputView), 4);
819 	gtk_text_view_set_left_margin(GTK_TEXT_VIEW(Console->InputView), 4);
820 	gtk_text_view_set_right_margin(GTK_TEXT_VIEW(Console->InputView), 4);
821 	gtk_text_view_set_monospace(GTK_TEXT_VIEW(Console->InputView), TRUE);
822 	gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(Console->InputView), TRUE);
823 	gtk_source_view_set_tab_width(GTK_SOURCE_VIEW(Console->InputView), 4);
824 	gtk_source_view_set_show_line_numbers(GTK_SOURCE_VIEW(Console->InputView), TRUE);
825 
826 	g_timeout_add(1000, (GSourceFunc)console_update_status, Console);
827 
828 	GError *Error = 0;
829 	g_irepository_require(NULL, "Gtk", "3.0", 0, &Error);
830 	g_irepository_require(NULL, "GtkSource", "4", 0, &Error);
831 	ml_compiler_define(Console->Compiler, "Console", ml_gir_instance_get(Console->Window, NULL));
832 	ml_compiler_define(Console->Compiler, "InputView", ml_gir_instance_get(Console->InputView, NULL));
833 	ml_compiler_define(Console->Compiler, "LogView", ml_gir_instance_get(Console->LogView, NULL));
834 	ml_compiler_define(Console->Compiler, "debugger", interactive_debugger(
835 		(void *)console_debug_enter,
836 		(void *)console_debug_exit,
837 		(void *)console_log,
838 		Console,
839 		GlobalGet,
840 		Globals
841 	));
842 
843 	return Console;
844 }
845