Source code for leo.plugins.bibtex

#@+leo-ver=5-thin
#@+node:timo.20050213160555: * @file bibtex.py
#@+<< docstring >>
#@+node:ekr.20050912175750: ** << docstring >>
#@@nocolor-node
#@@wrap
r''' Creates a BibTex file from an  '@bibtex <filename>' tree.

Nodes of the form '@<x> key' create entries in the file.

When the user creates a new node (presses enter in headline text) the plugin
automatically inserts a template for the entry in the body pane.

The 'templates' dict in the <\< globals >\> section defines the template. The
default, the template creates all required entries.

Double-clicking the @bibtex node writes the file. For example, the following
outline::

    -@bibtex biblio.bib
     +@book key,
      author = {A. Uthor},
      year = 1999

creates the following 'biblio.bib' file::

    @book{key,
    author = {A. Uthor},
    year= 1999}

@string nodes define strings and may contain multiple entries. The plugin writes
 all @string nodes at the start of the file. For example, the following
 outline::

    -@bibtext biblio.bib
     +@string
      j1 = {Journal1}
     +@article AUj1
      author = {A. Uthor},
      journal = j1
     +@string
      j2 = {Journal2}
      j3 = {Journal3}

creates the following file::

    @string{j1 = {Journal1}}
    @string{j2 = {Journal2}}
    @string{j3 = {Journal3}}

    @article{AUj1,
    author = {A. Uthor},
    journal = j1}

Headlines that do not start with '@' are organizer nodes: the plugin does not
write organizer nodes, but does write descendant nodes.

BibTeX files can be imported by creating an empty node with '@bibtex filename'
in the headline. Double-clicking it will read the file and parse it into a
@bibtex tree. No syntax checks are made: the file is expected to be a valid
BibTeX file.

'''
#@-<< docstring >>
import leo.core.leoGlobals as g
# By Timo Honkasalo: contributed under the same license as Leo.py itself.
# 2017/02/23: Rewritten by EKR
#@+<< define templates dict>>
#@+node:timo.20050215183130: ** <<define templates dict>>
#@@nobeautify

templates = {
    '@article':         'author       = {},\ntitle        = {},\njournal      = {},\nyear         = ',
    '@book':            'author       = {},\ntitle        = {},\npublisher    = {},\nyear         = ',
    '@booklet':         'title        = {}',
    '@conference':      'author       = {},\ntitle        = {},\nbooktitle    = {},\nyear         = ',
    '@inbook':          'author       = {},\ntitle        = {},\nchapter      = {},\npublisher    = {},\nyear         = ',
    '@incollection':    'author       = {},\ntitle        = {},\nbooktitle    = {},\npublisher    = {},\nyear         = ',
    '@inproceedings':   'author       = {},\ntitle        = {},\nbooktitle    = {},\nyear         = ',
    '@manual':          'title        = {},',
    '@mastersthesis':   'author       = {},\ntitle        = {},\nschool       = {},\nyear         = ',
    '@misc':            '',
    '@phdthesis':       'author       = {},\ntitle        = {},\nschool       = {},\nyear         = ',
    '@proceedings':     'title        = {},\nyear         = ',
    '@techreport':      'author       = {},\ntitle        = {},\ninstitution  = {},\nyear         = ',
    '@unpublished':     'author       = {},\ntitle        = {},\nnote         = {}'
}
#@-<< define templates dict>>
entrytypes = list(templates.keys())
entrytypes.append('@string')
#@+<< to do >>
#@+node:timo.20050213185039: ** <<to do>>
#@+at To do list (in approximate order of importance):
# 
# - Translating between non-ascii characters and LaTeX code when reading/writing
# - Checking for duplicate keys
# - Checking for missing commas when writing the file
# - Customisable config file (for defining the templates)
# - Easy access to the tree as a Python object for scripting (maybe Pybliographer)
# - Import/write in BibTeXml format
# - Sorting by chosen fields
# - Import/write in other bibliographic formats
# - Expanding strings
# - Syntax checking
# - Syntax highligting
#@-<< to do >>
#@+others
#@+node:ekr.20100128073941.5370: ** init
[docs]def init(): '''Return True if the plugin has loaded successfully.''' ok = not g.app.unitTesting if ok: # Register the handlers... g.registerHandler("headdclick1", onIconDoubleClick) g.registerHandler("headkey2", onHeadKey) g.plugin_signon(__name__) return ok
#@+node:timo.20050215222802: ** onHeadKey
[docs]def onHeadKey(tag, keywords): """ Write template for the entry in body pane. If body pane is empty, get template for the entry from a dictionary 'templates ' and write it in the body pane. 20141127 - note headkey2 now only fires on `Enter`, no need to check which key brought us here. """ # c = keywords.get("c") # To do: check for duplicate keys here. p = keywords.get("p") if not p: return h = p.h.strip() i = h.find(' ') kind = h[: i] if kind in templates.keys() and not p.b.strip(): # Fix bug 142: plugin overwrites body text. # Iterate on p2, not p! for p2 in p.parents(): if p2.h.startswith('@bibtex ') and not p.b.strip(): # write template, but only for new nodes. p.b = templates.get(kind) # c.frame.body.wrapper.setInsertPoint(16) return
#@+node:timo.20050213160555.3: ** onIconDoubleClick # # this does not check for proper filename syntax. # path is the current dir, or the place @folder points to # this should probably be changed to @path or so.
[docs]def onIconDoubleClick(tag, keywords): """ Read or write a bibtex file when the node is double-clicked. Write the @bibtex tree as bibtex file when the root node is double-clicked. If it has no child nodes, read bibtex file. """ p = keywords.get("p") or keywords.get("v") c = keywords.get("c") if not c or not p: return h = p.h.strip() if g.match_word(h, 0, "@bibtex"): base = g.os_path_dirname(c.fileName() or '') fn = g.os_path_finalize_join(base, h[8:]) if p.hasChildren(): writeTreeAsBibTex(c, fn, p) else: readBibTexFileIntoTree(c, fn, p)
#@+node:timo.20050214174623.1: ** readBibTexFileIntoTree
[docs]def readBibTexFileIntoTree(c, fn, p): '''Import a BibTeX file into a @bibtex tree.''' root = p.copy() g.es('reading:', fn) s = g.readFileIntoEncodedString(fn) # Read the encoded bytes for g.getEncodingAt() if not s or not s.strip(): return encoding = g.getEncodingAt(p, s) s = g.toUnicode(s, encoding=encoding) aList, entries, strings = [], [], [] # aList is a list of tuples (h,b). s = '\n' + ''.join([z.lstrip() for z in g.splitLines(s)]) for line in s.split('\n@')[1:]: kind, rest = line[: 6], line[7:].strip() if kind == 'string': strings.append(rest[: -1] + '\n') else: i = min(line.find(','), line.find('\n')) h = '@' + line[: i] h = h.replace('{', ' ').replace('(', ' ').replace('\n', '') b = line[i + 1:].rstrip().lstrip('\n')[: -1] entries.append((h, b),) if strings: h, b = '@string', ''.join(strings) aList.append((h, b),) aList.extend(entries) for h, b in aList: p = root.insertAsLastChild() p.b, p.h = b, h root.expand() c.redraw()
#@+node:timo.20050213160555.7: ** writeTreeAsBibTex
[docs]def writeTreeAsBibTex(c, fn, root): """Write root's *subtree* to bibFile.""" strings, entries = [], [] for p in root.subtree(): h = p.h if h.lower() == '@string': strings.extend([('@string{%s}\n\n' % z.rstrip()) for z in g.splitLines(p.b) if z.strip()]) else: i = h.find(' ') kind, rest = h[: i].lower(), h[i + 1:].rstrip() if kind in entrytypes: entries.append('%s{%s,\n%s}\n\n' % (kind, rest, p.b.rstrip())) if strings or entries: g.es('writing:', fn) encoding=g.getEncodingAt(root) with open(fn, 'wb') as f: s = ''.join(strings + entries) f.write(g.toEncodedString(s,encoding=encoding))
#@-others #@@language python #@@tabwidth -4 #@-leo