def _inject_expand_result(self, document, result, word_range, func): '''TODO Catch exceptions from provided function''' # Check what type of expand function it was if hasattr(func, 'template'): result = render_jinja_template(func.template, result) kate.kDebug('result={}'.format(repr(result))) assert(isinstance(result, str)) # with kate.makeAtomicUndo(document): document.removeText(word_range) kate.kDebug('Expanded text:\n{}'.format(result)) # Check if expand function requested a TemplateInterface2 to render # result content... if hasattr(func, 'use_template_iface') and func.use_template_iface: # Use TemplateInterface2 to insert a code snippet kate.kDebug('TI2 requested!') ti2 = document.activeView().templateInterface2() if ti2 is not None: ti2.insertTemplateText(word_range.start(), result, {}, None) return # Fallback to default (legacy) way... # Just insert text :) document.insertText(word_range.start(), result)
def expandUDFAtCursor(): ''' 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". TODO Split this function! ''' view = kate.activeView() document = view.document() try: word_range, argument_range = _wordAndArgumentAtCursorRanges(document, view.cursorPosition()) except ParseError as e: kate.ui.popup(i18nc('@title:window', 'Parse error'), e, 'dialog-warning') return word = document.text(word_range) expansions = getExpansionsFor(document.mimeType()) if word in expansions: func = expansions[word][0] else: kate.ui.popup( i18nc('@title:window', 'Error') , i18nc('@info:tooltip', 'Expansion "<icode>%1</icode>" 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())] kate.kDebug('Arguments = {}'.format(arguments)) # form a dictionary from args w/ '=' character, leave others in a list for arg in preArgs: if '=' in arg: key, value = [item.strip() for item in arg.split('=')] namedArgs[key] = value else: arguments.append(arg) # Call user expand function w/ parsed arguments and # possible w/ named params dict try: kate.kDebug('Arguments = {}'.format(arguments)) kate.kDebug('Named arguments = {}'.format(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, i18nc('@info', 'Traceback (most recent call last):<nl/>')) 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, i18nc('@info', '<link url="%1">%2</link>', filePath, fileName)) return text s = ''.join(l).strip() # FIXME: extract the filename and then use i18nc, instead of manipulate the i18nc text s = re.sub( i18nc('@info', 'File <filename>"(/[^\n]+)"</filename>, line') , replaceAbsolutePathWithLinkCallback , s ) kate.kDebug('EXPAND FAILURE: {}'.format(s)) kate.ui.popup( i18nc('@title:window', 'Error') , i18nc('@info:tooltip', '<bcode>%1</bcode>', s) , 'dialog-error' ) return # Check what type of expand function it was if hasattr(func, 'template'): replacement = render_jinja_template(func.template, replacement) assert(isinstance(replacement, str)) with kate.makeAtomicUndo(document): # Remove old text if argument_range is not None: document.removeText(argument_range) document.removeText(word_range) kate.kDebug('Expanded text:\n{}'.format(replacement)) # Check if expand function requested a TemplateInterface2 to render # result content... if hasattr(func, 'use_template_iface') and func.use_template_iface: # Use TemplateInterface2 to insert a code snippet kate.kDebug('TI2 requested!') ti2 = view.templateInterface2() if ti2 is not None: ti2.insertTemplateText(word_range.start(), replacement, {}, None) return # Fallback to default (legacy) way... # Just insert text :) document.insertText(word_range.start(), replacement)