fallback.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import vim
  2. import re
  3. import sys
  4. import os
  5. import os.path
  6. import operator
  7. import subprocess as sp
  8. import shlex
  9. from vim_pandoc.bib.collator import SourceCollator
  10. from vim_pandoc.bib.util import make_title_ascii
  11. try:
  12. local_bib_extensions = vim.vars["pandoc#biblio#bib_extensions"]
  13. except:
  14. local_bib_extensions = []
  15. bib_extensions = ["bib", "bibtex", "ris", "mods", "json", "enl", "wos", "medline", "copac", "xml"]
  16. # SUGGESTIONS
  17. bibtex_title_search = re.compile(r"^\s*[Tt]itle\s*=\s*{(?P<title>\S.*?)}.{,1}\n", re.MULTILINE | re.DOTALL)
  18. bibtex_booktitle_search = re.compile(r"^\s*[Bb]ooktitle\s*=\s*{(?P<booktitle>\S.*?)}.{,1}\n", re.MULTILINE | re.DOTALL)
  19. def get_bibtex_suggestions(text, query, use_bibtool=False, bib=None):
  20. global bibtex_title_search
  21. global bibtex_booktitle_search
  22. global bibtex_author_search
  23. global bibtex_editor_search
  24. global bibtex_crossref_search
  25. entries = []
  26. if use_bibtool:
  27. bibtex_id_search = re.compile(r".*{\s*(?P<id>.*),")
  28. extra_args = shlex.split(vim.vars["pandoc#biblio#bibtool_extra_args"])
  29. search = ["--", "select{$key title booktitle author editor \"%(query)s\"}" % {"query": query}]
  30. cmd = ["bibtool", "--preserve.key.case=on", *extra_args, *search, bib]
  31. text = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.PIPE).communicate()[0]
  32. if isinstance(text, bytes):
  33. text = text.decode("utf-8")
  34. else:
  35. bibtex_id_search = re.compile(r".*{\s*(?P<id>" + query + ".*),")
  36. for entry in [i for i in re.split("\n@", text)]:
  37. entry_dict = {}
  38. i1 = bibtex_id_search.match(entry)
  39. if i1:
  40. entry_dict["word"] = i1.group("id")
  41. title = "..."
  42. # search for title
  43. i2 = bibtex_title_search.search(entry)
  44. if i2:
  45. title = i2.group("title")
  46. else:
  47. i3 = bibtex_booktitle_search.search(entry)
  48. if i3:
  49. title = i3.group("booktitle")
  50. title = re.sub("[{}]", "", re.sub(r"\s+", " ", title))
  51. entry_dict["menu"] = make_title_ascii(title)
  52. entries.append(entry_dict)
  53. return entries
  54. ris_title_search = re.compile(r"^(TI|T1|CT|BT|T2|T3)\s*-\s*(?P<title>.*)\n", re.MULTILINE)
  55. def get_ris_suggestions(text, query):
  56. global ris_title_search
  57. global ris_author_search
  58. entries = []
  59. ris_id_search = re.compile(r"^ID\s*-\s*(?P<id>" + query + ".*)\n", re.MULTILINE)
  60. for entry in re.split(r"ER\s*-\s*\n", text):
  61. entry_dict = {}
  62. i1 = ris_id_search.search(entry)
  63. if i1:
  64. entry_dict["word"] = i1.group("id")
  65. title = "..."
  66. i2 = ris_title_search.search(entry)
  67. if i2:
  68. title = i2.group("title")
  69. entry_dict["menu"] = make_title_ascii(title)
  70. entries.append(entry_dict)
  71. return entries
  72. def get_json_suggestions(text, query):
  73. import json
  74. entries = []
  75. if sys.version_info.major == 2:
  76. string_matches = [u'title', u'id']
  77. name_matches = [u'author', u'editor']
  78. family_match = u'family'
  79. literal_match = u'literal'
  80. else:
  81. string_matches = ['title', 'id']
  82. name_matches = ['author', 'editor']
  83. family_match = 'family'
  84. literal_match = 'literal'
  85. try:
  86. data = json.loads(text)
  87. except:
  88. return entries
  89. if type(data) != list: return entries
  90. def check(string):
  91. return re.search(query, string, re.IGNORECASE)
  92. def test_entry(entry):
  93. if type(entry) != dict: return False
  94. for string in [entry.get(k) for k in string_matches]:
  95. if type(string) == str and check(string): return True
  96. for names in [entry.get(k) for k in name_matches]:
  97. if type(names) == list:
  98. for person in names:
  99. if type(person.get(family_match)) == str:
  100. if check(person[family_match]): return True
  101. elif type(person.get(literal_match)) == str:
  102. if check(person[literal_match]): return True
  103. for entry in filter(test_entry, data):
  104. entries.append({"word": entry.get('id'),
  105. "menu": make_title_ascii(entry.get("title", "No Title"))
  106. })
  107. return entries
  108. class FallbackCollator(SourceCollator):
  109. def collate(self):
  110. data = []
  111. for bib in self.find_bibfiles():
  112. bib_type = os.path.basename(bib).split(".")[-1].lower()
  113. if bib_type not in ("ris", "json", "bib", "bibtex"):
  114. break
  115. with open(bib) as f:
  116. text = f.read()
  117. ids = []
  118. if bib_type == "ris":
  119. ids = get_ris_suggestions(text, self.query)
  120. elif bib_type == "json":
  121. ids = get_json_suggestions(text, self.query)
  122. elif bib_type in ("bib", "bibtex"):
  123. if self.extra_args["use_bibtool"] == 1 \
  124. and vim.eval("executable('bibtool')") == '1':
  125. ids = get_bibtex_suggestions(bib, self.query, True, bib)
  126. else:
  127. ids = get_bibtex_suggestions(text, self.query)
  128. data.extend(ids)
  129. if len(data) > 0:
  130. data = sorted(data, key=operator.itemgetter("word"))
  131. return data