├── .gitignore ├── LICENSE ├── README.md ├── org2opml.py ├── sublime-text2-shortcuts.png ├── sublime_text2_shortcuts.opml └── sublime_text2_shortcuts.org /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | 21 | # Installer logs 22 | pip-log.txt 23 | 24 | # Unit test / coverage reports 25 | .coverage 26 | .tox 27 | nosetests.xml 28 | 29 | # Translations 30 | *.mo 31 | 32 | # Mr Developer 33 | .mr.developer.cfg 34 | .project 35 | .pydevproject 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Sreejith Kesavan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Org2OPML 2 | 3 | Converts Emacs Org-mode files to OPML format used by Mindnode and Freemind. 4 | 5 | ## Usage 6 | 7 | $ python org2opml.py 8 | Usage: org2opml.py 9 | 10 | $ python org2opml.py sublime_text2_shortcuts.org 11 | Exporting to OPML: sublime_text2_shortcuts.opml 12 | 13 | Here is a sample Mindmap created using OPML generated by Org2OPML:- 14 | 15 | ![Sublime Text 2 Cheatsheet](sublime-text2-shortcuts.png) 16 | -------------------------------------------------------------------------------- /org2opml.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # 3 | # Converts Emacs Org files to OPML so that Mindmaps can be generated 4 | # using both Freemind and Mindnode 5 | # 6 | # @author: Sreejith K 7 | # Created on 1 Aug 2013 8 | 9 | 10 | import re 11 | import os 12 | import sys 13 | import codecs 14 | import xml.etree.ElementTree as ET 15 | from xml.dom import minidom 16 | 17 | 18 | class Node(object): 19 | 20 | """Represents a Node. Also stores the references to 21 | all its children which are also Node instances. 22 | """ 23 | 24 | def __init__(self, level, text): 25 | self.level = level 26 | self.text = text 27 | self.children = [] 28 | 29 | def add_child(self, node): 30 | """Add a chld Node. 31 | """ 32 | self.children.append(node) 33 | 34 | 35 | class OrgParser(object): 36 | 37 | # Regular expressions for parsing the metadata 38 | NODE_RE = re.compile('(?P[*]+)\s+(?P.*)') 39 | TITLE_RE = re.compile('TITLE\s*:\s+(?P.*)') 40 | AUTHOR_RE = re.compile('AUTHOR\s*:\s+(?P<author>.*)') 41 | ROOT_RE = re.compile('ROOT\s*:\s+(?P<root>.*)') 42 | 43 | def __init__(self, org_file): 44 | self.org_file = org_file 45 | self.title = '' 46 | self.author = '' 47 | self.root_name = '' 48 | self.nodes = [] 49 | self.prev_node = None 50 | with codecs.open(org_file, 'r', encoding='UTF-8') as f: 51 | self.content = f.readlines() 52 | 53 | def parse(self): 54 | """Parse the content line by line 55 | """ 56 | for line in self.content: 57 | line = line.strip() 58 | if line.startswith('#+'): 59 | self.handle_meta(line[2:]) 60 | elif line.startswith('*'): 61 | self.add_node(line) 62 | 63 | def handle_meta(self, line): 64 | """Parse the metadata 65 | """ 66 | if line.startswith('TITLE'): 67 | match = self.TITLE_RE.search(line) 68 | if match: 69 | self.title = match.group('title') 70 | elif line.startswith('AUTHOR'): 71 | match = self.AUTHOR_RE.search(line) 72 | if match: 73 | self.author = match.group('author') 74 | elif line.startswith('ROOT'): 75 | match = self.ROOT_RE.search(line) 76 | if match: 77 | self.root_name = match.group('root') 78 | 79 | def add_node(self, line): 80 | """Create a node. Set the level and text. Assigns the parent Node 81 | """ 82 | match = self.NODE_RE.match(line) 83 | if match: 84 | level = match.group('level').count('*') 85 | text = match.group('text') 86 | newnode = Node(level=level, text=text) 87 | 88 | if level == 1: 89 | try: 90 | self.nodes[level - 1].append(newnode) 91 | except IndexError: 92 | self.nodes.append([newnode]) 93 | else: 94 | parent = self.nodes[level - 2][-1] 95 | parent.add_child(newnode) 96 | try: 97 | self.nodes[level - 1].append(newnode) 98 | except IndexError: 99 | self.nodes.append([newnode]) 100 | 101 | def to_opml(self): 102 | """Export the parsed Node information to OPML format 103 | """ 104 | skip_root = False 105 | # If there is only one root node. Make it as the root node in OPML 106 | if len(self.nodes) == 1: 107 | self.root_name = self.nodes[0].text 108 | skip_root = True 109 | 110 | root = ET.Element('opml', attrib={'version': '1.0'}) 111 | head = ET.SubElement(root, 'head') 112 | title = ET.SubElement(head, 'title') 113 | title.text = self.title 114 | author = ET.SubElement(head, 'ownername') 115 | author.text = self.author 116 | body = ET.SubElement(root, 'body') 117 | outline = ET.SubElement(body, 'outline', attrib={ 118 | 'text': self.root_name}) 119 | 120 | # Recursively iterate the Node and construct the XML ElementTree 121 | def iterate_children(node, ol): 122 | for child in node.children: 123 | element = ET.SubElement( 124 | ol, 'outline', attrib={'text': child.text}) 125 | iterate_children(child, element) 126 | 127 | # Iterate through the root nodes represented by single * 128 | for root_node in self.nodes[0]: 129 | if not skip_root: 130 | ol = ET.SubElement(outline, 'outline', attrib={ 131 | 'text': root_node.text}) 132 | iterate_children(root_node, ol) 133 | else: 134 | iterate_children(root_node, outline) 135 | 136 | opml_file = os.path.splitext(self.org_file)[0] + '.opml' 137 | 138 | # This code writes ugly XML 139 | # tree = ET.ElementTree(root) 140 | # tree.write(opml_file, encoding='UTF-8', xml_declaration=True) 141 | 142 | # Pretty print the XML into the file 143 | xmlstr = minidom.parseString(ET.tostring(root)).toprettyxml(encoding='UTF-8') 144 | with open(opml_file, 'w') as f: 145 | f.write(xmlstr) 146 | 147 | return opml_file 148 | 149 | 150 | if __name__ == '__main__': 151 | if len(sys.argv) < 2: 152 | print 'Usage: org2opml.py <input-org-file>' 153 | sys.exit(-2) 154 | 155 | p = OrgParser(sys.argv[1]) 156 | p.parse() 157 | print 'Exporting to OPML: %s' % p.to_opml() 158 | -------------------------------------------------------------------------------- /sublime-text2-shortcuts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semk/Org2OPML/11027b08415605f735a9e0a13493f4a92e3118d0/sublime-text2-shortcuts.png -------------------------------------------------------------------------------- /sublime_text2_shortcuts.opml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <opml version="1.0"> 3 | <head> 4 | <title>Sublime Text 2 Shortcuts 5 | Sreejith Kesavan 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /sublime_text2_shortcuts.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Sublime Text 2 Shortcuts 2 | #+AUTHOR: Sreejith Kesavan 3 | #+ROOT: Sublime Text 2 4 | 5 | 6 | * General 7 | ** Go to File (⌘T) 8 | ** Go to Project (⌘ + Control + P) 9 | ** Go to Methods (⌘R) 10 | ** Go to Line (Control + G) 11 | ** Toggle Side Bar (⌘KB) 12 | ** Command Prompt (⌘ + Shift + P) 13 | ** New Window (⌘ + Shift + N) 14 | 15 | * XML/HTML 16 | ** Select Content Into Tag (⌘ + Shift + A) 17 | ** Close Tag (⌘ + Option + .) 18 | 19 | * Find/Replace 20 | ** Find (⌘F) 21 | ** Replace (⌘ + Option + F) 22 | ** Find Next Occurrence of Current Word (Option + ⌘ + G) 23 | ** Find All Occurr­ences of Current Word (Control + ⌘ + G) 24 | ** Find in Files (⌘ + Shift + F) 25 | 26 | * Editing 27 | ** Select Line repea­tedly (⌘L) 28 | ** Select Word repea­tedly (⌘D) 29 | ** Select Content In Brackets (Control + Shift + M) 30 | ** Insert Line Before (⌘ + Shift + Enter) 31 | ** Insert Line After (⌘ + Enter) 32 | ** Delete Line (Control + Shift + K) 33 | ** Delete Line from Cursor to End of Line (⌘KK) 34 | ** Delete Line from Cursor to Start of Line (⌘K + Delete) 35 | ** Duplicate Line(s) (⌘ + Shift + D) 36 | ** Join Lines (⌘J) 37 | ** Upper Case (⌘KU) 38 | ** Lower Case (⌘KL) 39 | ** Comment (⌘/) 40 | ** Block Comment (⌘ + Option + /) 41 | ** Redo (⌘Y) 42 | ** Paste and Indent (⌘ + Shift + D) 43 | ** Auto Complete (Repeat for next selection) (Control + Space) 44 | ** Jump to Matching Bracket (Control + M) 45 | ** Undo Movement (Soft Undo) (⌘U) 46 | ** Redo Movement (Soft Redo) (⌘ + Shift + U) 47 | 48 | * Bookmarks 49 | ** Toggle Bookmark (⌘F2) 50 | ** Next Bookmark (F2) 51 | ** Previous Bookmark (Shift + F2) 52 | ** Clear Bookmarks (⌘ + Shift + F2) 53 | 54 | * Splits/Tabs 55 | ** Single Column (⌘ + Option + 1) 56 | ** Two Columns (⌘ + Option + 2) 57 | ** Grid (⌘ + Option + 5) 58 | ** Focus Group (Control + {1,2,3,4}) 59 | ** Move File to Group (Control + Shift + {1,2,3,4}) 60 | ** Select Tab (⌘ {1,2,3,4}) --------------------------------------------------------------------------------