1# Gedit snippets plugin 2# Copyright (C) 2005-2006 Jesse van den Kieboom <jesse@icecrew.nl> 3# 4# This program is free software; you can redistribute it and/or modify 5# it under the terms of the GNU General Public License as published by 6# the Free Software Foundation; either version 2 of the License, or 7# (at your option) any later version. 8# 9# This program is distributed in the hope that it will be useful, 10# but WITHOUT ANY WARRANTY; without even the implied warranty of 11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12# GNU General Public License for more details. 13# 14# You should have received a copy of the GNU General Public License 15# along with this program; if not, write to the Free Software 16# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17 18import os 19import weakref 20import sys 21import re 22 23from gi.repository import Gdk, Gtk 24 25import xml.etree.ElementTree as et 26from . import helper 27 28class NamespacedId: 29 def __init__(self, namespace, id): 30 if not id: 31 self.id = None 32 else: 33 if namespace: 34 self.id = namespace + '-' 35 else: 36 self.id = 'global-' 37 38 self.id += id 39 40class SnippetData: 41 PROPS = {'tag': '', 'text': '', 'description': 'New snippet', 42 'accelerator': '', 'drop-targets': ''} 43 44 def __init__(self, node, library): 45 self.priv_id = node.attrib.get('id') 46 47 self.set_library(library) 48 self.valid = False 49 self.set_node(node) 50 51 def can_modify(self): 52 return (self.library and (isinstance(self.library(), SnippetsUserFile))) 53 54 def set_library(self, library): 55 if library: 56 self.library = weakref.ref(library) 57 else: 58 self.library = None 59 60 self.id = NamespacedId(self.language(), self.priv_id).id 61 62 def set_node(self, node): 63 if self.can_modify(): 64 self.node = node 65 else: 66 self.node = None 67 68 self.init_snippet_data(node) 69 70 def init_snippet_data(self, node): 71 if node is None: 72 return 73 74 self.override = node.attrib.get('override') 75 76 self.properties = {} 77 props = SnippetData.PROPS.copy() 78 79 # Store all properties present 80 for child in node: 81 if child.tag in props: 82 del props[child.tag] 83 84 # Normalize accelerator 85 if child.tag == 'accelerator' and child.text != None: 86 keyval, mod = Gtk.accelerator_parse(child.text) 87 88 if Gtk.accelerator_valid(keyval, mod): 89 child.text = Gtk.accelerator_name(keyval, mod) 90 else: 91 child.text = '' 92 93 if self.can_modify(): 94 self.properties[child.tag] = child 95 else: 96 self.properties[child.tag] = child.text or '' 97 98 # Create all the props that were not found so we stay consistent 99 for prop in props: 100 if self.can_modify(): 101 child = et.SubElement(node, prop) 102 103 child.text = props[prop] 104 self.properties[prop] = child 105 else: 106 self.properties[prop] = props[prop] 107 108 self.check_validation() 109 110 def check_validation(self): 111 if not self['tag'] and not self['accelerator'] and not self['drop-targets']: 112 return False 113 114 library = Library() 115 keyval, mod = Gtk.accelerator_parse(self['accelerator']) 116 117 self.valid = library.valid_tab_trigger(self['tag']) and \ 118 (not self['accelerator'] or library.valid_accelerator(keyval, mod)) 119 120 def _format_prop(self, prop, value): 121 if prop == 'drop-targets' and value != '': 122 return re.split('\\s*[,;]\\s*', value) 123 else: 124 return value 125 126 def __getitem__(self, prop): 127 if prop in self.properties: 128 if self.can_modify(): 129 return self._format_prop(prop, self.properties[prop].text or '') 130 else: 131 return self._format_prop(prop, self.properties[prop] or '') 132 133 return self._format_prop(prop, '') 134 135 def __setitem__(self, prop, value): 136 if not prop in self.properties: 137 return 138 139 if isinstance(value, list): 140 value = ','.join(value) 141 142 if not self.can_modify() and self.properties[prop] != value: 143 # ohoh, this is not can_modify, but it needs to be changed... 144 # make sure it is transfered to the changes file and set all the 145 # fields. 146 # This snippet data container will effectively become the container 147 # for the newly created node, but transparently to whoever uses 148 # it 149 self._override() 150 151 if self.can_modify() and self.properties[prop].text != value: 152 if self.library(): 153 self.library().tainted = True 154 155 oldvalue = self.properties[prop].text 156 self.properties[prop].text = value 157 158 if prop == 'tag' or prop == 'accelerator' or prop == 'drop-targets': 159 container = Library().container(self.language()) 160 container.prop_changed(self, prop, oldvalue) 161 162 self.check_validation() 163 164 def language(self): 165 if self.library and self.library(): 166 return self.library().language 167 else: 168 return None 169 170 def is_override(self): 171 return self.override and Library().overridden[self.override] 172 173 def to_xml(self): 174 return self._create_xml() 175 176 def _create_xml(self, parent=None, update=False, attrib={}): 177 # Create a new node 178 if parent != None: 179 element = et.SubElement(parent, 'snippet', attrib) 180 else: 181 element = et.Element('snippet') 182 183 # Create all the properties 184 for p in self.properties: 185 prop = et.SubElement(element, p) 186 prop.text = self[p] 187 188 if update: 189 self.properties[p] = prop 190 191 return element 192 193 def _override(self): 194 # Find the user file 195 target = Library().get_user_library(self.language()) 196 197 # Create a new node there with override 198 element = self._create_xml(target.root, True, {'override': self.id}) 199 200 # Create an override snippet data, feed it element so that it stores 201 # all the values and then set the node to None so that it only contains 202 # the values in .properties 203 override = SnippetData(element, self.library()) 204 override.set_node(None) 205 override.id = self.id 206 207 # Set our node to the new element 208 self.node = element 209 210 # Set the override to our id 211 self.override = self.id 212 self.id = None 213 214 # Set the new library 215 self.set_library(target) 216 217 # The library is tainted because we added this snippet 218 target.tainted = True 219 220 # Add the override 221 Library().overridden[self.override] = override 222 223 def revert(self, snippet): 224 userlib = self.library() 225 self.set_library(snippet.library()) 226 227 userlib.remove(self.node) 228 229 self.set_node(None) 230 231 # Copy the properties 232 self.properties = snippet.properties 233 234 # Set the id 235 self.id = snippet.id 236 237 # Reset the override flag 238 self.override = None 239 240class SnippetsTreeBuilder(et.TreeBuilder): 241 def __init__(self, start=None, end=None): 242 et.TreeBuilder.__init__(self) 243 self.set_start(start) 244 self.set_end(end) 245 246 def set_start(self, start): 247 self._start_cb = start 248 249 def set_end(self, end): 250 self._end_cb = end 251 252 def start(self, tag, attrs): 253 result = et.TreeBuilder.start(self, tag, attrs) 254 255 if self._start_cb: 256 self._start_cb(result) 257 258 return result 259 260 def end(self, tag): 261 result = et.TreeBuilder.end(self, tag) 262 263 if self._end_cb: 264 self._end_cb(result) 265 266 return result 267 268class LanguageContainer: 269 def __init__(self, language): 270 self.language = language 271 self.snippets = [] 272 self.snippets_by_prop = {'tag': {}, 'accelerator': {}, 'drop-targets': {}} 273 self.accel_group = Gtk.AccelGroup() 274 self._refs = 0 275 276 def _add_prop(self, snippet, prop, value=0): 277 if value == 0: 278 value = snippet[prop] 279 280 if not value or value == '': 281 return 282 283 helper.snippets_debug('Added ', prop ,' ', value, ' to ', str(self.language)) 284 285 if prop == 'accelerator': 286 keyval, mod = Gtk.accelerator_parse(value) 287 self.accel_group.connect(keyval, mod, 0, \ 288 Library().accelerator_activated) 289 290 snippets = self.snippets_by_prop[prop] 291 292 if not isinstance(value, list): 293 value = [value] 294 295 for val in value: 296 if val in snippets: 297 snippets[val].append(snippet) 298 else: 299 snippets[val] = [snippet] 300 301 def _remove_prop(self, snippet, prop, value=0): 302 if value == 0: 303 value = snippet[prop] 304 305 if not value or value == '': 306 return 307 308 helper.snippets_debug('Removed ', prop, ' ', value, ' from ', str(self.language)) 309 310 if prop == 'accelerator': 311 keyval, mod = Gtk.accelerator_parse(value) 312 self.accel_group.disconnect_key(keyval, mod) 313 314 snippets = self.snippets_by_prop[prop] 315 316 if not isinstance(value, list): 317 value = [value] 318 319 for val in value: 320 try: 321 snippets[val].remove(snippet) 322 except: 323 True 324 325 def append(self, snippet): 326 self.snippets.append(snippet) 327 328 self._add_prop(snippet, 'tag') 329 self._add_prop(snippet, 'accelerator') 330 self._add_prop(snippet, 'drop-targets') 331 332 return snippet 333 334 def remove(self, snippet): 335 try: 336 self.snippets.remove(snippet) 337 except: 338 True 339 340 self._remove_prop(snippet, 'tag') 341 self._remove_prop(snippet, 'accelerator') 342 self._remove_prop(snippet, 'drop-targets') 343 344 def prop_changed(self, snippet, prop, oldvalue): 345 helper.snippets_debug('PROP CHANGED (', prop, ')', oldvalue) 346 347 self._remove_prop(snippet, prop, oldvalue) 348 self._add_prop(snippet, prop) 349 350 def from_prop(self, prop, value): 351 snippets = self.snippets_by_prop[prop] 352 353 if prop == 'drop-targets': 354 s = [] 355 356 # FIXME: change this to use 357 # gnomevfs.mime_type_get_equivalence when it comes 358 # available 359 for key, val in snippets.items(): 360 if not value.startswith(key): 361 continue 362 363 for snippet in snippets[key]: 364 if not snippet in s: 365 s.append(snippet) 366 367 return s 368 else: 369 if value in snippets: 370 return snippets[value] 371 else: 372 return [] 373 374 def ref(self): 375 self._refs += 1 376 377 return True 378 379 def unref(self): 380 if self._refs > 0: 381 self._refs -= 1 382 383 return self._refs != 0 384 385class SnippetsSystemFile: 386 def __init__(self, path=None): 387 self.path = path 388 self.loaded = False 389 self.language = None 390 self.ok = True 391 self.need_id = True 392 393 def load_error(self, message): 394 sys.stderr.write("An error occurred loading " + self.path + ":\n") 395 sys.stderr.write(message + "\nSnippets in this file will not be " \ 396 "available, please correct or remove the file.\n") 397 398 def _add_snippet(self, element): 399 if not self.need_id or element.attrib.get('id'): 400 self.loading_elements.append(element) 401 402 def set_language(self, element): 403 self.language = element.attrib.get('language') 404 405 if self.language: 406 self.language = self.language.lower() 407 408 def _set_root(self, element): 409 self.set_language(element) 410 411 def _preprocess_element(self, element): 412 if not self.loaded: 413 if not element.tag == "snippets": 414 self.load_error("Root element should be `snippets' instead " \ 415 "of `%s'" % element.tag) 416 return False 417 else: 418 self._set_root(element) 419 self.loaded = True 420 elif element.tag != 'snippet' and not self.insnippet: 421 self.load_error("Element should be `snippet' instead of `%s'" \ 422 % element.tag) 423 return False 424 else: 425 self.insnippet = True 426 427 return True 428 429 def _process_element(self, element): 430 if element.tag == 'snippet': 431 self._add_snippet(element) 432 self.insnippet = False 433 434 return True 435 436 def ensure(self): 437 if not self.ok or self.loaded: 438 return 439 440 self.load() 441 442 def parse_xml(self, readsize=16384): 443 if not self.path: 444 return 445 446 elements = [] 447 448 builder = SnippetsTreeBuilder( \ 449 lambda node: elements.append((node, True)), \ 450 lambda node: elements.append((node, False))) 451 452 parser = et.XMLParser(target=builder) 453 self.insnippet = False 454 455 try: 456 f = open(self.path, "r", encoding='utf-8') 457 except IOError: 458 self.ok = False 459 return 460 461 while True: 462 try: 463 data = f.read(readsize) 464 except IOError: 465 self.ok = False 466 break 467 468 if not data: 469 break 470 471 try: 472 parser.feed(data) 473 except Exception: 474 self.ok = False 475 break 476 477 for element in elements: 478 yield element 479 480 del elements[:] 481 482 f.close() 483 484 def load(self): 485 if not self.ok: 486 return 487 488 helper.snippets_debug("Loading library (" + str(self.language) + "): " + \ 489 self.path) 490 491 self.loaded = False 492 self.ok = False 493 self.loading_elements = [] 494 495 for element in self.parse_xml(): 496 if element[1]: 497 if not self._preprocess_element(element[0]): 498 del self.loading_elements[:] 499 return 500 else: 501 if not self._process_element(element[0]): 502 del self.loading_elements[:] 503 return 504 505 for element in self.loading_elements: 506 Library().add_snippet(self, element) 507 508 del self.loading_elements[:] 509 self.ok = True 510 511 # This function will get the language for a file by just inspecting the 512 # root element of the file. This is provided so that a cache can be built 513 # for which file contains which language. 514 # It returns the name of the language 515 def ensure_language(self): 516 if not self.loaded: 517 self.ok = False 518 519 for element in self.parse_xml(256): 520 if element[1]: 521 if element[0].tag == 'snippets': 522 self.set_language(element[0]) 523 self.ok = True 524 525 break 526 527 def unload(self): 528 helper.snippets_debug("Unloading library (" + str(self.language) + "): " + \ 529 self.path) 530 self.language = None 531 self.loaded = False 532 self.ok = True 533 534class SnippetsUserFile(SnippetsSystemFile): 535 def __init__(self, path=None): 536 SnippetsSystemFile.__init__(self, path) 537 self.tainted = False 538 self.need_id = False 539 540 def _set_root(self, element): 541 SnippetsSystemFile._set_root(self, element) 542 self.root = element 543 544 def add_prop(self, node, tag, data): 545 if data[tag]: 546 prop = et.SubElement(node, tag) 547 prop.text = data[tag] 548 549 return prop 550 else: 551 return None 552 553 def new_snippet(self, properties=None): 554 if (not self.ok) or self.root is None: 555 return None 556 557 element = et.SubElement(self.root, 'snippet') 558 559 if properties: 560 for prop in properties: 561 sub = et.SubElement(element, prop) 562 sub.text = properties[prop] 563 564 self.tainted = True 565 566 return Library().add_snippet(self, element) 567 568 def set_language(self, element): 569 SnippetsSystemFile.set_language(self, element) 570 571 filename = os.path.basename(self.path).lower() 572 573 if not self.language and filename == "global.xml": 574 self.modifier = True 575 elif self.language and filename == self.language + ".xml": 576 self.modifier = True 577 else: 578 self.modifier = False 579 580 def create_root(self, language): 581 if self.loaded: 582 helper.snippets_debug('Not creating root, already loaded') 583 return 584 585 if language: 586 root = et.Element('snippets', {'language': language}) 587 self.path = os.path.join(Library().userdir, language.lower() + '.xml') 588 else: 589 root = et.Element('snippets') 590 self.path = os.path.join(Library().userdir, 'global.xml') 591 592 self._set_root(root) 593 self.loaded = True 594 self.ok = True 595 self.tainted = True 596 self.save() 597 598 def remove(self, element): 599 try: 600 self.root.remove(element) 601 self.tainted = True 602 except: 603 return 604 605 try: 606 self.root[0] 607 except: 608 # No more elements, this library is useless now 609 Library().remove_library(self) 610 611 def save(self): 612 if not self.ok or self.root is None or not self.tainted: 613 return 614 615 path = os.path.dirname(self.path) 616 617 try: 618 if not os.path.isdir(path): 619 os.makedirs(path, 0o755) 620 except OSError: 621 # TODO: this is bad... 622 sys.stderr.write("Error in making dirs\n") 623 624 try: 625 helper.write_xml(self.root, self.path, ('text', 'accelerator')) 626 self.tainted = False 627 except IOError: 628 # Couldn't save, what to do 629 sys.stderr.write("Could not save user snippets file to " + \ 630 self.path + "\n") 631 632 def unload(self): 633 SnippetsSystemFile.unload(self) 634 self.root = None 635 636class Singleton(object): 637 _instance = None 638 639 def __new__(cls, *args, **kwargs): 640 if not cls._instance: 641 cls._instance = super(Singleton, cls).__new__( 642 cls, *args, **kwargs) 643 cls._instance.__init_once__() 644 645 return cls._instance 646 647class Library(Singleton): 648 def __init_once__(self): 649 self._accelerator_activated_cb = [] 650 self.loaded = False 651 self.check_buffer = Gtk.TextBuffer() 652 653 def set_dirs(self, userdir, systemdirs): 654 self.userdir = userdir 655 self.systemdirs = systemdirs 656 657 self.libraries = {} 658 self.containers = {} 659 self.overridden = {} 660 self.loaded_ids = [] 661 662 self.loaded = False 663 664 def add_accelerator_callback(self, cb): 665 self._accelerator_activated_cb.append(cb) 666 667 def remove_accelerator_callback(self, cb): 668 self._accelerator_activated_cb.remove(cb) 669 670 def accelerator_activated(self, group, obj, keyval, mod): 671 ret = False 672 673 for cb in self._accelerator_activated_cb: 674 ret = cb(group, obj, keyval, mod) 675 676 if ret: 677 break 678 679 return ret 680 681 def add_snippet(self, library, element): 682 container = self.container(library.language) 683 overrided = self.overrided(library, element) 684 685 if overrided: 686 overrided.set_library(library) 687 helper.snippets_debug('Snippet is overriden: ' + overrided['description']) 688 return None 689 690 snippet = SnippetData(element, library) 691 692 if snippet.id in self.loaded_ids: 693 helper.snippets_debug('Not added snippet ' + str(library.language) + \ 694 '::' + snippet['description'] + ' (duplicate)') 695 return None 696 697 snippet = container.append(snippet) 698 helper.snippets_debug('Added snippet ' + str(library.language) + '::' + \ 699 snippet['description']) 700 701 if snippet and snippet.override: 702 self.add_override(snippet) 703 704 if snippet.id: 705 self.loaded_ids.append(snippet.id) 706 707 return snippet 708 709 def container(self, language): 710 language = self.normalize_language(language) 711 712 if not language in self.containers: 713 self.containers[language] = LanguageContainer(language) 714 715 return self.containers[language] 716 717 def get_user_library(self, language): 718 target = None 719 720 if language in self.libraries: 721 for library in self.libraries[language]: 722 if isinstance(library, SnippetsUserFile) and library.modifier: 723 target = library 724 elif not isinstance(library, SnippetsUserFile): 725 break 726 727 if not target: 728 # Create a new user file then 729 helper.snippets_debug('Creating a new user file for language ' + \ 730 str(language)) 731 target = SnippetsUserFile() 732 target.create_root(language) 733 self.add_library(target) 734 735 return target 736 737 def new_snippet(self, language, properties=None): 738 language = self.normalize_language(language) 739 library = self.get_user_library(language) 740 741 return library.new_snippet(properties) 742 743 def revert_snippet(self, snippet): 744 # This will revert the snippet to the one it overrides 745 if not snippet.can_modify() or not snippet.override in self.overridden: 746 # It can't be reverted, shouldn't happen, but oh.. 747 return 748 749 # The snippet in self.overriden only contains the property contents and 750 # the library it belongs to 751 revertto = self.overridden[snippet.override] 752 del self.overridden[snippet.override] 753 754 if revertto: 755 snippet.revert(revertto) 756 757 if revertto.id: 758 self.loaded_ids.append(revertto.id) 759 760 def remove_snippet(self, snippet): 761 if not snippet.can_modify() or snippet.is_override(): 762 return 763 764 # Remove from the library 765 userlib = snippet.library() 766 userlib.remove(snippet.node) 767 768 # Remove from the container 769 container = self.containers[userlib.language] 770 container.remove(snippet) 771 772 def overrided(self, library, element): 773 id = NamespacedId(library.language, element.attrib.get('id')).id 774 775 if id in self.overridden: 776 snippet = SnippetData(element, None) 777 snippet.set_node(None) 778 779 self.overridden[id] = snippet 780 return snippet 781 else: 782 return None 783 784 def add_override(self, snippet): 785 helper.snippets_debug('Add override:', snippet.override) 786 if not snippet.override in self.overridden: 787 self.overridden[snippet.override] = None 788 789 def add_library(self, library): 790 library.ensure_language() 791 792 if not library.ok: 793 helper.snippets_debug('Library in wrong format, ignoring') 794 return False 795 796 helper.snippets_debug('Adding library (' + str(library.language) + '): ' + \ 797 library.path) 798 799 if library.language in self.libraries: 800 # Make sure all the user files are before the system files 801 if isinstance(library, SnippetsUserFile): 802 self.libraries[library.language].insert(0, library) 803 else: 804 self.libraries[library.language].append(library) 805 else: 806 self.libraries[library.language] = [library] 807 808 return True 809 810 def remove_library(self, library): 811 if not library.ok: 812 return 813 814 if library.path and os.path.isfile(library.path): 815 os.unlink(library.path) 816 817 try: 818 self.libraries[library.language].remove(library) 819 except KeyError: 820 True 821 822 container = self.containers[library.language] 823 824 for snippet in list(container.snippets): 825 if snippet.library() == library: 826 container.remove(snippet) 827 828 def add_user_library(self, path): 829 library = SnippetsUserFile(path) 830 return self.add_library(library) 831 832 def add_system_library(self, path): 833 library = SnippetsSystemFile(path) 834 return self.add_library(library) 835 836 def find_libraries(self, path, searched, addcb): 837 helper.snippets_debug("Finding in: " + path) 838 839 if not os.path.isdir(path): 840 return searched 841 842 files = os.listdir(path) 843 searched.append(path) 844 845 for f in files: 846 f = os.path.realpath(os.path.join(path, f)) 847 848 # Determine what language this file provides snippets for 849 if os.path.isfile(f): 850 addcb(f) 851 852 return searched 853 854 def normalize_language(self, language): 855 if language: 856 return language.lower() 857 858 return language 859 860 def remove_container(self, language): 861 for snippet in self.containers[language].snippets: 862 if snippet.id in self.loaded_ids: 863 self.loaded_ids.remove(snippet.id) 864 865 if snippet.override in self.overridden: 866 del self.overridden[snippet.override] 867 868 del self.containers[language] 869 870 def get_accel_group(self, language): 871 language = self.normalize_language(language) 872 container = self.container(language) 873 874 self.ensure(language) 875 return container.accel_group 876 877 def save(self, language): 878 language = self.normalize_language(language) 879 880 if language in self.libraries: 881 for library in self.libraries[language]: 882 if isinstance(library, SnippetsUserFile): 883 library.save() 884 else: 885 break 886 887 def ref(self, language): 888 language = self.normalize_language(language) 889 890 helper.snippets_debug('Ref:', language) 891 self.container(language).ref() 892 893 def unref(self, language): 894 language = self.normalize_language(language) 895 896 helper.snippets_debug('Unref:', language) 897 898 if language in self.containers: 899 if not self.containers[language].unref() and \ 900 language in self.libraries: 901 902 for library in self.libraries[language]: 903 library.unload() 904 905 self.remove_container(language) 906 907 def ensure(self, language): 908 self.ensure_files() 909 language = self.normalize_language(language) 910 911 # Ensure language as well as the global snippets (None) 912 for lang in (None, language): 913 if lang in self.libraries: 914 # Ensure the container exists 915 self.container(lang) 916 917 for library in self.libraries[lang]: 918 library.ensure() 919 920 def ensure_files(self): 921 if self.loaded: 922 return 923 924 searched = [] 925 searched = self.find_libraries(self.userdir, searched, \ 926 self.add_user_library) 927 928 for d in self.systemdirs: 929 searched = self.find_libraries(d, searched, \ 930 self.add_system_library) 931 932 self.loaded = True 933 934 def valid_accelerator(self, keyval, mod): 935 mod &= Gtk.accelerator_get_default_mod_mask() 936 937 return (mod and (Gdk.keyval_to_unicode(keyval) or \ 938 keyval in range(Gdk.KEY_F1, Gdk.KEY_F12 + 1))) 939 940 def valid_tab_trigger(self, trigger): 941 if not trigger: 942 return True 943 944 return helper.is_tab_trigger(trigger) 945 946 # Snippet getters 947 # =============== 948 def _from_prop(self, prop, value, language=None): 949 self.ensure_files() 950 951 result = [] 952 language = self.normalize_language(language) 953 954 if not language in self.containers: 955 return [] 956 957 self.ensure(language) 958 result = self.containers[language].from_prop(prop, value) 959 960 if len(result) == 0 and language and None in self.containers: 961 result = self.containers[None].from_prop(prop, value) 962 963 return result 964 965 # Get snippets for a given language 966 def get_snippets(self, language=None): 967 self.ensure_files() 968 language = self.normalize_language(language) 969 970 if not language in self.libraries: 971 return [] 972 973 self.ensure(language) 974 975 return list(self.containers[language].snippets) 976 977 # Get snippets for a given accelerator 978 def from_accelerator(self, accelerator, language=None): 979 return self._from_prop('accelerator', accelerator, language) 980 981 # Get snippets for a given tag 982 def from_tag(self, tag, language=None): 983 return self._from_prop('tag', tag, language) 984 985 # Get snippets for a given drop target 986 def from_drop_target(self, drop_target, language=None): 987 return self._from_prop('drop-targets', drop_target, language) 988 989# ex:ts=4:et: 990