Author-year citations for XHTML output.

git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@32969 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Richard Heck 2010-01-11 16:11:55 +00:00
parent 374b5ebd72
commit 782db81f77
5 changed files with 175 additions and 109 deletions

View File

@ -204,7 +204,8 @@ docstring convertLaTeXCommands(docstring const & str)
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
BibTeXInfo::BibTeXInfo(docstring const & key, docstring const & type) BibTeXInfo::BibTeXInfo(docstring const & key, docstring const & type)
: is_bibtex_(true), bib_key_(key), entry_type_(type), info_() : is_bibtex_(true), bib_key_(key), entry_type_(type), info_(),
modifier_(0)
{} {}
@ -423,25 +424,37 @@ docstring const BiblioInfo::getAbbreviatedAuthor(docstring const & key) const
} }
docstring const BiblioInfo::getYear(docstring const & key) const docstring const BiblioInfo::getCiteNumber(docstring const & key) const
{
BiblioInfo::const_iterator it = find(key);
if (it == end())
return docstring();
BibTeXInfo const & data = it->second;
return data.citeNumber();
}
docstring const BiblioInfo::getYear(docstring const & key, bool use_modifier) const
{ {
BiblioInfo::const_iterator it = find(key); BiblioInfo::const_iterator it = find(key);
if (it == end()) if (it == end())
return docstring(); return docstring();
BibTeXInfo const & data = it->second; BibTeXInfo const & data = it->second;
docstring year = data.getYear(); docstring year = data.getYear();
if (!year.empty()) if (year.empty()) {
return year; // let's try the crossref
// let's try the crossref docstring const xref = data.getXRef();
docstring const xref = data.getXRef(); if (xref.empty())
if (xref.empty()) return _("No year"); // no luck
return _("No year"); // no luck BiblioInfo::const_iterator const xrefit = find(xref);
BiblioInfo::const_iterator const xrefit = find(xref); if (xrefit == end())
if (xrefit == end()) return _("No year"); // no luck again
return _("No year"); // no luck again BibTeXInfo const & xref_data = xrefit->second;
BibTeXInfo const & xref_data = xrefit->second; year = xref_data.getYear();
return xref_data.getYear(); }
return data.getYear(); if (use_modifier && data.modifier() != 0)
year += data.modifier();
return year;
} }
@ -604,7 +617,15 @@ namespace {
// used in xhtml to sort a list of BibTeXInfo objects // used in xhtml to sort a list of BibTeXInfo objects
bool lSorter(BibTeXInfo const * lhs, BibTeXInfo const * rhs) bool lSorter(BibTeXInfo const * lhs, BibTeXInfo const * rhs)
{ {
return lhs->getAbbreviatedAuthor() < rhs->getAbbreviatedAuthor(); docstring const lauth = lhs->getAbbreviatedAuthor();
docstring const rauth = rhs->getAbbreviatedAuthor();
docstring const lyear = lhs->getYear();
docstring const ryear = rhs->getYear();
docstring const ltitl = lhs->operator[]("title");
docstring const rtitl = rhs->operator[]("title");
return (lauth < rauth)
|| (lauth == rauth && lyear < ryear)
|| (lauth == rauth && lyear == ryear && ltitl < rtitl);
} }
} }
@ -655,19 +676,50 @@ void BiblioInfo::collectCitedEntries(Buffer const & buf)
void BiblioInfo::makeCitationLabels(Buffer const & buf) void BiblioInfo::makeCitationLabels(Buffer const & buf)
{ {
collectCitedEntries(buf); collectCitedEntries(buf);
// FIXME It'd be nice to do author-year as well as numerical CiteEngine const engine = buf.params().citeEngine();
// and maybe even some other sorts of labels. bool const numbers =
(engine == ENGINE_BASIC || engine == ENGINE_NATBIB_NUMERICAL);
int keynumber = 0;
char modifier = 0;
// used to remember the last one we saw
// we'll be comparing entries to see if we need to add
// modifiers, like "1984a"
map<docstring, BibTeXInfo>::iterator last;
vector<docstring>::const_iterator it = cited_entries_.begin(); vector<docstring>::const_iterator it = cited_entries_.begin();
vector<docstring>::const_iterator const en = cited_entries_.end(); vector<docstring>::const_iterator const en = cited_entries_.end();
int keynumber = 0;
for (; it != en; ++it) { for (; it != en; ++it) {
map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it); map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
// this shouldn't happen, but... // this shouldn't happen, but...
if (biit == bimap_.end()) if (biit == bimap_.end())
// ...fail gracefully, anyway.
continue; continue;
BibTeXInfo & entry = biit->second; BibTeXInfo & entry = biit->second;
docstring const key = convert<docstring>(++keynumber); if (numbers) {
entry.setCiteKey(key); docstring const num = convert<docstring>(++keynumber);
entry.setCiteNumber(num);
} else {
if (it != cited_entries_.begin()
&& entry.getAbbreviatedAuthor() == last->second.getAbbreviatedAuthor()
// we access the year via getYear() so as to get it from the xref,
// if we need to do so
&& getYear(entry.key()) == getYear(last->second.key())) {
if (modifier == 0) {
// so the last one should have been 'a'
last->second.setModifier('a');
modifier = 'b';
} else if (modifier == 'z')
modifier = 'A';
else
modifier++;
} else {
modifier = 0;
}
entry.setModifier(modifier);
// remember the last one
last = biit;
}
} }
} }

View File

@ -86,10 +86,16 @@ public:
docstring const & label() const { return label_; } docstring const & label() const { return label_; }
/// ///
docstring const & key() const { return bib_key_; } docstring const & key() const { return bib_key_; }
/// numerical key for citing this entry. currently used only
/// by XHTML output routines.
docstring citeNumber() const { return cite_number_; }
/// ///
docstring citeKey() const { return cite_key_; } void setCiteNumber(docstring const & num) { cite_number_ = num; }
/// a,b,c, etc, for author-year. currently used only by XHTML
/// output routines.
char modifier() const { return modifier_; }
/// ///
void setCiteKey(docstring const & k) { cite_key_ = k; } void setModifier(char c) { modifier_ = c; }
/// ///
docstring entryType() const { return entry_type_; } docstring entryType() const { return entry_type_; }
/// ///
@ -114,9 +120,10 @@ private:
docstring entry_type_; docstring entry_type_;
/// a cache for getInfo() /// a cache for getInfo()
mutable docstring info_; mutable docstring info_;
/// key to use when citing this entry ///
/// currently used only by XHTML output routines docstring cite_number_;
docstring cite_key_; ///
char modifier_;
/// our map: <field, value> /// our map: <field, value>
std::map <docstring, docstring> bimap_; std::map <docstring, docstring> bimap_;
}; };
@ -136,10 +143,15 @@ public:
std::vector<docstring> const getEntries() const; std::vector<docstring> const getEntries() const;
/// \return the short form of an authorlist /// \return the short form of an authorlist
docstring const getAbbreviatedAuthor(docstring const & key) const; docstring const getAbbreviatedAuthor(docstring const & key) const;
/// \return the year from the bibtex data record /// \return the year from the bibtex data record for \param key
/// if \param use_modifier is true, then we will also append any
/// modifier for this entry (e.g., 1998b).
/// Note that this will get the year from the crossref if it's /// Note that this will get the year from the crossref if it's
/// not present in the record itself /// not present in the record itself.
docstring const getYear(docstring const & key) const; docstring const getYear(docstring const & key,
bool use_modifier = false) const;
///
docstring const getCiteNumber(docstring const & key) const;
/// \return formatted BibTeX data associated with a given key. /// \return formatted BibTeX data associated with a given key.
/// Empty if no info exists. /// Empty if no info exists.
/// Note that this will retrieve data from the crossref as needed. /// Note that this will retrieve data from the crossref as needed.
@ -177,7 +189,10 @@ public:
void collectCitedEntries(Buffer const & buf); void collectCitedEntries(Buffer const & buf);
/// A list of BibTeX keys cited in the current document, sorted by /// A list of BibTeX keys cited in the current document, sorted by
/// the last name of the author. /// the last name of the author.
std::vector<docstring> const & citedEntries() const { return cited_entries_; } /// Make sure you have called collectCitedEntries() before you try to
/// use this. You should probably call it just before you use this.
std::vector<docstring> const & citedEntries() const
{ return cited_entries_; }
/// ///
void makeCitationLabels(Buffer const & buf); void makeCitationLabels(Buffer const & buf);
/// ///

View File

@ -909,10 +909,17 @@ void InsetBibtex::validate(LaTeXFeatures & features) const
} }
// FIXME
// docstring InsetBibtex::entriesAsXHTML(vector<docstring> const & entries)
// And then here just: entriesAsXHTML(buffer().masterBibInfo().citedEntries())
docstring InsetBibtex::xhtml(XHTMLStream & xs, OutputParams const &) const docstring InsetBibtex::xhtml(XHTMLStream & xs, OutputParams const &) const
{ {
BiblioInfo const & bibinfo = buffer().masterBibInfo(); BiblioInfo const & bibinfo = buffer().masterBibInfo();
vector<docstring> const & cites = bibinfo.citedEntries(); vector<docstring> const & cites = bibinfo.citedEntries();
CiteEngine const engine = buffer().params().citeEngine();
bool const numbers =
(engine == ENGINE_BASIC || engine == ENGINE_NATBIB_NUMERICAL);
xs << StartTag("h2", "class='bibtex'") xs << StartTag("h2", "class='bibtex'")
<< _("References") << _("References")
<< EndTag("h2") << EndTag("h2")
@ -931,7 +938,17 @@ docstring InsetBibtex::xhtml(XHTMLStream & xs, OutputParams const &) const
// The same name/id problem we have elsewhere. // The same name/id problem we have elsewhere.
string const attr = "id='" + to_utf8(entry.key()) + "'"; string const attr = "id='" + to_utf8(entry.key()) + "'";
xs << CompTag("a", attr); xs << CompTag("a", attr);
docstring citekey = entry.citeKey(); docstring citekey;
if (numbers)
citekey = entry.citeNumber();
else {
docstring const auth = entry.getAbbreviatedAuthor();
// we do it this way so as to access the xref, if necessary
// note that this also gives us the modifier
docstring const year = bibinfo.getYear(*vit, true);
if (!auth.empty() && !year.empty())
citekey = auth + ' ' + year;
}
if (citekey.empty()) { if (citekey.empty()) {
citekey = entry.label(); citekey = entry.label();
if (citekey.empty()) if (citekey.empty())
@ -947,6 +964,7 @@ docstring InsetBibtex::xhtml(XHTMLStream & xs, OutputParams const &) const
<< bibinfo.getInfo(entry.key()) << bibinfo.getInfo(entry.key())
<< EndTag("span") << EndTag("span")
<< EndTag("div"); << EndTag("div");
xs.cr();
} }
xs << EndTag("div"); xs << EndTag("div");
return docstring(); return docstring();

View File

@ -199,22 +199,33 @@ string asValidLatexCommand(string const & input, CiteEngine const engine)
return output; return output;
} }
inline docstring wrapCitation(docstring const & key,
docstring const & content, bool for_xhtml)
{
if (!for_xhtml)
return content;
// we have to do the escaping here, because we will ultimately
// write this as a raw string, so as not to escape the tags.
return "<a href='#" + key + "'>" + html::htmlize(content) + "</a>";
}
} // anonymous namespace } // anonymous namespace
docstring InsetCitation::generateLabel() const docstring InsetCitation::generateLabel(bool for_xhtml) const
{ {
docstring label; docstring label;
label = complexLabel(); label = complexLabel(for_xhtml);
// Fallback to fail-safe // Fallback to fail-safe
if (label.empty()) if (label.empty())
label = basicLabel(); label = basicLabel(for_xhtml);
return label; return label;
} }
docstring InsetCitation::complexLabel() const docstring InsetCitation::complexLabel(bool for_xhtml) const
{ {
Buffer const & buf = buffer(); Buffer const & buf = buffer();
// Only start the process off after the buffer is loaded from file. // Only start the process off after the buffer is loaded from file.
@ -300,83 +311,90 @@ docstring InsetCitation::complexLabel() const
for (; it != end; ++it) { for (; it != end; ++it) {
// get the bibdata corresponding to the key // get the bibdata corresponding to the key
docstring const author = biblist.getAbbreviatedAuthor(*it); docstring const author = biblist.getAbbreviatedAuthor(*it);
docstring const year = biblist.getYear(*it); docstring const year = biblist.getYear(*it, for_xhtml);
docstring const citenum = for_xhtml ? biblist.getCiteNumber(*it) : *it;
// Something isn't right. Fail safely.
if (author.empty() || year.empty()) if (author.empty() || year.empty())
// We can't construct a "complex" label without that info.
// So fail safely.
return docstring(); return docstring();
// authors1/<before>; ... ; // authors1/<before>; ... ;
// authors_last, <after> // authors_last, <after>
if (cite_type == "cite") { if (cite_type == "cite") {
if (engine == ENGINE_BASIC) { if (engine == ENGINE_BASIC) {
label += *it + sep_str; label += wrapCitation(*it, citenum, for_xhtml) + sep_str;
} else if (engine == ENGINE_JURABIB) { } else if (engine == ENGINE_JURABIB) {
if (it == keys.begin()) if (it == keys.begin())
label += author + before_str + sep_str; label += wrapCitation(*it, author, for_xhtml) + before_str + sep_str;
else else
label += author + sep_str; label += wrapCitation(*it, author, for_xhtml) + sep_str;
} }
}
// nocite // nocite
} else if (cite_type == "nocite") { else if (cite_type == "nocite") {
label += *it + sep_str; label += *it + sep_str;
}
// (authors1 (<before> year); ... ; // (authors1 (<before> year); ... ;
// authors_last (<before> year, <after>) // authors_last (<before> year, <after>)
} else if (cite_type == "citet") { else if (cite_type == "citet") {
switch (engine) { switch (engine) {
case ENGINE_NATBIB_AUTHORYEAR: case ENGINE_NATBIB_AUTHORYEAR:
label += author + op_str + before_str + label += author + op_str + before_str +
year + cp + sep_str; wrapCitation(*it, year, for_xhtml) + cp + sep_str;
break; break;
case ENGINE_NATBIB_NUMERICAL: case ENGINE_NATBIB_NUMERICAL:
label += author + op_str + before_str + '#' + *it + cp + sep_str; label += author + op_str + before_str +
wrapCitation(*it, citenum, for_xhtml) + cp + sep_str;
break; break;
case ENGINE_JURABIB: case ENGINE_JURABIB:
label += before_str + author + op_str + label += before_str + author + op_str +
year + cp + sep_str; wrapCitation(*it, year, for_xhtml) + cp + sep_str;
break; break;
case ENGINE_BASIC: case ENGINE_BASIC:
break; break;
} }
}
// author, year; author, year; ... // author, year; author, year; ...
} else if (cite_type == "citep" || else if (cite_type == "citep" ||
cite_type == "citealp") { cite_type == "citealp") {
if (engine == ENGINE_NATBIB_NUMERICAL) { if (engine == ENGINE_NATBIB_NUMERICAL) {
label += *it + sep_str; label += wrapCitation(*it, citenum, for_xhtml) + sep_str;
} else { } else {
label += author + ", " + year + sep_str; label += wrapCitation(*it, author + ", " + year, for_xhtml) + sep_str;
} }
}
// (authors1 <before> year; // (authors1 <before> year;
// authors_last <before> year, <after>) // authors_last <before> year, <after>)
} else if (cite_type == "citealt") { else if (cite_type == "citealt") {
switch (engine) { switch (engine) {
case ENGINE_NATBIB_AUTHORYEAR: case ENGINE_NATBIB_AUTHORYEAR:
label += author + ' ' + before_str + label += author + ' ' + before_str +
year + sep_str; wrapCitation(*it, year, for_xhtml) + sep_str;
break; break;
case ENGINE_NATBIB_NUMERICAL: case ENGINE_NATBIB_NUMERICAL:
label += author + ' ' + before_str + '#' + *it + sep_str; label += author + ' ' + before_str + '#' +
wrapCitation(*it, citenum, for_xhtml) + sep_str;
break; break;
case ENGINE_JURABIB: case ENGINE_JURABIB:
label += before_str + author + ' ' + label += before_str +
year + sep_str; wrapCitation(*it, author + ' ' + year, for_xhtml) + sep_str;
break; break;
case ENGINE_BASIC: case ENGINE_BASIC:
break; break;
} }
// author; author; ...
} else if (cite_type == "citeauthor") {
label += author + sep_str;
}
// author; author; ...
else if (cite_type == "citeauthor") {
label += wrapCitation(*it, author, for_xhtml) + sep_str;
}
// year; year; ... // year; year; ...
} else if (cite_type == "citeyear" || else if (cite_type == "citeyear" ||
cite_type == "citeyearpar") { cite_type == "citeyearpar") {
label += year + sep_str; label += wrapCitation(*it, year, for_xhtml) + sep_str;
} }
} }
label = rtrim(rtrim(label), sep); label = rtrim(rtrim(label), sep);
@ -409,7 +427,7 @@ docstring InsetCitation::complexLabel() const
} }
docstring InsetCitation::basicLabel() const docstring InsetCitation::basicLabel(bool for_xhtml) const
{ {
docstring keys = getParam("key"); docstring keys = getParam("key");
docstring label; docstring label;
@ -420,10 +438,10 @@ docstring InsetCitation::basicLabel() const
while (contains(keys, ',')) { while (contains(keys, ',')) {
docstring key; docstring key;
keys = ltrim(split(keys, key, ',')); keys = ltrim(split(keys, key, ','));
label += ", " + key; label += ", " + wrapCitation(key, key, for_xhtml);
} }
} else { } else {
label = keys; label = wrapCitation(keys, keys, for_xhtml);
} }
docstring const & after = getParam("after"); docstring const & after = getParam("after");
@ -510,46 +528,9 @@ docstring InsetCitation::xhtml(XHTMLStream & xs, OutputParams const &) const
if (cmd == "nocite") if (cmd == "nocite")
return docstring(); return docstring();
BiblioInfo const & bi = buffer().masterBibInfo(); // have to output this raw, because generateLabel() will include tags
docstring const & key_list = getParam("key"); xs << XHTMLStream::NextRaw() << generateLabel(true);
if (key_list.empty())
return docstring();
// FIXME We should do a better job outputing different things for the
// different citation styles. For now, we use square brackets for every
// case.
xs << "[";
docstring const & before = getParam("before");
if (!before.empty())
xs << before << " ";
vector<docstring> const keys = getVectorFromString(key_list);
vector<docstring>::const_iterator it = keys.begin();
vector<docstring>::const_iterator const en = keys.end();
bool first = true;
for (; it != en; ++it) {
BiblioInfo::const_iterator const bt = bi.find(*it);
if (bt == bi.end())
continue;
BibTeXInfo const & bibinfo = bt->second;
if (!first) {
xs << ", ";
first = false;
}
docstring citekey = bibinfo.citeKey();
if (citekey.empty()) {
citekey = bibinfo.label();
if (citekey.empty())
citekey = *it;
}
string const attr = "href='#" + to_utf8(*it) + "'";
xs << StartTag("a", attr) << citekey << EndTag("a");
}
docstring const & after = getParam("after");
if (!after.empty())
xs << ", " << after;
xs << "]";
return docstring(); return docstring();
} }

View File

@ -74,12 +74,12 @@ public:
private: private:
/// ///
Inset * clone() const { return new InsetCitation(*this); } Inset * clone() const { return new InsetCitation(*this); }
/// This function does the donkey work of creating the pretty label /// tries to make a pretty label and makes a basic one if not
docstring generateLabel() const; docstring generateLabel(bool for_xhtml = false) const;
/// /// makes a pretty label
docstring complexLabel() const; docstring complexLabel(bool for_xhtml = false) const;
/// /// makes a very basic label, in case we can't make a pretty one
docstring basicLabel() const; docstring basicLabel(bool for_xhtml = false) const;
/// we'll eventually want to be able to get info on this from the /// we'll eventually want to be able to get info on this from the
/// various CiteEngines /// various CiteEngines
static ParamInfo param_info_; static ParamInfo param_info_;