Re: [Vimprobable-users] view the HTML source with an external editor?
Vimprobable is a lean web browser optimised for full keyboard control
Brought to you by:
hanness
From: Matthew C. <je...@gm...> - 2014-05-02 18:31:44
|
Hi all, Forgot to add, you can invoke it on a page with ":esource". If there was a demand for it, this could probably be updated to actually copy the full page source instead of relying on the jsapi_eval call to pull the innerHTML (outerHTML is closer but still excludes the doctype) and resave without the jsapi as well. -Matt On Fri, May 02, 2014 at 02:27:29PM -0400, Matthew Carter wrote: > Hi all, > > Attached is a patch to do what Marcos described, it also lets you edit > the source and upon saving and quitting your editor, it updates the > page's source code to match what you changed. > > Uses the same logic and URI handler as the special textarea editor > handling. > > It does not include the surrounding <html> tags or the doctype as that > makes it difficult to refresh the browser with what was edited (so > essentially you're getting a view of the > document.documentElement.innerHTML, which you can then change/look > through in your editor of choice). > > Thanks, > -Matt > > On Sun, Apr 20, 2014 at 03:35:10PM +0200, Marcos Cruz wrote: > > Hi all, > > > > Sometimes, when I need to examinate the source of a page, I prefer to do > > it with all the power of Vim instead of the internal viewer. So I get > > the page with wget or the Elinks browser and then launch Vim. > > > > I think currently there's no way to choose an external editor as source > > viewer. Am I right? If so, I suggest this feature. Would it be > > feasible? Do other Vimprobable users would find it useful? > > > > -- > > Marcos Cruz > > http://programandala.net > > > > ------------------------------------------------------------------------------ > > Learn Graph Databases - Download FREE O'Reilly Book > > "Graph Databases" is the definitive new guide to graph databases and their > > applications. Written by three acclaimed leaders in the field, > > this first edition is now available. Download your free book today! > > http://p.sf.net/sfu/NeoTech > > _______________________________________________ > > Vimprobable-users mailing list > > Vim...@li... > > https://lists.sourceforge.net/lists/listinfo/vimprobable-users > > -- > Matthew Carter > je...@gm... > diff --git a/config.h b/config.h > index e9d6bba..9b6a7cc 100644 > --- a/config.h > +++ b/config.h > @@ -154,6 +154,7 @@ Command commands[COMMANDSIZE] = { > { "bma", bookmark, {0} }, > { "bookmark", bookmark, {0} }, > { "source", view_source, {0} }, > + { "esource", edit_source, {0} }, > { "openeditor", open_editor, {0} }, > { "set", browser_settings, {0} }, > { "map", mappings, {0} }, > @@ -162,7 +163,7 @@ Command commands[COMMANDSIZE] = { > { "jumpright", scroll, {ScrollJumpTo | DirectionRight} }, > { "jumptop", scroll, {ScrollJumpTo | DirectionTop} }, > { "jumpbottom", scroll, {ScrollJumpTo | DirectionBottom} }, > - { "pageup", scroll, {ScrollMove | DirectionTop | UnitPage} }, > + { "pageup", scroll, {ScrollMove | DirectionTop | UnitPage} }, > { "pagedown", scroll, {ScrollMove | DirectionBottom | UnitPage} }, > { "navigationback", navigate, {NavigationBack} }, > { "navigationforward", navigate, {NavigationForward} }, > diff --git a/main.c b/main.c > index d70f88a..0c1a4c6 100644 > --- a/main.c > +++ b/main.c > @@ -69,7 +69,9 @@ static gboolean descend(const Arg *arg); > gboolean echo(const Arg *arg); > static gboolean focus_input(const Arg *arg); > static gboolean open_editor(const Arg *arg); > +static gboolean edit_source(const Arg *arg); > void _resume_from_editor(GPid child_pid, int status, gpointer data); > +void _resume_from_edit_source(GPid child_pid, int status, gpointer data); > static gboolean input(const Arg *arg); > static gboolean open_inspector(const Arg * arg); > static gboolean navigate(const Arg *arg); > @@ -1066,7 +1068,7 @@ static gboolean > open_inspector(const Arg * arg) { > gboolean inspect_enabled; > WebKitWebSettings *settings; > - State *state = &client.state; > + State *state = &client.state; > > settings = webkit_web_view_get_settings(client.gui.webview); > g_object_get(G_OBJECT(settings), "enable-developer-extras", &inspect_enabled, NULL); > @@ -1239,7 +1241,7 @@ open_arg(const Arg *arg) { > new = NULL; > /* check for external handlers */ > if (open_handler(s)) > - return TRUE; > + return TRUE; > /* check for search engines */ > p = strchr(s, ' '); > if (!p) { > @@ -1414,7 +1416,7 @@ revive(const Arg *arg) { > return FALSE; > } > > -static > +static > gboolean print_frame(const Arg *arg) > { > WebKitWebFrame *frame = webkit_web_view_get_main_frame(client.gui.webview); > @@ -1667,7 +1669,7 @@ zoom(const Arg *arg) { > return TRUE; > } > > -gboolean > +gboolean > fake_key_event(const Arg *a) { > if(!client.state.embed) { > return FALSE; > @@ -1677,7 +1679,7 @@ fake_key_event(const Arg *a) { > echo_message(Error, "Couldn't find the XDisplay."); > return FALSE; > } > - > + > XKeyEvent xk; > xk.display = xdpy; > xk.subwindow = None; > @@ -1697,12 +1699,12 @@ fake_key_event(const Arg *a) { > echo_message(Error, "Couldn't translate %s to keysym", a->s ); > return FALSE; > } > - > + > if( (xk.keycode = XKeysymToKeycode(xdpy, keysym)) == NoSymbol ) { > echo_message(Error, "Couldn't translate keysym to keycode"); > return FALSE; > } > - > + > xk.type = KeyPress; > if( !XSendEvent(xdpy, client.state.embed, True, KeyPressMask, (XEvent *)&xk) ) { > echo_message(Error, "XSendEvent failed"); > @@ -1930,7 +1932,7 @@ open_editor(const Arg *arg) { > g_free(message); > return FALSE; > } > - > + > /* mark the active text box as "under processing" */ > jsapi_evaluate_script( > "document.activeElement.disabled = true;" > @@ -1948,14 +1950,79 @@ open_editor(const Arg *arg) { > return TRUE; > } > > +/* open an external editor defined by the protocol handler for > +vimprobableedit on page source */ > +static gboolean > +edit_source(const Arg *arg) { > + char *text = NULL; > + gboolean success; > + GPid child_pid; > + gchar *value = NULL, *message = NULL, *tag = NULL, *edit_url = NULL; > + gchar *temp_file_name = g_strdup_printf("%s/vimprobableeditXXXXXX", > + temp_dir); > + int temp_file_handle = -1; > + > + jsapi_evaluate_script("document.documentElement.innerHTML", &value, &message); > + text = g_strdup(value); > + if (text == NULL) { > + g_free(value); > + g_free(message); > + return FALSE; > + } > + > + /* write text into temporary file */ > + temp_file_handle = mkstemp(temp_file_name); > + if (temp_file_handle == -1) { > + message = g_strdup_printf("Could not create temporary file: %s", > + strerror(errno)); > + echo_message(Error, message); > + g_free(value); > + g_free(message); > + g_free(text); > + return FALSE; > + } > + if (write(temp_file_handle, text, strlen(text)) != strlen(text)) { > + message = g_strdup_printf("Short write to temporary file: %s", > + strerror(errno)); > + echo_message(Error, message); > + g_free(value); > + g_free(message); > + g_free(text); > + return FALSE; > + } > + close(temp_file_handle); > + g_free(text); > + > + /* spawn editor */ > + edit_url = g_strdup_printf("vimprobableedit:%s", temp_file_name); > + success = open_handler_pid(edit_url, &child_pid); > + g_free(edit_url); > + if (!success) { > + echo_message(Error, "External editor open failed (no handler for" > + " vimprobableedit protocol?)"); > + unlink(temp_file_name); > + g_free(value); > + g_free(message); > + return FALSE; > + } > + > + g_child_watch_add(child_pid, _resume_from_edit_source, temp_file_name); > + > + /* temp_file_name is freed in _resume_from_editor */ > + g_free(value); > + g_free(message); > + g_free(tag); > + return TRUE; > +} > + > > /* pick up from where open_editor left the work to the glib event loop. > > -This is called when the external editor exits. > +This is called when the external editor exits. > > The data argument points to allocated memory containing the temporary file > name. */ > -void > +void > _resume_from_editor(GPid child_pid, int child_status, gpointer data) { > FILE *fp; > GString *set_value_js = g_string_new( > @@ -1963,7 +2030,7 @@ _resume_from_editor(GPid child_pid, int child_status, gpointer data) { > g_spawn_close_pid(child_pid); > gchar *value = NULL, *message = NULL; > gchar *temp_file_name = data; > - gchar buffer[BUF_SIZE] = ""; > + gchar buffer[BUF_SIZE] = ""; > gchar *buf_ptr = buffer; > int char_read; > > @@ -1991,7 +2058,7 @@ _resume_from_editor(GPid child_pid, int child_status, gpointer data) { > /* this would be too weird to even emit an error message */ > goto error_exit; > } > - jsapi_evaluate_script("document.activeElement.value = '';", > + jsapi_evaluate_script("document.activeElement.value = '';", > &value, &message); > g_free(value); > g_free(message); > @@ -2038,6 +2105,82 @@ error_exit: > g_free(message); > } > > +/* pick up from where edit_source left the work to the glib event loop. > + > +This is called when the external editor exits. > + > +The data argument points to allocated memory containing the temporary file > +name. */ > +void > +_resume_from_edit_source(GPid child_pid, int child_status, gpointer data) { > + FILE *fp; > + GString *set_value_js = g_string_new( > + "document.documentElement.innerHTML = \""); > + g_spawn_close_pid(child_pid); > + gchar *value = NULL, *message = NULL; > + gchar *temp_file_name = data; > + gchar buffer[BUF_SIZE] = ""; > + gchar *buf_ptr = buffer; > + int char_read; > + > + if (child_status) { > + echo_message(Error, "External editor returned with non-zero status," > + " discarding edits."); > + goto error_exit; > + } > + > + /* re-read the new contents of the file and put it into the HTML element */ > + if (!access(temp_file_name, R_OK) == 0) { > + message = g_strdup_printf("Could not access temporary file: %s", > + strerror(errno)); > + goto error_exit; > + } > + fp = fopen(temp_file_name, "r"); > + if (fp == NULL) { > + /* this would be too weird to even emit an error message */ > + goto error_exit; > + } > + jsapi_evaluate_script("document.documentElement.innerHTML = '';", > + &value, &message); > + g_free(value); > + g_free(message); > + > + while (EOF != (char_read = fgetc(fp))) { > + if (char_read == '\n') { > + *buf_ptr++ = '\\'; > + *buf_ptr++ = 'n'; > + } else if (char_read == '"') { > + *buf_ptr++ = '\\'; > + *buf_ptr++ = '"'; > + } else { > + *buf_ptr++ = char_read; > + } > + /* ship out as the buffer when space gets tight. This has > + fuzz to save on thinking, plus we have enough space for the > + trailing "; in any case. */ > + if (buf_ptr-buffer>=BUF_SIZE-10) { > + *buf_ptr = 0; > + g_string_append(set_value_js, buffer); > + buf_ptr = buffer; > + } > + } > + *buf_ptr++ = '"'; > + *buf_ptr++ = ';'; > + *buf_ptr = 0; > + g_string_append(set_value_js, buffer); > + fclose(fp); > + > + jsapi_evaluate_script(set_value_js->str, &value, &message); > + > +error_exit: > + > + g_string_free(set_value_js, TRUE); > + unlink(temp_file_name); > + g_free(temp_file_name); > + g_free(value); > + g_free(message); > +} > + > static gboolean > focus_input(const Arg *arg) { > static Arg a; > @@ -2199,16 +2342,16 @@ process_set_line(char *line) { > } else if (strlen(my_pair.what) == 7 && strncmp("cookies", my_pair.what, 7) == 0) { > /* cookie policy */ > if (strncmp(my_pair.value, "on", 2) == 0 || strncmp(my_pair.value, "true", 4) == 0 || > - strncmp(my_pair.value, "ON", 2) == 0 || strncmp(my_pair.value, "TRUE", 4) == 0 || > + strncmp(my_pair.value, "ON", 2) == 0 || strncmp(my_pair.value, "TRUE", 4) == 0 || > strncmp(my_pair.value, "all", 3) == 0 || strncmp(my_pair.value, "ALL", 3) == 0) { > CookiePolicy = SOUP_COOKIE_JAR_ACCEPT_ALWAYS; > - } else if (strncmp(my_pair.value, "off", 3) == 0 || strncmp(my_pair.value, "false", 5) == 0 || > - strncmp(my_pair.value, "OFF", 3) == 0 || strncmp(my_pair.value, "FALSE", 5) == 0 || > - strncmp(my_pair.value, "never", 5) == 0 || strncmp(my_pair.value, "NEVER", 5) == 5 || > + } else if (strncmp(my_pair.value, "off", 3) == 0 || strncmp(my_pair.value, "false", 5) == 0 || > + strncmp(my_pair.value, "OFF", 3) == 0 || strncmp(my_pair.value, "FALSE", 5) == 0 || > + strncmp(my_pair.value, "never", 5) == 0 || strncmp(my_pair.value, "NEVER", 5) == 5 || > strncmp(my_pair.value, "none", 4) == 0 || strncmp(my_pair.value, "NONE", 4) == 0) { > CookiePolicy = SOUP_COOKIE_JAR_ACCEPT_NEVER; > - } else if (strncmp(my_pair.value, "origin", 6) == 0 || strncmp(my_pair.value, "ORIGIN", 6) == 0 || > - strncmp(my_pair.value, "no_third", 8) == 0 || strncmp(my_pair.value, "NO_THIRD", 8) == 0 || > + } else if (strncmp(my_pair.value, "origin", 6) == 0 || strncmp(my_pair.value, "ORIGIN", 6) == 0 || > + strncmp(my_pair.value, "no_third", 8) == 0 || strncmp(my_pair.value, "NO_THIRD", 8) == 0 || > strncmp(my_pair.value, "no third", 8) == 0 || strncmp(my_pair.value, "NO THIRD", 8) == 0) { > CookiePolicy = SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY; > } else { > @@ -2334,7 +2477,7 @@ search_tag(const Arg * a) { > t = strlen(s) - 1; > while (isspace(s[t])) > t--; > - if (s[t] != ']') continue; > + if (s[t] != ']') continue; > while (t > 0) { > if (s[t] == ']') { > if (!intag) > @@ -2349,7 +2492,7 @@ search_tag(const Arg * a) { > while (k < intag) > foundtag[i++] = s[k++]; > foundtag[i] = '\0'; > - /* foundtag now contains the tag */ > + /* foundtag now contains the tag */ > if (strlen(foundtag) < MAXTAGSIZE && strcmp(tag, foundtag) == 0) { > i = 0; > while (isspace(s[i])) i++; > @@ -2798,7 +2941,7 @@ setup_cookies() > * is limited to handling cookies. > */ > void > -new_generic_request(SoupSession *session, SoupMessage *soup_msg, gpointer unused) > +new_generic_request(SoupSession *session, SoupMessage *soup_msg, gpointer unused) > { > SoupMessageHeaders *soup_msg_h; > SoupURI *uri; > @@ -2840,7 +2983,7 @@ handle_cookie_request(SoupMessage *soup_msg, gpointer unused) > { > SoupDate *soup_date; > cookie = soup_cookie_copy((SoupCookie *)resp_cookie->data); > - > + > if (client.config.cookie_timeout && cookie->expires == NULL) { > soup_date = soup_date_new_from_time_t(time(NULL) + client.config.cookie_timeout * 10); > soup_cookie_set_expires(cookie, soup_date); > @@ -2856,7 +2999,7 @@ handle_cookie_request(SoupMessage *soup_msg, gpointer unused) > soup_cookie_jar_add_cookie(client.net.file_cookie_jar, cookie); > } > } > - > + > soup_cookies_free(cookie_list); > } > -- Matthew Carter je...@gm... |