summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 1d75456)
raw | patch | inline | side by side (parent: 1d75456)
author | Jonas Fonseca <fonseca@diku.dk> | |
Sat, 9 Sep 2006 20:04:36 +0000 (22:04 +0200) | ||
committer | Jonas Fonseca <fonseca@antimatter.localdomain> | |
Sat, 9 Sep 2006 20:05:41 +0000 (22:05 +0200) |
For commits the title, author, and displayed date string is searchable.
Search text is not highlighted, the current line is simply used to jump
around and show which line matched.
Default keybindings:
'/' search forward
'?' search backward
'n' find next
'N' find previous
Note, this means that '?' no longer displays the help view, just use 'h'.
Search text is not highlighted, the current line is simply used to jump
around and show which line matched.
Default keybindings:
'/' search forward
'?' search backward
'n' find next
'N' find previous
Note, this means that '?' no longer displays the help view, just use 'h'.
TODO | patch | blob | history | |
manual.txt | patch | blob | history | |
tig.c | patch | blob | history |
index 3de54b460ea52b76f84027ad33a9f52abbb11645..316f2750c5479a6089d94aa433690b1dba565acb 100644 (file)
--- a/TODO
+++ b/TODO
----
Features that should be explored.
- - Searching.
-
- Locale support.
- When the user wants to "view" a commit, you could show from which branch
diff --git a/manual.txt b/manual.txt
index b7c3d5a70ef2c5dcd7875c63eeac378d1fb107d2..82e848e98dd4750e55c2c85b47d6503e3832977d 100644 (file)
--- a/manual.txt
+++ b/manual.txt
d Switch to diff view.
l Switch to log view.
p Switch to pager view.
-h, ? Show man page.
+h Show man page.
-----------------------------------------------------------------------------
[[view-manipulation]]
s Scroll view one page down.
-----------------------------------------------------------------------------
+[[searching]]
+Searching
+~~~~~~~~~
+
+`-------`--------------------------------------------------------------------
+Key Action
+-----------------------------------------------------------------------------
+/ Search the view. Opens a prompt for entering search regex to use.
+? Search backwards in the view. Also prompts for regex.
+n Find next match for the current search regex.
+N Find previous match for the current search regex.
+-----------------------------------------------------------------------------
+
[[misc-keys]]
Misc
~~~~
index 089a567ce0b9151cea3b5bdffffdaa0c61e20f84..ff1abddfe6d06573e319d30312975b11382749d1 100644 (file)
--- a/tig.c
+++ b/tig.c
#include <unistd.h>
#include <time.h>
+#include <sys/types.h>
+#include <regex.h>
+
#include <locale.h>
#include <langinfo.h>
#include <iconv.h>
REQ_(SCROLL_PAGE_UP, "Scroll one page up"), \
REQ_(SCROLL_PAGE_DOWN, "Scroll one page down"), \
\
+ REQ_GROUP("Searching") \
+ REQ_(SEARCH, "Search the view"), \
+ REQ_(SEARCH_BACK, "Search backwards in the view"), \
+ REQ_(FIND_NEXT, "Find next search match"), \
+ REQ_(FIND_PREV, "Find previous search match"), \
+ \
REQ_GROUP("Misc") \
REQ_(NONE, "Do nothing"), \
REQ_(PROMPT, "Bring up the prompt"), \
static bool opt_utf8 = TRUE;
static char opt_codeset[20] = "UTF-8";
static iconv_t opt_iconv = ICONV_NONE;
+static char opt_search[SIZEOF_STR] = "";
enum option_type {
OPT_NONE,
{ 'l', REQ_VIEW_LOG },
{ 'p', REQ_VIEW_PAGER },
{ 'h', REQ_VIEW_HELP },
- { '?', REQ_VIEW_HELP },
/* View manipulation */
{ 'q', REQ_VIEW_CLOSE },
{ 'w', REQ_SCROLL_PAGE_UP },
{ 's', REQ_SCROLL_PAGE_DOWN },
+ /* Searching */
+ { '/', REQ_SEARCH },
+ { '?', REQ_SEARCH_BACK },
+ { 'n', REQ_FIND_NEXT },
+ { 'N', REQ_FIND_PREV },
+
/* Misc */
{ 'Q', REQ_QUIT },
{ 'z', REQ_STOP_LOADING },
unsigned long offset; /* Offset of the window top */
unsigned long lineno; /* Current line number */
+ /* Searching */
+ char grep[SIZEOF_STR]; /* Search string */
+ regex_t regex; /* Pre-compiled regex */
+
/* If non-NULL, points to the view that opened this view. If this view
* is closed tig will switch back to the parent view. */
struct view *parent;
bool (*read)(struct view *view, char *data);
/* Depending on view, change display based on current line. */
bool (*enter)(struct view *view, struct line *line);
+ /* Search for regex in a line. */
+ bool (*grep)(struct view *view, struct line *line);
};
static struct view_ops pager_ops;
}
+/*
+ * Searching
+ */
+
+static void search_view(struct view *view, enum request request, const char *search);
+
+static bool
+find_next_line(struct view *view, unsigned long lineno, struct line *line)
+{
+ if (!view->ops->grep(view, line))
+ return FALSE;
+
+ if (lineno - view->offset >= view->height) {
+ view->offset = lineno;
+ view->lineno = lineno;
+ redraw_view(view);
+
+ } else {
+ unsigned long old_lineno = view->lineno - view->offset;
+
+ view->lineno = lineno;
+
+ wmove(view->win, old_lineno, 0);
+ wclrtoeol(view->win);
+ draw_view_line(view, old_lineno);
+
+ draw_view_line(view, view->lineno - view->offset);
+ redrawwin(view->win);
+ wrefresh(view->win);
+ }
+
+ report("Line %ld matches '%s'", lineno + 1, view->grep);
+ return TRUE;
+}
+
+static void
+find_next(struct view *view, enum request request)
+{
+ unsigned long lineno = view->lineno;
+ int direction;
+
+ if (!*view->grep) {
+ if (!*opt_search)
+ report("No previous search");
+ else
+ search_view(view, request, opt_search);
+ return;
+ }
+
+ switch (request) {
+ case REQ_SEARCH:
+ case REQ_FIND_NEXT:
+ direction = 1;
+ break;
+
+ case REQ_SEARCH_BACK:
+ case REQ_FIND_PREV:
+ direction = -1;
+ break;
+
+ default:
+ return;
+ }
+
+ if (request == REQ_FIND_NEXT || request == REQ_FIND_PREV)
+ lineno += direction;
+
+ /* Note, lineno is unsigned long so will wrap around in which case it
+ * will become bigger than view->lines. */
+ for (; lineno < view->lines; lineno += direction) {
+ struct line *line = &view->line[lineno];
+
+ if (find_next_line(view, lineno, line))
+ return;
+ }
+
+ report("No match found for '%s'", view->grep);
+}
+
+static void
+search_view(struct view *view, enum request request, const char *search)
+{
+ int regex_err;
+
+ if (*view->grep) {
+ regfree(&view->regex);
+ *view->grep = 0;
+ }
+
+ regex_err = regcomp(&view->regex, search, REG_EXTENDED);
+ if (regex_err != 0) {
+ char buf[SIZEOF_STR] = "unknown error";
+
+ regerror(regex_err, &view->regex, buf, sizeof(buf));
+ report("Search failed: %s", buf);;
+ return;
+ }
+
+ string_copy(view->grep, search);
+
+ find_next(view, request);
+}
+
/*
* Incremental updating
*/
open_view(view, opt_request, OPEN_RELOAD);
break;
+ case REQ_SEARCH:
+ case REQ_SEARCH_BACK:
+ search_view(view, request, opt_search);
+ break;
+
+ case REQ_FIND_NEXT:
+ case REQ_FIND_PREV:
+ find_next(view, request);
+ break;
+
case REQ_STOP_LOADING:
for (i = 0; i < ARRAY_SIZE(views); i++) {
view = &views[i];
return TRUE;
}
+static bool
+pager_grep(struct view *view, struct line *line)
+{
+ regmatch_t pmatch;
+ char *text = line->data;
+
+ if (!*text)
+ return FALSE;
+
+ if (regexec(&view->regex, text, 1, &pmatch, 0) == REG_NOMATCH)
+ return FALSE;
+
+ return TRUE;
+}
+
static struct view_ops pager_ops = {
"line",
pager_draw,
pager_read,
pager_enter,
+ pager_grep,
};
return TRUE;
}
+static bool
+main_grep(struct view *view, struct line *line)
+{
+ struct commit *commit = line->data;
+ enum { S_TITLE, S_AUTHOR, S_DATE, S_END } state;
+ char buf[DATE_COLS + 1];
+ regmatch_t pmatch;
+
+ for (state = S_TITLE; state < S_END; state++) {
+ char *text;
+
+ switch (state) {
+ case S_TITLE: text = commit->title; break;
+ case S_AUTHOR: text = commit->author; break;
+ case S_DATE:
+ if (!strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time))
+ continue;
+ text = buf;
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ if (regexec(&view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static struct view_ops main_ops = {
"commit",
main_draw,
main_read,
main_enter,
+ main_grep,
};
wbkgdset(status_win, get_line_attr(LINE_STATUS));
}
-static char *
+static char *
read_prompt(const char *prompt)
{
enum { READING, STOP, CANCEL } status = READING;
request = REQ_NONE;
break;
}
+ case REQ_SEARCH:
+ case REQ_SEARCH_BACK:
+ {
+ const char *prompt = request == REQ_SEARCH
+ ? "/" : "?";
+ char *search = read_prompt(prompt);
+
+ if (search)
+ string_copy(opt_search, search);
+ else
+ request = REQ_NONE;
+ break;
+ }
case REQ_SCREEN_RESIZE:
{
int height, width;