def toggleBlock(): 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: ui.popup("Oops", "It seems cursor positioned out of any #if0/#if1 block", "face-sad")
def changeParagraphWidth(step): view = kate.activeView() doc = kate.activeDocument() pos = view.cursorPosition() originRange, isBlock = getParagraphRange(doc, pos) if originRange.isEmpty(): ui.popup("Sorry", "can't detect commented paragraph at cursor...", "face-sad") 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 test(): doc = kate.activeDocument() view = kate.activeView() ui.popup( text="Current document MIME type: <b>" + doc.mimeType() + "</b><br/>hl: <b>" + doc.highlightingMode() + "</b>" , caption="Some document info: file type" , iconName="face-wink" )
def comment_char_checker(dummy, document): doc_type = document.highlightingMode() result = common.isKnownCommentStyle(doc_type) if not result: ui.popup( "Oops!", "Don't know how comments look like for " + doc_type + " documents!", "face-uncertain") return result
def doc_type_checker(doc_types, document): doc_type = document.highlightingMode() if doc_type not in doc_types: ui.popup( "Alert", "This action have no sense for " + doc_type + " documents!", "face-wink") return False return True
def changeParagraphWidth(step): view = kate.activeView() doc = kate.activeDocument() pos = view.cursorPosition() originRange, isBlock = getParagraphRange(doc, pos) if originRange.isEmpty(): ui.popup("Sorry", "can't detect commented paragraph at cursor...", "face-sad") return # Dunno what to do on empty range! indent = 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 doc_type_checker(doc_types, document): doc_type = document.highlightingMode() if doc_type not in doc_types: ui.popup( "Alert" , "This action have no sense for " + doc_type + " documents!" , "face-wink" ) return False return True
def comment_char_checker(dummy, document): doc_type = document.highlightingMode() result = common.isKnownCommentStyle(doc_type) if not result: ui.popup( "Oops!" , "Don't know how comments look like for " + doc_type + " documents!" , "face-uncertain" ) return result
def selection_mode_checker(selectionMode, document): view = document.activeView() result = selectionMode == view.blockSelection() if not result: if selectionMode: mode = 'block' else: mode = 'normal' ui.popup("Oops!", "This operation is for %s selection mode!" % mode, "face-sad") return result
def has_selection_checker(selectionState, document): view = document.activeView() result = selectionState == view.selection() print("*** has_selection: result=%s" % repr(result)) if not result: if not selectionState: should = "n't" else: should = '' ui.popup( "Oops!", "Document should%s have selection to perform this operation" % should, "face-sad") return result
def selection_mode_checker(selectionMode, document): view = document.activeView() result = selectionMode == view.blockSelection() if not result: if selectionMode: mode = 'block' else: mode = 'normal' ui.popup( "Oops!" , "This operation is for %s selection mode!" % mode , "face-sad" ) return result
def selectBlock(): 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: ui.popup("Oops", "It seems cursor positioned out of any #if0/#if1 block", "face-sad")
def has_selection_checker(selectionState, document): view = document.activeView() result = selectionState == view.selection() print("*** has_selection: result=%s" % repr(result)) if not result: if not selectionState: should = "n't" else: should = '' ui.popup( "Oops!" , "Document should%s have selection to perform this operation" % should , "face-sad" ) return result
def removeBlock(): ''' 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: ui.popup("Oops", "It seems cursor positioned out of any #if0/#if1 block", "face-sad")
def selectBlock(): '''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: ui.popup("Oops", "It seems cursor positioned out of any #if0/#if1 block", "face-sad")
def removeBlock(): 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) print r 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: ui.popup("Oops", "It seems cursor positioned out of any #if0/#if1 block", "face-sad")
def boostUnformat(): '''Merge everything between '(' and ')' into a single line''' document = kate.activeDocument() view = kate.activeView() try: r, nestedRanges, breakPositions = getRangeTopology(',') except LookupError as error: ui.popup("Failed to parse C++ expression", str(error), "face-sad") return if r.isEmpty(): # Is range empty? ui.popup("Failed to parse C++ expression", "Didn't found anything to format. Sorry", "face-sad") return # Nothing interesting wasn't found... # Rescan the range w/ ';' as breaker added if current range is a `for` statement if document.line(r.start().line())[0:r.start().column() - 1].rstrip().endswith('for'): try: r, nestedRanges, breakPositions = getRangeTopology(',;') except LookupError as error: ui.popup("Failed to parse C++ expression", str(error), "face-sad") return # Going to unformat a text whithin a selected range text = boostUnformatText(r, breakPositions)
def boostFormat(): document = kate.activeDocument() view = kate.activeView() try: r, nestedRanges, breakPositions = getRangeTopology(',') except LookupError as error: ui.popup("Failed to parse C++ expression", str(error), "face-sad") return if r.isEmpty(): # Is range empty? ui.popup( "Failed to parse C++ expression" , "Didn't found anything to format. Sorry" , "face-sad" ) return # Nothing interesting wasn't found... # Rescan the range w/ ';' as breaker added if current range is a `for` statement if document.line(r.start().line())[0:r.start().column() - 1].rstrip().endswith('for'): try: r, nestedRanges, breakPositions = getRangeTopology(',;') except LookupError as error: ui.popup("Failed to parse C++ expression", str(error), "face-sad") return # Going to unformat a text whithin a selected range text = boostUnformatText(r, breakPositions)
def toggleBlock(): ''' 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: ui.popup("Oops", "It seems cursor positioned out of any #if0/#if1 block", "face-sad")
def boostFormat(): '''Format function's/template's parameters list (or `for`'s) in a boost-like style I.e. when 2nd and the rest parameters has leading comma/semicolon and closing ')' or '>' on a separate line. THIS IS REALLY BETTER TO HAVE SUCH STYLE WHEN U HAVE A LONG PARAMETERS LIST! ''' document = kate.activeDocument() view = kate.activeView() try: r, nestedRanges, breakPositions = getRangeTopology(',') except LookupError as error: ui.popup("Failed to parse C++ expression", str(error), "face-sad") return if r.isEmpty(): # Is range empty? ui.popup( "Failed to parse C++ expression" , "Didn't found anything to format. Sorry..." , "face-sad" ) return # Nothing interesting wasn't found... # Rescan the range w/ ';' as breaker added if current range is a `for` statement if document.line(r.start().line())[0:r.start().column() - 1].rstrip().endswith('for'): try: r, nestedRanges, breakPositions = getRangeTopology(',;') except LookupError as error: ui.popup("Failed to parse C++ expression", str(error), "face-sad") return # Going to format a text whithin a selected range lineStr = document.line(r.start().line()) lineStrStripped = lineStr.lstrip() indent = len(lineStr) - len(lineStrStripped) if lineStrStripped.startswith(', '): indent += 2 text = boostFormatText(r, indent, breakPositions)
def expandAtCursor(): """Attempt text expansion on the word at the cursor. The expansions available are based firstly on the mimetype of the document, for example "text_x-c++src.expand" for "text/x-c++src", and secondly on "all.expand". """ document = kate.activeDocument() view = document.activeView() try: word_range, argument_range = wordAndArgumentAtCursorRanges( document, view.cursorPosition()) except ParseError as e: kate.popup('Parse error:', e) return word = document.text(word_range) mime = str(document.mimeType()) expansions = loadExpansions(mime) try: func = expansions[word] except KeyError: ui.popup('Error', 'Expansion "%s" not found :(' % word, 'dialog-warning') return arguments = [] namedArgs = {} if argument_range is not None: # strip parentheses and split arguments by comma preArgs = [ arg.strip() for arg in document.text(argument_range)[1:-1].split(',') if bool(arg.strip()) ] print('>> EXPAND: arguments = ' + repr(arguments)) # form a dictionary from args w/ '=' character, leave others in a list for arg in preArgs: print('>> EXPAND: current arg = ' + repr(arg)) if '=' in arg: key, value = [item.strip() for item in arg.split('=')] print('>> EXPAND: key = ' + repr(key)) print('>> EXPAND: value = ' + repr(value)) namedArgs[key] = value else: arguments.append(arg) # Call user expand function w/ parsed arguments and # possible w/ named params dict try: print('>> EXPAND: arguments = ' + repr(arguments)) print('>> EXPAND: named arguments = ' + repr(namedArgs)) if len(namedArgs): replacement = func(*arguments, **namedArgs) else: replacement = func(*arguments) except Exception as e: # remove the top of the exception, it's our code try: type, value, tb = sys.exc_info() sys.last_type = type sys.last_value = value sys.last_traceback = tb tblist = traceback.extract_tb(tb) del tblist[:1] l = traceback.format_list(tblist) if l: l.insert(0, 'Traceback (most recent call last):\n') l[len(l):] = traceback.format_exception_only(type, value) finally: tblist = tb = None # convert file names in the traceback to links. Nice. def replaceAbsolutePathWithLinkCallback(match): text = match.group() filePath = match.group(1) fileName = os.path.basename(filePath) text = text.replace(filePath, '<a href="%s">%s</a>' % (filePath, fileName)) return text s = ''.join(l).strip() s = re.sub('File "(/[^\n]+)", line', replaceAbsolutePathWithLinkCallback, s) ui.popup('Error', '<p style="white-space:pre">%s</p>' % s, 'dialog-error') return #KateDocumentConfig::cfReplaceTabsDyn indentCharacters = indentationCharacters(document) # convert newlines followed by tab characters to whatever spacing # the user... uses. for i in range(100): if '\n' + (indentCharacters * i) + '\t' in replacement: replacement = replacement.replace( '\n' + (indentCharacters * i) + '\t', '\n' + (indentCharacters * (i + 1))) insertPosition = word_range.start() line = document.line(insertPosition.line()) # autoindent: add the line's leading whitespace for each newline # in the expansion whitespace = '' for character in line: if character in ' \t': whitespace += character else: break replacement = replacement.replace('\n', '\n' + whitespace) # is desired cursor position set? cursorAdvancement = None if '%{cursor}' in replacement: cursorAdvancement = replacement.index('%{cursor}') # strip around that word replacement = replacement[:cursorAdvancement] + replacement[ cursorAdvancement + 9:] # make the removal and insertion an atomic operation document.startEditing() if argument_range is not None: document.removeText(argument_range) document.removeText(word_range) document.insertText(insertPosition, replacement) # end before moving the cursor to avoid a crash document.endEditing() if cursorAdvancement is not None: # TODO The smartInterface isn't available anymore! # But it's successor (movingInterface) isn't available yet in # PyKDE4 <= 4.8.3 (at least) :( -- so, lets move a cursor manually... while True: currentLength = document.lineLength(insertPosition.line()) if cursorAdvancement <= currentLength: break else: insertPosition.setLine(insertPosition.line() + 1) cursorAdvancement -= currentLength + 1 # NOTE +1 for every \n char insertPosition.setColumn(insertPosition.column() + cursorAdvancement) view.setCursorPosition(insertPosition)