Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1#@+leo-ver=5-thin 

2#@+node:ekr.20160504080826.1: * @file ../plugins/importers/leo_json.py 

3"""The @auto importer for .json files.""" 

4# 

5# This module must **not** be named json, to avoid conflicts with the json standard library. 

6import json 

7from leo.core import leoGlobals as g 

8from leo.core import leoNodes 

9#@+others 

10#@+node:ekr.20160504080826.2: ** class JSON_Scanner 

11class JSON_Scanner: 

12 """A class to read .json files.""" 

13 # Not a subclass of the Importer class. 

14 #@+others 

15 #@+node:ekr.20160504080826.3: *3* json.__init__ 

16 def __init__(self, 

17 importCommands, 

18 language='json', 

19 alternate_language=None, 

20 ** kwargs 

21 ): 

22 """The ctor for the JSON_Scanner class.""" 

23 self.c = c = importCommands.c 

24 self.gnx_dict = {} 

25 # Keys are gnx's. Values are vnode_dicts. 

26 self.tab_width = c.tab_width 

27 self.vnodes_dict = {} 

28 # Keys are gnx's. Values are already-created vnodes. 

29 #@+node:ekr.20160504093537.1: *3* json.create_nodes 

30 def create_nodes(self, parent, parent_d): 

31 """Create the tree of nodes rooted in parent.""" 

32 d = self.gnx_dict 

33 for child_gnx in parent_d.get('children'): 

34 d2 = d.get(child_gnx) 

35 if child_gnx in self.vnodes_dict: 

36 # It's a clone. 

37 v = self.vnodes_dict.get(child_gnx) 

38 n = parent.numberOfChildren() 

39 child = leoNodes.Position(v) 

40 child._linkAsNthChild(parent, n) 

41 # Don't create children again. 

42 else: 

43 child = parent.insertAsLastChild() 

44 child.h = d2.get('h') or '<**no h**>' 

45 child.b = d2.get('b') or '' 

46 if d2.get('gnx'): 

47 child.v.findIndex = gnx = d2.get('gnx') 

48 self.vnodes_dict[gnx] = child.v 

49 if d2.get('ua'): 

50 child.u = d2.get('ua') 

51 self.create_nodes(child, d2) 

52 #@+node:ekr.20161015213011.1: *3* json.report 

53 def report(self, s): 

54 """Issue a message.""" 

55 g.es_print(s) 

56 #@+node:ekr.20160504092347.1: *3* json.run 

57 def run(self, s, parent, parse_body=False): 

58 """The common top-level code for all scanners.""" 

59 c = self.c 

60 ok = self.scan(s, parent) 

61 if ok: 

62 for p in parent.self_and_subtree(): 

63 p.clearDirty() 

64 # #1451: The caller should be responsible for this. 

65 # if changed: 

66 # c.setChanged() 

67 # else: 

68 # c.clearChanged() 

69 else: 

70 parent.setDirty() 

71 c.setChanged() 

72 return ok 

73 #@+node:ekr.20160504092347.2: *4* json.escapeFalseSectionReferences 

74 def escapeFalseSectionReferences(self, s): 

75 """ 

76 Probably a bad idea. Keep the apparent section references. 

77 The perfect-import write code no longer attempts to expand references 

78 when the perfectImportFlag is set. 

79 """ 

80 return s 

81 # result = [] 

82 # for line in g.splitLines(s): 

83 # r1 = line.find('<<') 

84 # r2 = line.find('>>') 

85 # if r1>=0 and r2>=0 and r1<r2: 

86 # result.append("@verbatim\n") 

87 # result.append(line) 

88 # else: 

89 # result.append(line) 

90 # return ''.join(result) 

91 #@+node:ekr.20160504092347.3: *4* json.checkBlanksAndTabs 

92 def checkBlanksAndTabs(self, s): 

93 """Check for intermixed blank & tabs.""" 

94 # Do a quick check for mixed leading tabs/blanks. 

95 blanks = tabs = 0 

96 for line in g.splitLines(s): 

97 lws = line[0 : g.skip_ws(line, 0)] 

98 blanks += lws.count(' ') 

99 tabs += lws.count('\t') 

100 ok = blanks == 0 or tabs == 0 

101 if not ok: 

102 self.report('intermixed blanks and tabs') 

103 return ok 

104 #@+node:ekr.20160504092347.4: *4* json.regularizeWhitespace 

105 def regularizeWhitespace(self, s): 

106 """Regularize leading whitespace in s: 

107 Convert tabs to blanks or vice versa depending on the @tabwidth in effect. 

108 This is only called for strict languages.""" 

109 changed = False 

110 lines = g.splitLines(s) 

111 result = [] 

112 tab_width = self.tab_width 

113 if tab_width < 0: # Convert tabs to blanks. 

114 for line in lines: 

115 i, w = g.skip_leading_ws_with_indent(line, 0, tab_width) 

116 s = g.computeLeadingWhitespace(w, -abs(tab_width)) + line[i:] # Use negative width. 

117 if s != line: 

118 changed = True 

119 result.append(s) 

120 elif tab_width > 0: # Convert blanks to tabs. 

121 for line in lines: 

122 s = g.optimizeLeadingWhitespace(line, abs(tab_width)) # Use positive width. 

123 if s != line: 

124 changed = True 

125 result.append(s) 

126 if changed: 

127 action = 'tabs converted to blanks' if self.tab_width < 0 else 'blanks converted to tabs' 

128 message = 'inconsistent leading whitespace. %s' % action 

129 self.report(message) 

130 return ''.join(result) 

131 #@+node:ekr.20160504082809.1: *3* json.scan 

132 def scan(self, s, parent): 

133 """Create an outline from a MindMap (.csv) file.""" 

134 # pylint: disable=no-member 

135 # pylint confuses this module with the stdlib json module 

136 c = self.c 

137 self.gnx_dict = {} 

138 try: 

139 d = json.loads(s) 

140 for d2 in d.get('nodes', []): 

141 gnx = d2.get('gnx') 

142 self.gnx_dict[gnx] = d2 

143 top_d = d.get('top') 

144 if top_d: 

145 # Don't set parent.h or parent.gnx or parent.v.u. 

146 parent.b = top_d.get('b') or '' 

147 self.create_nodes(parent, top_d) 

148 c.redraw() 

149 return bool(top_d) 

150 except Exception: 

151 # Fix #1098 

152 try: 

153 obj = json.loads(s) 

154 except Exception: 

155 g.error('Bad .json file: %s' % parent.h) 

156 g.es_exception() 

157 obj = s 

158 parent.b = g.objToString(obj) 

159 c.redraw() 

160 return True 

161 #@-others 

162#@-others 

163def do_import(c, s, parent): 

164 return JSON_Scanner(c.importCommands).run(s, parent) 

165importer_dict = { 

166 '@auto': ['@auto-json',], 

167 'func': do_import, 

168 'extensions': ['.json',], 

169} 

170#@@language python 

171#@@tabwidth -4 

172#@-leo