def comment_leo_lines(p): '''Replace lines with Leonine syntax with special comments.''' # Choose the comment string so it appears nowhere in s. s0 = p.b n = 5 while s0.find('#' + ('!' * n)) > -1: n += 1 comment = '#' + ('!' * n) # Create a dict of directives. d = {} for z in g.globalDirectiveList: d[z] = True # Convert all Leonine lines to special comments. i, lines, result = 0, g.splitLines(s0), [] while i < len(lines): progress = i s = lines[i] # Comment out any containing a section reference. j = s.find('<<') k = s.find('>>') if j > -1 else -1 if -1 < j < k: result.append(comment + s) # Generate a properly-indented pass line. j2 = g.skip_ws(s, 0) result.append('%spass\n' % (' ' * j2)) elif s.lstrip().startswith('@'): # Comment out all other Leonine constructs. if starts_doc_part(s): # Comment the entire doc part, until @c or @code. result.append(comment + s) i += 1 while i < len(lines): s = lines[i] result.append(comment + s) i += 1 if ends_doc_part(s): break else: j = g.skip_ws(s, 0) assert s[j] == '@' j += 1 k = g.skip_id(s, j, chars='-') if k > j: word = s[j:k] if word == 'others': # Remember the original @others line. result.append(comment + s) # Generate a properly-indented pass line. result.append('%spass\n' % (' ' * (j - 1))) else: # Comment only Leo directives, not decorators. result.append(comment + s if word in d else s) else: result.append(s) else: # A plain line. result.append(s) if i == progress: i += 1 return comment, ''.join(result)
def comment_leo_lines(p): '''Replace lines with Leonine syntax with special comments.''' # Choose the comment string so it appears nowhere in s. s0 = p.b n = 5 while s0.find('#' + ('!' * n)) > -1: n += 1 comment = '#' + ('!' * n) # Create a dict of directives. d = {} for z in g.globalDirectiveList: d[z] = True # Convert all Leonine lines to special comments. i, lines, result = 0, g.splitLines(s0), [] while i < len(lines): progress = i s = lines[i] # Comment out any containing a section reference. j = s.find('<<') k = s.find('>>') if j > -1 else -1 if -1 < j < k: result.append(comment + s) # Generate a properly-indented pass line. j2 = g.skip_ws(s, 0) result.append('%spass\n' % (' ' * j2)) elif s.lstrip().startswith('@'): # Comment out all other Leonine constructs. if starts_doc_part(s): # Comment the entire doc part, until @c or @code. result.append(comment + s) i += 1 while i < len(lines): s = lines[i] result.append(comment + s) i += 1 if ends_doc_part(s): break else: j = g.skip_ws(s, 0) assert s[j] == '@' j += 1 k = g.skip_id(s, j, chars='-') if k > j: word = s[j: k] if word == 'others': # Remember the original @others line. result.append(comment + s) # Generate a properly-indented pass line. result.append('%spass\n' % (' ' * (j - 1))) else: # Comment only Leo directives, not decorators. result.append(comment + s if word in d else s) else: result.append(s) else: # A plain line. result.append(s) if i == progress: i += 1 return comment, ''.join(result)
def deleteComments(self, event=None): #@+<< deleteComments docstring >> #@+node:ekr.20171123135625.37: *3* << deleteComments docstring >> #@@pagewidth 50 """ Removes one level of comment delimiters from all selected lines. The applicable @language directive determines the comment delimiters to be removed. Removes single-line comments if possible; removes block comments for languages like html that lack single-line comments. *See also*: add-comments. """ #@-<< deleteComments docstring >> c = self p = c.p head, lines, tail, oldSel, oldYview = self.getBodyLines() result = [] if not lines: g.warning('no text selected') return # The default language in effect at p. language = c.frame.body.colorizer.scanLanguageDirectives(p) if c.hasAmbiguousLanguage(p): language = c.getLanguageAtCursor(p, language) d1, d2, d3 = g.set_delims_from_language(language) if d1: # Remove the single-line comment delim in front of each line d1b = d1 + ' ' n1, n1b = len(d1), len(d1b) for s in lines: i = g.skip_ws(s, 0) if g.match(s, i, d1b): result.append(s[:i] + s[i + n1b :]) elif g.match(s, i, d1): result.append(s[:i] + s[i + n1 :]) else: result.append(s) else: # Remove the block comment delimiters from each line. n2, n3 = len(d2), len(d3) for s in lines: i = g.skip_ws(s, 0) j = s.find(d3, i + n2) if g.match(s, i, d2) and j > -1: first = i + n2 if g.match(s, first, ' '): first += 1 last = j if g.match(s, last - 1, ' '): last -= 1 result.append(s[:i] + s[first:last] + s[j + n3 :]) else: result.append(s) result = ''.join(result) c.updateBodyPane( head, result, tail, undoType='Delete Comments', oldSel=None, oldYview=oldYview)
def deleteComments(self, event=None): #@+<< deleteComments docstring >> #@+node:ekr.20171123135625.37: *3* << deleteComments docstring >> #@@pagewidth 50 ''' Removes one level of comment delimiters from all selected lines. The applicable @language directive determines the comment delimiters to be removed. Removes single-line comments if possible; removes block comments for languages like html that lack single-line comments. *See also*: add-comments. ''' #@-<< deleteComments docstring >> c = self p = c.p head, lines, tail, oldSel, oldYview = self.getBodyLines() result = [] if not lines: g.warning('no text selected') return # The default language in effect at p. language = c.frame.body.colorizer.scanLanguageDirectives(p) if c.hasAmbiguousLanguage(p): language = c.getLanguageAtCursor(p, language) d1, d2, d3 = g.set_delims_from_language(language) if d1: # Remove the single-line comment delim in front of each line d1b = d1 + ' ' n1, n1b = len(d1), len(d1b) for s in lines: i = g.skip_ws(s, 0) if g.match(s, i, d1b): result.append(s[: i] + s[i + n1b:]) elif g.match(s, i, d1): result.append(s[: i] + s[i + n1:]) else: result.append(s) else: # Remove the block comment delimiters from each line. n2, n3 = len(d2), len(d3) for s in lines: i = g.skip_ws(s, 0) j = s.find(d3, i + n2) if g.match(s, i, d2) and j > -1: first = i + n2 if g.match(s, first, ' '): first += 1 last = j if g.match(s, last - 1, ' '): last -= 1 result.append(s[: i] + s[first: last] + s[j + n3:]) else: result.append(s) result = ''.join(result) c.updateBodyPane(head, result, tail, undoType='Delete Comments', oldSel=None, oldYview=oldYview)
def skipToEndOfTag(self,s,i,start): '''Skip to the end of an open tag. return i,ok,complete where complete is True if the tag of the form <name/> ''' trace = False complete,ok = False,False while i < len(s): progress = i if i == '"': i = self.skipString(s,i) elif g.match(s,i,'<!--'): i = self.skipComment(s,i) elif g.match(s,i,'<'): complete,ok = False,False ; break elif g.match(s,i,'/>'): i = g.skip_ws(s,i+2) complete,ok = True,True ; break elif g.match(s,i,'>'): i += 1 complete,ok = False,True ; break else: i += 1 assert progress < i if trace: g.trace('ok',ok,repr(s[start:i])) return i,ok,complete
def skipSigTail(self, s, i, kind): """Skip from the end of the arg list to the start of the block.""" trace = False and self.trace # Pascal interface has no tail. if kind == "class": return i, True start = i i = g.skip_ws(s, i) for z in self.sigFailTokens: if g.match(s, i, z): if trace: g.trace("failToken", z, "line", g.skip_line(s, i)) return i, False while i < len(s): if self.startsComment(s, i): i = self.skipComment(s, i) elif g.match(s, i, self.blockDelim1): if trace: g.trace(repr(s[start:i])) return i, True else: i += 1 if trace: g.trace("no block delim") return i, False
def adjustDefStart(self, s, i): '''A hook to allow the Python importer to adjust the start of a class or function to include decorators. ''' # Invariant: i does not change. # Invariant: start is the present return value. try: assert s[i] != '\n' start = j = g.find_line_start(s, i) if i > 0 else 0 # g.trace('entry',j,i,repr(s[j:i+10])) assert j == 0 or s[j - 1] == '\n' while j > 0: progress = j j1 = j = g.find_line_start(s, j - 2) # g.trace('line',repr(s[j:progress])) j = g.skip_ws(s, j) if not g.match(s, j, '@'): break k = g.skip_id(s, j + 1) word = s[j:k] # Leo directives halt the scan. if word and word in g.globalDirectiveList: break # A decorator. start = j = j1 assert j < progress # g.trace('**returns %s, %s' % (repr(s[start:i]),repr(s[i:i+20]))) return start except AssertionError: g.es_exception() return i
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 addComments(self, event=None): #@+<< addComments docstring >> #@+node:ekr.20171123135625.35: *3* << addComments docstring >> #@@pagewidth 50 ''' Converts all selected lines to comment lines using the comment delimiters given by the applicable @language directive. Inserts single-line comments if possible; inserts block comments for languages like html that lack single-line comments. @bool indent_added_comments If True (the default), inserts opening comment delimiters just before the first non-whitespace character of each line. Otherwise, inserts opening comment delimiters at the start of each line. *See also*: delete-comments. ''' #@-<< addComments docstring >> c = self p = c.p head, lines, tail, oldSel, oldYview = self.getBodyLines() if not lines: g.warning('no text selected') return # The default language in effect at p. language = c.frame.body.colorizer.scanLanguageDirectives(p) if c.hasAmbiguousLanguage(p): language = c.getLanguageAtCursor(p, language) d1, d2, d3 = g.set_delims_from_language(language) d2 = d2 or '' d3 = d3 or '' if d1: openDelim, closeDelim = d1 + ' ', '' else: openDelim, closeDelim = d2 + ' ', ' ' + d3 # Comment out non-blank lines. indent = c.config.getBool('indent-added-comments', default=True) result = [] for line in lines: if line.strip(): i = g.skip_ws(line, 0) if indent: result.append(line[0:i] + openDelim + line[i:].replace('\n', '') + closeDelim + '\n') else: result.append(openDelim + line.replace('\n', '') + closeDelim + '\n') else: result.append(line) result = ''.join(result) c.updateBodyPane(head, result, tail, undoType='Add Comments', oldSel=None, oldYview=oldYview)
def tokenize(self, s): '''Tokenize comments, strings, identifiers, whitespace and operators.''' i, result = 0, [] while i < len(s): # Loop invariant: at end: j > i and s[i:j] is the new token. j = i ch = s[i] if ch in '@\n': # Make *sure* these are separate tokens. j += 1 elif ch == '#': # Preprocessor directive. j = g.skip_to_end_of_line(s, i) elif ch in ' \t': j = g.skip_ws(s, i) elif ch.isalpha() or ch == '_': j = g.skip_c_id(s, i) elif g.match(s, i, '//'): j = g.skip_line(s, i) elif g.match(s, i, '/*'): j = self.skip_block_comment(s, i) elif ch in "'\"": j = g.skip_string(s, i) else: j += 1 assert j > i result.append(''.join(s[i:j])) i = j # Advance. return result
def parseHeadline(s): """ Parse a headline of the form @kind:name=val Return (kind,name,val). Leo 4.11.1: Ignore everything after @data name. """ kind = name = val = None if g.match(s, 0, g.u('@')): i = g.skip_id(s, 1, chars=g.u('-')) i = g.skip_ws(s, i) kind = s[1:i].strip() if kind: # name is everything up to '=' if kind == g.u('data'): # i = g.skip_ws(s,i) j = s.find(g.u(' '), i) if j == -1: name = s[i:].strip() else: name = s[i:j].strip() else: j = s.find(g.u('='), i) if j == -1: name = s[i:].strip() else: name = s[i:j].strip() # val is everything after the '=' val = s[j + 1:].strip() # g.trace("%50s %10s %s" %(name,kind,val)) return kind, name, val
def adjustDefStart(self, s, i): '''A hook to allow the Python importer to adjust the start of a class or function to include decorators. ''' # Invariant: i does not change. # Invariant: start is the present return value. try: assert s[i] != '\n' start = j = g.find_line_start(s, i) if i > 0 else 0 # g.trace('entry',j,i,repr(s[j:i+10])) assert j == 0 or s[j - 1] == '\n' while j > 0: progress = j j1 = j = g.find_line_start(s, j - 2) # g.trace('line',repr(s[j:progress])) j = g.skip_ws(s, j) if not g.match(s, j, '@'): break k = g.skip_id(s, j + 1) word = s[j: k] # Leo directives halt the scan. if word and word in g.globalDirectiveList: break # A decorator. start = j = j1 assert j < progress # g.trace('**returns %s, %s' % (repr(s[start:i]),repr(s[i:i+20]))) return start except AssertionError: g.es_exception() return i
def compare_lines(self, s1, s2): if self.ignoreLeadingWhitespace: s1 = s1.lstrip() s2 = s2.lstrip() if self.ignoreInteriorWhitespace: k1 = g.skip_ws(s1, 0) k2 = g.skip_ws(s2, 0) ws1 = s1[:k1] ws2 = s2[:k2] tail1 = s1[k1:] tail2 = s2[k2:] tail1 = tail1.replace(" ", "").replace("\t", "") tail2 = tail2.replace(" ", "").replace("\t", "") s1 = ws1 + tail1 s2 = ws2 + tail2 return s1 == s2
def parseHeadline(s): """ Parse a headline of the form @kind:name=val Return (kind,name,val). Leo 4.11.1: Ignore everything after @data name. """ kind = name = val = None if g.match(s, 0, g.u('@')): i = g.skip_id(s, 1, chars=g.u('-')) i = g.skip_ws(s, i) kind = s[1: i].strip() if kind: # name is everything up to '=' if kind == g.u('data'): # i = g.skip_ws(s,i) j = s.find(g.u(' '), i) if j == -1: name = s[i:].strip() else: name = s[i: j].strip() else: j = s.find(g.u('='), i) if j == -1: name = s[i:].strip() else: name = s[i: j].strip() # val is everything after the '=' val = s[j + 1:].strip() # g.trace("%50s %10s %s" %(name,kind,val)) return kind, name, val
def compare_lines(self, s1, s2): if self.ignoreLeadingWhitespace: s1 = s1.lstrip() s2 = s2.lstrip() if self.ignoreInteriorWhitespace: k1 = g.skip_ws(s1, 0) k2 = g.skip_ws(s2, 0) ws1 = s1[: k1] ws2 = s2[: k2] tail1 = s1[k1:] tail2 = s2[k2:] tail1 = tail1.replace(" ", "").replace("\t", "") tail2 = tail2.replace(" ", "").replace("\t", "") s1 = ws1 + tail1 s2 = ws2 + tail2 return s1 == s2
def skipToEndOfTag(self, s, i, start): '''Skip to the end of an open tag. return i,ok,complete where complete is True if the tag of the form <name/> ''' trace = False complete, ok = False, False while i < len(s): progress = i if i == '"': i = self.skipString(s, i) elif g.match(s, i, '<!--'): i = self.skipComment(s, i) elif g.match(s, i, '<'): complete, ok = False, False break elif g.match(s, i, '/>'): i = g.skip_ws(s, i + 2) complete, ok = True, True break elif g.match(s, i, '>'): i += 1 complete, ok = False, True break else: i += 1 assert progress < i if trace: g.trace('ok', ok, repr(s[start:i])) return i, ok, complete
def tokenize(self, s): '''Tokenize comments, strings, identifiers, whitespace and operators.''' i, result = 0, [] while i < len(s): # Loop invariant: at end: j > i and s[i:j] is the new token. j = i ch = s[i] if ch in '@\n': # Make *sure* these are separate tokens. j += 1 elif ch == '#': # Preprocessor directive. j = g.skip_to_end_of_line(s, i) elif ch in ' \t': j = g.skip_ws(s, i) elif ch.isalpha() or ch == '_': j = g.skip_c_id(s, i) elif g.match(s, i, '//'): j = g.skip_line(s, i) elif g.match(s, i, '/*'): j = self.skip_block_comment(s, i) elif ch in "'\"": j = g.skip_string(s, i) else: j += 1 assert j > i result.append(''.join(s[i: j])) i = j # Advance. return result
def skipCodeBlock (self,s,i,kind): '''Skip the code block in a function or class definition.''' trace = False start = i if kind == 'class': i = self.skipInterface(s,i) else: i = self.skipBlock(s,i,delim1=None,delim2=None) if self.sigFailTokens: i = g.skip_ws(s,i) for z in self.sigFailTokens: if g.match(s,i,z): if trace: g.trace('failtoken',z) return start,False if i > start: i = self.skipNewline(s,i,kind) if trace: g.trace(g.callers()) g.trace('returns...\n',g.listToString(g.splitLines(s[start:i]))) return i,True
def skipCodeBlock (self,s,i,kind): trace = False ; verbose = True # if trace: g.trace('***',g.callers()) startIndent = self.startSigIndent if trace: g.trace('startIndent',startIndent) assert startIndent is not None i = start = g.skip_ws_and_nl(s,i) parenCount = 0 underIndentedStart = None # The start of trailing underindented blank or comment lines. while i < len(s): progress = i ch = s[i] if g.is_nl(s,i): if trace and verbose: g.trace(g.get_line(s,i)) backslashNewline = (i > 0 and g.match(s,i-1,'\\\n')) if backslashNewline: # An underindented line, including docstring, # does not end the code block. i += 1 # 2010/11/01 else: i = g.skip_nl(s,i) j = g.skip_ws(s,i) if g.is_nl(s,j): pass # We have already made progress. else: i,underIndentedStart,breakFlag = self.pythonNewlineHelper( s,i,parenCount,startIndent,underIndentedStart) if breakFlag: break elif ch == '#': i = g.skip_to_end_of_line(s,i) elif ch == '"' or ch == '\'': i = g.skip_python_string(s,i) elif ch in '[{(': i += 1 ; parenCount += 1 # g.trace('ch',ch,parenCount) elif ch in ']})': i += 1 ; parenCount -= 1 # g.trace('ch',ch,parenCount) else: i += 1 assert(progress < i) # The actual end of the block. if underIndentedStart is not None: i = underIndentedStart if trace: g.trace('***backtracking to underindent range') if trace: g.trace(g.get_line(s,i)) if 0 < i < len(s) and not g.match(s,i-1,'\n'): g.trace('Can not happen: Python block does not end in a newline.') g.trace(g.get_line(s,i)) return i,False # 2010/02/19: Include all following material # until the next 'def' or 'class' i = self.skipToTheNextClassOrFunction(s,i,startIndent) if (trace or self.trace) and s[start:i].strip(): g.trace('%s returns\n' % (kind) + s[start:i]) return i,True
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 skipCodeBlock(self, s, i, kind): trace = False verbose = True # if trace: g.trace('***',g.callers()) startIndent = self.startSigIndent if trace: g.trace('startIndent', startIndent) assert startIndent is not None i = start = g.skip_ws_and_nl(s, i) parenCount = 0 underIndentedStart = None # The start of trailing underindented blank or comment lines. while i < len(s): progress = i ch = s[i] if g.is_nl(s, i): if trace and verbose: g.trace(g.get_line(s, i)) backslashNewline = (i > 0 and g.match(s, i - 1, '\\\n')) if backslashNewline: # An underindented line, including docstring, # does not end the code block. i += 1 # 2010/11/01 else: i = g.skip_nl(s, i) j = g.skip_ws(s, i) if g.is_nl(s, j): pass # We have already made progress. else: i, underIndentedStart, breakFlag = self.pythonNewlineHelper( s, i, parenCount, startIndent, underIndentedStart) if breakFlag: break elif ch == '#': i = g.skip_to_end_of_line(s, i) elif ch == '"' or ch == '\'': i = g.skip_python_string(s, i) elif ch in '[{(': i += 1 parenCount += 1 # g.trace('ch',ch,parenCount) elif ch in ']})': i += 1 parenCount -= 1 # g.trace('ch',ch,parenCount) else: i += 1 assert (progress < i) # The actual end of the block. if underIndentedStart is not None: i = underIndentedStart if trace: g.trace('***backtracking to underindent range') if trace: g.trace(g.get_line(s, i)) if 0 < i < len(s) and not g.match(s, i - 1, '\n'): g.trace('Can not happen: Python block does not end in a newline.') g.trace(g.get_line(s, i)) return i, False # 2010/02/19: Include all following material # until the next 'def' or 'class' i = self.skipToTheNextClassOrFunction(s, i, startIndent) if (trace or self.trace) and s[start:i].strip(): g.trace('%s returns\n' % (kind) + s[start:i]) return i, True
def doStartLine (self): before = self.s[0:self.scol] i = g.skip_ws(before,0) self.ws = self.s[0:i] if self.ws: self.array.append(self.ws)
def doStartLine(self): before = self.s[0:self.scol] i = g.skip_ws(before, 0) self.ws = self.s[0:i] if self.ws: self.array.append(self.ws)
def isLeoHeader(self, s): tag = "@+leo" j = s.find(tag) if j > 0: i = g.skip_ws(s, 0) if i < j: return s[i: j] else: return None else: return None
def isLeoHeader(self, s): tag = "@+leo" j = s.find(tag) if j > 0: i = g.skip_ws(s, 0) if i < j: return s[i:j] return None
def getLine (self,s,i): i,j = g.getLine(s,i) line = s[i:j] nows = i == g.skip_ws(s,i) line = line.strip() return i,j,nows,line
def doOp (self): val = self.val outer = self.lineParenLevel <= 0 or (self.parenLevel == 0 and self.squareBracketLevel == 0) # New in Python 2.4: '@' is an operator, not an error token. if self.val == '@': self.array.append(self.val) # Preserve whitespace after @. i = g.skip_ws(self.s,self.scol+1) ws = self.s[self.scol+1:i] if ws: self.array.append(ws) elif val == '(': # Nothing added; strip leading blank before function calls but not before Python keywords. strip = self.lastName=='name' and not keyword.iskeyword(self.prevName) self.put('(',strip=strip) self.parenLevel += 1 ; self.lineParenLevel += 1 elif val in ('=','==','+=','-=','!=','<=','>=','<','>','<>','*','**','+','&','|','/','//'): # Add leading and trailing blank in outer mode. s = g.choose(outer,' %s ','%s') self.put(s % val) elif val in ('^','~','{','['): # Add leading blank in outer mode. s = g.choose(outer,' %s','%s') self.put(s % val) if val == '[': self.squareBracketLevel += 1 elif val in (',',':','}',']',')'): # Add trailing blank in outer mode. s = g.choose(outer,'%s ','%s') self.put(s % val) if val == ']': self.squareBracketLevel -= 1 if val == ')': self.parenLevel -= 1 ; self.lineParenLevel -= 1 # no difference between outer and inner modes elif val in (';','%'): # Add leading and trailing blank. self.put(' %s ' % val) elif val == '>>': # Add leading blank. self.put(' %s' % val) elif val == '<<': # Add trailing blank. self.put('%s ' % val) elif val in ('-'): # Could be binary or unary. Or could be a hyphen in a section name. # Add preceding blank only for non-id's. if outer: if self.array: prev = self.array[-1].rstrip() if prev and prev[-1] not in string.digits + string.letters: self.put(' %s' % val) else: self.put(val) else: self.put(val) # Try to leave whitespace unchanged. else: self.put(val) else: self.put(val)
def doOp (self): val = self.val outer = self.lineParenLevel <= 0 or (self.parenLevel == 0 and self.squareBracketLevel == 0) # New in Python 2.4: '@' is an operator, not an error token. if self.val == '@': self.array.append(self.val) # Preserve whitespace after @. i = g.skip_ws(self.s,self.scol+1) ws = self.s[self.scol+1:i] if ws: self.array.append(ws) elif val == '(': # Nothing added; strip leading blank before function calls but not before Python keywords. strip = self.lastName=='name' and not keyword.iskeyword(self.prevName) self.put('(',strip=strip) self.parenLevel += 1 ; self.lineParenLevel += 1 elif val in ('=','==','+=','-=','!=','<=','>=','<','>','<>','*','**','+','&','|','/','//'): # Add leading and trailing blank in outer mode. s = ' %s ' if outer else '%s' self.put(s % val) elif val in ('^','~','{','['): # Add leading blank in outer mode. s = ' %s' if outer else '%s' self.put(s % val) if val == '[': self.squareBracketLevel += 1 elif val in (',',':','}',']',')'): # Add trailing blank in outer mode. s = '%s ' if outer else '%s' self.put(s % val) if val == ']': self.squareBracketLevel -= 1 if val == ')': self.parenLevel -= 1 ; self.lineParenLevel -= 1 # no difference between outer and inner modes elif val in (';','%'): # Add leading and trailing blank. self.put(' %s ' % val) elif val == '>>': # Add leading blank. self.put(' %s' % val) elif val == '<<': # Add trailing blank. self.put('%s ' % val) elif val in ('-'): # Could be binary or unary. Or could be a hyphen in a section name. # Add preceding blank only for non-id's. if outer: if self.array: prev = self.array[-1].rstrip() if prev and prev[-1] not in string.digits + string.letters: self.put(' %s' % val) else: self.put(val) else: self.put(val) # Try to leave whitespace unchanged. else: self.put(val) else: self.put(val)
def doErrorToken(self): self.array.append(self.val) if self.val == '@': # Preserve whitespace after @. i = g.skip_ws(self.s, self.scol + 1) ws = self.s[self.scol + 1:i] if ws: self.array.append(ws)
def doErrorToken (self): self.array.append(self.val) if self.val == '@': # Preserve whitespace after @. i = g.skip_ws(self.s,self.scol+1) ws = self.s[self.scol+1:i] if ws: self.array.append(ws)
def isSectionLine(self, s, i): i = g.skip_ws(s, i) if not g.match(s, i, '['): return False, None, i k = s.find('\n', i + 1) if k == -1: k = len(s) j = s.find(']', i + 1) if -1 < j < k: return True, s[i: j + 1], i else: return False, None, i
def context_names(self): '''Return the present context name.''' if self.context_stack: result = [] for stack_i in -1, -2: try: fn, kind, indent, s = self.context_stack[stack_i] except IndexError: result.append('') break s = s.strip() assert kind in ('class', 'def'), kind i = g.skip_ws(s, 0) i += len(kind) i = g.skip_ws(s, i) j = g.skip_c_id(s, i) result.append(s[i: j]) return reversed(result) else: return ['', '']
def getShortcut(self, h): '''Returns the keyboard shortcut from the given headline string''' shortcut = None i = h.find('@key') if i > -1: j = g.skip_ws(h, i + len('@key')) if g.match(h, j, '='): j += 1 shortcut = h[j:].strip() return shortcut
def checkBlanksAndTabs(self, s): '''Check for intermixed blank & tabs.''' # Do a quick check for mixed leading tabs/blanks. blanks = tabs = 0 for line in g.splitLines(s): lws = line[0:g.skip_ws(line, 0)] blanks += lws.count(' ') tabs += lws.count('\t') ok = blanks == 0 or tabs == 0 if not ok: self.report('intermixed blanks and tabs') return ok
def getColor(self, h): '''Returns the background color from the given headline string''' color = None tag = '@color' i = h.find(tag) if i > -1: j = g.skip_ws(h, i + len(tag)) if g.match(h, j, '='): j += 1 k = h.find('@', j + 1) if k == -1: k = len(h) color = h[j:k].strip() return color
def getColor(self, h): '''Returns the background color from the given headline string''' color = None tag = '@color' i = h.find(tag) if i > -1: j = g.skip_ws(h, i + len(tag)) if g.match(h, j, '='): j += 1 k = h.find('@', j + 1) if k == -1: k = len(h) color = h[j: k].strip() return color
def checkBlanksAndTabs(self, s): '''Check for intermixed blank & tabs.''' # Do a quick check for mixed leading tabs/blanks. blanks = tabs = 0 for line in g.splitLines(s): lws = line[0: g.skip_ws(line, 0)] blanks += lws.count(' ') tabs += lws.count('\t') ok = blanks == 0 or tabs == 0 if not ok: self.report('intermixed blanks and tabs') return ok
def context_names(self): '''Return the present context names.''' result = [] n = len(self.context_stack) for i in n - 1, n - 2: if i >= 0: fn, kind, s = self.context_stack[i] assert kind in ('class', 'def', 'module'), kind if kind == 'module': result.append(s.strip()) else: # Append the name following the class or def. i = g.skip_ws(s, 0) i += len(kind) i = g.skip_ws(s, i) j = g.skip_c_id(s, i) result.append(s[i: j]) else: result.append('') break # g.trace(list(reversed(result))) return reversed(result)
def context_names(self): '''Return the present context names.''' result = [] n = len(self.context_stack) for i in n - 1, n - 2: if i >= 0: fn, kind, s = self.context_stack[i] assert kind in ('class', 'def', 'module'), kind if kind == 'module': result.append(s.strip()) else: # Append the name following the class or def. i = g.skip_ws(s, 0) i += len(kind) i = g.skip_ws(s, i) j = g.skip_c_id(s, i) result.append(s[i:j]) else: result.append('') break # g.trace(list(reversed(result))) return reversed(result)
def context_names(self): """Return the present context names.""" result = [] n = len(self.context_stack) for i in n - 1, n - 2: if i >= 0: fn, kind, s = self.context_stack[i] assert kind in ("class", "def", "module"), kind if kind == "module": result.append(s.strip()) else: # Append the name following the class or def. i = g.skip_ws(s, 0) i += len(kind) i = g.skip_ws(s, i) j = g.skip_c_id(s, i) result.append(s[i:j]) else: result.append("") break # g.trace(list(reversed(result))) return reversed(result)
def addComments(self, event=None): #@+<< addComments docstring >> #@+node:ekr.20171123135625.35: *3* << addComments docstring >> #@@pagewidth 50 ''' Converts all selected lines to comment lines using the comment delimiters given by the applicable @language directive. Inserts single-line comments if possible; inserts block comments for languages like html that lack single-line comments. @bool indent_added_comments If True (the default), inserts opening comment delimiters just before the first non-whitespace character of each line. Otherwise, inserts opening comment delimiters at the start of each line. *See also*: delete-comments. ''' #@-<< addComments docstring >> c = self; p = c.p head, lines, tail, oldSel, oldYview = self.getBodyLines() if not lines: g.warning('no text selected') return # The default language in effect at p. language = c.frame.body.colorizer.scanLanguageDirectives(p) if c.hasAmbiguousLanguage(p): language = c.getLanguageAtCursor(p, language) d1, d2, d3 = g.set_delims_from_language(language) d2 = d2 or ''; d3 = d3 or '' if d1: openDelim, closeDelim = d1 + ' ', '' else: openDelim, closeDelim = d2 + ' ', ' ' + d3 # Comment out non-blank lines. indent = c.config.getBool('indent-added-comments', default=True) result = [] for line in lines: if line.strip(): i = g.skip_ws(line, 0) if indent: result.append(line[0: i] + openDelim + line[i:].replace('\n', '') + closeDelim + '\n') else: result.append(openDelim + line.replace('\n', '') + closeDelim + '\n') else: result.append(line) result = ''.join(result) c.updateBodyPane(head, result, tail, undoType='Add Comments', oldSel=None, oldYview=oldYview)
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 getShortcut(self, h): '''Return the keyboard shortcut from the given headline string''' shortcut = None i = h.find('@key') if i > -1: j = g.skip_ws(h, i + len('@key')) if g.match(h, j, '='): j += 1 if 0: shortcut = h[j:].strip() else: # new logic 1/3/2014 Jake Peck k = h.find('@', j + 1) if k == -1: k = len(h) shortcut = h[j:k].strip() return shortcut
def getShortcut(self, h): '''Return the keyboard shortcut from the given headline string''' shortcut = None i = h.find('@key') if i > -1: j = g.skip_ws(h, i + len('@key')) if g.match(h, j, '='): j += 1 if 0: shortcut = h[j:].strip() else: # new logic 1/3/2014 Jake Peck k = h.find('@', j + 1) if k == -1: k = len(h) shortcut = h[j: k].strip() return shortcut
def extendSignature(self, s, i): '''Extend the text to be added to the class node following the signature. The text *must* end with a newline.''' # Add a docstring to the class node, # And everything on the line following it j = g.skip_ws_and_nl(s, i) if g.match(s, j, '"""') or g.match(s, j, "'''"): j = g.skip_python_string(s, j) if j < len(s): # No scanning error. # Return the docstring only if nothing but whitespace follows. j = g.skip_ws(s, j) if g.is_nl(s, j): return j + 1 return i
def getArgs(self, h): args = [] tag = '@args' i = h.find(tag) if i > -1: j = g.skip_ws(h, i + len(tag)) # 2011/10/16: Make '=' sign optional. if g.match(h, j, '='): j += 1 s = h[j + 1:].strip() args = s.split(',') args = [z.strip() for z in args] # g.trace('args',repr(args)) return args
def remove_level_stars(self, s): i = g.skip_ws(s, 0) # Remove leading stars. while i < len(s) and s[i] == '*': i += 1 # Remove optional level number. while i < len(s) and s[i].isdigit(): i += 1 # Remove trailing stars. while i < len(s) and s[i] == '*': i += 1 # Remove one blank. if i < len(s) and s[i] == ' ': i += 1 return s[i:]
def getColor(self, h): """Returns the background color from the given headline string""" color = None tag = "@color" i = h.find(tag) if i > -1: j = g.skip_ws(h, i + len(tag)) if g.match(h, j, "="): j += 1 k = h.find("@", j + 1) if k == -1: k = len(h) color = h[j:k].strip() return color
def parseOptionLine(self, s): '''Parse a line containing name=val and return (name,value) or None. If no value is found, default to True.''' s = s.strip() if s.endswith(','): s = s[:-1] # Get name. Names may contain '-' and '_'. i = g.skip_id(s, 0, chars='-_') name = s[:i] if not name: return None j = g.skip_ws(s, i) if g.match(s, j, '='): val = s[j + 1:].strip() # g.trace(val) return name, val else: # g.trace('*True') return name, 'True'
def yankHelper(self, event, pop): """ Helper for yank and yank-pop: pop = False: insert the first entry of the kill ring. pop = True: insert the next entry of the kill ring. """ c = self.c w = self.editWidget(event) if not w: return current = c.p if not current: return text = w.getAllText() i, j = w.getSelectionRange() clip_text = self.getClipboard() if not g.app.globalKillBuffer and not clip_text: return undoType = 'yank-pop' if pop else 'yank' self.beginCommand(w, undoType=undoType) try: if not pop or self.lastYankP and self.lastYankP != current: self.reset = 0 s = self.kbiterator.__next__() if s is None: s = clip_text or '' if i != j: w.deleteTextSelection() if s != s.lstrip(): # s contains leading whitespace. i2, j2 = g.getLine(text, i) k = g.skip_ws(text, i2) if i2 < i <= k: # Replace the line's leading whitespace by s's leading whitespace. w.delete(i2, k) i = i2 w.insert(i, s) # Fix bug 1099035: Leo yank and kill behaviour not quite the same as emacs. # w.setSelectionRange(i,i+len(s),insert=i+len(s)) w.setInsertPoint(i + len(s)) self.lastYankP = current.copy() finally: self.endCommand(changed=True, setLabel=True)
def getArgs(self, h): args = [] tag = '@args' i = h.find(tag) if i > -1: j = g.skip_ws(h, i + len(tag)) # 2011/10/16: Make '=' sign optional. if g.match(h, j, '='): j += 1 if 0: s = h[j + 1:].strip() else: # new logic 1/3/2014 Jake Peck k = h.find('@', j + 1) if k == -1: k = len(h) s = h[j:k].strip() args = s.split(',') args = [z.strip() for z in args] # g.trace('args',repr(args)) return args
def skipCodeBlock(self, s, i, kind): '''Skip the code block in a function or class definition.''' trace = False start = i if kind == 'class': i = self.skipInterface(s, i) else: i = self.skipBlock(s, i, delim1=None, delim2=None) if self.sigFailTokens: i = g.skip_ws(s, i) for z in self.sigFailTokens: if g.match(s, i, z): if trace: g.trace('failtoken', z) return start, False if i > start: i = self.skipNewline(s, i, kind) if trace: g.trace(g.callers()) g.trace('returns...\n', g.listToString(g.splitLines(s[start:i]))) return i, True
def parseOptionLine (self,s): '''Parse a line containing name=val and return (name,value) or None. If no value is found, default to True.''' s = s.strip() if s.endswith(','): s = s[:-1] # Get name. Names may contain '-' and '_'. i = g.skip_id(s,0,chars='-_') name = s [:i] if not name: return None j = g.skip_ws(s,i) if g.match(s,j,'='): val = s [j+1:].strip() # g.trace(val) return name,val else: # g.trace('*True') return name,'True'
def getArgs (self,h): args = [] tag = '@args' i = h.find(tag) if i > -1: j = g.skip_ws(h,i+len(tag)) # 2011/10/16: Make '=' sign optional. if g.match(h,j,'='): j += 1 if 0: s = h[j+1:].strip() else: # new logic 1/3/2014 Jake Peck k = h.find('@', j+1) if k == -1: k = len(h) s = h[j:k].strip() args = s.split(',') args = [z.strip() for z in args] # g.trace('args',repr(args)) return args
def startsFunction(self, s, i): '''Return True if s[i:] starts a function. Sets sigStart, sigEnd, sigId and codeEnd ivars.''' self.startSigIndent = self.getLeadingIndent(s, i) self.sigStart = i self.codeEnd = self.sigEnd = self.sigId = None if not g.match(s, i, '('): return False end = self.skipBlock(s, i) # g.trace('%3s %15s block: %s' % (i,repr(s[i:i+10]),repr(s[i:end]))) if not g.match(s, end - 1, ')'): return False i = g.skip_ws(s, i + 1) if not g.match_word(s, i, 'defun'): return False i += len('defun') sigEnd = i = g.skip_ws_and_nl(s, i) j = self.skipId(s, i) # Bug fix: 2009/09/30 word = s[i:j] if not word: return False self.codeEnd = end + 1 self.sigEnd = sigEnd self.sigId = word return True
def getArgs(self, p): '''Return the list of @args field of p.h.''' args = [] if not p: return args h, tag = p.h, '@args' i = h.find(tag) if i > -1: j = g.skip_ws(h, i + len(tag)) # 2011/10/16: Make '=' sign optional. if g.match(h, j, '='): j += 1 if 0: s = h[j + 1:].strip() else: # new logic 1/3/2014 Jake Peck k = h.find('@', j + 1) if k == -1: k = len(h) s = h[j:k].strip() args = s.split(',') args = [z.strip() for z in args] # if args: g.trace(args) return args
def skipSigTail(self, s, i, kind): '''Skip from the end of the arg list to the start of the block.''' trace = False and self.trace # Pascal interface has no tail. if kind == 'class': return i, True start = i i = g.skip_ws(s, i) for z in self.sigFailTokens: if g.match(s, i, z): if trace: g.trace('failToken', z, 'line', g.skip_line(s, i)) return i, False while i < len(s): if self.startsComment(s, i): i = self.skipComment(s, i) elif g.match(s, i, self.blockDelim1): if trace: g.trace(repr(s[start:i])) return i, True else: i += 1 if trace: g.trace('no block delim') return i, False