Skip to content

Instantly share code, notes, and snippets.

@matthewlmcclure
Created June 19, 2013 19:57
Show Gist options
  • Save matthewlmcclure/5817501 to your computer and use it in GitHub Desktop.
Save matthewlmcclure/5817501 to your computer and use it in GitHub Desktop.
Add incremental search to less. 2013-06-19-02
===================================================================
RCS file: RCS/command.c,v
retrieving revision 1.92
diff -u -r1.92 command.c
--- command.c 2012/07/03 20:10:42 1.92
+++ command.c 2013/06/19 19:37:59
@@ -47,6 +47,8 @@
extern int shift_count;
extern int oldbot;
extern int forw_prompt;
+extern int incremental_search;
+extern int same_pos_bell;
#if SHELL_ESCAPE
static char *shellcmd = NULL; /* For holding last shell command for "!!" */
@@ -61,6 +63,7 @@
static int optgetname;
static POSITION bottompos;
static int save_hshift;
+static struct mark searchstack[CMDBUF_SIZE];
#if PIPEC
static char pipec;
#endif
@@ -203,7 +206,7 @@
{
case A_F_SEARCH:
case A_B_SEARCH:
- multi_search(cbuf, (int) number);
+ multi_search(cbuf, (int) number, 0);
break;
#if HILITE_SEARCH
case A_FILTER:
@@ -516,6 +519,7 @@
mca_char(c)
int c;
{
+ char *cbuf;
int ret;
switch (mca)
@@ -606,6 +610,43 @@
return (MCA_DONE);
}
+ if (incremental_search && (mca == A_F_SEARCH || mca == A_B_SEARCH))
+ {
+ /*
+ * Special case for incremental search.
+ * Run multi_search with incremental=1
+ * and repaint prompt after each char in search.
+ * Don't ring the same-position bell while typing.
+ */
+ same_pos_bell = 0;
+ if (is_erase_char(c))
+ {
+ /* Return to previous incremental search position. */
+ struct mark *m = &searchstack[len_cmdbuf()];
+ if (edit_ifile(m->m_ifile))
+ return (MCA_DONE);
+ jump_loc(m->m_scrpos.pos, m->m_scrpos.ln);
+ forw_prompt = 0;
+ } else if (len_cmdbuf() > 0)
+ {
+ /* Remember this position for later erase_char. */
+ struct mark *m = &searchstack[len_cmdbuf()-1];
+ struct scrpos scrpos;
+ get_scrpos(&scrpos);
+ m->m_scrpos = scrpos;
+ m->m_ifile = curr_ifile;
+ }
+ cbuf = get_cmdbuf();
+ if (len_cmdbuf() == 0)
+ undo_search();
+ else if (valid_pattern(cbuf))
+ multi_search(cbuf, (int) number, 1);
+ same_pos_bell = 1;
+ /* Repaint search prompt and pattern. */
+ mca_search();
+ cmd_putstr(cbuf);
+ }
+
/*
* Need another character.
*/
@@ -771,7 +812,7 @@
* We have just run out of ungotten chars.
*/
unget_end = 0;
- if (len_cmdbuf() == 0 || !empty_screen())
+ if (len_cmdbuf() == 0)
return (getchr());
/*
* Command is incomplete, so try to complete it.
@@ -856,9 +897,10 @@
* If SRCH_PAST_EOF is set, continue the search thru multiple files.
*/
static void
-multi_search(pattern, n)
+multi_search(pattern, n, incremental)
char *pattern;
int n;
+ int incremental;
{
register int nomore;
IFILE save_ifile;
@@ -933,7 +975,7 @@
* Didn't find it.
* Print an error message if we haven't already.
*/
- if (n > 0)
+ if (n > 0 && !incremental)
error("Pattern not found", NULL_PARG);
if (changed_file)
@@ -1376,7 +1418,7 @@
if (number <= 0) number = 1; \
mca_search(); \
cmd_exec(); \
- multi_search((char *)NULL, (int) number);
+ multi_search((char *)NULL, (int) number, 0);
case A_F_SEARCH:
===================================================================
RCS file: RCS/forwback.c,v
retrieving revision 1.35
diff -u -r1.35 forwback.c
--- forwback.c 2012/05/21 17:23:54 1.35
+++ forwback.c 2013/06/19 19:08:22
@@ -13,6 +13,7 @@
public int squished;
public int no_back_scroll = 0;
public int forw_prompt;
+public int same_pos_bell = 1;
extern int sigs;
extern int top_scroll;
@@ -264,7 +265,7 @@
forw_prompt = 1;
}
- if (nlines == 0)
+ if (nlines == 0 && same_pos_bell)
eof_bell();
else if (do_repaint)
repaint();
@@ -315,7 +316,7 @@
}
}
- if (nlines == 0)
+ if (nlines == 0 && same_pos_bell)
eof_bell();
else if (do_repaint)
repaint();
===================================================================
RCS file: RCS/less.h,v
retrieving revision 1.64
diff -u -r1.64 less.h
--- less.h 2012/05/21 17:23:54 1.64
+++ less.h 2013/06/18 23:14:35
@@ -288,6 +288,15 @@
int ln;
};
+/*
+ * A mark is an ifile (input file) plus a position within the file.
+ */
+struct mark
+{
+ IFILE m_ifile;
+ struct scrpos m_scrpos;
+};
+
typedef union parg
{
char *p_string;
===================================================================
RCS file: RCS/mark.c,v
retrieving revision 1.17
diff -u -r1.17 mark.c
--- mark.c 2012/05/21 17:23:54 1.17
+++ mark.c 2013/06/19 19:08:22
@@ -8,14 +8,6 @@
extern int jump_sline;
/*
- * A mark is an ifile (input file) plus a position within the file.
- */
-struct mark {
- IFILE m_ifile;
- struct scrpos m_scrpos;
-};
-
-/*
* The table of marks.
* Each mark is identified by a lowercase or uppercase letter.
* The final one is lmark, for the "last mark"; addressed by the apostrophe.
===================================================================
RCS file: RCS/opttbl.c,v
retrieving revision 1.58
diff -u -r1.58 opttbl.c
--- opttbl.c 2012/12/08 22:15:26 1.58
+++ opttbl.c 2013/06/19 19:09:06
@@ -46,6 +46,7 @@
public int follow_mode; /* F cmd Follows file desc or file name? */
public int oldbot; /* Old bottom of screen behavior {{REMOVE}} */
public int opt_use_backslash; /* Use backslash escaping in option parsing */
+public int incremental_search; /* Display search results after each char in pattern */
#if HILITE_SEARCH
public int hilite_search; /* Highlight matched search patterns? */
#endif
@@ -101,6 +102,7 @@
static struct optname x_optname = { "tabs", NULL };
static struct optname X__optname = { "no-init", NULL };
static struct optname y_optname = { "max-forw-scroll", NULL };
+static struct optname Y__optname = { "incremental-search", NULL };
static struct optname z_optname = { "window", NULL };
static struct optname quote_optname = { "quotes", NULL };
static struct optname tilde_optname = { "tilde", NULL };
@@ -385,6 +387,14 @@
NULL
}
},
+ { 'Y', &Y__optname,
+ BOOL, OPT_OFF, &incremental_search, NULL,
+ {
+ "Normal search (press Enter to search)",
+ "Incremental search",
+ NULL
+ }
+ },
{ 'z', &z_optname,
NUMBER, -1, &swindow, NULL,
{
===================================================================
RCS file: RCS/pattern.c,v
retrieving revision 1.9
diff -u -r1.9 pattern.c
--- pattern.c 2012/06/17 00:03:32 1.9
+++ pattern.c 2013/06/19 19:26:57
@@ -13,10 +13,11 @@
* Compile a search pattern, for future use by match_pattern.
*/
static int
-compile_pattern2(pattern, search_type, comp_pattern)
+compile_pattern2(pattern, search_type, comp_pattern, show_error)
char *pattern;
int search_type;
void **comp_pattern;
+ int show_error;
{
if (search_type & SRCH_NO_REGEX)
return (0);
@@ -30,7 +31,8 @@
if (re_compile_pattern(pattern, strlen(pattern), comp))
{
free(comp);
- error("Invalid pattern", NULL_PARG);
+ if (show_error)
+ error("Invalid pattern", NULL_PARG);
return (-1);
}
if (*pcomp != NULL)
@@ -43,7 +45,8 @@
if (regcomp(comp, pattern, REGCOMP_FLAG))
{
free(comp);
- error("Invalid pattern", NULL_PARG);
+ if (show_error)
+ error("Invalid pattern", NULL_PARG);
return (-1);
}
if (*pcomp != NULL)
@@ -61,7 +64,8 @@
if (comp == NULL)
{
parg.p_string = (char *) errstring;
- error("%s", &parg);
+ if (show_error)
+ error("%s", &parg);
return (-1);
}
*pcomp = comp;
@@ -71,7 +75,8 @@
int *pcomp = (int *) comp_pattern;
if ((parg.p_string = re_comp(pattern)) != NULL)
{
- error("%s", &parg);
+ if (show_error)
+ error("%s", &parg);
return (-1);
}
*pcomp = 1;
@@ -81,7 +86,8 @@
char **pcomp = (char **) comp_pattern;
if ((comp = regcmp(pattern, 0)) == NULL)
{
- error("Invalid pattern", NULL_PARG);
+ if (show_error)
+ error("Invalid pattern", NULL_PARG);
return (-1);
}
if (pcomp != NULL)
@@ -91,7 +97,10 @@
#if HAVE_V8_REGCOMP
struct regexp *comp;
struct regexp **pcomp = (struct regexp **) comp_pattern;
- if ((comp = regcomp(pattern)) == NULL)
+ reg_show_error = show_error;
+ comp = regcomp(pattern);
+ reg_show_error = 1;
+ if (comp == NULL)
{
/*
* regcomp has already printed an error message
@@ -126,7 +135,7 @@
cvt_pattern = (char*) ecalloc(1, cvt_length(strlen(pattern), CVT_TO_LC));
cvt_text(cvt_pattern, pattern, (int *)NULL, (int *)NULL, CVT_TO_LC);
}
- result = compile_pattern2(cvt_pattern, search_type, comp_pattern);
+ result = compile_pattern2(cvt_pattern, search_type, comp_pattern, 1);
if (cvt_pattern != pattern)
free(cvt_pattern);
return (result);
@@ -176,6 +185,24 @@
}
/*
+ * Can a pattern be successfully compiled?
+ */
+ public int
+valid_pattern(pattern)
+ char *pattern;
+{
+ void *comp_pattern;
+ int result;
+
+ CLEAR_PATTERN(comp_pattern);
+ result = compile_pattern2(pattern, 0, &comp_pattern, 0);
+ if (result != 0)
+ return (0);
+ uncompile_pattern(&comp_pattern);
+ return (1);
+}
+
+/*
* Is a compiled pattern null?
*/
public int
===================================================================
RCS file: RCS/search.c,v
retrieving revision 1.97
diff -u -r1.97 search.c
--- search.c 2012/06/23 23:48:33 1.97
+++ search.c 2013/06/19 18:52:06
@@ -1236,12 +1236,16 @@
* This function is called by the V8 regcomp to report
* errors in regular expressions.
*/
+public int reg_show_error = 1;
+
void
regerror(s)
char *s;
{
PARG parg;
+ if (!reg_show_error)
+ return;
parg.p_string = s;
error("%s", &parg);
}
@walles
Copy link

walles commented Sep 29, 2013

With this patch installed, search history doesn't work any more for me.

Try this:

  • Open some file.
  • Type "/and" (no quotes)
  • Less now searches for "and", which is good.
  • Type to get out of search mode
  • Now, type "/or"
  • Less now searches for "or", so far so good
  • Press up arrow

Current result:

  • Screen flashes a bit, nothing else happens

Expected result:

  • Should have reverted to searching for "and" since "and" should be before "or" in the search history.

@sunaku
Copy link

sunaku commented Nov 1, 2013

@walles would you mind re-filing your bug report at https://github.com/matthewlmcclure/less/issues ? Because this seems to be a one-off gist, and the main development seems to be done at the above linked Git repository. 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment