[PATCH] Add support to psql for edit-and-execute-command
Hi hackers,
Attached is a small patch for adding "edit-and-execute-command" readline
support to psql. Bash has this concept and I miss it when using psql. It
allows you to amend the current line in an editor by pressing "v" (when
in vi mode) or "C-x C-e" (when in emacs mode). Those are the default
bindings from bash although of course they can be amended in inputrc.
Most of the patch is actually shifting "do_edit" from "command.c" to
"common.c". There is a small amendment to that function to allow vi to
launch at the correct column offset.
I noticed that there is some logic in configure for detecting certain
readline functions. I assume this is for compatibility sake with
libedit/editline? Rather than testing for each rl_* function I hid the
functionality behind HAVE_READLINE_READLINE_H .. don't know if this is
acceptable?
-Joe
Attachments:
0001-Add-support-to-psql-for-edit-and-execute-command.patchtext/plainDownload
From a314fa15f6bdf5329d3045d736e02b6835107591 Mon Sep 17 00:00:00 2001
From: Joe Wildish <joe@lateraljoin.com>
Date: Sun, 17 May 2020 21:57:10 +0100
Subject: [PATCH] Add support to psql for edit-and-execute-command
Bash has an edit-and-execute-command Readline function that brings the
current line into an editor to be amended rather than having to edit it
in-place at the prompt. This commit adds the same functionality to psql.
We use the same default bindings ("v" in vi mode, "C-x C-e" in emacs
mode) as bash.
---
doc/src/sgml/ref/psql-ref.sgml | 38 ++++--
src/bin/psql/command.c | 234 +-------------------------------
src/bin/psql/common.c | 236 +++++++++++++++++++++++++++++++++
src/bin/psql/common.h | 3 +
src/bin/psql/tab-complete.c | 67 ++++++++++
5 files changed, 337 insertions(+), 241 deletions(-)
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 07bf272a20..0cbc6809d4 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -4458,12 +4458,14 @@ testdb=> \set PROMPT1 '%[%033[1;33;40m%]%n@%/%R%[%033[0m%]%# '
<application>psql</application> supports the <application>Readline</application>
library for convenient line editing and retrieval. The command
history is automatically saved when <application>psql</application>
- exits and is reloaded when
- <application>psql</application> starts up. Tab-completion is also
- supported, although the completion logic makes no claim to be an
- <acronym>SQL</acronym> parser. The queries generated by tab-completion
- can also interfere with other SQL commands, e.g. <literal>SET
- TRANSACTION ISOLATION LEVEL</literal>.
+ exits and is reloaded when <application>psql</application> starts up.
+ </para>
+
+ <para>
+ Tab-completion is also supported, although the completion logic makes no
+ claim to be an <acronym>SQL</acronym> parser. The queries generated by
+ tab-completion can also interfere with other SQL commands, e.g.
+ <literal>SET TRANSACTION ISOLATION LEVEL</literal>.
If for some reason you do not like the tab completion, you
can turn it off by putting this in a file named
<filename>.inputrc</filename> in your home directory:
@@ -4472,9 +4474,27 @@ $if psql
set disable-completion on
$endif
</programlisting>
- (This is not a <application>psql</application> but a
- <application>Readline</application> feature. Read its documentation
- for further details.)
+ </para>
+
+ <para>
+ In addition to the usual line editing and retrieval commands,
+ <application>psql</application> also supports invoking an editor on the current
+ line to amend the query text, rather than having to edit it in-place at the
+ prompt. This is known as "edit-and-execute-command". The default Readline
+ binding for this is "v" when in vi mode and "C-x C-e" when in emacs mode. However,
+ the binding can be configured in the usual way from the
+ <filename>.inputrc</filename> file:
+<programlisting>
+$if psql
+"Z": edit-and-execute-command
+$endif
+</programlisting>
+ </para>
+
+ <para>
+ (The <filename>.inputrc</filename> configuration file is not a
+ <application>psql</application> but a <application>Readline</application>
+ feature. Read its documentation for further details.)
</para>
</refsect3>
</refsect2>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index a5160f91de..ecd2605ca9 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -148,8 +148,6 @@ static void discard_query_text(PsqlScanState scan_state, ConditionalStack cstack
static void copy_previous_query(PQExpBuffer query_buf, PQExpBuffer previous_buf);
static bool do_connect(enum trivalue reuse_previous_specification,
char *dbname, char *user, char *host, char *port);
-static bool do_edit(const char *filename_arg, PQExpBuffer query_buf,
- int lineno, bool *edited);
static bool do_shell(const char *command);
static bool do_watch(PQExpBuffer query_buf, double sleep);
static bool lookup_object_oid(EditableObjectType obj_type, const char *desc,
@@ -1008,7 +1006,7 @@ exec_command_edit(PsqlScanState scan_state, bool active_branch,
/* If query_buf is empty, recall previous query for editing */
copy_previous_query(query_buf, previous_buf);
- if (do_edit(fname, query_buf, lineno, NULL))
+ if (do_edit(fname, query_buf, lineno, 0, NULL))
status = PSQL_CMD_NEWEDIT;
else
status = PSQL_CMD_ERROR;
@@ -1131,7 +1129,7 @@ exec_command_ef_ev(PsqlScanState scan_state, bool active_branch,
{
bool edited = false;
- if (!do_edit(NULL, query_buf, lineno, &edited))
+ if (!do_edit(NULL, query_buf, lineno, 0, &edited))
status = PSQL_CMD_ERROR;
else if (!edited)
puts(_("No changes"));
@@ -3511,234 +3509,6 @@ UnsyncVariables(void)
}
-/*
- * do_edit -- handler for \e
- *
- * If you do not specify a filename, the current query buffer will be copied
- * into a temporary one.
- */
-static bool
-editFile(const char *fname, int lineno)
-{
- const char *editorName;
- const char *editor_lineno_arg = NULL;
- char *sys;
- int result;
-
- Assert(fname != NULL);
-
- /* Find an editor to use */
- editorName = getenv("PSQL_EDITOR");
- if (!editorName)
- editorName = getenv("EDITOR");
- if (!editorName)
- editorName = getenv("VISUAL");
- if (!editorName)
- editorName = DEFAULT_EDITOR;
-
- /* Get line number argument, if we need it. */
- if (lineno > 0)
- {
- editor_lineno_arg = getenv("PSQL_EDITOR_LINENUMBER_ARG");
-#ifdef DEFAULT_EDITOR_LINENUMBER_ARG
- if (!editor_lineno_arg)
- editor_lineno_arg = DEFAULT_EDITOR_LINENUMBER_ARG;
-#endif
- if (!editor_lineno_arg)
- {
- pg_log_error("environment variable PSQL_EDITOR_LINENUMBER_ARG must be set to specify a line number");
- return false;
- }
- }
-
- /*
- * On Unix the EDITOR value should *not* be quoted, since it might include
- * switches, eg, EDITOR="pico -t"; it's up to the user to put quotes in it
- * if necessary. But this policy is not very workable on Windows, due to
- * severe brain damage in their command shell plus the fact that standard
- * program paths include spaces.
- */
-#ifndef WIN32
- if (lineno > 0)
- sys = psprintf("exec %s %s%d '%s'",
- editorName, editor_lineno_arg, lineno, fname);
- else
- sys = psprintf("exec %s '%s'",
- editorName, fname);
-#else
- if (lineno > 0)
- sys = psprintf("\"%s\" %s%d \"%s\"",
- editorName, editor_lineno_arg, lineno, fname);
- else
- sys = psprintf("\"%s\" \"%s\"",
- editorName, fname);
-#endif
- result = system(sys);
- if (result == -1)
- pg_log_error("could not start editor \"%s\"", editorName);
- else if (result == 127)
- pg_log_error("could not start /bin/sh");
- free(sys);
-
- return result == 0;
-}
-
-
-/* call this one */
-static bool
-do_edit(const char *filename_arg, PQExpBuffer query_buf,
- int lineno, bool *edited)
-{
- char fnametmp[MAXPGPATH];
- FILE *stream = NULL;
- const char *fname;
- bool error = false;
- int fd;
-
- struct stat before,
- after;
-
- if (filename_arg)
- fname = filename_arg;
- else
- {
- /* make a temp file to edit */
-#ifndef WIN32
- const char *tmpdir = getenv("TMPDIR");
-
- if (!tmpdir)
- tmpdir = "/tmp";
-#else
- char tmpdir[MAXPGPATH];
- int ret;
-
- ret = GetTempPath(MAXPGPATH, tmpdir);
- if (ret == 0 || ret > MAXPGPATH)
- {
- pg_log_error("could not locate temporary directory: %s",
- !ret ? strerror(errno) : "");
- return false;
- }
-
- /*
- * No canonicalize_path() here. EDIT.EXE run from CMD.EXE prepends the
- * current directory to the supplied path unless we use only
- * backslashes, so we do that.
- */
-#endif
-#ifndef WIN32
- snprintf(fnametmp, sizeof(fnametmp), "%s%spsql.edit.%d.sql", tmpdir,
- "/", (int) getpid());
-#else
- snprintf(fnametmp, sizeof(fnametmp), "%s%spsql.edit.%d.sql", tmpdir,
- "" /* trailing separator already present */ , (int) getpid());
-#endif
-
- fname = (const char *) fnametmp;
-
- fd = open(fname, O_WRONLY | O_CREAT | O_EXCL, 0600);
- if (fd != -1)
- stream = fdopen(fd, "w");
-
- if (fd == -1 || !stream)
- {
- pg_log_error("could not open temporary file \"%s\": %m", fname);
- error = true;
- }
- else
- {
- unsigned int ql = query_buf->len;
-
- /* force newline-termination of what we send to editor */
- if (ql > 0 && query_buf->data[ql - 1] != '\n')
- {
- appendPQExpBufferChar(query_buf, '\n');
- ql++;
- }
-
- if (fwrite(query_buf->data, 1, ql, stream) != ql)
- {
- pg_log_error("%s: %m", fname);
-
- if (fclose(stream) != 0)
- pg_log_error("%s: %m", fname);
-
- if (remove(fname) != 0)
- pg_log_error("%s: %m", fname);
-
- error = true;
- }
- else if (fclose(stream) != 0)
- {
- pg_log_error("%s: %m", fname);
- if (remove(fname) != 0)
- pg_log_error("%s: %m", fname);
- error = true;
- }
- }
- }
-
- if (!error && stat(fname, &before) != 0)
- {
- pg_log_error("%s: %m", fname);
- error = true;
- }
-
- /* call editor */
- if (!error)
- error = !editFile(fname, lineno);
-
- if (!error && stat(fname, &after) != 0)
- {
- pg_log_error("%s: %m", fname);
- error = true;
- }
-
- if (!error && before.st_mtime != after.st_mtime)
- {
- stream = fopen(fname, PG_BINARY_R);
- if (!stream)
- {
- pg_log_error("%s: %m", fname);
- error = true;
- }
- else
- {
- /* read file back into query_buf */
- char line[1024];
-
- resetPQExpBuffer(query_buf);
- while (fgets(line, sizeof(line), stream) != NULL)
- appendPQExpBufferStr(query_buf, line);
-
- if (ferror(stream))
- {
- pg_log_error("%s: %m", fname);
- error = true;
- }
- else if (edited)
- {
- *edited = true;
- }
-
- fclose(stream);
- }
- }
-
- /* remove temp file */
- if (!filename_arg)
- {
- if (remove(fname) == -1)
- {
- pg_log_error("%s: %m", fname);
- error = true;
- }
- }
-
- return !error;
-}
-
-
/*
* process_file
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index 06f801764b..4f87f71ed4 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -12,10 +12,15 @@
#include <math.h>
#include <signal.h>
#ifndef WIN32
+#include <sys/stat.h> /* for stat() */
+#include <fcntl.h> /* open() flags */
#include <unistd.h> /* for write() */
#else
#include <io.h> /* for _write() */
#include <win32.h>
+#include <fcntl.h>
+#include <direct.h>
+#include <sys/stat.h> /* for stat() */
#endif
#include "command.h"
@@ -33,6 +38,7 @@ static bool DescribeQuery(const char *query, double *elapsed_msec);
static bool ExecQueryUsingCursor(const char *query, double *elapsed_msec);
static bool command_no_begin(const char *query);
static bool is_select_command(const char *query);
+static bool editFile(const char *fname, int lineno, int colno);
/*
@@ -2304,3 +2310,233 @@ recognized_connection_string(const char *connstr)
{
return uri_prefix_length(connstr) != 0 || strchr(connstr, '=') != NULL;
}
+
+
+bool
+do_edit(const char *filename_arg, PQExpBuffer query_buf,
+ int lineno, int colno, bool *edited)
+{
+ char fnametmp[MAXPGPATH];
+ FILE *stream = NULL;
+ const char *fname;
+ bool error = false;
+ int fd;
+
+ struct stat before,
+ after;
+
+ if (filename_arg)
+ fname = filename_arg;
+ else
+ {
+ /* make a temp file to edit */
+#ifndef WIN32
+ const char *tmpdir = getenv("TMPDIR");
+
+ if (!tmpdir)
+ tmpdir = "/tmp";
+#else
+ char tmpdir[MAXPGPATH];
+ int ret;
+
+ ret = GetTempPath(MAXPGPATH, tmpdir);
+ if (ret == 0 || ret > MAXPGPATH)
+ {
+ pg_log_error("could not locate temporary directory: %s",
+ !ret ? strerror(errno) : "");
+ return false;
+ }
+
+ /*
+ * No canonicalize_path() here. EDIT.EXE run from CMD.EXE prepends the
+ * current directory to the supplied path unless we use only
+ * backslashes, so we do that.
+ */
+#endif
+#ifndef WIN32
+ snprintf(fnametmp, sizeof(fnametmp), "%s%spsql.edit.%d.sql", tmpdir,
+ "/", (int) getpid());
+#else
+ snprintf(fnametmp, sizeof(fnametmp), "%s%spsql.edit.%d.sql", tmpdir,
+ "" /* trailing separator already present */ , (int) getpid());
+#endif
+
+ fname = (const char *) fnametmp;
+
+ fd = open(fname, O_WRONLY | O_CREAT | O_EXCL, 0600);
+ if (fd != -1)
+ stream = fdopen(fd, "w");
+
+ if (fd == -1 || !stream)
+ {
+ pg_log_error("could not open temporary file \"%s\": %m", fname);
+ error = true;
+ }
+ else
+ {
+ unsigned int ql = query_buf->len;
+
+ /* force newline-termination of what we send to editor */
+ if (ql > 0 && query_buf->data[ql - 1] != '\n')
+ {
+ appendPQExpBufferChar(query_buf, '\n');
+ ql++;
+ }
+
+ if (fwrite(query_buf->data, 1, ql, stream) != ql)
+ {
+ pg_log_error("%s: %m", fname);
+
+ if (fclose(stream) != 0)
+ pg_log_error("%s: %m", fname);
+
+ if (remove(fname) != 0)
+ pg_log_error("%s: %m", fname);
+
+ error = true;
+ }
+ else if (fclose(stream) != 0)
+ {
+ pg_log_error("%s: %m", fname);
+ if (remove(fname) != 0)
+ pg_log_error("%s: %m", fname);
+ error = true;
+ }
+ }
+ }
+
+ if (!error && stat(fname, &before) != 0)
+ {
+ pg_log_error("%s: %m", fname);
+ error = true;
+ }
+
+ /* call editor */
+ if (!error)
+ error = !editFile(fname, lineno, colno);
+
+ if (!error && stat(fname, &after) != 0)
+ {
+ pg_log_error("%s: %m", fname);
+ error = true;
+ }
+
+ if (!error && before.st_mtime != after.st_mtime)
+ {
+ stream = fopen(fname, PG_BINARY_R);
+ if (!stream)
+ {
+ pg_log_error("%s: %m", fname);
+ error = true;
+ }
+ else
+ {
+ /* read file back into query_buf */
+ char line[1024];
+
+ resetPQExpBuffer(query_buf);
+ while (fgets(line, sizeof(line), stream) != NULL)
+ appendPQExpBufferStr(query_buf, line);
+
+ if (ferror(stream))
+ {
+ pg_log_error("%s: %m", fname);
+ error = true;
+ }
+ else if (edited)
+ {
+ *edited = true;
+ }
+
+ fclose(stream);
+ }
+ }
+
+ /* remove temp file */
+ if (!filename_arg)
+ {
+ if (remove(fname) == -1)
+ {
+ pg_log_error("%s: %m", fname);
+ error = true;
+ }
+ }
+
+ return !error;
+}
+
+
+/*
+ * do_edit -- handler for \e
+ *
+ * If you do not specify a filename, the current query buffer will be copied
+ * into a temporary one.
+ */
+static bool
+editFile(const char *fname, int lineno, int colno)
+{
+ const char *editorName;
+ const char *editor_lineno_arg = NULL;
+ char *sys;
+ int result;
+
+ Assert(fname != NULL);
+
+ /* Find an editor to use */
+ editorName = getenv("PSQL_EDITOR");
+ if (!editorName)
+ editorName = getenv("EDITOR");
+ if (!editorName)
+ editorName = getenv("VISUAL");
+ if (!editorName)
+ editorName = DEFAULT_EDITOR;
+
+ /* Get line number argument, if we need it. */
+ if (lineno > 0)
+ {
+ editor_lineno_arg = getenv("PSQL_EDITOR_LINENUMBER_ARG");
+#ifdef DEFAULT_EDITOR_LINENUMBER_ARG
+ if (!editor_lineno_arg)
+ editor_lineno_arg = DEFAULT_EDITOR_LINENUMBER_ARG;
+#endif
+ if (!editor_lineno_arg)
+ {
+ pg_log_error("environment variable PSQL_EDITOR_LINENUMBER_ARG must be set to specify a line number");
+ return false;
+ }
+ }
+
+ /*
+ * On Unix the EDITOR value should *not* be quoted, since it might include
+ * switches, eg, EDITOR="pico -t"; it's up to the user to put quotes in it
+ * if necessary. But this policy is not very workable on Windows, due to
+ * severe brain damage in their command shell plus the fact that standard
+ * program paths include spaces.
+ */
+#ifndef WIN32
+ if (lineno > 0 && strcmp(editorName, DEFAULT_EDITOR) == 0)
+ sys = psprintf("exec %s -c'call cursor(%d,%d)' '%s'",
+ editorName, lineno, colno, fname);
+ else if (lineno > 0)
+ sys = psprintf("exec %s %s%d '%s'",
+ editorName, editor_lineno_arg, lineno, fname);
+ else
+ sys = psprintf("exec %s '%s'",
+ editorName, fname);
+#else
+ if (lineno > 0)
+ sys = psprintf("\"%s\" %s%d \"%s\"",
+ editorName, editor_lineno_arg, lineno, fname);
+ else
+ sys = psprintf("\"%s\" \"%s\"",
+ editorName, fname);
+#endif
+ result = system(sys);
+ if (result == -1)
+ pg_log_error("could not start editor \"%s\"", editorName);
+ else if (result == 127)
+ pg_log_error("could not start /bin/sh");
+ free(sys);
+
+ return result == 0;
+}
\ No newline at end of file
diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h
index ec4e83c9fd..76767732a4 100644
--- a/src/bin/psql/common.h
+++ b/src/bin/psql/common.h
@@ -41,4 +41,7 @@ extern void expand_tilde(char **filename);
extern bool recognized_connection_string(const char *connstr);
+extern bool do_edit(const char *filename_arg, PQExpBuffer query_buf,
+ int lineno, int colno, bool *edited);
+
#endif /* COMMON_H */
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index eb018854a5..bbdc3bce4f 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -76,6 +76,12 @@
/* word break characters */
#define WORD_BREAKS "\t\n@$><=;|&{() "
+#ifdef HAVE_READLINE_READLINE_H
+/* Helpers to define emacs bindings for Control-X */
+#define READLINE_CONTROL_CHARACTER_MASK 0x1f /* 0x20 - 1 */
+#define READLINE_CTRL(c) ((c) & READLINE_CONTROL_CHARACTER_MASK)
+#endif
+
/*
* Since readline doesn't let us pass any state through to the tab completion
* callback, we have to use this global variable to let get_previous_words()
@@ -1138,6 +1144,10 @@ static char **get_previous_words(int point, char **buffer, int *nwords);
static char *get_guctype(const char *varname);
+#ifdef HAVE_READLINE_READLINE_H
+static int edit_and_execute_command(int count, int c);
+#endif
+
#ifdef USE_FILENAME_QUOTING_FUNCTIONS
static char *quote_file_name(char *fname, int match_type, char *quote_pointer);
static char *dequote_file_name(char *fname, int quote_char);
@@ -1160,6 +1170,17 @@ initialize_readline(void)
rl_basic_word_break_characters = WORD_BREAKS;
+ /*
+ * Bind 'v' or 'C-xC-e' to invoke vi or emacs and run result as commands.
+ */
+#ifdef HAVE_READLINE_READLINE_H
+ {
+ rl_add_defun("edit-and-execute-command", edit_and_execute_command, -1);
+ rl_bind_key_if_unbound_in_map(READLINE_CTRL('E'), edit_and_execute_command, emacs_ctlx_keymap);
+ rl_bind_key_if_unbound_in_map('v', edit_and_execute_command, vi_movement_keymap);
+ }
+#endif
+
/*
* We should include '"' in rl_completer_quote_characters too, but that
* will require some upgrades to how we handle quoted identifiers, so
@@ -4814,6 +4835,52 @@ get_guctype(const char *varname)
return guctype;
}
+
+/*
+ * Edit the current line in vi (or $EDITOR, $VISUAL).
+ */
+#ifdef HAVE_READLINE_READLINE_H
+static int
+edit_and_execute_command(int count, int c)
+{
+ PQExpBuffer buffer = createPQExpBuffer();
+ bool edited = false;
+ bool result;
+ int point, lineno, colno;
+
+ appendPQExpBufferStr(buffer, rl_line_buffer);
+
+ /* Discover which line and column we are at. */
+ lineno = 1;
+ colno = 0;
+ for(point = rl_point; point >= 1; point--)
+ {
+ if (rl_line_buffer[point] == '\n')
+ lineno++;
+ if (lineno == 1)
+ colno++;
+ }
+
+ result = do_edit(NULL, buffer, lineno, colno, &edited);
+
+ if (result && edited)
+ {
+ rl_clear_visible_line();
+ rl_extend_line_buffer(buffer->len);
+ rl_replace_line(buffer->data, 1);
+ rl_reset_line_state();
+ rl_redisplay();
+
+ /* accept the line */
+ rl_newline(1, c);
+ }
+
+ destroyPQExpBuffer(buffer);
+
+ return result ? 0 : 1;
+}
+#endif
+
#ifdef USE_FILENAME_QUOTING_FUNCTIONS
/*
--
2.26.2
On Mon, May 18, 2020 at 1:30 AM Joe Wildish <joe@lateraljoin.com> wrote:
Attached is a small patch for adding "edit-and-execute-command" readline
support to psql. Bash has this concept and I miss it when using psql. It
allows you to amend the current line in an editor by pressing "v" (when
in vi mode) or "C-x C-e" (when in emacs mode). Those are the default
bindings from bash although of course they can be amended in inputrc.
The only difference from \e is that you don't need to jump to the end of
input first, I guess?
--
Alex
On 18 May 2020, at 7:08, Oleksandr Shulgin wrote:
The only difference from \e is that you don't need to jump to the end
of
input first, I guess?
AIUI, \e will edit the last thing in history or a specific line number
from history, whereas the patch will allow the current line to be
edited. That is 99% of the time what I want.
My work flow is typically "Run some queries" => "Go back to some recent
query by searching history, often not the most recent" => "Edit query".
To do the edit in an editor (without the patch), I've been deliberately
nobbling the query once found from history. This allows it to execute
(and fail) but places it as the most recent thing in history. Then I hit
"\e".
-Joe
po 18. 5. 2020 v 12:05 odesílatel Joe Wildish <joe@lateraljoin.com> napsal:
On 18 May 2020, at 7:08, Oleksandr Shulgin wrote:
The only difference from \e is that you don't need to jump to the end of
input first, I guess?AIUI, \e will edit the last thing in history or a specific line number
from history, whereas the patch will allow the current line to be edited.
That is 99% of the time what I want.My work flow is typically "Run some queries" => "Go back to some recent
query by searching history, often not the most recent" => "Edit query". To
do the edit in an editor (without the patch), I've been deliberately
nobbling the query once found from history. This allows it to execute (and
fail) but places it as the most recent thing in history. Then I hit "\e".
\e is working with not empty line too.You can check
select 1\e
Your patch just save skip on end line and \e
Personally I think so it is good idea
Pavel
-Joe
Show quoted text
On 18 May 2020, at 11:09, Pavel Stehule wrote:
\e is working with not empty line too.You can check
select 1\e
Your patch just save skip on end line and \e
Personally I think so it is good idea
Thanks. I did not realise that \e at the end of a line would edit that
line. (although you do need to remove the terminator I notice).
-Joe
po 18. 5. 2020 v 12:16 odesílatel Joe Wildish <joe@lateraljoin.com> napsal:
On 18 May 2020, at 11:09, Pavel Stehule wrote:
\e is working with not empty line too.You can check
select 1\e
Your patch just save skip on end line and \e
Personally I think so it is good idea
Thanks. I did not realise that \e at the end of a line would edit that
line. (although you do need to remove the terminator I notice).
it is different method with little bit different usage - both method can be
supported
-Joe