def _ask_for_CMakeLists_location_and_try_open(start_dir_to_show, cur_doc_dir): selected_dir = KUrlRequesterDialog.getUrl( start_dir_to_show , kate.mainInterfaceWindow().window() , i18nc('@title:window', '<filename>CMakeLists.txt</filename> location') ) kate.kDebug('CMakeHelper: selected_dir={}'.format(selected_dir)) if selected_dir.isEmpty(): return # User pressed 'Cancel' selected_dir = selected_dir.toLocalFile() # Get selected path # Is it relative? if not os.path.isabs(selected_dir): # Yep, join w/ a path of the current doc selected_dir = os.path.abspath(os.path.join(cur_doc_dir, selected_dir)) # Check if there CMakeLists.txt present cmakelists = os.path.join(selected_dir, _CMAKE_LISTS) if _is_there_CMakeLists(selected_dir): # Open it! _openDocumentNoCheck(QUrl.fromLocalFile(cmakelists)) else: kate.ui.popup( i18nc('@title:window', 'Error') , i18nc('@info:tooltip', 'No such file <filename>%1</filename>', cmakelists) , 'dialog-error' )
def _collect_colors(document): '''Scan a given document and collect unique colors Returns a list of QColor objects. ''' result = [] # Iterate over document's lines trying to find #colors for l in range(0, document.lines()): line = document.line(l) # Get the current line start = 0 # Set initial position to 0 (line start) while start < len(line): # Repeat 'till the line end start = line.find('#', start) # Try to find a '#' character (start of #color) if start == -1: # Did we found smth? break # No! Nothing to do... # Try to get a word right after the '#' char end = start + 1 for c in line[end:]: if not (c in string.hexdigits or c in string.ascii_letters): break end += 1 color_range = KTextEditor.Range(l, start, l, end) color_str = document.text(color_range) color = QColor(color_str) if color.isValid() and color not in result: result.append(color) kate.kDebug('ColorUtils: scan for #colors found {}'.format(color_str)) start = end return result
def tryOpen(): view = kate.activeView() assert('View expected to be valid' and view is not None) assert('This action supposed to select some text before' and view.selection()) doc = view.document() doc_url = doc.url() new_url = KUrl(_try_make_url_from_text(view.selectionText())) kate.kDebug('Current document URI: {}'.format(repr(doc_url))) # 0) Make sure it is valid if not new_url.isValid(): kate.ui.popup( i18nc('@title:window', 'Error') , i18nc('@info:tooltip', "Selected text doesn't looks like a valid URI") , 'dialog-error' ) return # 1) Is it have some schema? and current document is not 'Untitled'... if new_url.isRelative() and not doc_url.isEmpty(): # Replace filename from the doc_url w/ the current selection new_url = doc_url.upUrl() new_url.addPath(view.selectionText()) kate.kDebug('Trying URI: {}'.format(repr(new_url))) # Ok, try to open it finally _try_open_show_error(new_url)
def track_input(self, char, cursor): assert('parameter expected to be a string' and isinstance(char, str)) #kate.kDebug('@KeySequenceTracker: input="{}"'.format(char)) #kate.kDebug('@KeySequenceTracker: curr-pos=({},{})'.format(cursor.line(),cursor.column())) #if self.next_expected_pos is not None: #kate.kDebug('@KeySequenceTracker: next-pos=({},{})'.format(self.next_expected_pos.line(),self.next_expected_pos.column())) if len(char) != 1: self.reset() # Looks like copy-n-pasted text... drop it! elif char == KeySequenceTracker.TRIGGER_CHAR: if self.next_expected_pos is not None: # Is tracking already enabled? self._dispatch() # Yep, then this is a final char... Handle it! self.reset() # Ready for next key sequence else: kate.kDebug('@KeySequenceTracker: ---[start tracking key sequence]---') self.next_expected_pos = KTextEditor.Cursor(cursor.line(), cursor.column() + 1) self.moved = False elif self.next_expected_pos is not None: # Do we record? # ATTENTION Ugly PyKDE do not export some vital functions! # Like relation operators for types (Cursor and Range for example)... is_equal_cursors = self.next_expected_pos.line() == cursor.line() \ and self.next_expected_pos.column() == cursor.column() if is_equal_cursors: self.sequence += char # Yeah! Just collect a one more char self.next_expected_pos = KTextEditor.Cursor(cursor.line(), cursor.column() + 1) else: kate.kDebug('@KeySequenceTracker: ---[next-pos != cursor]---') self.reset()
def _is_there_CMakeLists(path): '''Try to find `CMakeLists.txt` in a given path''' kate.kDebug('CMakeHelper: checking `{}` for CMakeLists.txt'.format(path)) assert(isinstance(path, str)) if os.access(os.path.join(path, _CMAKE_LISTS), os.R_OK): return True return False
def _dispatch(self): kate.kDebug('@KeySequenceTracker: collected key sequence "{}"'.format(self.sequence)) view = kate.activeView() cursor = view.cursorPosition() word_range = KTextEditor.Range( cursor.line() , cursor.column() - len(self.sequence) - 2 , cursor.line() , cursor.column() ) document = view.document() assert('Sanity check' and view is not None) mimeType = document.mimeType() expansions = getExpansionsFor(mimeType) if not hasattr(dynamic, 'registered_handlers') or mimeType not in dynamic.registered_handlers: return assert('Sanity check' and isinstance(dynamic.registered_handlers[mimeType], list)) for func in dynamic.registered_handlers[mimeType]: assert('Sanity check' and inspect.isfunction(func) and hasattr(func, 'match_regex')) match = func.match_regex.search(self.sequence) if match is not None: result = func(self.sequence, match) self._inject_expand_result(document, result, word_range, func) return
def _get_1st_arg_if_add_subdirectory_cmd(document, line): # TODO WTF? '\badd_subdirectory' doesn't match! Just wanted to # make sure that 'add_subdirectory' is not a part of some other word... match = _ADD_SUB_RE.search(document.line(line)) if match: kate.kDebug('CMakeHelper: line[{}] "{}" match: {}'.format(line, document.line(line), match.groups())) return [subdir for subdir in match.group(1).split() if subdir != _SUBDIR_OPT][0] return None
def addDir(self): path = KFileDialog.getExistingDirectory( KUrl('') , self , i18nc('@title:window', 'Select a Directory with CMake Modules') ) kate.kDebug('CMakeCC: got path={}'.format(path)) self.moduleDirs.addItem(str(path))
def track_cursor(self, cursor): kate.kDebug('@KeySequenceTracker: moved to ({},{})'.format(cursor.line(),cursor.column())) if self.next_expected_pos is not None \ and ( \ self.next_expected_pos.line() != cursor.line() \ or (self.next_expected_pos.column() + 1) != cursor.column() \ ): kate.kDebug('@KeySequenceTracker: moved from ({},{})'.format(self.next_expected_pos.line(),self.next_expected_pos.column())) self.reset()
def createSignalAutocompleteCMake(view=None, *args, **kwargs): try: view = view or kate.activeView() if view: kate.kDebug('CMake Helper Plugin: Registering completer') cci = view.codeCompletionInterface() cci.registerCompletionModel(_cmake_completion_model) except: kate.kDebug('CMake Helper Plugin: Unable to get an active view')
def getTextBlockAroundCursor(doc, pos, upPred, downPred): start = _getTextBlockAroundCursor(doc, pos.line(), doc.lines(), TOWARDS_START, upPred) end = _getTextBlockAroundCursor(doc, pos.line(), doc.lines(), TOWARDS_END, downPred) if start != end: start += 1 kate.kDebug( "getTextBlockAroundCursor[pos={},{}]: ({},{}), ({},{})".format(pos.line(), pos.column(), start, 0, end, 0) ) return KTextEditor.Range(start, 0, end, 0)
def _decorator(func): if not hasattr(dynamic, 'registered_handlers'): setattr(dynamic, 'registered_handlers', dict()) mimeType = kate.activeDocument().mimeType() if mimeType not in dynamic.registered_handlers: dynamic.registered_handlers[mimeType] = list() dynamic.registered_handlers[mimeType].append(func) setattr(func, 'match_regex', regex) kate.kDebug('Set KS expander for {} to {}'.format(mimeType, func.__name__)) return func
def destroy(): '''Plugins that use a toolview need to delete it for reloading to work.''' kate.kDebug('Unloading...') global _cmake_completion_model if _cmake_completion_model: _cmake_completion_model = None global _cmake_tool_view if _cmake_tool_view: _cmake_tool_view = None
def _mergeExpansions(left, right): assert(isinstance(left, dict) and isinstance(right, dict)) result = left for exp_key, exp_tuple in right.items(): if exp_key not in result: result[exp_key] = exp_tuple else: result[exp_key] = result[exp_key] kate.kDebug('WARNING: Ignore duplicate expansion `{}` from {}'.format(exp_key, exp_tuple[1])) kate.kDebug('WARNING: First defined here {}'.format(result[exp_key][1])) return result
def openDocument(url): local_file = url.toLocalFile() kate.kDebug('CMakeCC: going to open the document: {}'.format(local_file)) if os.access(local_file, os.R_OK): _openDocumentNoCheck(url) else: kate.ui.popup( i18nc('@title:window', 'Error') , i18nc('@info:tooltip', 'Unable to open the document: <filename>%1</filename>', local_file) , 'dialog-error' )
def jinja_environment_configurator(func): ''' ATTENTION Be aware that functions marked w/ jinja_environment_configurator decorator should have two leading underscores in their name! Otherwise, they will be available as ordinal expand functions, which is definitely UB. This made to keep `expand` code simple... ''' if not hasattr(jinja_environment_configurator, 'registered_configurators'): setattr(jinja_environment_configurator, 'registered_configurators', dict()) mimeType = kate.activeDocument().mimeType() jinja_environment_configurator.registered_configurators[mimeType] = func kate.kDebug('Set jinja2 environment configurator for {} to {}'.format(mimeType, func.__name__)) return func
def _loadCompleters(self): # Load available command completers for directory in kate.applicationDirectories('cmake_utils/command_completers'): kate.kDebug('CMakeCC: directory={}'.format(directory)) sys.path.append(directory) for completer in glob.glob(os.path.join(directory, '*_cc.py')): kate.kDebug('CMakeCC: completer={}'.format(completer)) cc_name = os.path.basename(completer).split('.')[0] module = importlib.import_module(cc_name, "cmake_utils.command_completers") if hasattr(module, self._cc_registrar_fn_name): r = getattr(module, self._cc_registrar_fn_name) if isinstance(r, types.FunctionType): r(self.__command_completers)
def _getJinjaEnvironment(baseDir): kate.kDebug('Make a templates loader for a base dir: {}'.format(baseDir)) env = jinja2.Environment(loader=jinja2.FileSystemLoader(baseDir)) env.filters['editable'] = _makeEditableField env.tests['boolean'] = _is_bool env.tests['integer'] = _is_int if hasattr(jinja_environment_configurator, 'registered_configurators'): mimeType = kate.activeDocument().mimeType() configurator = jinja_environment_configurator.registered_configurators[mimeType] kate.kDebug('Setup jinja2 environment for {} [{}]'.format(mimeType, configurator.__name__)) env = configurator(env) return env
def _loadExpansionsFromFile(path): kate.kDebug('Loading expansions from {}'.format(path)) name = os.path.basename(path).split('.')[0] module = imp.load_source(name, path) expansions = {} # expansions are everything that don't begin with '__' and are callable for name in dir(module): o = getattr(module, name) # ignore builtins. Note that it is callable.__name__ that is used # to set the expansion key so you are free to reset it to something # starting with two underscores (or more importantly, a Python # keyword) # NOTE Detect ONLY a real function! if not name.startswith('__') and inspect.isfunction(o): expansions[o.__name__] = (o, path) kate.kDebug('Adding expansion `{}`'.format(o.__name__)) return expansions
def toggleDocTypeSeisitiveActions(): view = kate.activeView() if view is None: return doc_type = view.document().highlightingMode() kate.kDebug('toggleDocTypeSeisitiveActions: doc_type={}'.format(doc_type)) clnt = kate.getXmlGuiClient() if doc_type in ['C++', 'C++/Qt4', 'C']: clnt.stateChanged('if0_actions') else: clnt.stateChanged('if0_actions', KXMLGUIClient.StateReverse) if common.isKnownCommentStyle(doc_type): clnt.stateChanged('comment_actions') else: clnt.stateChanged('comment_actions', KXMLGUIClient.StateReverse)
def toggleSelectionSensitiveActions(view): clnt = kate.getXmlGuiClient() filename = _try_make_url_from_text(view.selectionText()) kate.kDebug('Original new filename: {}'.format(repr(filename))) if view.selection() and len(filename) < _SANE_URI_LENGTH: clnt.stateChanged('has_selection') # Change action text... if _URI_LENGTH_SHOW_THRESHOLD < len(filename): lead_pos = int(2 * _URI_LENGTH_SHOW_THRESHOLD / 3) tail_pos = -int(_URI_LENGTH_SHOW_THRESHOLD / 3) filename = filename[:lead_pos] + '...' + filename[tail_pos:] assert('Sanity check' and hasattr(tryOpen, 'action')) kate.kDebug('New filename: {}'.format(filename)) tryOpen.action.setText( i18nc('@ation:inmenu', 'Open <filename>%1</filename>', filename) ) else: clnt.stateChanged('has_selection', KXMLGUIClient.StateReverse) tryOpen.action.setText(i18nc('@ation:inmenu', 'Open selected document'))
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 init(): # Set default value if not configured yet kate.kDebug('Loading...') if settings.CMAKE_BINARY not in kate.sessionConfiguration: kate.sessionConfiguration[settings.CMAKE_BINARY] = settings.CMAKE_BINARY_DEFAULT if settings.PARENT_DIRS_LOOKUP_CNT not in kate.sessionConfiguration: kate.sessionConfiguration[settings.PARENT_DIRS_LOOKUP_CNT] = 0 if settings.AUX_MODULE_DIRS not in kate.sessionConfiguration: kate.sessionConfiguration[settings.AUX_MODULE_DIRS] = [] if settings.PROJECT_DIR not in kate.sessionConfiguration: kate.sessionConfiguration[settings.PROJECT_DIR] = '' if settings.TOOLVIEW_ADVANCED_MODE not in kate.sessionConfiguration: kate.sessionConfiguration[settings.TOOLVIEW_ADVANCED_MODE] = False if settings.TOOLVIEW_BEAUTIFY not in kate.sessionConfiguration: kate.sessionConfiguration[settings.TOOLVIEW_BEAUTIFY] = True # Create completion model global _cmake_completion_model if _cmake_completion_model is None: kate.kDebug('CMake Helper Plugin: Create a completer') _cmake_completion_model = CMakeCompletionModel(kate.application) _cmake_completion_model.modelReset.connect(_reset) # Make an instance of a cmake tool view global _cmake_tool_view if _cmake_tool_view is None: kate.kDebug('CMake Helper Plugin: Create a tool view') _cmake_tool_view = CMakeToolView()
def updateColors(self, view=None): '''Scan a document for #colors Returns a list of tuples: QColor and range in a document TODO Some refactoring needed to reduce code duplication (@sa _get_color_range_under_cursor()) ''' self.colors = [] # Clear previous colors if view: document = view.document() else: try: document = kate.activeDocument() except kate.NoActiveView: return # Do nothing if we can't get a current document # Iterate over document's lines trying to find #colors for l in range(0, document.lines()): line = document.line(l) # Get the current line start = 0 # Set initial position to 0 (line start) while start < len(line): # Repeat 'till the line end start = line.find('#', start) # Try to find a '#' character (start of #color) if start == -1: # Did we found smth? break # No! Nothing to do... # Try to get a word right after the '#' char end = start + 1 for c in line[end:]: if not (c in string.hexdigits): break end += 1 color_range = KTextEditor.Range(l, start, l, end) color_str = document.text(color_range) color = QColor(color_str) if color.isValid(): self.colors.append(ColorRangePair(color, color_range)) kate.kDebug('ColorUtilsToolView: scan for #colors found {}'.format(color_str)) start = end
def render_jinja_template(template, data): assert(isinstance(data, dict)) assert(isinstance(template, str)) # Add some predefined variables # - `nl` == new line character if 'nl' not in data: data['nl'] = '\n' # - `tab` == one TAB character if 'tab' not in data: data['tab'] = '\t' # - `space` == one space character if 'space' not in data: data['space'] = ' ' result = None # Ok, going to render some jinja2 template... filename = kate.findApplicationResource('{}/{}'.format(_JINJA_TEMPLATES_BASE_DIR, template)) if not filename: kate.ui.popup( i18nc('@title:window', 'Error') , i18nc('@info:tooltip', 'Template file not found <filename>%1</filename>', template) , 'dialog-error' ) return result kate.kDebug('found abs template: {}'.format(filename)) # Get a corresponding environment for jinja! base_dir_pos = filename.find(_JINJA_TEMPLATES_BASE_DIR) assert(base_dir_pos != -1) basedir = filename[:base_dir_pos + len(_JINJA_TEMPLATES_BASE_DIR)] filename = filename[base_dir_pos + len(_JINJA_TEMPLATES_BASE_DIR) + 1:] env = _getJinjaEnvironment(basedir) kate.kDebug('basedir={}, template_rel={}'.format(basedir, filename)) try: tpl = env.get_template(filename) kate.kDebug('data dict={}'.format(data)) result = tpl.render(data) except jinja2.TemplateError as e: kate.ui.popup( i18nc('@title:window', 'Error') , i18nc( '@info:tooltip' , 'Template file error [<filename>%1</filename>]: <status>%2</status>' , template , e.message ) , 'dialog-error' ) return result
def _try_syntactic_completer(self, syntax, document, cursor, word, comp_list): kate.kDebug('CMakeCC: generic completer: syntax='+str(syntax)) kate.kDebug('CMakeCC: generic completer: comp_list='+str(comp_list)) result = [] if isinstance(syntax, list): for sid, s in enumerate(syntax): (items, stop) = s.complete(document, cursor, word, comp_list, sid) if stop: return items result += items else: (items, stop) = syntax.complete(document, cursor, word, comp_list) result = items kate.kDebug('CMakeCC: generic completer result={}'.format(result)) # TODO sort | uniq return result
def apply(self): kate.sessionConfiguration[settings.CMAKE_BINARY] = self.cmakeBinary.text() kate.sessionConfiguration[settings.PARENT_DIRS_LOOKUP_CNT] = self.parentDirsLookupCnt.value() try: cmake_help_parser.validate_cmake_executable(kate.sessionConfiguration[settings.CMAKE_BINARY]) except ValueError as error: kate.ui.popup( i18nc('@title:window', 'Error') , i18nc('@info:tooltip', 'CMake executable test run failed:<nl/><message>%1</message>', error) , 'dialog-error' ) # TODO Store the following for a current session! kate.sessionConfiguration[settings.PROJECT_DIR] = self.projectBuildDir.text() kate.sessionConfiguration[settings.AUX_MODULE_DIRS] = [] for i in range(0, self.moduleDirs.count()): kate.sessionConfiguration[settings.AUX_MODULE_DIRS].append(self.moduleDirs.item(i).text()) # Show some spam kate.kDebug('CMakeCC: config save: CMAKE_BINARY={}'.format(kate.sessionConfiguration[settings.CMAKE_BINARY])) kate.kDebug('CMakeCC: config save: AUX_MODULE_DIRS={}'.format(kate.sessionConfiguration[settings.AUX_MODULE_DIRS])) kate.kDebug('CMakeCC: config save: PROJECT_DIR={}'.format(kate.sessionConfiguration[settings.PROJECT_DIR])) kate.sessionConfiguration.save()
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 on_text_changed(doc, rng1, text, rng2): kate.kDebug('Text changed: rng1={}, rng2={}, text={}'.format(repr(text)))
def on_text_removed(doc, rng, text): kate.kDebug('Remove text: {}'.format(repr(text)))