From ec3ae7165e94dd8f449b60eee55f07f059c8de02 Mon Sep 17 00:00:00 2001 From: DancingQuanta Date: Sun, 29 Jul 2018 16:08:50 +0100 Subject: [PATCH 1/6] Moved read/write_cache functions to utils to be shared between unite builder and denite source --- python/citation_vim/builder.py | 15 --------------- python/citation_vim/utils.py | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/python/citation_vim/builder.py b/python/citation_vim/builder.py index 8a6b09a..021cd97 100644 --- a/python/citation_vim/builder.py +++ b/python/citation_vim/builder.py @@ -2,7 +2,6 @@ import os.path import string -import pickle from citation_vim.utils import raiseError from citation_vim.item import Item @@ -106,20 +105,6 @@ def get_parser(self): parser = ZoteroParser(self.context) return parser - def read_cache(self): - """ - Returns items from the cache file. - """ - with open(self.cache_file, 'rb') as in_file: - return pickle.load(in_file) - - def write_cache(self, items): - """ - Writes the cache file. - """ - with open(self.cache_file, 'wb') as out_file: - pickle.dump(items, out_file) - def is_cached(self): """ Returns boolean based on cache and target file dates diff --git a/python/citation_vim/utils.py b/python/citation_vim/utils.py index 6c7ea12..e53fe94 100644 --- a/python/citation_vim/utils.py +++ b/python/citation_vim/utils.py @@ -4,6 +4,7 @@ import re import os.path from datetime import datetime, timedelta +import pickle def compat_str(string): if sys.version_info[0] == 2: @@ -36,3 +37,17 @@ def raiseError(message): def strip_braces(string): return re.sub("[{.*}]+", "", string) + +def read_cache(cache_file): + """ + Returns items from the cache file. + """ + with open(cache_file, 'rb') as in_file: + return pickle.load(in_file) + +def write_cache(cache_file, items): + """ + Writes the cache file. + """ + with open(cache_file, 'wb') as out_file: + pickle.dump(items, out_file) From 3498d76e7e27db9e3d70bcc55688304a307085d4 Mon Sep 17 00:00:00 2001 From: DancingQuanta Date: Sun, 29 Jul 2018 16:53:00 +0100 Subject: [PATCH 2/6] Added denite source for citation.vim (source, subsources and collection) and kind --- rplugin/python3/denite/kind/citation.py | 35 +++ rplugin/python3/denite/source/citation.py | 229 ++++++++++++++++++ .../denite/source/citation_collection.py | 36 +++ 3 files changed, 300 insertions(+) create mode 100644 rplugin/python3/denite/kind/citation.py create mode 100644 rplugin/python3/denite/source/citation.py create mode 100644 rplugin/python3/denite/source/citation_collection.py diff --git a/rplugin/python3/denite/kind/citation.py b/rplugin/python3/denite/kind/citation.py new file mode 100644 index 0000000..d857759 --- /dev/null +++ b/rplugin/python3/denite/kind/citation.py @@ -0,0 +1,35 @@ +import os +import re +from .word import Kind as Base + + +class Kind(Base): + + def __init__(self, vim): + super().__init__(vim) + + self.name = 'citation' + self.default_action = 'append' + self.persist_actions = ['preview'] + + def action_execute(self, context): + target = context['targets'][0] + self.vim.call('denite#util#execute_command', + target['action__command']) + + def action_open(self, context): + target = context['targets'][0] + path = target['action__path'] + + if (re.match('https?://', path) + or os.path.splitext(path)[1] == ".pdf"): + self.vim.call('denite#util#open', path) + + def action_directory(self, context): + target = context['targets'][0] + path = target['action__path'] + self.vim.call('denite#util#open', path) + + def action_preview(self, context): + pass + diff --git a/rplugin/python3/denite/source/citation.py b/rplugin/python3/denite/source/citation.py new file mode 100644 index 0000000..62e2cf2 --- /dev/null +++ b/rplugin/python3/denite/source/citation.py @@ -0,0 +1,229 @@ +import sys +from os.path import abspath, join, pardir +from .base import Base + +sys.path.append( + abspath(join(__file__, pardir, pardir, pardir, pardir, pardir, 'python'))) +from citation_vim.utils import read_cache, write_cache, is_current, raiseError +from citation_vim.item import Item + +sub_sources = [ + "abstract", + "author", + "collection", + "combined", + "date", + "doi", + "duplicate_keys", + "file", + "isbn", + "publication", + "key", + "key_inner", + "language", + "issue", + "notes", + "pages", + "publisher", + "tags", + "title", + "type", + "url", + "volume", + "zotero_key" +] + +key_title_banned_regex = r"\b(a|an|the|some|from|on|in|to|of|do|with|der|die|das|ein|eine|einer|eines|einem|einen|un|une|la|le|l|el|las|los|al|uno|una|unos|unas|de|des|del|d)\W" +key_clean_regex = "[^A-Za-z0-9\!\$\&\*\+\-\.\/\:\;\<\>\?\[\]\^\_\`\|]+" + + +class Source(Base): + """ Zotero/Bibtex source for Denite.nvim """ + + def __init__(self, vim): + super().__init__(vim) + + self.name = 'citation' + self.kind = 'citation' + self.matchers = ['matcher_fuzzy'] + self.is_public_context = True + + self.sub_sources = sub_sources + self.__cache = False + self.__cache_file = None + + self.vars = { + 'cache_path': "", + 'mode': 'zotero', + 'zotero_version': 5, + 'zotero_path': '~/Zotero', + 'zotero_attachment_path': '~/Zotero/library', + 'collection': "", + 'bibtex_file': "", + 'reverse_order': True, + 'et_al_limit': 5, + 'key_clean_regex': key_clean_regex, + 'key_title_banned_regex': key_title_banned_regex, + 'key_format': "", + 'key_outer_prefix': '[', + 'key_inner_prefix': '@', + 'key_suffix': ']', + 'desc_format': '{}∶ {} ‴{}‴ ₋{}₋ ₍{}₎', + 'desc_fields': ["type", "key", "title", "author", "date"], + 'wrap_chars': '||', + 'highlight_dash': "‾⁻−₋‐⋯┄–—―∼┈─▭▬┉━┅₌⁼‗", + 'highlight_bar': "‖│┃┆∥┇┊┋", + 'highlight_bracket': "⊂〔₍⁽⊃〕₎⁾", + 'highlight_arrow': "◀◁<‹▶▷>›", + 'highlight_colon': "∶∷→⇒≫", + 'highlight_blob': "♯♡◆◇◊○◎●◐◑∗∙⊙⊚⌂★☺☻▪■□▢▣▤▥▦▧▨▩", + 'highlight_tiny': "、。‸₊⁺∘♢☆☜☞♢☼", + 'highlight_text': "″‴‶‷", + } + + def on_init(self, context): + if len(context['args']) >= 1: + context['__source'] = 'source_field' + context['__field'] = context['args'].pop(0) + + # Set mode; zotero or bibtex + self._set_mode(context) + else: + context['__source'] = 'sub_sources' + + def gather_candidates(self, context): + if context['__source'] is 'sub_sources': + return self._gather_sub_sources() + else: + return self._gather_items(context) + + def _set_mode(self, context): + if self.vars['mode'] == "bibtex" and self.vars['bibtex_file']: + self._enable_cache(context) + elif self.vars['mode'] == "zotero" and self.vars['zotero_path']: + if len(context['args']) > 0: + context['__searchkeys'] = context['args'].pop(0) + self.__cache = False + else: + context['__searchkeys'] = [] + self._enable_cache(context) + else: + raiseError("'mode' must be set to 'zotero' or 'bibtex'") + + def _enable_cache(self, context): + self.__cache = True + self.__cache_file = join(self.vars['cache_path'], "citation_vim_cache") + + def _gather_sub_sources(self): + # Generate candidates and return it + candidates = [] + for sub_source in self.sub_sources: + candidates.append({ + "word": sub_source, + "action__command": self._set_sub_source(sub_source), + }) + return candidates + + def _set_sub_source(self, sub_source): + return ':Denite citation:{}'.format(sub_source) + + def _gather_items(self, context): + candidates = [] + + if context['__field'] is 'duplicate_keys': + items = self._get_duplicate_key(context) + else: + items = self._get_items(context) + + if context['__field'] is 'key': + text = (self.vars['key_outer_prefix'] + + self.vars['key_inner_prefix'] + + '{}' + + self.vars['key_suffix']) + elif context['__field'] is 'key_inner': + text = self.vars['key_inner_prefix'] + '{}' + else: + text = '{}' + + if context['__field'] is 'url': + file_url = 'url' + else: + file_url = 'file' + + # Update vars with source field + self.vars['source_field'] = context['__field'] + + # Retirn items + for item in items: + if (not self.vars['collection'] + or self.vars['collection'] in item.collections): + candidate = { + "word": item.describe(self.vars), + "action__text": text.format(getattr(item, context['__field'])), + "action__path": getattr(item, file_url), + "action__command": self._set_message(item.combined), + } + candidates.append(candidate) + return candidates + + def _set_message(self, message): + return "echo {}".format(message) + + def _get_duplicate_keys(self, context): + """ + Returns an array of collections. + """ + self.vars['collection'] = "" + context['__field'] = 'key' + self.__cache = False + + # Get items + items = self._get_items(context) + + # Filter itesm for duplicate keys + items.sort(key=lambda item: item.key) + last_item = Item() + last_item.key = "" + filtered_items = [] + for item in items: + if last_item.key == item.key: + filtered_items.append(item) + last_item = item + return filtered_items + + def _get_items(self, context): + """ + Returns items from cache or parser + """ + if self.__cache and self._is_cached(): + return read_cache(self.__cache_file) + parser = self._get_parser() + items = parser.load() + if context['__reverse_order']: + items.reverse() + if self.__cache: + write_cache(self.__cache_file, items) + return items + + def _get_parser(self): + """ + Returns a bibtex or zotero parser. + """ + if self.vars['mode'] == "bibtex": + from citation_vim.bibtex.parser import BibtexParser + parser = BibtexParser(self.vars) + elif self.vars['mode'] == "zotero": + from citation_vim.zotero.parser import ZoteroParser + parser = ZoteroParser(self.vars) + return parser + + def _is_cached(self): + """ + Returns boolean based on cache and target file dates + """ + if self.vars['mode'] == 'bibtex': + file_path = self.vars['bibtex_file'] + elif self.vars['mode'] == 'zotero': + zotero_database = join(self.vars['zotero_path'], "zotero.sqlite") + file_path = zotero_database + return is_current(file_path, self.__cache_file) diff --git a/rplugin/python3/denite/source/citation_collection.py b/rplugin/python3/denite/source/citation_collection.py new file mode 100644 index 0000000..fa7a424 --- /dev/null +++ b/rplugin/python3/denite/source/citation_collection.py @@ -0,0 +1,36 @@ +from os.path import dirname +from .citation import Source as Base + + +class Source(Base): + + def __init__(self, vim): + super().__init__(vim) + + self.name = 'citation_collection' + self.description = "search citation collection" + self.kind = 'command' + + def gather_candidates(self, context): + """ + Returns an array of collections. + """ + candidates = {} + collections = {} + for item in self._get_items(context): + for col in item.collections: + if not col in collections: + collections[col] = col + + candidates.append({ + "word": col, + "action__command": self._set_col(col), + # "action__type": ": ", + "action__text": col, + "action__path": col, + "action__directory": dirname(col), + }) + return candidates + + def _set_collection(self, collection): + return "call denite#custom#var('citation', 'collection', '{}')".format(collection) From 41cf342da8d458b6c6af1d36492c2c3ff550b671 Mon Sep 17 00:00:00 2001 From: DancingQuanta Date: Sun, 12 Aug 2018 22:14:56 +0100 Subject: [PATCH 3/6] Refactor bibtex and zotero code and fixed searchkeys --- rplugin/python3/denite/source/citation.py | 47 ++++++++++++----------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/rplugin/python3/denite/source/citation.py b/rplugin/python3/denite/source/citation.py index 62e2cf2..81dc0b4 100644 --- a/rplugin/python3/denite/source/citation.py +++ b/rplugin/python3/denite/source/citation.py @@ -51,6 +51,7 @@ def __init__(self, vim): self.sub_sources = sub_sources self.__cache = False self.__cache_file = None + self._parser = None self.vars = { 'cache_path': "", @@ -79,6 +80,7 @@ def __init__(self, vim): 'highlight_blob': "♯♡◆◇◊○◎●◐◑∗∙⊙⊚⌂★☺☻▪■□▢▣▤▥▦▧▨▩", 'highlight_tiny': "、。‸₊⁺∘♢☆☜☞♢☼", 'highlight_text': "″‴‶‷", + 'searchkeys': [], } def on_init(self, context): @@ -98,22 +100,34 @@ def gather_candidates(self, context): return self._gather_items(context) def _set_mode(self, context): + """ Set up bibtex or zotero mode """ if self.vars['mode'] == "bibtex" and self.vars['bibtex_file']: - self._enable_cache(context) + # Enable cache + self._enable_cache() + # Get parser + from citation_vim.bibtex.parser import BibtexParser + self._parser = BibtexParser(self.vars) elif self.vars['mode'] == "zotero" and self.vars['zotero_path']: - if len(context['args']) > 0: - context['__searchkeys'] = context['args'].pop(0) - self.__cache = False - else: - context['__searchkeys'] = [] - self._enable_cache(context) + # Check if searchkeys are given and set cache mode accordingly. + self._get_searchkeys(context) + # Get parser + from citation_vim.zotero.parser import ZoteroParser + self._parser = ZoteroParser(self.vars) else: raiseError("'mode' must be set to 'zotero' or 'bibtex'") - def _enable_cache(self, context): + def _enable_cache(self): self.__cache = True self.__cache_file = join(self.vars['cache_path'], "citation_vim_cache") + def _get_searchkeys(self, context): + if len(context['args']) > 0: + self.vars['searchkeys'] = context['args'].pop(0) + self.__cache = False + else: + self.vars['searchkeys'] = [] + self._enable_cache() + def _gather_sub_sources(self): # Generate candidates and return it candidates = [] @@ -197,26 +211,13 @@ def _get_items(self, context): """ if self.__cache and self._is_cached(): return read_cache(self.__cache_file) - parser = self._get_parser() - items = parser.load() - if context['__reverse_order']: + items = self._parser.load() + if self.vars['reverse_order']: items.reverse() if self.__cache: write_cache(self.__cache_file, items) return items - def _get_parser(self): - """ - Returns a bibtex or zotero parser. - """ - if self.vars['mode'] == "bibtex": - from citation_vim.bibtex.parser import BibtexParser - parser = BibtexParser(self.vars) - elif self.vars['mode'] == "zotero": - from citation_vim.zotero.parser import ZoteroParser - parser = ZoteroParser(self.vars) - return parser - def _is_cached(self): """ Returns boolean based on cache and target file dates From 4c3455829629c83b3ae65fcdc02bb58c98e5cf00 Mon Sep 17 00:00:00 2001 From: DancingQuanta Date: Wed, 1 Aug 2018 07:58:29 +0100 Subject: [PATCH 4/6] Modify default action for source (field) and subsource (kind) --- rplugin/python3/denite/kind/citation.py | 17 +++++++++++++---- rplugin/python3/denite/source/citation.py | 5 +---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/rplugin/python3/denite/kind/citation.py b/rplugin/python3/denite/kind/citation.py index d857759..ca9a32d 100644 --- a/rplugin/python3/denite/kind/citation.py +++ b/rplugin/python3/denite/kind/citation.py @@ -9,13 +9,22 @@ def __init__(self, vim): super().__init__(vim) self.name = 'citation' - self.default_action = 'append' + self.default_action = 'default' self.persist_actions = ['preview'] - def action_execute(self, context): + def action_default(self, context): target = context['targets'][0] - self.vim.call('denite#util#execute_command', - target['action__command']) + source_context = target['source_context'] + + if source_context['__source'] is 'sub_sources': + self.action_field(context) + else: + self.action_append(context) + + def action_field(self, context): + target = context['targets'][0] + cmd = 'Denite citation:{}'.format(target['action__source_field']) + self.vim.command(cmd) def action_open(self, context): target = context['targets'][0] diff --git a/rplugin/python3/denite/source/citation.py b/rplugin/python3/denite/source/citation.py index 81dc0b4..aa23edd 100644 --- a/rplugin/python3/denite/source/citation.py +++ b/rplugin/python3/denite/source/citation.py @@ -134,13 +134,10 @@ def _gather_sub_sources(self): for sub_source in self.sub_sources: candidates.append({ "word": sub_source, - "action__command": self._set_sub_source(sub_source), + "action__source_field": sub_source, }) return candidates - def _set_sub_source(self, sub_source): - return ':Denite citation:{}'.format(sub_source) - def _gather_items(self, context): candidates = [] From 95b44751a64cd7815275cedc8129dbfea957f3cc Mon Sep 17 00:00:00 2001 From: DancingQuanta Date: Sun, 26 Aug 2018 15:18:00 +0100 Subject: [PATCH 5/6] Denite citation kind to reuse existing denite kinds' actions --- rplugin/python3/denite/kind/citation.py | 27 ++++++++++++------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/rplugin/python3/denite/kind/citation.py b/rplugin/python3/denite/kind/citation.py index ca9a32d..1549469 100644 --- a/rplugin/python3/denite/kind/citation.py +++ b/rplugin/python3/denite/kind/citation.py @@ -1,16 +1,19 @@ import os import re -from .word import Kind as Base +from .word import Kind as Word +from .command import Kind as Command +from .file import Kind as File +from .directory import Kind as Directory -class Kind(Base): +class Kind(Word, Command, File, Directory): def __init__(self, vim): super().__init__(vim) self.name = 'citation' self.default_action = 'default' - self.persist_actions = ['preview'] + # self.persist_actions = ['preview'] def action_default(self, context): target = context['targets'][0] @@ -19,7 +22,7 @@ def action_default(self, context): if source_context['__source'] is 'sub_sources': self.action_field(context) else: - self.action_append(context) + Word.action_append(self, context) def action_field(self, context): target = context['targets'][0] @@ -27,18 +30,14 @@ def action_field(self, context): self.vim.command(cmd) def action_open(self, context): - target = context['targets'][0] - path = target['action__path'] - - if (re.match('https?://', path) - or os.path.splitext(path)[1] == ".pdf"): + for target in context['targets']: + path = target['action__path'] self.vim.call('denite#util#open', path) def action_directory(self, context): - target = context['targets'][0] - path = target['action__path'] - self.vim.call('denite#util#open', path) + Directory.action_open(self, context) def action_preview(self, context): - pass - + target = context['targets'][0] + if 'action__command' in target: + Command.action_execute(self, context) From c89310e47276882895e0f825a3ba74259718403a Mon Sep 17 00:00:00 2001 From: DancingQuanta Date: Sun, 26 Aug 2018 22:25:22 +0100 Subject: [PATCH 6/6] Correct denite type of zotero_version --- rplugin/python3/denite/source/citation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rplugin/python3/denite/source/citation.py b/rplugin/python3/denite/source/citation.py index aa23edd..a7554e4 100644 --- a/rplugin/python3/denite/source/citation.py +++ b/rplugin/python3/denite/source/citation.py @@ -112,6 +112,7 @@ def _set_mode(self, context): self._get_searchkeys(context) # Get parser from citation_vim.zotero.parser import ZoteroParser + self.vars['zotero_version'] = int(self.vars['zotero_version']) self._parser = ZoteroParser(self.vars) else: raiseError("'mode' must be set to 'zotero' or 'bibtex'")