diff options
author | Matthias Clasen <mclasen@redhat.com> | 2020-08-07 09:21:29 -0400 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2020-08-07 11:54:10 -0400 |
commit | 05255bfc90310445dae52683ce45729e599d078b (patch) | |
tree | dd4adb95c40311933b795ee320f6200920ad4a27 | |
parent | 41ef2c5f341687cd834792986643c893a654cfdf (diff) | |
download | gtk+-05255bfc90310445dae52683ce45729e599d078b.tar.gz |
gtk-demo: Modernize source highlighting
Drop the homegrown highlighting code, and just use highlight
to produce Pango markup.
When using an external highlighter, we can also highlight css,
xml, headers, at least.
-rw-r--r-- | demos/gtk-demo/main.c | 447 |
1 files changed, 93 insertions, 354 deletions
diff --git a/demos/gtk-demo/main.c b/demos/gtk-demo/main.c index 95541fcfd9..1589b3d4bc 100644 --- a/demos/gtk-demo/main.c +++ b/demos/gtk-demo/main.c @@ -234,373 +234,106 @@ activate_run (GSimpleAction *action, gtk_demo_run (demo, window); } -/* Stupid syntax highlighting. - * - * No regex was used in the making of this highlighting. - * It should only work for simple cases. This is good, as - * that's all we should have in the demos. - */ -/* This code should not be used elsewhere, except perhaps as an example of how - * to iterate through a text buffer. - */ -enum { - STATE_NORMAL, - STATE_IN_COMMENT -}; - -static const char *tokens[] = -{ - "/*", - "\"", - NULL -}; - -static const char *types[] = +static GBytes * +fontify_text (const char *format, + const char *text) { - "static", - "const ", - "void", - " int ", - " char ", - "char ", - "float", - "double", - "gint8", - "gint16", - "gint32", - "guint", - "guint8", - "guint16", - "guint32", - "guchar", - "glong", - "gboolean" , - "gshort", - "gushort", - "gulong", - "gpointer", - "NULL", - "GList", - "GSList", - "FALSE", - "TRUE", - "FILE ", - "GtkColorSelection ", - "GtkWidget ", - "GtkButton ", - "GdkColor ", - "GdkRectangle ", - "GdkEventExpose ", - "GdkGC ", - "GdkPixbufLoader ", - "GdkPixbuf ", - "GError", - "size_t", - "GtkAboutDialog ", - "GtkAction ", - "GtkActionEntry ", - "GtkRadioActionEntry ", - "GtkIconFactory ", - "GtkTextBuffer ", - "GtkStatusbar ", - "GtkTextIter ", - "GtkTextMark ", - "GdkEventWindowState ", - "GtkActionGroup ", - "GtkUIManager ", - "GtkRadioAction ", - "GtkActionClass ", - "GtkToggleActionEntry ", - "GtkAssistant ", - "GtkBuilder ", - "GtkSizeGroup ", - "GtkTreeModel ", - "GtkTreeSelection ", - "GdkDisplay ", - "GdkScreen ", - "GdkSurface ", - "GdkEventButton ", - "GdkCursor ", - "GtkTreeIter ", - "GtkTreeViewColumn ", - "GdkDisplayManager ", - "GdkClipboard ", - "GtkIconSize ", - "GtkImage ", - "GdkDragContext ", - "GtkSelectionData ", - "GtkDialog ", - "GtkMenuItem ", - "GtkListStore ", - "GtkCellLayout ", - "GtkCellRenderer ", - "GtkTreePath ", - "GtkTreeStore ", - "GtkEntry ", - "GtkEditable ", - "GtkEditableInterface ", - "GdkPixmap ", - "GdkEventConfigure ", - "GdkEventMotion ", - "GdkModifierType ", - "GtkEntryCompletion ", - "GtkToolItem ", - "GDir ", - "GtkIconView ", - "GtkCellRendererText ", - "GtkContainer ", - "GtkPaned ", - "GtkPrintOperation ", - "GtkPrintContext ", - "cairo_t ", - "PangoLayout " - "PangoFontDescription ", - "PangoRenderer ", - "PangoMatrix ", - "PangoContext ", - "PangoLayout ", - "GtkToggleButton ", - "GString ", - "GtkIconSize ", - "GtkTreeView ", - "GtkTextTag ", - "GdkEvent ", - "GdkEventKey ", - "GtkTextView ", - "GdkBitmap ", - "GtkTextChildAnchor ", - "GArray ", - "GtkCellEditable ", - "GtkCellRendererToggle ", - NULL -}; - -static const char *control[] = -{ - " if ", - " while ", - " else", - " do ", - " for ", - "?", - ":", - "return ", - "goto ", - NULL -}; -void -parse_chars (char *text, - char **end_ptr, - int *state, - const char **tag, - gboolean start) -{ - int i; - char *next_token; - - /* Handle comments first */ - if (*state == STATE_IN_COMMENT) + GSubprocess *subprocess; + static gboolean warned = FALSE; + GBytes *stdin_buf; + GBytes *stdout_buf = NULL; + GBytes *stderr_buf = NULL; + GError *error = NULL; + char *format_arg; + + format_arg = g_strconcat ("--syntax=", format, NULL); + subprocess = g_subprocess_new (G_SUBPROCESS_FLAGS_STDIN_PIPE | + G_SUBPROCESS_FLAGS_STDOUT_PIPE | + G_SUBPROCESS_FLAGS_STDERR_PIPE, + &error, + "highlight", + format_arg, + "--out-format=pango", + NULL); + g_free (format_arg); + + if (!subprocess) { - *end_ptr = strstr (text, "*/"); - if (*end_ptr) + if (!warned) { - *end_ptr += 2; - *state = STATE_NORMAL; - *tag = "comment"; + g_warning ("%s", error->message); + warned = TRUE; } - return; + g_clear_error (&error); + + return NULL; } - *tag = NULL; - *end_ptr = NULL; + stdin_buf = g_bytes_new_static (text, strlen (text)); - /* check for comment */ - if (!strncmp (text, "/*", 2)) + if (!g_subprocess_communicate (subprocess, + stdin_buf, + NULL, + &stdout_buf, + &stderr_buf, + &error)) { - *end_ptr = strstr (text, "*/"); - if (*end_ptr) - *end_ptr += 2; - else - *state = STATE_IN_COMMENT; - *tag = "comment"; - return; - } + g_clear_pointer (&stdin_buf, g_bytes_unref); + g_clear_pointer (&stdout_buf, g_bytes_unref); + g_clear_pointer (&stderr_buf, g_bytes_unref); - /* check for preprocessor defines */ - if (*text == '#' && start) - { - *end_ptr = NULL; - *tag = "preprocessor"; - return; - } + g_warning ("%s", error->message); + g_clear_error (&error); - /* functions */ - if (start && * text != '\t' && *text != ' ' && *text != '{' && *text != '}') - { - if (strstr (text, "(")) - { - *end_ptr = strstr (text, "("); - *tag = "function"; - return; - } + return NULL; } - /* check for types */ - for (i = 0; types[i] != NULL; i++) - if (!strncmp (text, types[i], strlen (types[i])) || - (start && types[i][0] == ' ' && !strncmp (text, types[i] + 1, strlen (types[i]) - 1))) - { - *end_ptr = text + strlen (types[i]); - *tag = "type"; - return; - } - - /* check for control */ - for (i = 0; control[i] != NULL; i++) - if (!strncmp (text, control[i], strlen (control[i]))) - { - *end_ptr = text + strlen (control[i]); - *tag = "control"; - return; - } - - /* check for string */ - if (text[0] == '"') - { - int maybe_escape = FALSE; - *end_ptr = text + 1; - *tag = "string"; - while (**end_ptr != '\000') - { - if (**end_ptr == '\"' && !maybe_escape) - { - *end_ptr += 1; - return; - } - if (**end_ptr == '\\') - maybe_escape = TRUE; - else - maybe_escape = FALSE; - *end_ptr += 1; - } - return; - } + g_bytes_unref (stdin_buf); - /* not at the start of a tag. Find the next one. */ - for (i = 0; tokens[i] != NULL; i++) + if (g_subprocess_get_exit_status (subprocess) != 0) { - next_token = strstr (text, tokens[i]); - if (next_token) - { - if (*end_ptr) - *end_ptr = (*end_ptr<next_token)?*end_ptr:next_token; - else - *end_ptr = next_token; - } - } + if (stderr_buf) + g_warning ("%s", (char *)g_bytes_get_data (stderr_buf, NULL)); - for (i = 0; types[i] != NULL; i++) - { - next_token = strstr (text, types[i]); - if (next_token) - { - if (*end_ptr) - *end_ptr = (*end_ptr<next_token)?*end_ptr:next_token; - else - *end_ptr = next_token; - } + g_clear_pointer (&stdout_buf, g_bytes_unref); } - for (i = 0; control[i] != NULL; i++) - { - next_token = strstr (text, control[i]); - if (next_token) - { - if (*end_ptr) - *end_ptr = (*end_ptr<next_token)?*end_ptr:next_token; - else - *end_ptr = next_token; - } - } + g_clear_pointer (&stderr_buf, g_bytes_unref); + + g_object_unref (subprocess); + + return stdout_buf; } -/* While not as cool as c-mode, this will do as a quick attempt at highlighting */ void -fontify (GtkTextBuffer *source_buffer) +fontify (const char *format, + GtkTextBuffer *source_buffer) { - GtkTextIter start_iter, next_iter, tmp_iter; - int state; + GtkTextIter start, end; char *text; - char *start_ptr, *end_ptr; - const char *tag; - - gtk_text_buffer_create_tag (source_buffer, "source", - "font", "monospace", - NULL); - gtk_text_buffer_create_tag (source_buffer, "comment", - "foreground", "DodgerBlue", - NULL); - gtk_text_buffer_create_tag (source_buffer, "type", - "foreground", "ForestGreen", - NULL); - gtk_text_buffer_create_tag (source_buffer, "string", - "foreground", "RosyBrown", - "weight", PANGO_WEIGHT_BOLD, - NULL); - gtk_text_buffer_create_tag (source_buffer, "control", - "foreground", "purple", - NULL); - gtk_text_buffer_create_tag (source_buffer, "preprocessor", - "style", PANGO_STYLE_OBLIQUE, - "foreground", "burlywood4", - NULL); - gtk_text_buffer_create_tag (source_buffer, "function", - "weight", PANGO_WEIGHT_BOLD, - "foreground", "DarkGoldenrod4", - NULL); - - gtk_text_buffer_get_bounds (source_buffer, &start_iter, &tmp_iter); - gtk_text_buffer_apply_tag_by_name (source_buffer, "source", &start_iter, &tmp_iter); - - state = STATE_NORMAL; + GBytes *bytes; - gtk_text_buffer_get_iter_at_offset (source_buffer, &start_iter, 0); + gtk_text_buffer_get_bounds (source_buffer, &start, &end); + text = gtk_text_buffer_get_text (source_buffer, &start, &end, TRUE); - next_iter = start_iter; - while (gtk_text_iter_forward_line (&next_iter)) + bytes = fontify_text (format, text); + if (bytes) { - gboolean start = TRUE; - start_ptr = text = gtk_text_iter_get_text (&start_iter, &next_iter); - - do - { - parse_chars (start_ptr, &end_ptr, &state, &tag, start); - - start = FALSE; - if (end_ptr) - { - tmp_iter = start_iter; - gtk_text_iter_forward_chars (&tmp_iter, end_ptr - start_ptr); - } - else - { - tmp_iter = next_iter; - } - if (tag) - gtk_text_buffer_apply_tag_by_name (source_buffer, tag, &start_iter, &tmp_iter); + const char *markup; + gsize len; - start_iter = tmp_iter; - start_ptr = end_ptr; - } - while (end_ptr); - - g_free (text); - start_iter = next_iter; + markup = g_bytes_get_data (bytes, &len); + gtk_text_buffer_delete (source_buffer, &start, &end); + gtk_text_buffer_insert_markup (source_buffer, &start, markup, len); + g_bytes_unref (bytes); } + + g_free (text); } static GtkWidget * -display_image (const char *resource) +display_image (const char *format, + const char *resource) { GtkWidget *sw, *image; @@ -614,7 +347,8 @@ display_image (const char *resource) } static GtkWidget * -display_text (const char *resource) +display_text (const char *format, + const char *resource) { GtkTextBuffer *buffer; GtkWidget *textview, *sw; @@ -641,8 +375,10 @@ display_text (const char *resource) buffer = gtk_text_buffer_new (NULL); gtk_text_buffer_set_text (buffer, g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes)); - if (g_str_has_suffix (resource, ".c")) - fontify (buffer); + + if (format) + fontify (format, buffer); + gtk_text_view_set_buffer (GTK_TEXT_VIEW (textview), buffer); g_bytes_unref (bytes); @@ -657,7 +393,8 @@ display_text (const char *resource) } static GtkWidget * -display_video (const char *resource) +display_video (const char *format, + const char *resource) { GtkWidget *video; @@ -684,18 +421,20 @@ display_nothing (const char *resource) static struct { const char *extension; - GtkWidget * (* display_func) (const char *resource); + const char *format; + GtkWidget * (* display_func) (const char *format, + const char *resource); } display_funcs[] = { - { ".gif", display_image }, - { ".jpg", display_image }, - { ".png", display_image }, - { ".c", display_text }, - { ".css", display_text }, - { ".glsl", display_text }, - { ".h", display_text }, - { ".txt", display_text }, - { ".ui", display_text }, - { ".webm", display_video } + { ".gif", NULL, display_image }, + { ".jpg", NULL, display_image }, + { ".png", NULL, display_image }, + { ".c", "c", display_text }, + { ".css", "css", display_text }, + { ".glsl", NULL, display_text }, + { ".h", "c", display_text }, + { ".txt", NULL, display_text }, + { ".ui", "xml", display_text }, + { ".webm", NULL, display_video } }; static void @@ -725,7 +464,7 @@ add_data_tab (const char *demoname) } if (j < G_N_ELEMENTS(display_funcs)) - widget = display_funcs[j].display_func (resource_name); + widget = display_funcs[j].display_func (display_funcs[j].format, resource_name); else widget = display_nothing (resource_name); @@ -920,7 +659,7 @@ load_file (const char *demoname, g_strfreev (lines); - fontify (source_buffer); + fontify ("c", source_buffer); gtk_text_buffer_end_irreversible_action (source_buffer); gtk_text_view_set_buffer (GTK_TEXT_VIEW (source_view), source_buffer); |