def inline_comment(): ''' Append or align an inlined comment at position 60 for the current line or the selection. Move cursor to the start of a comment, if nothing has changed. If there wasn't any comment aside of #else/#endif put corresponding #if condition as default comment text ''' document = kate.activeDocument() view = kate.activeView() pos = view.cursorPosition() commentCh = common.getCommentStyleForDoc(document) if view.selection(): # If selected smth on a single line... common.extendSelectionToWholeLine(view) selectedText = view.selectionText().split('\n') if not bool(selectedText[-1]): selectedText = selectedText[0:-1] insertionText = [] firstColumn = -1 for textLine in selectedText: (currentLine, column) = processLine(textLine, commentCh) if firstColumn == -1: firstColumn = column insertionText += currentLine # Modify current document if bool(insertionText): document.startEditing() document.removeText(view.selectionRange()) pos = view.cursorPosition() document.insertText(pos, '\n'.join(insertionText) + '\n') pos.setColumn(firstColumn) view.setCursorPosition(pos) view.removeSelection() document.endEditing() else: (text, column) = processLine(document.line(pos.line()), commentCh) # Apply result (if smth really has changed) originalText = document.line(pos.line()) if bool(text) and (len(text) != 1 or originalText != text[0]): document.startEditing() # Start edit transaction: document.removeLine(pos.line()) # Remove current line # insert resulting text line by line... pos.setColumn(0) document.insertText(pos, '\n'.join(text) + '\n') document.endEditing() # End transaction # Move cursor to desired position pos.setColumn(column) view.setCursorPosition(pos)
def comment_block(): '''Wrap selected text (or current line) into a #if0/#endif block''' view = kate.activeView() # This operation have no sense for partly selected lines common.extendSelectionToWholeLine(view) start = -1 end = -1 if view.selection(): sr = view.selectionRange() start = sr.start().line() end = sr.end().line() + 1 else: start = view.cursorPosition().line() end = start + 2 # Do it! document = kate.activeDocument() if start != -1 and end != -1: document.startEditing() # Start edit transaction document.insertLine(start, '#if 0') document.insertLine(end, '#endif') view.removeSelection() document.endEditing() # End transaction
def processLine(line, commentCh): result = [] column = kate.configuration[COMMENT_START_POS] # Split line before and after a comment (before, comment, after) = line.partition(commentCh) before_s = before.rstrip() # Is there a comment on a line? if bool(comment): # Is there is any text before inline comment position? if bool(before_s): # Yes! Is text before not longer than desired comment position if len(before_s) < (kate.configuration[COMMENT_START_POS] + 1): # Yep, just reformat the line... result.append(before_s + (' ' * (kate.configuration[COMMENT_START_POS] - len(before_s))) + commentCh + after.rstrip()) else: # Move comment to the line above column = len(before) - len(before.lstrip()) # NOTE Try to fix Doxygen comment on the fly: '///<' or '//!<' --> '///' if after[1] == '<' and (after[0] == '!' or after[0] == '/'): after = '/' + after[2:] result.append(' ' * column + commentCh + after.rstrip()) result.append(before_s) else: # No! The line contains only whitespaces... # Is comment after or 'close before' to inline comment position? if len(before) > (kate.configuration[COMMENT_START_POS] / 6): # Align comment to desired position... result.append(' ' * kate.configuration[COMMENT_START_POS] + commentCh + after.rstrip()) else: # TODO Align comment to closest to div 4 position... result.append(line.rstrip()) else: # There is no comments... What about any text? if bool(before_s): # Is it longer that inline comment position? if len(before_s) > (kate.configuration[COMMENT_START_POS]): column = len(before) - len(before.lstrip()) result.append(' ' * column + commentCh + ' ') result.append(before_s) else: result.append(before_s + ' ' * (kate.configuration[COMMENT_START_POS] - len(before_s)) + commentCh + ' ') # Check for preprocessor directives #else/#endif and try to append # corresponding #if condition as a comment for current line if bool(BLOCK_ELSE_ENDIF_MATCH_RE.search(before_s)): document = kate.activeDocument() view = kate.activeView() # Make list of ranges of #if*/#endif blocks blocksList = buildIfEndifMap(document) # Locate an index of a block where cursor currently positioned (check commented block too) idx = locateBlock(view.cursorPosition().line(), blocksList, True) # Get #if condition (if block located) if idx != -1: # TODO Need to strip possible comment! matchObj = BLOCK_START_GET_COND_RE.search(document.line(blocksList[idx][0])) if bool(matchObj): result[-1] += matchObj.group(4) else: # No text! Just add a comment... result.append(' ' * kate.configuration[COMMENT_START_POS] + commentCh + ' ') return (result, column + len(commentCh) + 1)
def turnFromBlockComment(): document = kate.activeDocument() view = kate.activeView() pos = view.cursorPosition() if view.selection(): sr = view.selectionRange() start = sr.start().line() end = sr.end().line() else: # Try to detect block comment (/* ... */) r = common.getTextBlockAroundCursor( document , pos , [pred.blockCommentStart, neg(pred.startsWith('*'))] , [pred.blockCommentEnd, neg(pred.startsWith('*'))] ) start = r.start().line() - 1 end = r.end().line() + 1 # Replace comments insertionText = list() align = None for i in range(start, end): line = str(document.line(i)) sline = line.lstrip() if align == None: align = ' ' * (len(line) - len(sline)) if sline.startswith('/**') or sline.startswith('*/'): continue if sline.startswith('*'): insertionText.append(align + sline.replace('*', '///', 1)) originRange = KTextEditor.Range(start, 0, end, 0) pos.setPosition(start, len(align) + 3) insertPos = KTextEditor.Cursor(start, 0) # Update the document if bool(insertionText): document.startEditing() # Start edit transaction: document.removeText(originRange) # Remove current line # insert resulting text line by line... document.insertText(insertPos, '\n'.join(insertionText) + '\n') # Move cursor to desired position view.setCursorPosition(pos) document.endEditing() # End transaction
def remove_block(): ''' Remove a block of code commented with #if0 or #if1-#else''' document = kate.activeDocument() view = kate.activeView() # Make list of ranges of #if*/#endif blocks blocksList = buildIfEndifMap(document) # Locate a block where cursor currently positioned idx = locateBlock(view.cursorPosition().line(), blocksList, False) if idx != -1: # Get current value v = BLOCK_START_SEARCH_RE.search(str(document.line(blocksList[idx][0]))).group(1) # Do nothing if it's not a #if0/#if1 if v not in ('0', 'false', '1', 'true'): return document.startEditing() # Start edit transaction # What to remove? if v in ('0', 'false'): # Remove `then` part if blocksList[idx][2] != -1: # Is there `#else` part? # Yeah! Remove `#endif` line and then from `#if` to `#else` (including) document.removeLine(blocksList[idx][1]) r = KTextEditor.Range(blocksList[idx][0], 0, blocksList[idx][2] + 1, 0) else: # No! So just remove whole block r = KTextEditor.Range(blocksList[idx][0], 0, blocksList[idx][1] + 1, 0) document.removeText(r) else: if blocksList[idx][2] != -1: # Is there `#else` part? # Yeah! Remove from `#else` to `#endif` block and then `#if` line r = KTextEditor.Range(blocksList[idx][2], 0, blocksList[idx][1] + 1, 0) document.removeText(r) document.removeLine(blocksList[idx][0]) else: # No! Ok just remove `#endif` line and then `#if` document.removeLine(blocksList[idx][1]) document.removeLine(blocksList[idx][0]) document.endEditing() # End transaction else: kate.ui.popup( i18nc('@title:window', 'Alert') , i18nc( '@info:tooltip' , 'The cursor is not positioned in any <icode>#if0</icode>/<icode>#if1</icode> block' ) , 'dialog-information' )
def move_above(): '''Move an inlined comment before the current line w/ same indentation level''' document = kate.activeDocument() view = kate.activeView() pos = view.cursorPosition() commentCh = common.getCommentStyleForDoc(document) insertionText = list() line = document.line(pos.line()) # Split a line before and after a comment (before, comment, after) = str(line).partition(commentCh) before_ls = before.lstrip() column = len(before) - len(before_ls) doxCommentOffset = 0 # Is there is a comment in a line? if bool(comment): # Yeah! It is... Now what about any text?? if bool(before.strip()): if after[0:2] == '/<': after = '/' + after[2:] doxCommentOffset = 1 insertionText.append(' ' * column + comment + after) else: # There is comment alone... Just leave it... return else: # Oops! There is no inline comment... Ok just add new one above. insertionText.append(' ' * column + commentCh) column += len(commentCh) + doxCommentOffset insertionText.append(before.rstrip()); # Update the document if bool(insertionText): document.startEditing() # Start edit transaction: document.removeLine(pos.line()) # Remove current line # insert resulting text line by line... pos.setColumn(0) document.insertText(pos, '\n'.join(insertionText) + '\n') # Move cursor to desired position pos.setColumn(column) view.setCursorPosition(pos) document.endEditing() # End transaction
def turnToBlockComment(): document = kate.activeDocument() view = kate.activeView() pos = view.cursorPosition() if view.selection(): sr = view.selectionRange() start = sr.start().line() end = sr.end().line() else: r = common.getTextBlockAroundCursor( document , pos , [neg(any_of(pred.startsWith('///'), pred.startsWith('//!')))] , [neg(any_of(pred.startsWith('///'), pred.startsWith('//!')))] ) start = r.start().line() end = r.end().line() # Replace comments in every line insertionText = list() align = None for i in range(start, end): line = str(document.line(i)) sline = line.lstrip() if align == None: align = ' ' * (len(line) - len(sline)) insertionText.append(align + sline.replace('///', ' *', 1).replace('//!', ' *', 1)) originRange = KTextEditor.Range(start, 0, end, 0) pos.setPosition(start + 1, len(align) + 3) insertPos = KTextEditor.Cursor(start, 0) # Update the document if bool(insertionText): document.startEditing() # Start edit transaction: document.removeText(originRange) # Remove current line # insert resulting text ... document.insertText(insertPos, align + '/**\n' + '\n'.join(insertionText) + '\n' + align + ' */\n'); # Move cursor to desired position view.setCursorPosition(pos) document.endEditing() # End transaction
def toggle_doxy_comment(): ''' Turn block of '///' doxygen comments into /** * */ and vise versa ''' document = kate.activeDocument() view = kate.activeView() pos = view.cursorPosition() # Determine type of current comment line = str(document.line(pos.line())).strip() if line.startswith('///'): turnToBlockComment() elif line.startswith('*') or line.startswith('/*') or line.startswith('*/'): turnFromBlockComment() else: return
def toggle_block(): ''' Switch a current code block to ON(#if1) or OFF(#if0) state. Current means that cursor placed inside of it. ''' document = kate.activeDocument() view = kate.activeView() # Make list of ranges of #if*/#endif blocks blocksList = buildIfEndifMap(document) # Locate a block where cursor currently positioned idx = locateBlock(view.cursorPosition().line(), blocksList, False) if idx != -1: # Get current value v = BLOCK_START_SEARCH_RE.search(str(document.line(blocksList[idx][0]))).group(1) # Toggle it! if v in ('0', 'false'): newValue = '1' elif v in ('1', 'true'): newValue = '0' else: return # Replace string document.startEditing() # Start edit transaction document.removeLine(blocksList[idx][0]) # TODO Do not lose formatting! document.insertLine(blocksList[idx][0], '#if ' + newValue) document.endEditing() # End transaction else: kate.ui.popup( i18nc('@title:window', 'Alert') , i18nc( '@info:tooltip' , 'The cursor is not positioned in any <icode>#if0</icode>/<icode>#if1</icode> block' ) , 'dialog-information' )
def select_block(): '''Set selection of a current (where cursor positioned) #if0/#endif block''' document = kate.activeDocument() view = kate.activeView() # Make list of ranges of #if*/#endif blocks blocksList = buildIfEndifMap(document) # Locate a block where cursor currently positioned idx = locateBlock(view.cursorPosition().line(), blocksList) if idx != -1: r = KTextEditor.Range(blocksList[idx][0], 0, blocksList[idx][1] + 1, 0) view.setSelection(r) else: kate.ui.popup( i18nc('@title:window', 'Alert') , i18nc( '@info:tooltip' , 'The cursor is not positioned in any <icode>#if0</icode>/<icode>#if1</icode> block' ) , 'dialog-information' )
def getRangeTopology(breakChars): '''Get range opened w/ `openCh' and closed w/ `closeCh' @return tuple w/ current range, list of nested ranges and list of positions of break characters @note Assume cursor positioned whithin that range already. ''' document = kate.activeDocument() view = kate.activeView() pos = view.cursorPosition() stack = list() nestedRanges = list() breakPositions = list() firstIteration = True found = False # Iterate from the current line towards a document start for cl in range(pos.line(), -1, -1): lineStr = str(document.line(cl)) if not firstIteration: # skip first iteration pos.setColumn(len(lineStr)) # set current column to the end of current line else: firstIteration = False # do nothing on first iteration # Iterate from the current column to a line start for cc in range(pos.column() - 1, -1, -1): #kate.kDebug("c: current position" + str(cl) + "," + str(cc) + ",ch='" + lineStr[cc] + "'") # Check open/close brackets if lineStr[cc] == ')': # found closing char: append its position to the stack stack.append((cl, cc, False)) #kate.kDebug("o( Add position: " + str(stack[-1])) continue if lineStr[cc] == '(': # found open char... if len(stack): # if stack isn't empty (i.e. there are some closing chars met) #kate.kDebug("o( Pop position: " + str(stack[-1])) nrl, nrc, isT = stack.pop() # remove last position from the stack if not isT: nestedRanges.append( # and append a nested range KTextEditor.Range(cl, cc, nrl, nrc) ) else: raise LookupError( i18nc( '@info' , 'Misbalanced brackets: at <numid>%1</numid>,<numid>%2</numid> and <numid>%3</numid>,<numid>%4</numid>' , cl + 1, cc + 1, nrl + 1, nrc + 1 ) ) else: # otherwise, openPos = (cl, cc + 1, False) # remember range start (exclude an open char) #kate.kDebug("o( Found position: " + str(openPos)) found = True break continue # Check for template angel brackets if lineStr[cc] == '>': if looksLikeTemplateAngelBracket(lineStr, cc): stack.append((cl, cc, True)) #kate.kDebug("o< Add position: " + str(stack[-1])) #else: #kate.kDebug("o< Doesn't looks like template: " + str(cl) + "," + str(cc)) continue if lineStr[cc] == '<': if not looksLikeTemplateAngelBracket(lineStr, cc): #kate.kDebug("o< Doesn't looks like template: " + str(cl) + "," + str(cc + 1)) pass elif len(stack): # if stack isn't empty (i.e. there are some closing chars met) #kate.kDebug("o< Pop position: " + str(stack[-1])) nrl, nrc, isT = stack.pop() # remove last position from the stack if isT: nestedRanges.append( # and append a nested range KTextEditor.Range(cl, cc, nrl, nrc) ) else: raise LookupError( i18nc( '@info' , 'Misbalanced brackets: at <numid>%1</numid>,<numid>%2</numid> and <numid>%3</numid>,<numid>%4</numid>' , cl + 1, cc + 1, nrl + 1, nrc + 1 ) ) else: openPos = (cl, cc + 1, True) # remember range start (exclude an open char) #kate.kDebug("o< Found position: " + str(openPos)) found = True break continue if lineStr[cc] in breakChars and len(stack) == 0: breakPositions.append(KTextEditor.Cursor(cl, cc)) # Did we found smth on the current line? if found: break # Yep! Break the outer loop if not found: return (KTextEditor.Range(), list(), list()) # Return empty ranges if nothing found assert(len(stack) == 0) # stack expected to be empty! breakPositions.reverse() # reverse breakers list required cuz we found 'em in a reverse order :) # Iterate from the current position towards the end of a document pos = view.cursorPosition() # get current cursor position again firstIteration = True found = False for cl in range(pos.line(), document.lines()): lineStr = str(document.line(cl)) if not firstIteration: # skip first iteration pos.setColumn(0) # set current column to the start of current line else: firstIteration = False # do nothing on first iteration for cc in range(pos.column(), len(lineStr)): #kate.kDebug("c: current position" + str(cl) + "," + str(cc) + ",ch='" + lineStr[cc] + "'") # Check open/close brackets if lineStr[cc] == '(': stack.append((cl, cc, False)) #kate.kDebug("c) Add position: " + str(stack[-1])) continue if lineStr[cc] == ')': if len(stack): #kate.kDebug("c) Pop position: " + str(stack[-1])) nrl, nrc, isT = stack.pop() # remove a last position from the stack if not isT: nestedRanges.append( # and append a nested range KTextEditor.Range(nrl, nrc, cl, cc) ) else: raise LookupError( i18nc( '@info' , 'Misbalanced brackets: at <numid>%1</numid>,<numid>%2</numid> and <numid>%3</numid>,<numid>%4</numid>' , nrl + 1, nrc + 1, cl + 1, cc + 1 ) ) else: closePos = (cl, cc, False) # remember the range end #kate.kDebug("c) Found position: " + str(closePos)) found = True break continue # Check for template angel brackets if lineStr[cc] == '<': if looksLikeTemplateAngelBracket(lineStr, cc): stack.append((cl, cc, True)) #kate.kDebug("c> Add position: " + str(stack[-1])) #else: #kate.kDebug("c> Doesn't looks like template: " + str(cl) + "," + str(cc)) continue if lineStr[cc] == '>': if not looksLikeTemplateAngelBracket(lineStr, cc): #kate.kDebug("c> Doesn't looks like template: " + str(cl) + "," + str(cc)) pass elif len(stack): # if stack isn't empty (i.e. there are some closing chars met) #kate.kDebug("c> Pop position: " + str(stack[-1])) nrl, nrc, isT = stack.pop() # remove last position from the stack if isT: nestedRanges.append( # and append a nested range KTextEditor.Range(cl, cc, nrl, nrc) ) else: raise LookupError( i18nc( '@info' , 'Misbalanced brackets: at <numid>%1</numid>,<numid>%2</numid> and <numid>%3</numid>,<numid>%4</numid>' , nrl + 1, nrc + 1, cl + 1, cc + 1 ) ) else: closePos = (cl, cc, True) # remember the range end kate.kDebug("c> Found position: " + str(closePos)) found = True break continue if lineStr[cc] in breakChars and len(stack) == 0: breakPositions.append(KTextEditor.Cursor(cl, cc)) # Did we found smth on the current line? if found: break # Yep! Break the outer loop if not found: return (KTextEditor.Range(), list(), list()) # Return empty ranges if nothing found assert(len(stack) == 0) # stack expected to be empty! if openPos[2] != closePos[2]: raise LookupError( i18nc( '@info' , 'Misbalanced brackets: at <numid>%1</numid>,<numid>%2</numid> and <numid>%3</numid>,<numid>%4</numid>' , openPos[0] + 1, openPos[1] + 1, closePos[0] + 1, closePos[1] + 1 ) ) return (KTextEditor.Range(openPos[0], openPos[1], closePos[0], closePos[1]), nestedRanges, breakPositions)
def changeParagraphWidth(step): view = kate.activeView() doc = kate.activeDocument() pos = view.cursorPosition() originRange, isBlock = getParagraphRange(doc, pos) if originRange.isEmpty(): kate.ui.popup( i18nc('@title:window', 'Alert') , i18nc( '@info:tooltip' , 'Unable to detect a commented paragraph at cursor...' ) , 'dialog-information' ) return # Dunno what to do on empty range! indent = common.getCurrentLineIndentation(view) # detect current align # Processing: # 0) split text into left stripped lines originalText = view.document().text(originRange) lines = [line.lstrip() for line in originalText.split('\n')] # 1) detect comment style comment = [c.strip() for c in lines[0].split(' ')][0] # 2) strip leading comments (and possible left spaces) from each line lines = [line[len(comment):].lstrip() for line in lines] # 3) get a desired width of the current paragraph if step == -1: # 3.1) For shrink it is really simple: we just want to fit last word # to the next line, and it is enough to specify max(line size) - 1 newSize = len(max(lines, key=len)) - 1 elif step == 1: # 3.2) To extend paragraph we just want to append a first word from the next # after longest line. currentMax = 0 prevLineWasLongest = False delta = 0 for line in lines: # 3.2.1) if current maximum was changed on prevoius iteration, # get length of a first word on a line if prevLineWasLongest: # NOTE +1 for one space delta = len([word.strip() for word in line.split(' ')][0]) + 1 # 3.2.2) is current line longer than we've seen before? lineSize = len(line) prevLineWasLongest = bool(currentMax < lineSize) if prevLineWasLongest: currentMax = lineSize newSize = currentMax + delta else: assert(not 'Incorrect step specified') # 4) wrap the text res = textwrap.wrap(' '.join(lines), newSize, break_long_words=False) # 5) form a text from the result list align = ' ' * indent + comment + ' ' text = align + ('\n' + align).join(res) + '\n' # Return text only if smth really changed if originalText != text: # Update document only if smth really has changed doc.startEditing() # Start edit transaction: doc.removeText(originRange) # Remove the origin range doc.insertText(originRange.start(), text) # Insert modified text view.setCursorPosition(originRange.start()) # Move cursor to the start of the origin range doc.endEditing() # End transaction
def move_inline(): ''' Move a comment at the current line as inline comment of the next line (if latter has no comment yet) ''' document = kate.activeDocument() view = kate.activeView() pos = view.cursorPosition() commentCh = common.getCommentStyleForDoc(document) insertionText = [] currentLine = document.line(pos.line()) auxLine2Remove = 0 # Split a line before and after a comment (before, comment, after) = currentLine.partition(commentCh) # Is there some text on a line? if bool(before.strip()): return # Aha... move cursor co comment u stupid bastard! else: # No! What about comment? if bool(comment): # Aha... the comment is here. Ok. Lets get a line below the current... lineBelow = document.line(pos.line() + 1) (b_before, b_comment, b_after) = lineBelow.partition(commentCh) auxLine2Remove = 1 # Check for text and comment in it... if bool(b_before.strip()): # Text present... Comment? if bool(b_comment): # Comment too... just leave it... return else: # Just text.... no comment. Ok lets work! # (if there is some space remains for inline comment) b_before_s = b_before.rstrip() if len(b_before_s) > kate.configuration[COMMENT_START_POS]: # Oops! No space remains! Get outa here return else: doxCommentOffset = 0 if after[0:2] == '/ ': after = '/< ' + after[2:] doxCommentOffset = 2 insertionText.append( b_before_s + ' ' * (kate.configuration[COMMENT_START_POS] - len(b_before_s)) + commentCh + after.rstrip() ) column = kate.configuration[COMMENT_START_POS] + 3 + doxCommentOffset else: # No text on the line below! Dunno what damn user wants... return else: # Nothing! Just blank line... Dunno what to do... return pass # Update the document if bool(insertionText): document.startEditing() # Start edit transaction: if auxLine2Remove != 0: document.removeLine(pos.line() + auxLine2Remove) document.removeLine(pos.line()) # Remove current line # insert resulting text line by line... pos.setColumn(0) document.insertText(pos, '\n'.join(insertionText) + '\n') # Move cursor to desired position pos.setColumn(column) view.setCursorPosition(pos) document.endEditing() # End transaction