Coverage for C:\Repos\leo-editor\leo\plugins\importers\org.py: 86%

57 statements  

« prev     ^ index     » next       coverage.py v6.4, created at 2022-05-24 10:21 -0500

1#@+leo-ver=5-thin 

2#@+node:ekr.20140723122936.18146: * @file ../plugins/importers/org.py 

3"""The @auto importer for the org language.""" 

4import re 

5from leo.core import leoGlobals as g 

6from leo.plugins.importers import linescanner 

7Importer = linescanner.Importer 

8#@+others 

9#@+node:ekr.20140723122936.18072: ** class Org_Importer 

10class Org_Importer(Importer): 

11 """The importer for the org lanuage.""" 

12 

13 def __init__(self, importCommands, **kwargs): 

14 """Org_Importer.__init__""" 

15 super().__init__( 

16 importCommands, 

17 language='plain', # A reasonable @language 

18 state_class=None, 

19 strict=False, 

20 ) 

21 self.tc = self.load_nodetags() 

22 

23 #@+others 

24 #@+node:ekr.20171120084611.2: *3* org_i.clean_headline 

25 # Recognize :tag: syntax only at the end of headlines. 

26 # Use :tag1:tag2: to specify two tags, not :tag1: :tag2: 

27 tag_pattern = re.compile(r':([\w_@]+:)+\s*$') 

28 

29 def clean_headline(self, s, p=None): 

30 """ 

31 Return a cleaned up headline for p. 

32 Also parses org-mode tags. 

33 """ 

34 if p and self.tc: 

35 # Support for #578: org-mode tags. 

36 m = self.tag_pattern.search(s) 

37 if m: 

38 i = m.start() 

39 # head = s[:i].strip() 

40 tail = s[i + 1 : -1].strip() 

41 tags = tail.split(':') 

42 for tag in tags: 

43 self.tc.add_tag(p, tag) 

44 return s 

45 

46 #@+node:ekr.20161123194634.1: *3* org_i.gen_lines & helper 

47 # #1037: eat only one space. 

48 org_pattern = re.compile(r'^(\*+)\s(.*)$') 

49 

50 def gen_lines(self, lines, parent): 

51 """Node generator for org mode.""" 

52 self.vnode_info = { 

53 # Keys are vnodes, values are inner dicts. 

54 parent.v: { 

55 'lines': [], 

56 } 

57 } 

58 self.parents = [parent] 

59 for line in lines: 

60 m = self.org_pattern.match(line) 

61 if m: 

62 # Cut back the stack, then allocate a new node. 

63 level = len(m.group(1)) 

64 self.parents = self.parents[:level] 

65 self.find_parent( 

66 level=level, 

67 h=m.group(2)) 

68 else: 

69 p = self.parents[-1] 

70 self.add_line(p, line) 

71 #@+node:ekr.20161123194732.2: *4* org_i.find_parent 

72 def find_parent(self, level, h): 

73 """ 

74 Return the parent at the indicated level, allocating 

75 place-holder nodes as necessary. 

76 """ 

77 assert level >= 0 

78 n = level - len(self.parents) 

79 while level >= len(self.parents): 

80 headline = h if n == 0 else 'placeholder' 

81 # This works, but there is no way perfect import will pass the result. 

82 n -= 1 

83 child = self.create_child_node( 

84 parent=self.parents[-1], 

85 line=None, 

86 headline=headline, 

87 ) 

88 self.parents.append(child) 

89 return self.parents[level] 

90 #@+node:ekr.20190210091845.1: *4* org_i.create_child_node 

91 def create_child_node(self, parent, line, headline): 

92 """Create a child node of parent.""" 

93 child = parent.insertAsLastChild() 

94 self.vnode_info[child.v] = { 

95 'lines': [], 

96 } 

97 if line: 

98 self.add_line(child, line) 

99 assert isinstance(headline, str), repr(headline) 

100 # #1037: do rstrip, not strip. 

101 # #1087: do not strip at all! 

102 child.h = headline 

103 return child 

104 #@+node:ekr.20171120084611.5: *3* org_i.load_nodetags 

105 def load_nodetags(self): 

106 """ 

107 Load the nodetags.py plugin if necessary. 

108 Return c.theTagController. 

109 """ 

110 c = self.c 

111 if not getattr(c, 'theTagController', None): 

112 g.app.pluginsController.loadOnePlugin('nodetags.py', verbose=False) 

113 return getattr(c, 'theTagController', None) 

114 #@+node:ekr.20161126074103.1: *3* org_i.post_pass 

115 def post_pass(self, parent): 

116 """ 

117 Optional Stage 2 of the importer pipeline, consisting of zero or more 

118 substages. Each substage alters nodes in various ways. 

119 

120 Subclasses may freely override this method, **provided** that all 

121 substages use the API for setting body text. Changing p.b directly will 

122 cause asserts to fail later in i.finish(). 

123 """ 

124 self.clean_all_headlines(parent) 

125 #@-others 

126#@-others 

127importer_dict = { 

128 '@auto': ['@auto-org', '@auto-org-mode',], 

129 'func': Org_Importer.do_import(), 

130 'extensions': ['.org'], 

131} 

132#@@language python 

133#@@tabwidth -4 

134#@-leo