hans / obsidian-citation-plugin

Obsidian plugin which integrates your academic reference manager with the Obsidian editor. Search your references from within Obsidian and automatically create and reference literature notes for papers and books.
MIT License
1.12k stars 83 forks source link

Automatic generation of references #199

Closed carey036 closed 1 year ago

carey036 commented 2 years ago

Is your feature request related to a problem? Please describe. No A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like A clear and concise description of what you want to happen. thanks a lot for your work!

Is this possible to generate references in this way.

additional, we can also change the format of the citation by locate each tag.

Describe alternatives you've considered A clear and concise description of any alternative solutions or features you've considered.

Additional context Add any other context or screenshots about the feature request here.

ashprice commented 1 year ago

Using HTML tags over some other formatting seems like it should be user preference, IMO.

I too would like to see this added to this plugin, but for now, this is possible with some other plugins, assuming I understand what you mean.

If I understand you, you want to take a citekey, eg. @c.r2015 and generate one of two kinds of references (I'm not sure which you want, or if you want a third option, that of one intext reference and one endnote citation that are both automatically placed...):

  1. Inline references. An inline reference for our citekey @c.r2015 would be:

(Cinque and Rizzi 2015, pp. 51-65)

  1. Endnote references. An endnote reference for our citekey @c.r2015 would be:

Cinque, G. and Rizzi, L. (2015). The Cartography of Syntactic Structures. In: B. Heine and H. Narrog (eds., 2015), The Oxford Handbook of Linguistic Analaysis, pp. 51–65. Oxford University Press.

I've been working on a rough sketch of this functionality using the templater plugin. Attached is the current state of the JavaScript file used for this (needs finishing/touching up, there's some redundancy, is a WIP.)

https://github.com/ashprice/obsidian-templates/blob/main/citationString.js

You can use this template with the templater plugin by calling it in a note with:

<%${tp.user.citationString("{{citekey}}")}%>

These templates can be bound to keybindings, and set to autoload or to load with a binding. This makes inserting a formatted citation (as opposed to a markdown/pandoc-ready citation) relatively easy. You can easily rewrite the JS to do as you describe, but to print with html tags instead of just plain markdown.

I started doing this because I wanted to reference citations in notes, but to have what I was referencing clearer than a citekey. Sure, I can print the strings as in-text fields using the current features of this plugin well enough that I can identify it, but it made sense to me for it to adhere to my 'normal' citation style guidelines, as opposed to deviating from it. This requires scripting for conditionals.

One thing I am stuck on (and I don't mean to hijack the issue here) is accessing the _containerTitle - I've combed through main.js and I just can't figure out how to pull it. (I am inexperienced with JS.)

ashprice commented 1 year ago

Ah. I can access booktitle/journaltitle/etc., and just reimplement the citation plugin's _containerTitle myself for now.

carey036 commented 1 year ago

Using HTML tags over some other formatting seems like it should be user preference, IMO.

I too would like to see this added to this plugin, but for now, this is possible with some other plugins, assuming I understand what you mean.

If I understand you, you want to take a citekey, eg. @c.r2015 and generate one of two kinds of references (I'm not sure which you want, or if you want a third option, that of one intext reference and one endnote citation that are both automatically placed...):

  1. Inline references. An inline reference for our citekey @c.r2015 would be:

(Cinque and Rizzi 2015, pp. 51-65)

  1. Endnote references. An endnote reference for our citekey @c.r2015 would be:

Cinque, G. and Rizzi, L. (2015). The Cartography of Syntactic Structures. In: B. Heine and H. Narrog (eds., 2015), The Oxford Handbook of Linguistic Analaysis, pp. 51–65. Oxford University Press.

I've been working on a rough sketch of this functionality using the templater plugin. Attached is the current state of the JavaScript file used for this (needs finishing/touching up, there's some redundancy, is a WIP.)

https://github.com/ashprice/obsidian-templates/blob/main/citationString.js

You can use this template with the templater plugin by calling it in a note with:

<%${tp.user.citationString("{{citekey}}")}%>

These templates can be bound to keybindings, and set to autoload or to load with a binding. This makes inserting a formatted citation (as opposed to a markdown/pandoc-ready citation) relatively easy. You can easily rewrite the JS to do as you describe, but to print with html tags instead of just plain markdown.

I started doing this because I wanted to reference citations in notes, but to have what I was referencing clearer than a citekey. Sure, I can print the strings as in-text fields using the current features of this plugin well enough that I can identify it, but it made sense to me for it to adhere to my 'normal' citation style guidelines, as opposed to deviating from it. This requires scripting for conditionals.

One thing I am stuck on (and I don't mean to hijack the issue here) is accessing the _containerTitle - I've combed through main.js and I just can't figure out how to pull it. (I am inexperienced with JS.)

thanks for your answering.

but it seems that it cant get all citekey automaticly from the editing note? do i need to wtire a specific note for the references?

i want to achieve this. i can casually cite papers (such as c.r(2015) said ...)(may add html tag here) without ref(such asCinque and Rizzi 2015, pp. 51-65), when i finish this note, i can get what i cited in this note, and add ref(such asCinque and Rizzi 2015, pp. 51-65) onekey.

i am not sure how to get the editing note content, may we could get citekey from the html tag.

ashprice commented 1 year ago

Hmm, you're right. The closest I can get to this right now is including a template as the citation plugins default markdown citation form (this has the disadvantage of meaning you can't use that normally), which means you can access the list of citekeys but then you have to manually run the templater plugin on the file.

So that would work like:

1) Hit the insert markdown citation button, pick your citation, eg. @c.r2015 2) This will appear:

<% `${tp.user.citationString("c.r2015")}` %>

3) You press the keybinding assigned to run all templates, so that you get this: <citation id="citekey">(Cinque and Rizzi 2015, pp. 51-65)</citation>

Or whatever else you put for 3.

This has the disadvantage of extra key-presses, and it means you can't use the default markdown insertion command.

Another solution that I noticed is that the templater plugin can be assigned to read certain directories. So if you have your citation plugin writing to a folder of literature 'notes,' you can have those notes contain a formatted citation. So say we're making notes in ./notes/, and we have literature 'notes' that look like the below in ./lit-notes/:

@c.r2015 (our filename) Cinque, G. and Rizzi, L. (2015). *The Cartography of Syntactic Structures*. In: B. Heine and H. Narrog (eds., 2015), The Oxford handbook of linguistic analysis, pp. 51–65. Oxford University Press.

or alternatively

@c.r2015 (our filename) (Cinque and Rizzi 2015, pp. 51-65)

(or the same with html tags if you want them)

Given the templater is reading directories, we can now use it in our file in ./notes/ to just print the contents of the file with the title of @c.r2015. Presumably you could have two directories, one with full citations, and one with short citations, and then append something to the name. So for example you have filenames like full@c.r2015 for all the longer citations, and then just @c.r2015 for shorter ones. But if you don't use the literature notes, just having a directory with one or the other would be simpler, I guess. It might become hard to identify what each citekey is if you're only using short citations.

It's also a bit uncomfortable and unintuitive. There's probably a far easier way, I'll reply back if I figure out a way that plays nicer with all the plugins...

carey036 commented 1 year ago
function parse2Dom(str: string) {
    var div = document.createElement("div");
    div.innerHTML = str;
    return div;
}
this.addCommand({
    id: 'add ref',
    name: 'Add Ref',
    editorCallback: (editor: Editor, view: MarkdownView) => {
        const content = parse2Dom(editor.getValue());
        const citation = content.getElementsByTagName("citation")
        console.log(citation)
        var ref = ""
        for (var i = 0; i < citation.length; i++) {
            ref += "[" + (i + 1) + "] " + citation[i].attributes.author.value + " (" + citation[i].attributes.year.value + ")" + citation[i].attributes.title.value + "\n"
        }
        editor.replaceSelection(ref);
    }
});

it is a piece code of obsidian plugins, it seems it could work better, but it still need to revised, i am not good at js.

in this code, it add a command named "add ref", it could get the content of the note and add ref automaticly.

we can change the markdown citation templates of Citation to <citation author="{{#each entry.author}}{{#if @first}}{{this.family}}{{this.given}}{{/if}}{{/each}}" year="{{year}}" title="{{title}}">waht you want to show</citation>,then use "add ref", it will find all citation , and add ref automaticly. @ashprice

carey036 commented 1 year ago

I build this, may you can have it a try. https://github.com/carey036/zoteroObsidian/releases/tag/0.0.1

may it could be included into Citation?

carey036 commented 1 year ago

@ashprice i found a good way. i will close this issue.

you can use the obsidian plugin "Inline Scripts", with script

function citationString(citekey) {

    function yearString(citekey) {
      let data = app.plugins.plugins['obsidian-citation-plugin'].library.entries[citekey].data;
      let yearString = data.fields.date[0].split("-")[0];
      return yearString
    }

    function authorsString(citekey) {
      let fullAuthorList = [];
      let data = app.plugins.plugins['obsidian-citation-plugin'].library.entries[citekey].data;
      let authors = [];
      if (data.creators.author)
        authors = data.creators.author;
      else
        authors = [{literal: "NaN"}];

      authors.forEach(author => {
        const [firstNamesArray, lastNamesString] = findFirstLast(author);
        let str = [];
        str.push(lastNamesString)
        str.push(",");
        firstNamesArray.forEach(name => {
          str.push(` ${name[0]}.`);
        });
        fullAuthorList.push(str.join(""));
      });
      const authorString = joinAuthors(fullAuthorList);
      return replaceIllegalFileNameCharactersInString(authorString);
    }

    function joinAuthors(fullAuthorList) {
      const len = fullAuthorList.length;
      if (len == 1)
        return fullAuthorList[0];
      else if (len == 2)
        return fullAuthorList.join("\ and ");
      else if (len >= 3)
        return fullAuthorList[0] + " et al.";
    }

    function findFirstLast(author) {
      let lastNamesString = "";
      let firstNamesArray = [];
      if (author.lastName) {
        firstNamesArray = author.firstName.split(" ");
        lastNamesString = author.lastName;
      }
      else {
        let parts = author.literal.split(" ");
        firstNamesArray = parts.slice(0,parts.length-1);
        lastNamesString = parts[parts.length-1];
      }
      return [firstNamesArray, lastNamesString];
    }

    function replaceIllegalFileNameCharactersInString(string) {
      return string.replace(/[\\\/@]*/g, '');
    }

    function editorsString(citekey) {
      let fullEditorList = [];
      let data = app.plugins.plugins['obsidian-citation-plugin'].library.entries[citekey].data;
      let editors = [];
      if (data.creators.editor)
        editors = data.creators.editor;
      else
        editors = [{literal: "NaN"}];

      editors.forEach(editor => {
        const [firstNamesArray, lastNamesString] = findFirstLast(editor);
        let str = [];
        str.push(lastNamesString)
        str.push(",");
        firstNamesArray.forEach(name => {
          str.push(` ${name[0]}.`);
        });
        fullEditorList.push(str.join(""));
      });
      const editorString = joinEditors(fullEditorList);
      return replaceIllegalFileNameCharactersInString(editorString);
    }

    function joinEditors(fullEditorList) {
      const len = fullEditorList.length;
      if (len == 1)
        return fullEditorList[0];
      else if (len == 2)
        return fullEditorList.join("\ and ");
      else if (len == 3 || len == 4)
        return fullEditorList.slice(0, -1).join("\, ") + `\, and ${fullEditorList[len - 1]}`;
      else if (len >= 5)
        return fullEditorList[0] + " et al.";
    }

    function titleString(citekey) {
      let data = app.plugins.plugins['obsidian-citation-plugin'].library.entries[citekey].data;
      let titleString = data.fields.title;
      return titleString;
    }

    function containerString(citekey) {
      let data = app.plugins.plugins['obsidian-citation-plugin'].library.entries[citekey].data;
      let containerString = data.containerTitle;
      return containerString;
    }

    function publisherString(citekey) {
      let data = app.plugins.plugins['obsidian-citation-plugin'].library.entries[citekey].data;
      let publisherString = data.fields.publisher;
      return publisherString;
    }

    function pageString(citekey) {
      let data = app.plugins.plugins['obsidian-citation-plugin'].library.entries[citekey].data;
      let pageString = data.fields.pages;
      return pageString;
    }

    let data = app.plugins.plugins['obsidian-citation-plugin'].library.entries[citekey].data;
    let authorString = "";
    let editorString = "";
    if (data.creators.author) {
      authorString = authorsString(citekey);
    }
    if (data.creators.editor) {
      editorString = editorsString(citekey);
    }
    let year = yearString(citekey);
    let title = titleString(citekey);
    let publisher = publisherString(citekey);
    let page = pageString(citekey);
    let container = containerString(citekey);

    let citationString = "";
    if (authorString) {
      citationString = `${authorString} (${year}). ${title}`;
    } else if (editorString) {
      citationString = `${editorString} (ed., ${year}). ${title}`;
    }
    if (container) {
      citationString += `. In: ${editorString} (ed., ${year}), ${container}`;
    }
    if (page) {
      const rangeRegex = /^\d+[-–—]\d+$/;
      if (rangeRegex.test(page)) {
        citationString += `, pp. ${page}.`;
      } else {
        citationString += `, p. ${page}.`;
      }
    } else {
      citationString += `.`;
    }
    if (publisher) {
      citationString += ` ${publisher}.`;
    }
    return citationString;
}
function parse2Dom(str){
    var div = document.createElement("div");
    div.innerHTML = str;
    return div;
}
content = parse2Dom(app.workspace.activeEditor.data)
const citations = Array.from(content.getElementsByTagName("citation"))
var ref = ""
const references = new Set()
citations.forEach((citation, i) => {
    const reference = citationString(citation.attributes.citekey.value)
    if(!references.has(reference)){
        references.add(reference)
        ref += `[${i+1}] ${citationString(citation.attributes.citekey.value)}\n\n`
    }
});
console.log(ref)
return ref;

image

most of code is written by you.

with this setting, you can type ;;myref in the editing note. image

then type Enter, you will get the ref image

ashprice commented 1 year ago

Hey @carey036 nice work!

I just wanted to write and say sorry for not replying. I've been very busy, and this kind of dropped down my list of to-do's. This solution is quite elegant and pretty cool.

FWIW the function to grab authors was originally written by someone else, I think over on the Obsidian forums, I just borrowed it (and I think changed it a bit) + made one for editors. Probably one could do with pulling the DOI and URL for citations where those exist. (Or not, if that's your preference in terms of referencing style.)