Arrange for plaintorich to keep track of the link between match area and search terms. Use this in preview to allow walking the matches for a given search term group

This commit is contained in:
"Jean-Francois Dockes ext:(%22)
2012-08-18 12:15:43 +02:00
parent dc7b3420a0
commit 20c04952f2
8 changed files with 291 additions and 156 deletions
+47 -41
View File
@@ -49,13 +49,22 @@ static string vecStringToString(const vector<string>& t)
return sterms;
}
struct MatchEntry {
pair<int, int> offs;
unsigned int grpidx;
MatchEntry(int sta, int sto, unsigned int idx)
: offs(sta, sto), grpidx(idx)
{
}
};
// Text splitter used to take note of the position of query terms
// inside the result text. This is then used to insert highlight tags.
class TextSplitPTR : public TextSplit {
public:
// Out: begin and end byte positions of query terms/groups in text
vector<pair<int, int> > tboffs;
vector<MatchEntry> tboffs;
TextSplitPTR(const HighlightData& hdata)
: m_wcount(0), m_hdata(hdata)
@@ -67,7 +76,7 @@ class TextSplitPTR : public TextSplit {
for (vector<vector<string> >::const_iterator vit = hdata.groups.begin();
vit != hdata.groups.end(); vit++) {
if (vit->size() == 1) {
m_terms.insert(vit->front());
m_terms[vit->front()] = vit - hdata.groups.begin();
} else if (vit->size() > 1) {
for (vector<string>::const_iterator it = vit->begin();
it != vit->end(); it++) {
@@ -91,8 +100,9 @@ class TextSplitPTR : public TextSplit {
// pos, bts, bte));
// If this word is a search term, remember its byte-offset span.
if (m_terms.find(dumb) != m_terms.end()) {
tboffs.push_back(pair<int, int>(bts, bte));
map<string, unsigned int>::const_iterator it = m_terms.find(dumb);
if (it != m_terms.end()) {
tboffs.push_back(MatchEntry(bts, bte, (*it).second));
}
// If word is part of a search group, update its positions list
@@ -114,13 +124,13 @@ class TextSplitPTR : public TextSplit {
virtual bool matchGroups();
private:
virtual bool matchGroup(const vector<string>& terms, int dist);
virtual bool matchGroup(unsigned int idx);
// Word count. Used to call checkCancel from time to time.
int m_wcount;
// In: user query terms
set<string> m_terms;
map<string, unsigned int> m_terms;
// m_gterms holds all the terms in m_groups, as a set for quick lookup
set<string> m_gterms;
@@ -191,9 +201,12 @@ static bool do_proximity_test(int window, vector<vector<int>* >& plists,
return false;
}
// Find NEAR matches for the input group of terms, update highlight map
bool TextSplitPTR::matchGroup(const vector<string>& terms, int window)
// Find NEAR matches for one group of terms, update highlight map
bool TextSplitPTR::matchGroup(unsigned int grpidx)
{
const vector<string>& terms = m_hdata.groups[grpidx];
int window = m_hdata.groups[grpidx].size() + m_hdata.slacks[grpidx];
LOGDEB0(("TextSplitPTR::matchGroup:d %d: %s\n", window,
vecStringToString(terms).c_str()));
@@ -203,26 +216,23 @@ bool TextSplitPTR::matchGroup(const vector<string>& terms, int window)
// A revert plist->term map. This is so that we can find who is who after
// sorting the plists by length.
map<vector<int>*, string> plistToTerm;
// For traces
vector<string> realgroup;
// Find the position list for each term in the group. Not all
// necessarily exist (esp for NEAR where terms have been
// stem-expanded: we don't know which matched)
// Find the position list for each term in the group. It is
// possible that this particular group was not actually matched by
// the search, so that some terms are not found.
for (vector<string>::const_iterator it = terms.begin();
it != terms.end(); it++) {
map<string, vector<int> >::iterator pl = m_plists.find(*it);
if (pl == m_plists.end()) {
LOGDEB0(("TextSplitPTR::matchGroup: [%s] not found in m_plists\n",
(*it).c_str()));
continue;
return false;
}
plists.push_back(&(pl->second));
plistToTerm[&(pl->second)] = *it;
realgroup.push_back(*it);
}
LOGDEB0(("TextSplitPTR::matchGroup:d %d:real group after expansion %s\n",
window, vecStringToString(realgroup).c_str()));
// I think this can't actually happen, was useful when we used to
// prune the groups, but doesn't hurt.
if (plists.size() < 2) {
LOGDEB0(("TextSplitPTR::matchGroup: no actual groups found\n"));
return false;
@@ -261,15 +271,13 @@ bool TextSplitPTR::matchGroup(const vector<string>& terms, int window)
SETMINMAX(pos, sta, sto);
minpos = sto+1;
// Translate the position window into a byte offset window
int bs = 0;
map<int, pair<int, int> >::iterator i1 = m_gpostobytes.find(sta);
map<int, pair<int, int> >::iterator i2 = m_gpostobytes.find(sto);
if (i1 != m_gpostobytes.end() && i2 != m_gpostobytes.end()) {
LOGDEB0(("TextSplitPTR::matchGroup: pushing bpos %d %d\n",
i1->second.first, i2->second.second));
tboffs.push_back(pair<int, int>(i1->second.first,
i2->second.second));
bs = i1->second.first;
tboffs.push_back(MatchEntry(i1->second.first,
i2->second.second, grpidx));
} else {
LOGDEB(("matchGroup: no bpos found for %d or %d\n", sta, sto));
}
@@ -284,22 +292,23 @@ bool TextSplitPTR::matchGroup(const vector<string>& terms, int window)
/** Sort integer pairs by increasing first value and decreasing width */
class PairIntCmpFirst {
public:
bool operator()(pair<int,int> a, pair<int, int>b) {
if (a.first != b.first)
return a.first < b.first;
return a.second > b.second;
bool operator()(const MatchEntry& a, const MatchEntry& b) {
if (a.offs.first != b.offs.first)
return a.offs.first < b.offs.first;
return a.offs.second > b.offs.second;
}
};
// Look for matches to PHRASE and NEAR term groups. Actually, we
// handle all groups as NEAR (ignore order).
// Look for matches to PHRASE and NEAR term groups and finalize the
// matched regions list (sort it by increasing start then decreasing
// length)
// Actually, we handle all groups as NEAR (ignore order).
bool TextSplitPTR::matchGroups()
{
for (unsigned int i = 0; i < m_hdata.groups.size(); i++) {
if (m_hdata.groups[i].size() <= 1)
continue;
matchGroup(m_hdata.groups[i],
m_hdata.groups[i].size() + m_hdata.slacks[i]);
matchGroup(i);
}
// Sort regions by increasing start and decreasing width.
@@ -324,6 +333,7 @@ bool PlainToRich::plaintorich(const string& in,
{
Chrono chron;
m_hdata = &hdata;
// Compute the positions for the query terms. We use the text
// splitter to break the text into words, and compare the words to
// the search terms,
@@ -346,8 +356,8 @@ bool PlainToRich::plaintorich(const string& in,
// Iterator for the list of input term positions. We use it to
// output highlight tags and to compute term positions in the
// output text
vector<pair<int, int> >::iterator tPosIt = splitter.tboffs.begin();
vector<pair<int, int> >::iterator tPosEnd = splitter.tboffs.end();
vector<MatchEntry>::iterator tPosIt = splitter.tboffs.begin();
vector<MatchEntry>::iterator tPosEnd = splitter.tboffs.end();
#if 0
for (vector<pair<int, int> >::const_iterator it = splitter.tboffs.begin();
@@ -365,8 +375,6 @@ bool PlainToRich::plaintorich(const string& in,
int hadcr = 0;
int inindent = 1;
// Value for numbered anchors at each term match
int anchoridx = 1;
// HTML state
bool intag = false, inparamvalue = false;
// My tag state
@@ -391,22 +399,20 @@ bool PlainToRich::plaintorich(const string& in,
// we are at or after a term match, mark.
if (tPosIt != tPosEnd) {
int ibyteidx = chariter.getBpos();
if (ibyteidx == tPosIt->first) {
if (ibyteidx == tPosIt->offs.first) {
if (!intag && ibyteidx >= (int)headend) {
*olit += startAnchor(anchoridx);
*olit += startMatch();
*olit += startMatch(tPosIt->grpidx);
}
anchoridx++;
inrcltag = 1;
} else if (ibyteidx == tPosIt->second) {
} else if (ibyteidx == tPosIt->offs.second) {
// Output end of match region tags
if (!intag && ibyteidx > (int)headend) {
*olit += endMatch();
*olit += endAnchor();
}
// Skip all highlight areas that would overlap this one
int crend = tPosIt->second;
while (tPosIt != splitter.tboffs.end() && tPosIt->first < crend)
int crend = tPosIt->offs.second;
while (tPosIt != splitter.tboffs.end() &&
tPosIt->offs.first < crend)
tPosIt++;
inrcltag = 0;
}
+26 -8
View File
@@ -21,6 +21,7 @@
#include <list>
#include "hldata.h"
#include "cstr.h"
/**
* A class for highlighting search results. Overridable methods allow
@@ -31,7 +32,7 @@
class PlainToRich {
public:
PlainToRich()
: m_inputhtml(false)
: m_inputhtml(false), m_eolbr(false), m_hdata(0)
{
}
@@ -71,18 +72,35 @@ public:
);
/* Overridable output methods for headers, highlighting and marking tags */
virtual std::string header() {return snull;}
virtual std::string startMatch() {return snull;}
virtual std::string endMatch() {return snull;}
virtual std::string startAnchor(int) {return snull;}
virtual std::string endAnchor() {return snull;}
virtual std::string startChunk() {return snull;}
virtual std::string header()
{
return cstr_null;
}
/** Return match prefix (e.g.: <div class="match">).
@param groupidx the index into hdata.groups */
virtual std::string startMatch(unsigned int)
{
return cstr_null;
}
/** Return data for end of match area (e.g.: </div>). */
virtual std::string endMatch()
{
return cstr_null;
}
virtual std::string startChunk()
{
return cstr_null;
}
protected:
const std::string snull;
bool m_inputhtml;
// Use <br> to break plain text lines (else caller has used a <pre> tag)
bool m_eolbr;
const HighlightData *m_hdata;
};
#endif /* _PLAINTORICH_H_INCLUDED_ */
+8 -3
View File
@@ -40,9 +40,14 @@ static const string cstr_hlfontcolor("<font color=\"blue\">");
static const string cstr_hlendfont("</font>");
class PlainToRichHtReslist : public PlainToRich {
public:
virtual ~PlainToRichHtReslist() {}
virtual string startMatch() {return cstr_hlfontcolor;}
virtual string endMatch() {return cstr_hlendfont;}
virtual string startMatch(unsigned int)
{
return cstr_hlfontcolor;
}
virtual string endMatch()
{
return cstr_hlendfont;
}
};
static PlainToRichHtReslist g_hiliter;