def nextSlide(self, event=None): c = self.c p = c.p if p == self.slide: p = self.slide.threadNext() oldSlide = self.slide else: oldSlide = None while p: h = p.h.strip() if self.ignored(p): p = p.threadNext() elif h.startswith('@slideshow'): self.select(p) return g.es('At %s of slide show' % 'end' if oldSlide else 'start') elif g.match_word(h, 0, '@ignore') or g.match_word( h, 0, '@noslide'): p = p.nodeAfterTree() else: return self.select(p) # elif h.startswith('@slide'): # return self.select(p) # else: p = p.threadNext() return g.es('At end of slide show' if self. slideShowRoot else 'Not in any slide show')
def scanForOptionDocParts(self, p, s): '''Return a dictionary containing all options from @rst-options doc parts in p. Multiple @rst-options doc parts are allowed: this code aggregates all options. ''' d = {} n = 0 lines = g.splitLines(s) while n < len(lines): line = lines[n] n += 1 if line.startswith('@'): i = g.skip_ws(line, 1) for kind in ('@rst-options', '@rst-option'): if g.match_word(line, i, kind): # Allow options on the same line. line = line[i + len(kind):] d.update(self.scanOption(p, line)) # Add options until the end of the doc part. while n < len(lines): line = lines[n] n += 1 found = False for stop in ('@c', '@code', '@'): if g.match_word(line, 0, stop): found = True break if found: break else: d.update(self.scanOption(p, line)) break return d
def write_root(self, root): '''Process all nodes in an @ad tree.''' fn = self.ad_filename(root) if not fn: g.es_print('Can not happen: not a @ad node: %r' % root.h) return None path, self.output_file = self.open_file(fn) if not self.output_file: return None # Write only the body of the root. self.write_body(root) # Write all nodes of the tree, except ignored nodes. self.level_offset = self.compute_level_offset(root) self.root_level = root.level() p = root.threadNext() # Returns a copy. after = root.nodeAfterTree() while p and p != after: h = p.h.rstrip() if g.match_word(h, 0, '@ignore-tree'): p.moveToNodeAfterTree() continue if g.match_word(h, 0, '@ignore-node'): p.moveToThreadNext() continue if not g.match_word(h, 0, '@no-head'): self.write_headline(p) self.write_body(p) p.moveToThreadNext() self.output_file.close() return path
def scanForOptionDocParts (self,p,s): '''Return a dictionary containing all options from @rst-options doc parts in p. Multiple @rst-options doc parts are allowed: this code aggregates all options. ''' d = {} ; n = 0 ; lines = g.splitLines(s) while n < len(lines): line = lines[n] ; n += 1 if line.startswith('@'): i = g.skip_ws(line,1) for kind in ('@rst-options','@rst-option'): if g.match_word(line,i,kind): # Allow options on the same line. line = line[i+len(kind):] d.update(self.scanOption(p,line)) # Add options until the end of the doc part. while n < len(lines): line = lines[n] ; n += 1 ; found = False for stop in ('@c','@code', '@'): if g.match_word(line,0,stop): found = True ; break if found: break else: d.update(self.scanOption(p,line)) break return d
def ignored (self,p): for p2 in p.self_and_parents(): if g.match_word(p2.h,0,'@ignore') or g.match_word(p2.h,0,'@noslide'): return True else: return False
def nextSlide (self,event=None): c = self.c ; p = c.p if p == self.slide: p = self.slide.threadNext() oldSlide = self.slide else: oldSlide = None while p: h = p.h.strip() if self.ignored(p): p = p.threadNext() elif h.startswith('@slideshow'): self.select(p) return g.es('At %s of slide show' % g.choose(oldSlide,'end','start')) elif g.match_word(h,0,'@ignore') or g.match_word(h,0,'@noslide'): p = p.nodeAfterTree() else: return self.select(p) # elif h.startswith('@slide'): # return self.select(p) # else: p = p.threadNext() else: return g.es(g.choose(self.slideShowRoot, 'At end of slide show', 'Not in any slide show'))
def on_icondclick(tag, keywords): c = keywords.get("c") p = keywords.get("p") h = p.h if g.match_word(h,0,"@expfolder"): if p.hasChildren(): result = g.app.gui.runAskYesNoDialog(c, "Reread?", "Reread contents of folder "+h[11:]+"?") if result == "no": return kids = [] for cp in p.subtree(): if cp.isDirty() and g.match_word(cp.h, 0, "@text"): kids.append(cp.copy()) if kids != []: result = g.app.gui.runAskYesNoDialog(c, "Reread?", "Save changed @text nodes?") if result == "yes": for kid in kids: savetextnode(c, kid) # delete children while p.firstChild(): p.firstChild().doDelete() #changed = c.isChanged() dir = h[11:] dirs = [] files = [] for file in os.listdir(dir): path = os.path.join(dir, file) if os.path.isdir(path): dirs.append(path) else: files.append(path) #g.es('dirs: '+str(dirs)) #g.es('files: '+str(files)) dirs.sort() files.sort() for f in files: pn = p.insertAsNthChild(0) if os.path.splitext(f)[1] in textexts: c.setHeadString(pn, "@text "+f) pn.clearDirty() else: c.setHeadString(pn, f) #pn.clearDirty() for d in dirs: pn = p.insertAsNthChild(0) c.setHeadString(pn, "@expfolder "+d) #pn.clearDirty() #p.clearDirty() #c.setChanged(changed) c.expandSubtree(p)
def ignored(self, p): for p2 in p.self_and_parents(): if g.match_word(p2.h, 0, '@ignore') or g.match_word( p2.h, 0, '@noslide'): return True else: return False
def match(self, fn, i, m, s): '''Handle the next match.''' trace = False self.n_matches += 1 indent = g.skip_ws(s, 0) # Update the context and enter data. if g.match_word(s, indent, 'def'): self.update_context(fn, indent, 'def', s) for i, name in enumerate(m.groups()): if name: aList = self.defs_d.get(name, []) def_tuple = self.context_stack[: -1], s aList.append(def_tuple) self.defs_d[name] = aList break elif g.match_word(s, indent, 'class'): self.update_context(fn, indent, 'class', s) for i, name in enumerate(m.groups()): if name: aList = self.classes_d.get(name, []) class_tuple = self.context_stack[: -1], s aList.append(class_tuple) self.classes_d[name] = aList elif s.find('return') > -1: context, name = self.context_names() j = s.find('#') if j > -1: s = s[: j] s = s.strip() if s: aList = self.returns_d.get(name, []) return_tuple = context, s aList.append(return_tuple) self.returns_d[name] = aList else: # A call. for i, name in enumerate(m.groups()): if name: context2, context1 = self.context_names() j = s.find('#') if j > -1: s = s[: j] s = s.strip().strip(',').strip() if s: aList = self.calls_d.get(name, []) call_tuple = context2, context1, s aList.append(call_tuple) self.calls_d[name] = aList break if trace: print('%4s %4s %3s %3s %s' % ( self.n_matches, i, len(self.context_stack), indent, s.rstrip()))
def next(self): '''Find the next screencast node and execute its script. Call m.quit if no more nodes remain.''' trace = False and not g.unitTesting m = self c = m.c k = c.k m.delete_widgets() # Restore k.state from m.k_state. if m.k_state.kind and m.k_state.kind != m.state_name: k.setState(kind=m.k_state.kind, n=m.k_state.n, handler=m.k_state.handler) while m.p: if trace: g.trace(m.p.h) h = m.p.h.replace('_', '').replace('-', '') if g.match_word(h, 0, '@ignorenode'): m.p.moveToThreadNext() elif g.match_word(h, 0, '@ignoretree') or g.match_word( h, 0, '@button'): m.p.moveToNodeAfterTree() elif m.p.b.strip(): p_next = m.p.threadNext() p_old = m.p.copy() if g.match_word(m.p.h, 0, '@text'): c.redraw( m.p ) # Selects the node, thereby showing the body text. else: m.exec_node(m.p) # Save k.state in m.k_state. if k.state: if k.state.kind == m.state_name: m.clear_state() else: m.set_state(k.state) # Re-enable m.state_handler. if not m.quit_flag: k.setState(m.state_name, 1, m.state_handler) # Change m.p only if the script has not already changed it. if not m.p or m.p == p_old: m.p = p_next break else: m.p.moveToThreadNext() else: # No non-empty node found. m.quit()
def onIconDoubleClick(tag, keywords): """ Read or write a bibtex file when the node is double-clicked. Write the @bibtex tree as bibtex file when the root node is double-clicked. If it has no child nodes, read bibtex file. """ p = keywords.get("p") or keywords.get("v") c = keywords.get("c") if not c or not p: return h = p.h.strip() if g.match_word(h, 0, "@bibtex"): fn = g.os_path_finalize_join(g.os_path_dirname(c.fileName() or ""), h[8:]) if p.hasChildren(): bibFile = open(fn, "w") writeTreeAsBibTex(bibFile, p, c) bibFile.close() g.es("wrote: %s" % fn) else: try: bibFile = open(fn, "r") except IOError: g.es("not found: %s" % fn, color="red") return g.es("reading: " + fn) readBibTexFileIntoTree(bibFile, c) bibFile.close()
def cloneToAtSpot(self, event=None): """ Create a clone of the selected node and move it to the last @spot node of the outline. Create the @spot node if necessary. """ c = self; u = c.undoer; p = c.p if not p: return # 2015/12/27: fix bug 220: do not allow clone-to-at-spot on @spot node. if p.h.startswith('@spot'): g.es("can not clone @spot node", color='red') return last_spot = None for p2 in c.all_positions(): if g.match_word(p2.h, 0, '@spot'): last_spot = p2.copy() if not last_spot: last = c.lastTopLevel() last_spot = last.insertAfter() last_spot.h = '@spot' undoData = c.undoer.beforeCloneNode(p) c.endEditing() # Capture any changes to the headline. clone = p.copy() clone._linkAsNthChild(last_spot, n=last_spot.numberOfChildren()) clone.setDirty() c.setChanged() if c.validateOutline(): u.afterCloneNode(clone, 'Clone Node', undoData) c.contractAllHeadlines() c.redraw() c.selectPosition(clone) else: clone.doDelete() c.setCurrentPosition(p)
def starts_block(self, i, lines, new_state, prev_state): '''True if the new state starts a block.''' def end(line): # Buggy: 'end' could appear in a string or comment. # However, this code is much better than before. i = line.find('end') return i if i > -1 and g.match_word(line, i, 'end') else -1 if prev_state.context: return False line = lines[i] m = self.function_pattern.match(line) if m and end(line) < m.start(): self.start_stack.append('function') return True # Don't create separate nodes for assigned functions, # but *do* push 'function2' on the start_stack for the later 'end' statement. m = self.function_pattern2.search(line) if m and end(line) < m.start(): self.start_stack.append('function2') return False # Not a function. Handle constructs ending with 'end'. line = line.strip() if end(line) == -1: for z in ('do', 'for', 'if', 'while',): if g.match_word(line, 0, z): self.start_stack.append(z) break return False
def ends_block(self, line, new_state, prev_state, stack): '''True if line ends a function or procedure.''' if prev_state.context: return False ls = line.lstrip() val = g.match_word(ls, 0, 'end') return val
def onSelect (tag,keywords): c = keywords.get('c') or keywords.get('new_c') if not c: return v = keywords.get("new_v") h = v.h if g.match_word(h,0,"@folder"): sync_node_to_folder(c,v,h[8:])
def onIconDoubleClick(tag,keywords): """Read or write a bibtex file when the node is double-clicked. Write the @bibtex tree as bibtex file when the root node is double-clicked. If it has no child nodes, read bibtex file.""" v = keywords.get("p") or keywords.get("v") c = keywords.get("c") h = v.h.strip() if g.match_word(h,0,"@bibtex"): fname = h[8:] if v.hasChildren(): #@+<< write bibtex file >> #@+node:timo.20050213160555.6: *3* << write bibtex file >> bibFile = file(fname,'w') writeTreeAsBibTex(bibFile, v, c) bibFile.close() g.es('written: '+str(fname)) #@-<< write bibtex file >> else: #@+<< read bibtex file >> #@+node:timo.20050214174623: *3* << read bibtex file >> g.es('reading: ' + str(fname)) try: bibFile = file(fname,'r') except IOError: g.es('IOError: file not found') return readBibTexFileIntoTree(bibFile, c) bibFile.close()
def onIconDoubleClick(tag,keywords): """ Read or write a bibtex file when the node is double-clicked. Write the @bibtex tree as bibtex file when the root node is double-clicked. If it has no child nodes, read bibtex file. """ p = keywords.get("p") or keywords.get("v") c = keywords.get("c") if not c or not p: return h = p.h.strip() if g.match_word(h,0,"@bibtex"): fn = g.os_path_finalize_join(g.os_path_dirname(c.fileName() or ''),h[8:]) if p.hasChildren(): bibFile = open(fn,'w') writeTreeAsBibTex(bibFile,p,c) bibFile.close() g.es('wrote: %s' % fn) else: try: bibFile = open(fn,'r') except IOError: g.es('not found: %s' % fn,color='red') return g.es('reading: ' + fn) readBibTexFileIntoTree(bibFile,c) bibFile.close()
def nextSlideShow(self, event=None): c = self.c self.findFirstSlideShow() if not self.firstSlideShow: g.es('No slide show found') return if not self.slideShowRoot: self.select(self.firstSlideShow) return p = c.p h = p.h.strip() if h.startswith('@slideshow'): p = p.threadNext() while p: h = p.h.strip() if self.ignored(p): p = p.threadNext() elif h.startswith('@slideshow'): self.select(p) return elif g.match_word(h, 0, '@ignore'): p = p.nodeAfterTree() else: p = p.threadNext() self.select(self.slideShowRoot) g.es('At start of last slide show')
def getButtonText(self,h): '''Returns the button text found in the given headline string''' tag = "@button" if g.match_word(h,0,tag): h = h[len(tag):].strip() for tag in ('@key','@args','@color',): i = h.find(tag) if i > -1: j = h.find('@',i+1) if i < j: h = h[:i] + h[j+1:] else: h = h[:i] h = h.strip() #i = h.find('@key') #if i > -1: # buttonText = h[:i].strip() else: buttonText = h fullButtonText = buttonText return buttonText
def scanHeadlineForOptions(self, p): '''Return a dictionary containing the options implied by p's headline.''' h = p.h.strip() if p == self.topNode: return {} # Don't mess with the root node. if g.match_word(h, 0, self.getOption('@rst-options')): return self.scanOptions(p, p.b) else: # Careful: can't use g.match_word because options may have '-' chars. i = g.skip_id(h, 0, chars='@-') word = h[0:i] for option, ivar, val in ( ('@rst-no-head', 'ignore_this_headline', True), ('@rst-head', 'show_this_headline', True), ('@rst-no-headlines', 'show_headlines', False), ('@rst-ignore', 'ignore_this_tree', True), ('@rst-ignore-node', 'ignore_this_node', True), ('@rst-ignore-tree', 'ignore_this_tree', True), # ('@rst-preformat','preformat_this_node',True), ): name = self.getOption(option) if name: d = {name: val} return d return {}
def create_uas(self, at_uas, root): """Recreate uA's from the @ua nodes in the @uas tree.""" # Create an *inner* gnx dict. # Keys are gnx's, values are positions *within* root's tree. d = {} for p in root.self_and_subtree(copy=False): d[p.v.gnx] = p.copy() # Recreate the uA's for the gnx's given by each @ua node. for at_ua in at_uas.children(): h, b = at_ua.h, at_ua.b gnx = h[4:].strip() if b and gnx and g.match_word(h, 0, '@ua'): p = d.get(gnx) if p: # Handle all recent variants of the node. lines = g.splitLines(b) if b.startswith('unl:') and len(lines) == 2: # pylint: disable=unbalanced-tuple-unpacking unl, ua = lines else: unl, ua = None, b if ua.startswith('ua:'): ua = ua[3:] if ua: ua = self.unpickle(ua) p.v.u = ua else: g.trace('Can not unpickle uA in', p.h, repr(unl), type(ua), ua[:40])
def finishCreate(self): '''Find or make the @chapters and @chapter trash nodes.''' trace = (False or g.trace_startup) and not g.unitTesting if trace: print('cc.finishCreate', g.callers()) cc = self c = cc.c if cc.findChaptersNode(): if hasattr(c.frame.iconBar, 'createChaptersIcon'): if not cc.tt: cc.tt = c.frame.iconBar.createChaptersIcon() # Create the main chapter cc.chaptersDict['main'] = chapter(c, cc, 'main') tag = '@chapter' for p in c.all_unique_positions(): h = p.h # if h.startswith(tag) and not h.startswith('@chapters'): if g.match_word(h, 0, tag): tabName = h[len(tag):].strip() if tabName and tabName not in ('main', ): if cc.chaptersDict.get(tabName): self.error('duplicate chapter name: %s' % tabName) else: cc.chaptersDict[tabName] = chapter(c, cc, tabName) # Always select the main chapter. # It can be alarming to open a small chapter in a large .leo file. cc.selectChapterByName('main', collapse=False)
def cloneNodeToChapterHelper(self, toChapterName): cc, c, p, u, undoType = self, self.c, self.c.p, self.c.undoer, 'Clone Node To Chapter' # Find the @chapter nodes and related chapter objects. fromChapter = cc.getSelectedChapter() if not fromChapter: return cc.note('no @chapter %s' % (fromChapter and fromChapter.name)) toChapter = cc.getChapter(toChapterName) if not toChapter: return cc.note('no @chapter %s' % (toChapter.name)) # Find the root of each chapter. toRoot = toChapter.findRootNode() assert toRoot if g.match_word(p.h, 0, '@chapter'): return cc.note('can not clone @chapter node') # Open the group undo. c.undoer.beforeChangeGroup(toRoot, undoType) # Do the clone. c.clone handles the inner undo. clone = c.clone() # Do the move. undoData2 = u.beforeMoveNode(clone) if toChapter.name == 'main': clone.moveAfter(toChapter.p) else: clone.moveToLastChildOf(toRoot) u.afterMoveNode(clone, 'Move Node', undoData2, dirtyVnodeList=[]) c.redraw(clone) c.setChanged(True) # Close the group undo. # Only the ancestors of the moved node get set dirty. dirtyVnodeList = clone.setAllAncestorAtFileNodesDirty() c.undoer.afterChangeGroup(clone, undoType, reportFlag=False, dirtyVnodeList=dirtyVnodeList) toChapter.p = clone.copy() toChapter.select() fromChapter.p = p.copy()
def cleanAtCleanTree(self, event): ''' Adjust whitespace in the nearest @clean tree, searching c.p and its ancestors. ''' c = self.c # Look for an @clean node. for p in c.p.self_and_parents(): if g.match_word(p.h, 0, '@clean') and p.h.rstrip().endswith('.py'): break else: g.es_print('no an @clean node found', p.h, color='blue') return # pylint: disable=undefined-loop-variable # p is certainly defined here. bunch = c.undoer.beforeChangeTree(p) n = 0 undoType = 'clean-@clean-tree' for p2 in p.subtree(): if self.cleanAtCleanNode(p2, undoType): n += 1 if n > 0: c.setChanged(True) c.undoer.afterChangeTree(p, undoType, bunch) g.es_print('%s node%s cleaned' % (n, g.plural(n)))
def create_uas(self,at_uas,root): '''Recreate uA's from the @ua nodes in the @uas tree.''' trace = False and not g.unitTesting # Create an *inner* gnx dict. # Keys are gnx's, values are positions *within* root's tree. d = {} for p in root.self_and_subtree(): d[p.v.gnx] = p.copy() # Recreate the uA's for the gnx's given by each @ua node. for at_ua in at_uas.children(): h,b = at_ua.h,at_ua.b gnx = h[4:].strip() if b and gnx and g.match_word(h,0,'@ua'): p = d.get(gnx) if p: # Handle all recent variants of the node. lines = g.splitLines(b) if b.startswith('unl:') and len(lines) == 2: # pylint: disable=unbalanced-tuple-unpacking unl,ua = lines else: unl,ua = None,b if ua.startswith('ua:'): ua = ua[3:] if ua: ua = self.unpickle(ua) if trace: g.trace('set',p.h,ua) p.v.u = ua else: g.trace('Can not unpickle uA in',p.h,type(ua),ua[:40]) elif trace: g.trace('no match for gnx:',repr(gnx),'unl:',unl) elif trace: g.trace('unexpected child of @uas node',at_ua)
def copyNodeToChapterHelper (self,toChapterName): cc,c,p,u,undoType = self,self.c,self.c.p,self.c.undoer,'Copy Node To Chapter' if g.match_word(p.h,0,'@chapter'): return cc.note('can not copy @chapter node') # Get the chapter objects. fromChapter = cc.getSelectedChapter() if not fromChapter: return cc.note('no @chapter %s' % (toChapterName)) toChapter = cc.getChapter(toChapterName) if not toChapter: return cc.note('no such chapter: %s' % toChapterName) toRoot = toChapter.findRootNode() assert toRoot # For undo, we treat the copy like a pasted (inserted) node. undoData = u.beforeInsertNode(toRoot,pasteAsClone=False,copiedBunchList=[]) s = c.fileCommands.putLeoOutline() p2 = c.fileCommands.getLeoOutline(s) p2.moveToLastChildOf(toRoot) c.redraw(p2) u.afterInsertNode(p2,undoType,undoData) c.setChanged(True) toChapter.p = p2.copy() toChapter.select() fromChapter.p = p.copy()
def getPathOld(p): # NOT USED, my version which does its own @path scanning p = p.copy() path = [] while p: h = p.h if g.match_word(h, 0, "@path"): # top of the tree path.insert(0, os.path.expanduser(h[6:].strip())) d = os.path.join(*path) return d elif h.startswith('@'): # some other directive, run away break elif isDirNode(p): # a directory path.insert(0, h.strip('/*')) elif not p.hasChildren(): # a leaf node, assume a file path.insert(0, h.strip('*')) p = p.parent() return None
def scanHeadlineForOptions (self,p): '''Return a dictionary containing the options implied by p's headline.''' h = p.h.strip() if p == self.topNode: return {} # Don't mess with the root node. if g.match_word(h,0,self.getOption('@rst-options')): return self.scanOptions(p,p.b) else: # Careful: can't use g.match_word because options may have '-' chars. # i = g.skip_id(h,0,chars='@-') # word = h[0:i] for option,ivar,val in ( ('@rst-no-head','ignore_this_headline',True), ('@rst-head' ,'show_this_headline',True), ('@rst-no-headlines','show_headlines',False), ('@rst-ignore','ignore_this_tree',True), ('@rst-ignore-node','ignore_this_node',True), ('@rst-ignore-tree','ignore_this_tree',True), # ('@rst-preformat','preformat_this_node',True), ): name = self.getOption(option) if name: d = { name: val } return d return {}
def starts_block(self, i, lines, new_state, prev_state): '''True if the new state starts a block.''' def end(line): # Buggy: 'end' could appear in a string or comment. # However, this code is much better than before. i = line.find('end') return i if i > -1 and g.match_word(line, i, 'end') else -1 if prev_state.context: return False line = lines[i] m = self.function_pattern.match(line) if m and end(line) < m.start(): self.start_stack.append('function') return True # Don't create separate nodes for assigned functions, # but *do* push 'function2' on the start_stack for the later 'end' statement. m = self.function_pattern2.search(line) if m and end(line) < m.start(): self.start_stack.append('function2') return False # Not a function. Handle constructs ending with 'end'. line = line.strip() if end(line) == -1: for z in ( 'do', 'for', 'if', 'while', ): if g.match_word(line, 0, z): self.start_stack.append(z) break return False
def cleanAtCleanTree(self, event): """ Adjust whitespace in the nearest @clean tree, searching c.p and its ancestors. """ c = self.c # Look for an @clean node. for p in c.p.self_and_parents(copy=False): if g.match_word(p.h, 0, '@clean') and p.h.rstrip().endswith('.py'): break else: g.es_print('no an @clean node found', p.h, color='blue') return # pylint: disable=undefined-loop-variable # p is certainly defined here. bunch = c.undoer.beforeChangeTree(p) n = 0 undoType = 'clean-@clean-tree' for p2 in p.subtree(): if self.cleanAtCleanNode(p2, undoType): n += 1 if n > 0: c.setChanged() c.undoer.afterChangeTree(p, undoType, bunch) g.es_print(f"{n} node{g.plural(n)} cleaned")
def getPathOld(p): # NOT USED, my version which does its own @path scanning p = p.copy() path = [] while p: h = p.h if g.match_word(h,0,"@path"): # top of the tree path.insert(0,os.path.expanduser(h[6:].strip())) d = os.path.join(*path) return d elif h.startswith('@'): # some other directive, run away break elif isDirNode(p): # a directory path.insert(0,h.strip('/*')) elif not p.hasChildren(): # a leaf node, assume a file path.insert(0,h.strip('*')) p = p.parent() return None
def finishCreate(self): '''Find or make the @chapters and @chapter trash nodes.''' trace = (False or g.trace_startup) and not g.unitTesting if trace: g.es_debug('(cc)') cc, c = self, self.c if cc.findChaptersNode(): if hasattr(c.frame.iconBar, 'createChaptersIcon'): if not cc.tt: cc.tt = c.frame.iconBar.createChaptersIcon() # Create the main chapter cc.chaptersDict['main'] = Chapter(c, cc, 'main') tag = '@chapter' for p in c.all_unique_positions(): h = p.h # if h.startswith(tag) and not h.startswith('@chapters'): if g.match_word(h, 0, tag): tabName = h[len(tag):].strip() if tabName and tabName not in ('main',): if cc.chaptersDict.get(tabName): self.error('duplicate chapter name: %s' % tabName) else: cc.chaptersDict[tabName] = Chapter(c, cc, tabName) # Fix bug: https://github.com/leo-editor/leo-editor/issues/31 cc.initing = False # Always select the main chapter. # It can be alarming to open a small chapter in a large .leo file. cc.selectChapterByName('main', collapse=False)
def is_root(p): """ A predicate returning True if p is an @<file> node that is not under @nopylint. """ for parent in p.self_and_parents(): if g.match_word(parent.h, 0, '@nopylint'): return False return p.isAnyAtFileNode() and p.h.strip().endswith('.py')
def on_select2 (tag,keywords): c = keywords.get("c") if g.match_word(c.p.h,0,"@read-only"): disable_body(c.frame.body) else: enable_body(c.frame.body)
def on_select2(tag, keywords): c = keywords.get("c") if g.match_word(c.p.h, 0, "@read-only"): disable_body(c.frame.body) else: enable_body(c.frame.body)
def FindRunChildren(p): global RunList for child in p.children(): if g.match_word(child.h,0,"@run"): RunList.append(child) FindRunChildren(child)
def ends_block(self, line, new_state, prev_state, stack): '''True if line ends a function or procedure.''' if prev_state.context: return False else: ls = line.lstrip() val = g.match_word(ls, 0, 'end') return val
def on_save(tag,keywords): c = keywords.get("c") if not c: return for p in c.all_positions(): h = p.h if g.match_word(h,0,"@text") and p.isDirty(): savetextnode(c, p) c.setBodyString(p, "")
def on_open(tag,keywords): c = keywords.get("c") if not c: return for p in c.all_positions(): h = p.h if g.match_word(h,0,"@text"): readtextnode(c, p) c.redraw()
def convert_legacy_outline(self, event=None): """ Convert @rst-preformat nodes and `@ @rst-options` doc parts. """ c = self.c for p in c.all_unique_positions(): if g.match_word(p.h, 0, '@rst-preformat'): self.preformat(p) self.convert_rst_options(p)
def moveNodeToChapterHelper (self,toChapterName): cc = self ; c = cc.c ; p = c.p ; u = c.undoer undoType = 'Move Node To Chapter' if g.match_word(p.h,0,'@chapter'): return cc.note('can not move @chapter node to another chapter') # Get the chapter objects. fromChapter = cc.getSelectedChapter() toChapter = cc.getChapter(toChapterName) if not fromChapter: return cc.note('no selected chapter') if not fromChapter: return cc.note('no chapter: %s' % (toChapterName)) # Get the roots fromRoot,toRoot = fromChapter.root(),toChapter.root() if not fromRoot: return cc.note('no @chapter %s' % (fromChapter.name)) if not toRoot: return cc.note('no @chapter %s' % (toChapter.name)) if toChapter.name == 'main': sel = (p.threadBack() != fromRoot and p.threadBack()) or p.nodeAfterTree() else: sel = p.threadBack() or p.nodeAfterTree() if sel: # Get 'before' undo data. inAtIgnoreRange = p.inAtIgnoreRange() undoData = u.beforeMoveNode(p) dirtyVnodeList = p.setAllAncestorAtFileNodesDirty() # Do the move. if toChapter.name == 'main': p.moveAfter(toChapter.p) else: p.moveToLastChildOf(toRoot) c.redraw(sel) c.setChanged(True) # Do the 'after' undo operation. if inAtIgnoreRange and not p.inAtIgnoreRange(): # The moved nodes have just become newly unignored. dirtyVnodeList2 = p.setDirty() # Mark descendent @thin nodes dirty. dirtyVnodeList.extend(dirtyVnodeList2) else: # No need to mark descendents dirty. dirtyVnodeList2 = p.setAllAncestorAtFileNodesDirty() dirtyVnodeList.extend(dirtyVnodeList2) u.afterMoveNode(p,undoType,undoData,dirtyVnodeList=dirtyVnodeList) if sel: toChapter.p = p.copy() toChapter.select() fromChapter.p = sel.copy() else: cc.note('can not move the last remaining node of a chapter.')
def next (self): '''Find the next screencast node and execute its script. Call m.quit if no more nodes remain.''' trace = False and not g.unitTesting m = self ; c = m.c ; k = c.k m.delete_widgets() # Restore k.state from m.k_state. if m.k_state.kind and m.k_state.kind != m.state_name: k.setState(kind=m.k_state.kind,n=m.k_state.n,handler=m.k_state.handler) while m.p: if trace: g.trace(m.p.h) h = m.p.h.replace('_','').replace('-','') if g.match_word(h,0,'@ignorenode'): m.p.moveToThreadNext() elif g.match_word(h,0,'@ignoretree') or g.match_word(h,0,'@button'): m.p.moveToNodeAfterTree() elif m.p.b.strip(): p_next = m.p.threadNext() p_old = m.p.copy() if g.match_word(m.p.h,0,'@text'): c.redraw(m.p) # Selects the node, thereby showing the body text. else: m.exec_node(m.p) # Save k.state in m.k_state. if k.state: if k.state.kind == m.state_name: m.clear_state() else: m.set_state(k.state) # Re-enable m.state_handler. if not m.quit_flag: k.setState(m.state_name,1,m.state_handler) # Change m.p only if the script has not already changed it. if not m.p or m.p == p_old: m.p = p_next break else: m.p.moveToThreadNext() else: # No non-empty node found. m.quit()
def on_headkey2(tag, keywords): c = keywords.get("c") p = keywords.get("p") h = p.h ch = keywords.get("ch") if ch in ('\n', '\r') and g.match_word(h, 0, "@read-only"): # on-the-fly update of @read-only directives changed = insert_read_only_node(c, p, h[11:]) c.setChanged(changed)
def on_icondclick(tag, keywords): c = keywords["c"] p = keywords["p"] h = p.h if g.match_word(h, 0, "@text"): if p.b != "": result = g.app.gui.runAskYesNoDialog(c, "Query", "Read from file " + h[6:] + "?") if result == "no": return readtextnode(c, p)
def on_icondclick(tag, keywords): c = keywords['c'] p = keywords['p'] h = p.h if g.match_word(h,0,"@text"): if p.b != "": result = g.app.gui.runAskYesNoDialog(c, "Query", "Read from file "+h[6:]+"?") if result == "no": return readtextnode(c, p)
def findFirstSlideShow(self): c = self.c for p in c.all_positions(): h = p.h.strip() if h.startswith('@slideshow'): self.firstSlideShow = p.copy() return p elif g.match_word(h, 0, '@ignore'): p = p.nodeAfterTree() self.firstSlideShow = None return None
def ends_block(self, line, new_state, prev_state, stack): '''True if line ends a function or procedure.''' trace = False and g.unitTesting if prev_state.context: if trace: g.trace('in context', repr(prev_state.context)) return False else: ls = line.lstrip() val = g.match_word(ls, 0, 'end') if trace and val: g.trace(' ', val, repr(line)) return val
def createAllButtons(self): '''Scan for @button, @rclick, @command, @plugin and @script nodes.''' c = self.c if self.scanned: return # Defensive. self.scanned = True # First, create standard buttons. if self.createRunScriptButton: self.createRunScriptIconButton() if self.createScriptButtonButton: self.createScriptButtonIconButton() if self.createDebugButton: self.createDebugIconButton() # Next, create common buttons and commands. self.createCommonButtons() self.createCommonCommands() # Last, scan for user-defined nodes. table = ( ('@button', self.handleAtButtonNode), ('@command', self.handleAtCommandNode), ('@plugin', self.handleAtPluginNode), ('@rclick', self.handleAtRclickNode), # Jake Peck. ('@script', self.handleAtScriptNode), ) p = c.rootPosition() while p: gnx = p.v.gnx if p.isAtIgnoreNode(): p.moveToNodeAfterTree() elif gnx in self.seen: # tag:#657 if g.match_word(p.h, 0, '@rclick'): self.handleAtRclickNode(p) p.moveToThreadNext() else: self.seen.add(gnx) for kind, func in table: if g.match_word(p.h, 0, kind): func(p) break p.moveToThreadNext()
def OnIconDoubleClick(tag,keywords): global RunNode,RunList,OwnIdleHook,ExitCode c=keywords.get('c') if not c or not c.exists: return p = c.p # g.trace(c.shortFileName()) h = p.h if g.match_word(h,0,"@run"): if RunNode or RunList: g.error("@run already running!") else: #@+<< handle double click in @run icon >> #@+node:ekr.20040910102554: *4* << handle double click in @run icon >> RunList = [] for p2 in p.self_and_subtree(): if g.match_word(p2.h,0,"@run"): # g.trace(p2.h) # 2009/10/30: don't use iter copy arg. RunList.append(p2.copy()) ExitCode = None OwnIdleHook = True g.enableIdleTimeHook(idleTimeDelay=100) #@-<< handle double click in @run icon >> elif g.match_word(h,0,"@in"): if RunNode: #@+<< handle double click in @in icon >> #@+node:ekr.20040910102554.1: *4* << handle double click in @in icon >> b = p.b try: In.write(b.encode(Encoding)+"\n") In.flush() g.es(b) except IOError as ioerr: g.error("@run IOError: "+str(ioerr))
def cleanAtCleanNode(self, p, undoType): '''Adjust whitespace in p, part of an @clean tree.''' s = p.b.strip() if not s or p.h.strip().startswith('<<'): return False ws = '\n\n' if g.match_word(s, 0, 'class') else '\n' s2 = ws + s + ws changed = s2 != p.b if changed: p.b = s2 p.setDirty() return changed