Improvements to cursor highlighting in the source panel

* TexRow now computes rows from a DocIterator. In practice, the cursor
  highlighting is now correct inside insets, it is no longer restricted to the
  topmost level. It certainly also makes forward-search more precise.

* Added the option to disable a texrow when not needed, for perf.

* Fixed a bug where the last paragraph was not properly highlighted.

Limitations:

* TexRow still does not handle: math (e.g. multi-cell), sub-captions, inset
  arguments.
This commit is contained in:
Guillaume Munch 2015-09-15 05:56:01 +01:00
parent d5a5fbb8ee
commit afed7d06fa
8 changed files with 115 additions and 84 deletions

View File

@ -1842,14 +1842,7 @@ void Buffer::writeLaTeXSource(otexstream & os,
}
runparams_in.encoding = runparams.encoding;
// Just to be sure. (Asger)
os.texrow().newline();
//for (int i = 0; i<d->texrow.rows(); i++) {
// int id,pos;
// if (d->texrow.getIdFromRow(i+1,id,pos) && id>0)
// lyxerr << i+1 << ":" << id << ":" << getParFromID(id).paragraph().asString()<<"\n";
//}
os.texrow().finalize();
LYXERR(Debug::INFO, "Finished making LaTeX file.");
LYXERR(Debug::INFO, "Row count was " << os.texrow().rows() - 1 << '.');
@ -1886,7 +1879,7 @@ void Buffer::writeDocBookSource(odocstream & os, string const & fname,
LaTeXFeatures features(*this, params(), runparams);
validate(features);
d->texrow.reset();
d->texrow.reset(false);
DocumentClass const & tclass = params().documentClass();
string const & top_element = tclass.latexname();
@ -3570,7 +3563,6 @@ auto_ptr<TexRow> Buffer::getSourceCode(odocstream & os, string const & format,
params().validate(features);
runparams.use_polyglossia = features.usePolyglossia();
texrow.reset(new TexRow());
texrow->reset();
texrow->newline();
texrow->newline();
// latex or literate
@ -3578,6 +3570,7 @@ auto_ptr<TexRow> Buffer::getSourceCode(odocstream & os, string const & format,
// the real stuff
latexParagraphs(*this, text(), ots, runparams);
texrow->finalize();
// Restore the parenthood
if (!master)
@ -3613,13 +3606,13 @@ auto_ptr<TexRow> Buffer::getSourceCode(odocstream & os, string const & format,
} else {
// latex or literate
texrow.reset(new TexRow());
texrow->reset();
texrow->newline();
texrow->newline();
otexstream ots(os, *texrow);
if (master)
runparams.is_child = true;
writeLaTeXSource(ots, string(), runparams, output);
texrow->finalize();
}
}
return texrow;

View File

@ -12,6 +12,8 @@
#include <config.h>
#include "DocIterator.h"
#include "Paragraph.h"
#include "TexRow.h"
#include "support/debug.h"
@ -22,19 +24,19 @@
namespace lyx {
void TexRow::reset()
void TexRow::reset(bool enable)
{
rowlist.clear();
lastid = -1;
lastpos = -1;
enabled_ = enable;
}
void TexRow::start(int id, int pos)
{
if (started)
if (!enabled_ || started)
return;
lastid = id;
lastpos = pos;
started = true;
@ -43,19 +45,29 @@ void TexRow::start(int id, int pos)
void TexRow::newline()
{
int const id = lastid;
RowList::value_type tmp(id, lastpos);
if (!enabled_)
return;
RowList::value_type tmp(lastid, lastpos);
rowlist.push_back(tmp);
started = false;
}
void TexRow::newlines(int num_lines)
{
if (!enabled_)
return;
for (int i = 0; i < num_lines; ++i) {
newline();
}
}
void TexRow::finalize()
{
if (!enabled_)
return;
newline();
}
bool TexRow::getIdFromRow(int row, int & id, int & pos) const
{
if (row <= 0 || row > int(rowlist.size())) {
@ -70,27 +82,64 @@ bool TexRow::getIdFromRow(int row, int & id, int & pos) const
}
int TexRow::getRowFromIdPos(int id, int pos) const
std::pair<int,int> TexRow::rowFromDocIterator(DocIterator const & dit) const
{
bool foundid = false;
// this loop finds the last *nonempty* row with the same id
// and position <= pos
RowList::const_iterator bestrow = rowlist.begin();
bool found = false;
size_t best_slice = 0;
size_t const n = dit.depth();
// this loop finds the last row of the topmost possible CursorSlice
RowList::const_iterator best_beg_row = rowlist.begin();
RowList::const_iterator best_end_row = rowlist.begin();
RowList::const_iterator it = rowlist.begin();
RowList::const_iterator const end = rowlist.end();
for (; it != end; ++it) {
if (it->id() == id && it->pos() <= pos) {
foundid = true;
if (bestrow->id() != id || it->pos() > bestrow->pos())
bestrow = it;
} else if (foundid)
break;
if (found) {
// Compute the best end row. It is the one that matches pos+1.
CursorSlice const & best = dit[best_slice];
if (best.text()
&& it->id() == best.paragraph().id()
&& it->pos() == best.pos() + 1
&& (best_end_row->id() != it->id()
|| best_end_row->pos() < it->pos()))
best_end_row = it;
}
for (size_t i = best_slice; i < n && dit[i].text(); ++i) {
int const id = dit[i].paragraph().id();
if (it->id() == id) {
if (it->pos() <= dit[i].pos()
&& (best_beg_row->id() != id
|| it->pos() > best_beg_row->pos())) {
found = true;
best_slice = i;
best_beg_row = best_end_row = it;
}
//found CursorSlice
break;
}
}
}
if (!foundid)
return rowlist.size();
return distance(rowlist.begin(), bestrow) + 1;
if (!found)
return std::make_pair(-1,-1);
int const beg_i = distance(rowlist.begin(), best_beg_row) + 1;
// remove one to the end
int const end_i = std::max(beg_i,
(int)distance(rowlist.begin(), best_end_row));
return std::make_pair(beg_i,end_i);
}
LyXErr & operator<<(LyXErr & l, TexRow & texrow)
{
if (l.enabled()) {
for (int i = 0; i < texrow.rows(); i++) {
int id,pos;
if (texrow.getIdFromRow(i+1,id,pos) && id>0)
l << i+1 << ":" << id << ":" << pos << "\n";
}
}
return l;
}
} // namespace lyx

View File

@ -14,11 +14,14 @@
#ifndef TEXROW_H
#define TEXROW_H
#include <vector>
#include "support/debug.h"
#include <vector>
namespace lyx {
class LyXErr;
class DocIterator;
/// Represents the correspondence between paragraphs and the generated
/// LaTeX file
@ -26,10 +29,13 @@ namespace lyx {
class TexRow {
public:
///
TexRow() : lastid(-1), lastpos(-1), started(false) {}
TexRow(bool enable = true)
: lastid(-1), lastpos(-1), started(false), enabled_(enable) {}
/// Clears structure
void reset();
/// TexRow is often computed to be immediately discarded. Set enable to
/// false if texrow is not needed
void reset(bool enable = true);
/// Define what paragraph and position the next row will represent
void start(int id, int pos);
@ -40,6 +46,9 @@ public:
/// Insert multiple nodes when zero or more lines are completed
void newlines(int num_lines);
/// Call when code generation is complete
void finalize();
/**
* getIdFromRow - find pid and position for a given row
* @param row row number to find
@ -52,13 +61,9 @@ public:
*/
bool getIdFromRow(int row, int & id, int & pos) const;
/**
* getRowFromIdPos - find row containing a given id and pos
* @param id of the paragraph
* @param pos a given position in that paragraph
* @return the row number within the rowlist
*/
int getRowFromIdPos(int id, int pos) const;
/// Finds the best pair of rows for dit
/// returns (-1,-1) if not found.
std::pair<int,int> rowFromDocIterator(DocIterator const & dit) const;
/// Returns the number of rows contained
int rows() const { return rowlist.size(); }
@ -92,8 +97,12 @@ private:
int lastpos;
/// Is id/pos already registered for current row?
bool started;
///
bool enabled_;
};
LyXErr & operator<<(LyXErr &, TexRow &);
} // namespace lyx

View File

@ -3976,15 +3976,11 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
command = lyxrc.forward_search_pdf;
}
DocIterator tmpcur = bv->cursor();
// Leave math first
while (tmpcur.inMathed())
tmpcur.pop_back();
int row = tmpcur.inMathed() ? 0 : doc_buffer->texrow().getRowFromIdPos(
tmpcur.paragraph().id(), tmpcur.pos());
DocIterator cur = bv->cursor();
int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
LYXERR(Debug::ACTION, "Forward search: row:" << row
<< " id:" << tmpcur.paragraph().id());
if (!row || command.empty()) {
<< " cur:" << cur);
if (row == -1 || command.empty()) {
dr.setMessage(_("Couldn't proceed."));
break;
}

View File

@ -110,7 +110,8 @@ auto_ptr<TexRow> ViewSourceWidget::getContent(BufferView const * view,
odocstringstream ostr;
auto_ptr<TexRow> texrow = view->buffer().getSourceCode(ostr, format,
par_begin, par_end + 1, output, master);
str = ostr.str();
//ensure that the last line can always be selected in its full width
str = ostr.str() + "\n";
return texrow;
}
@ -229,35 +230,20 @@ void ViewSourceWidget::realUpdateView()
} else if (texrow.get()) {
// Use the available position-to-row conversion to highlight
// the current selection in the source
//
// FIXME:
// * it is currently impossible to highlight the very last line
// of a document, because TexRow gives the wrong data.
// * we currently only compute the top-level position, which
// makes it impossible to highlight inside an inset. It is not
// a limitation of TexRow, but replacing bottom() with top()
// works partially and causes segfaults with math. Solving
// this could be seen as a solution to #4725.
// * even if we keep computing the top-level position, the data
// given by TexRow is false if there is e.g. a float of a
// footnote in the paragraph
CursorSlice beg = bv_->cursor().selectionBegin().bottom();
CursorSlice end = bv_->cursor().selectionEnd().bottom();
int const beg_par = beg.paragraph().id();
int const end_par = end.paragraph().id();
int const beg_pos = beg.pos();
int const end_pos = end.pos();
int const beg_row = texrow->getRowFromIdPos(beg_par, beg_pos);
int end_row, next_end_row;
if (beg_par != end_par || beg_pos != end_pos) {
end_row = texrow->getRowFromIdPos(end_par, max(0, end_pos - 1));
next_end_row = texrow->getRowFromIdPos(end_par, end_pos);
} else {
end_row = beg_row;
next_end_row = texrow->getRowFromIdPos(beg_par, beg_pos + 1);
int beg_row, end_row;
{
DocIterator beg = bv_->cursor().selectionBegin();
DocIterator end = bv_->cursor().selectionEnd();
std::pair<int,int> beg_rows = texrow->rowFromDocIterator(beg);
beg_row = beg_rows.first;
if (beg != end) {
end.backwardChar();
std::pair<int,int> end_rows = texrow->rowFromDocIterator(end);
end_row = end_rows.second;
} else {
end_row = beg_rows.second;
}
}
if (end_row != next_end_row)
end_row = next_end_row - 1;
QTextCursor c = QTextCursor(viewSourceTV->document());
@ -303,7 +289,7 @@ void ViewSourceWidget::realUpdateView()
c.clearSelection();
viewSourceTV->setTextCursor(c);
viewSourceTV->horizontalScrollBar()->setValue(h_scroll);
}
} // else if (texrow)
}

View File

@ -729,7 +729,7 @@ private:
static docstring buffer_to_latex(Buffer & buffer)
{
OutputParams runparams(&buffer.params().encoding());
TexRow texrow;
TexRow texrow(false);
odocstringstream ods;
otexstream os(ods, texrow);
runparams.nice = true;
@ -1051,7 +1051,7 @@ docstring latexifyFromCursor(DocIterator const & cur, int len)
Buffer const & buf = *cur.buffer();
LBUFERR(buf.params().isLatex());
TexRow texrow;
TexRow texrow(false);
odocstringstream ods;
otexstream os(ods, texrow);
OutputParams runparams(&buf.params().encoding());
@ -1399,7 +1399,7 @@ static void findAdvReplace(BufferView * bv, FindAndReplaceOptions const & opt, M
LYXERR(Debug::FIND, "After pasteParagraphList() cur=" << cur << endl);
sel_len = repl_buffer.paragraphs().begin()->size();
} else if (cur.inMathed()) {
TexRow texrow;
TexRow texrow(false);
odocstringstream ods;
otexstream os(ods, texrow);
OutputParams runparams(&repl_buffer.params().encoding());

View File

@ -24,7 +24,6 @@
#include "Paragraph.h"
#include "ParagraphParameters.h"
#include "TextClass.h"
#include "TexRow.h"
#include "insets/InsetBibitem.h"
#include "insets/InsetArgument.h"

View File

@ -27,7 +27,6 @@ class Encoding;
class Layout;
class Paragraph;
class OutputParams;
class TexRow;
class Text;
/** Export optional and required arguments of the paragraph \p par.