Coverage for C:\Repos\leo-editor\leo\plugins\importers\leo_json.py: 15%
101 statements
« prev ^ index » next coverage.py v6.4, created at 2022-05-24 10:21 -0500
« prev ^ index » next coverage.py v6.4, created at 2022-05-24 10:21 -0500
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 # Keys are gnx's. Values are vnode_dicts.
25 self.gnx_dict = {}
26 self.tab_width = c.tab_width
27 # Keys are gnx's. Values are already-created vnodes.
28 self.vnodes_dict = {}
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