Correctly load documents moved elsewhere after save.

It is now possible opening documents that where manually moved to a
different location after they were saved and still produce an output.
Indeed, (hopefully) all needed included files are now still found.
When the moved document is saved again, all paths are accordingly updated.
Of course, for this to work, a document has to be saved in Format 490,
at least.

As an example, after converting the user guide to the last format, it can
be copied anywhere and opened without the need of adapting the paths of
included files or moving them to a proper place.

There is one glitch I am aware of. When moving a child document (but not
the master) the path to the master is correctly updated but it won't be
recognized as such. This is because LyX checks that the parent actually
includes this document but, of course, being the parent document not
touched, it appears not including this child. Anyway, it will also occur
when saving the child to a different location and the user is warned
on the terminal about this fact when the moved child is loaded.
However, there is no problem when it is the master that has been moved.
This commit is contained in:
Enrico Forestieri 2015-05-16 19:51:53 +02:00
parent 853283ca87
commit 62d36bf04d
10 changed files with 64 additions and 9 deletions

View File

@ -249,7 +249,8 @@ public:
/// map from children inclusion positions to their scope and their buffer
PositionScopeBufferMap position_to_children;
/// Keeps track of old buffer filePath() for save-as operations
/// Contains the old buffer filePath() while saving-as, or the
/// directory where the document was last saved while loading.
string old_position;
/** Keeps track of the path of local layout files.
@ -1030,7 +1031,9 @@ bool Buffer::readDocument(Lexer & lex)
params().indiceslist().addDefault(B_("Index"));
// read main text
d->old_position = originFilePath();
bool const res = text().read(lex, errorList, d->inset);
d->old_position.clear();
// inform parent buffer about local macros
if (parent()) {
@ -3027,6 +3030,15 @@ string Buffer::filePath() const
}
string Buffer::originFilePath() const
{
if (FileName::isAbsolute(params().origin))
return params().origin;
return filePath();
}
string Buffer::layoutPos() const
{
return d->layout_position;

View File

@ -403,6 +403,13 @@ public:
/// It is always an absolute path.
std::string filePath() const;
/** Returns the path where the document was last saved.
* It may be different from filePath() if the document was later
* manually moved to a different location.
* It is always an absolute path.
*/
std::string originFilePath() const;
/** Returns the path where a local layout file lives.
* An empty string is returned for standard system and user layouts.
* If possible, it is always relative to the buffer path.
@ -731,6 +738,9 @@ public:
/// In all other cases, this is a no-op and name is returned unchanged.
/// If a non-empty ext is given, the existence of name.ext is checked
/// but the returned path will not contain this extension.
/// Similarly, when loading a document that was moved from the location
/// where it was saved, return the correct path relative to the new
/// location.
std::string includedFilePath(std::string const & name,
std::string const & ext = empty_string()) const;

View File

@ -682,6 +682,19 @@ string BufferParams::readToken(Lexer & lex, string const & token,
} else if (token == "\\master") {
lex.eatLine();
master = lex.getString();
if (!filepath.empty() && FileName::isAbsolute(origin)) {
bool const isabs = FileName::isAbsolute(master);
FileName const abspath(isabs ? master : origin + master);
bool const moved = filepath != FileName(origin);
if (moved && abspath.exists()) {
docstring const path = isabs
? from_utf8(master)
: from_utf8(abspath.realPath());
docstring const refpath =
from_utf8(filepath.absFileName());
master = to_utf8(makeRelPath(path, refpath));
}
}
} else if (token == "\\suppress_date") {
lex >> suppress_date;
} else if (token == "\\justification") {

View File

@ -538,7 +538,7 @@ Inset * readInset(Lexer & lex, Buffer * buf)
//Worst case, we could put it in each case below. Better, we could
//pass the lexer to the constructor and let the params be built there.
InsetCommandParams inscmd(code);
inscmd.read(lex);
inscmd.read(lex, buf);
switch (code) {
case BIBITEM_CODE:

View File

@ -195,7 +195,7 @@ void InsetBibitem::doDispatch(Cursor & cur, FuncRequest & cmd)
void InsetBibitem::read(Lexer & lex)
{
InsetCommand::read(lex);
InsetCommand::read(lex, &buffer());
if (prefixIs(getParam("key"), key_prefix)) {
int const key = convert<int>(getParam("key").substr(key_prefix.length()));

View File

@ -244,7 +244,7 @@ bool InsetCommand::string2params(string const & data,
lex.setContext("InsetCommand::string2params");
lex >> name.c_str(); // check for name
lex >> "CommandInset";
params.read(lex);
params.read(lex, 0);
return true;
}

View File

@ -66,7 +66,7 @@ public:
///
void write(std::ostream & os) const { p_.write(os); }
///
void read(Lexer & lex) { p_.read(lex); }
void read(Lexer & lex, Buffer const * buf) { p_.read(lex, buf); }
///
void doDispatch(Cursor & cur, FuncRequest & cmd);
///

View File

@ -275,7 +275,7 @@ void InsetCommandParams::setCmdName(string const & name)
}
void InsetCommandParams::read(Lexer & lex)
void InsetCommandParams::read(Lexer & lex, Buffer const * buffer)
{
lex.setContext("InsetCommandParams::read");
lex >> insetName(insetCode_).c_str();
@ -302,7 +302,25 @@ void InsetCommandParams::read(Lexer & lex)
}
if (info_.hasParam(token)) {
lex.next(true);
params_[token] = lex.getDocString();
docstring data = lex.getDocString();
if (buffer && token == "filename") {
data = from_utf8(buffer->includedFilePath(to_utf8(data)));
} else if (buffer && token == "bibfiles") {
int i = 0;
docstring newdata;
docstring bib = support::token(data, ',', i);
while (!bib.empty()) {
bib = from_utf8(buffer->includedFilePath(to_utf8(bib), "bib"));
if (!newdata.empty())
newdata.append(1, ',');
newdata.append(bib);
bib = support::token(data, ',', ++i);
}
data = newdata;
} else if (buffer && token == "options") {
data = from_utf8(buffer->includedFilePath(to_utf8(data), "bst"));
}
params_[token] = data;
} else {
lex.printError("Unknown parameter name `$$Token' for command " + cmdName_);
throw ExceptionMessage(WarningException,
@ -353,6 +371,8 @@ void InsetCommandParams::Write(ostream & os, Buffer const * buffer) const
bib = token(data, ',', ++i);
}
data = newdata;
} else if (buffer && name == "options") {
data = buffer->includedFilePath(data, "bst");
}
os << name << ' '
<< Lexer::quoteString(data)

View File

@ -114,7 +114,7 @@ public:
///
InsetCode code() const { return insetCode_; }
///
void read(Lexer &);
void read(Lexer &, Buffer const *);
/// Parse the command
///
void write(std::ostream &) const;

View File

@ -293,7 +293,7 @@ void InsetGraphics::read(Lexer & lex)
{
lex.setContext("InsetGraphics::read");
//lex >> "Graphics";
readInsetGraphics(lex, buffer().filePath(), params_);
readInsetGraphics(lex, buffer().originFilePath(), params_);
graphic_->update(params().as_grfxParams());
}