def test_location(): tu = get_tu(baseInput) one = get_cursor(tu, 'one') two = get_cursor(tu, 'two') assert one is not None assert two is not None assert_location(one.location, line=1, column=5, offset=4) assert_location(two.location, line=2, column=5, offset=13) # adding a linebreak at top should keep columns same tu = get_tu('\n' + baseInput) one = get_cursor(tu, 'one') two = get_cursor(tu, 'two') assert one is not None assert two is not None assert_location(one.location, line=2, column=5, offset=5) assert_location(two.location, line=3, column=5, offset=14) # adding a space should affect column on first line only tu = get_tu(' ' + baseInput) one = get_cursor(tu, 'one') two = get_cursor(tu, 'two') assert_location(one.location, line=1, column=6, offset=5) assert_location(two.location, line=2, column=5, offset=14) # define the expected location ourselves and see if it matches # the returned location tu = get_tu(baseInput) file = File.from_name(tu, 't.c') location = SourceLocation.from_position(tu, file, 1, 5) cursor = Cursor.from_location(tu, location) one = get_cursor(tu, 'one') assert one is not None assert one == cursor # Ensure locations referring to the same entity are equivalent. location2 = SourceLocation.from_position(tu, file, 1, 5) assert location == location2 location3 = SourceLocation.from_position(tu, file, 1, 4) assert location2 != location3 offset_location = SourceLocation.from_offset(tu, file, 5) cursor = Cursor.from_location(tu, offset_location) verified = False for n in [n for n in tu.cursor.get_children() if n.spelling == 'one']: assert n == cursor verified = True assert verified
def generateDoxygenForSourceLocation(line, col): filename = vim.current.buffer.name index = Index.create() tu = index.parse(filename, vim.eval("g:clang_doxygen_clang_args"), [(filename, "\n".join(vim.current.buffer[:]))]) # Skip whitespace at beginning of line indent = re.match(r'^\s*', vim.current.buffer[line - 1]).span()[1] col = max(col, indent + 1) c = Cursor.from_location(tu, SourceLocation.from_position(tu, File.from_name(tu, filename), line, col)) # If there is no declaration at the source location try to find the nearest one. while c is not None: # If cursor is on a TypeRef in a FunctionTemplate, manually go backward in the source. if c.kind == CursorKind.TYPE_REF: pLine, pCol = previousSourceLocation(c.extent.start.line, c.extent.start.column) c = Cursor.from_location(tu, SourceLocation.from_position(tu, File.from_name(tu, filename), pLine, pCol)) continue # If cursor is on a NamespaceRef, manually go forward in the source. elif c.kind == CursorKind.NAMESPACE_REF: nLine, nCol = nextSourceLocation(c.extent.end.line, c.extent.end.column) c = Cursor.from_location(tu, SourceLocation.from_position(tu, File.from_name(tu, filename), nLine, nCol)) continue elif c.kind == CursorKind.FUNCTION_DECL: return handleFunctionDecl(c) elif c.kind == CursorKind.CXX_METHOD: return handleFunctionDecl(c) elif c.kind == CursorKind.CONSTRUCTOR: return handleFunctionDecl(c) elif c.kind == CursorKind.DESTRUCTOR: return handleFunctionDecl(c) elif c.kind == CursorKind.FUNCTION_TEMPLATE: return handleFunctionTemplate(c) elif c.kind == CursorKind.CLASS_DECL: return handleClassDecl(c) elif c.kind == CursorKind.CLASS_TEMPLATE: return handleClassDecl(c) elif c.kind == CursorKind.OBJC_INSTANCE_METHOD_DECL: return handleFunctionDecl(c) elif c.kind == CursorKind.OBJC_INTERFACE_DECL: return handleClassDecl(c) elif c.kind == CursorKind.OBJC_CATEGORY_DECL: return handleClassDecl(c) elif c.kind == CursorKind.OBJC_IMPLEMENTATION_DECL: return handleClassDecl(c) # Cursor is not on a supported type, go to the lexical parent else: c = c.lexical_parent if c is None: print "Error: No supported declaration found at %s:%i,%i.\n" % (filename, line, col) return (None, None)
def gotoDeclaration(preview=True): global debug debug = int(vim.eval("g:clang_debug")) == 1 params = getCompileParams(vim.current.buffer.name) line, col = vim.current.window.cursor timer = CodeCompleteTimer(debug, vim.current.buffer.name, line, col, params) with libclangLock: tu = getCurrentTranslationUnit(params['args'], getCurrentFile(), vim.current.buffer.name, timer, update = True) if tu is None: print("Couldn't get the TranslationUnit") return f = File.from_name(tu, vim.current.buffer.name) loc = SourceLocation.from_position(tu, f, line, col + 1) cursor = Cursor.from_location(tu, loc) defs = [cursor.get_definition(), cursor.referenced] for d in defs: if d is not None and loc != d.location: loc = d.location if loc.file is not None: jumpToLocation(loc.file.name, loc.line, loc.column, preview) break timer.finish()
def gotoDeclaration(preview=True): global debug debug = int(vim.eval("g:clang_debug")) == 1 params = getCompileParams(vim.current.buffer.name) line, col = vim.current.window.cursor timer = CodeCompleteTimer(debug, vim.current.buffer.name, line, col, params) with libclangLock: tu = getCurrentTranslationUnit(params['args'], getCurrentFile(), vim.current.buffer.name, timer, update=True) if tu is None: print("Couldn't get the TranslationUnit") return f = File.from_name(tu, vim.current.buffer.name) loc = SourceLocation.from_position(tu, f, line, col + 1) cursor = Cursor.from_location(tu, loc) defs = [cursor.get_definition(), cursor.referenced] for d in defs: if d is not None and loc != d.location: loc = d.location if loc.file is not None: jumpToLocation(loc.file.name, loc.line, loc.column, preview) break timer.finish()
def get_usr_under_cursor(self, file_name, line, col): with self.c_parse_lock: if file_name not in self.current_file_tus: return "" tu = self.current_file_tus[file_name] f = File.from_name(tu, file_name) loc = SourceLocation.from_position(tu, f, int(line), int(col)) cursor = Cursor.from_location(tu, loc) while cursor is not None and (not cursor.referenced or not cursor.referenced.get_usr()): nextCursor = cursor.lexical_parent if nextCursor is not None and nextCursor == cursor: return "" cursor = nextCursor if cursor is None: return "" cursor = cursor.referenced if cursor is None: return "" return { 'usr': cursor.get_usr(), 'file': str(cursor.location.file), 'line': cursor.location.line, 'column': cursor.location.column }
def local_references(libclang, translation_unit, path, line, column): source_location = translation_unit.get_location(path, (line, column)) cursor = Cursor.from_location(translation_unit, source_location) if cursor is None: return None if cursor.referenced is not None: cursor = cursor.referenced if cursor.kind == CursorKind.NO_DECL_FOUND: return None references = [] def visitor(_context, cursor, source_range): range_dict = range_dict_relative(source_range) # The end location is non-inclusive. range_dict['end']['column'] -= 1 references.append(range_dict) return 1 # continue visitor_obj = CXCursorAndRangeVisitor() visitor_obj.visit = CFUNCTYPE(c_int, c_void_p, Cursor, SourceRange)(visitor) libclang.clang_findReferencesInFile(cursor, source_location.file, visitor_obj) return { 'cursor_name': cursor.spelling, 'cursor_kind': cursor.kind.name, 'references': references, }
def name_of(node): spelling = node.spelling.decode('utf-8') if spelling: return spelling typedef_node = Cursor.from_location(node.translation_unit, node.extent.end) if typedef_node.kind == CursorKind.TYPEDEF_DECL: return name_of(typedef_node) else: return ''
def GetCurrentUsrCursor(tu): line, col = vim.current.window.cursor col = col + 1 f = File.from_name(tu, vim.current.buffer.name) loc = SourceLocation.from_position(tu, f, line, col) cursor = Cursor.from_location(tu, loc) while cursor is not None and (not cursor.referenced or not cursor.referenced.get_usr()): nextCursor = cursor.lexical_parent if nextCursor is not None and nextCursor == cursor: return None cursor = nextCursor if cursor is None: return None return cursor.referenced
def get_declaration_info(self, request): contents = request['contents'] line = request['line'] column = request['column'] # Update the translation unit with the latest contents. translation_unit = self._update_translation_unit(contents) if not translation_unit: return None location = translation_unit.get_location(self.src, (line + 1, column + 1)) cursor = Cursor.from_location(translation_unit, location) cursor = cursor.referenced if cursor is None: return None return self.get_declaration_info_for_cursor(cursor)
def convert_canonical(typ): decl = typ.get_declaration() struct_name = decl.displayname.decode('utf-8') if not struct_name: typedef_node = Cursor.from_location(decl.translation_unit, decl.extent.end) if typedef_node.kind == CursorKind.TYPEDEF_DECL: struct_name = typedef_node.displayname.decode('utf-8') if struct_name: sb = [struct_name] else: sb = [RECORD_KEYWORD[decl.kind], ' { '] for node in decl.get_children(): name = node.spelling.decode('utf-8') sb.append(to_cc(node.type, name)) sb.append('; ') sb.append('}') return (''.join(sb), '', '')
def find_declaration(self, data, lines): self.join_queue() context = data['context'] src = self.get_src("\n".join(lines), context) filepath = context['filepath'] bcol = context['bcol'] lnum = context['lnum'] args, directory = self.get_args_dir(data) tu = self.get_tu(filepath, args, directory, src) f = File.from_name(tu, filepath) location = SourceLocation.from_position(tu, f, lnum, bcol) cursor = Cursor.from_location(tu, location) defs = [cursor.get_definition(), cursor.referenced] for d in defs: if d is None: logger.info("d None") continue d_loc = d.location if d_loc.file is None: logger.info("location.file None") continue ret = {} ret['file'] = d_loc.file.name ret['lnum'] = d_loc.line ret['bcol'] = d_loc.column return ret # we failed finding the declaration, maybe there's some syntax error # stopping us. Report it to the user. logger.info('reading Diagnostic for this tu, args: %s', args) for diag in tu.diagnostics: # type: Diagnostic if diag.severity < diag.Error: pass self.nvim.call('ncm2_pyclang#error', diag.format()) return {}
def get_usr_under_cursor(self, file_name, line, col): with self.c_parse_lock: if file_name not in self.current_file_tus: return "" tu = self.current_file_tus[file_name] f = File.from_name(tu, file_name) loc = SourceLocation.from_position(tu, f, int(line), int(col)) cursor = Cursor.from_location(tu, loc) while cursor is not None and (not cursor.referenced or not cursor.referenced.get_usr()): nextCursor = cursor.lexical_parent if nextCursor is not None and nextCursor == cursor: return "" cursor = nextCursor if cursor is None: return "" cursor = cursor.referenced if cursor is None: return "" return {'usr': cursor.get_usr(), 'file': str(cursor.location.file), 'line': cursor.location.line, 'column': cursor.location.column}
def gotoDeclaration(self, preview=True): params = self.getCompileParams(self.vim.current.buffer.name) line, col = self.vim.current.window.cursor tu = self.getCurrentTranslationUnit(params['args'], self.getCurrentFile(), self.vim.current.buffer.name, update = True) if tu is None: print("Couldn't get the TranslationUnit") return f = File.from_name(tu, self.vim.current.buffer.name) loc = SourceLocation.from_position(tu, f, line, col + 1) cursor = Cursor.from_location(tu, loc) defs = [cursor.get_definition(), cursor.referenced] for d in defs: if d is not None and loc != d.location: loc = d.location if loc.file is not None: self.jumpToLocation(loc.file.name, loc.line, loc.column, preview) break
def test_location(): index = Index.create() tu = index.parse('t.c', unsaved_files = [('t.c',baseInput)]) for n in tu.cursor.get_children(): if n.spelling == 'one': assert_location(n.location,line=1,column=5,offset=4) if n.spelling == 'two': assert_location(n.location,line=2,column=5,offset=13) # adding a linebreak at top should keep columns same tu = index.parse('t.c', unsaved_files = [('t.c',"\n"+baseInput)]) for n in tu.cursor.get_children(): if n.spelling == 'one': assert_location(n.location,line=2,column=5,offset=5) if n.spelling == 'two': assert_location(n.location,line=3,column=5,offset=14) # adding a space should affect column on first line only tu = index.parse('t.c', unsaved_files = [('t.c'," "+baseInput)]) for n in tu.cursor.get_children(): if n.spelling == 'one': assert_location(n.location,line=1,column=6,offset=5) if n.spelling == 'two': assert_location(n.location,line=2,column=5,offset=14) # define the expected location ourselves and see if it matches # the returned location tu = index.parse('t.c', unsaved_files = [('t.c',baseInput)]) file = File.from_name(tu, 't.c') location = SourceLocation.from_position(tu, file, 1, 5) cursor = Cursor.from_location(tu, location) for n in tu.cursor.get_children(): if n.spelling == 'one': assert n == cursor
def local_references(libclang, translation_unit, path, line, column): source_location = translation_unit.get_location(path, (line, column)) cursor = Cursor.from_location(translation_unit, source_location) if cursor is None: return None if cursor.referenced is not None: cursor = cursor.referenced if cursor.kind == CursorKind.NO_DECL_FOUND: return None references = [] def visitor(_context, cursor, source_range): references.append(range_dict_relative(source_range)) return 1 # continue visitor_obj = CXCursorAndRangeVisitor() visitor_obj.visit = CFUNCTYPE(c_int, c_void_p, Cursor, SourceRange)(visitor) libclang.clang_findReferencesInFile(cursor, source_location.file, visitor_obj) return { 'cursor_name': cursor.spelling, 'cursor_kind': cursor.kind.name, 'references': references, }
def get_static_locations(dynamic_locations, clang_include_paths): """ Get locations for certain constructs which are only available statically. - Variable declarations without any executable code "int i;" - Case statements "case foo:" - Default statements "default: " """ static_locations = [] def ancestor_node(n): """ Get the nearest significant ancestor. """ if n.kind == CursorKind.FUNCTION_DECL: return n else: if n.semantic_parent is None: return n else: return ancestor_node(n.semantic_parent) def good(n): """ Node should be added to the trace. """ if n.kind in (CursorKind.VAR_DECL, CursorKind.CASE_STMT, CursorKind.DEFAULT_STMT): return True else: return False filepaths = defaultdict(list) for l in dynamic_locations: filepaths[l.filepath].append(l) for filepath, locations in filepaths.items(): log.debug( f'Parsing source file {filepath} with args {clang_include_paths}') root = nodeutils.parse(filepath, clang_include_paths) ancestors = [] file = File.from_name(root.translation_unit, filepath) for l in locations: source_location = SourceLocation.from_position( root.translation_unit, file, l.lineno, l.column) node = Cursor.from_location(root.translation_unit, source_location) l.node = node if node.kind.is_invalid(): continue ancestor = ancestor_node(node) if ancestor not in ancestors: log.debug( f'node {nodeutils.pp(node)} has ancestor {nodeutils.pp(ancestor)}' ) ancestors.append(ancestor) for a in ancestors: if a.kind.is_translation_unit(): continue # Do not include global constructs else: nodes = nodeutils.find(a, good) locations = [ Location(n.location.file.name, n.location.line, n.location.column, n) for n in nodes ] for l in locations: log.debug(f'static location {l}') static_locations += locations return static_locations
def cursor(self): """ Get a Cursor object for TextMate's current cursor point (file,line,column). """ return Cursor.from_location(self.tu, self.tu.get_location(env['TM_FILEPATH'], ( int(env['TM_LINE_NUMBER']), int(env['TM_COLUMN_NUMBER'])-1 ) ))
def get_function_at(self, line_no): loc = self.translation_unit.get_location(self.source_file, (line_no, -1)) cursor = Cursor.from_location(self.translation_unit, loc) return self._get_function_rec(cursor)
def get_declaration_location_and_spelling(translation_unit, contents, flags, absolute_path, line, column): def log(s): logger.info('%s:%d:%d - %s', os.path.basename(absolute_path), line, column, s) source_location = translation_unit.get_location(absolute_path, (line, column)) cursor = Cursor.from_location(translation_unit, source_location) if cursor is None: log('No cursor') return None # Don't allow clicks/tooltips on most declarations, as their content is usually obvious. # Make an exception for variable declarations, as these can often have auto types. if cursor.kind != CursorKind.VAR_DECL and cursor.kind.is_declaration(): log('Ignoring declaration') return None referenced = cursor.referenced if referenced is None or referenced.location is None or referenced.location.file is None: # If cursor is over an include statement, attempt to resolve the location # of the included file. line_text = get_line(contents, line) bounds = get_include_path_extent(line_text) if bounds is not None: start_col, end_col = bounds # Don't allow hyperclick if cursor is not over the include name, i.e.: # #include "header.h" # ^^^^^^^^ if column < start_col or column > end_col: return None filename = resolve_include(absolute_path, line_text, flags) if filename is None: return None # Point location to beginning of the found included file (line 0, column 0) location = { 'file': filename, 'line': 0, 'column': 0, # Show destination file of hyperclick in hover popover 'type': filename, 'spelling': None, 'extent': { 'start': { 'line': line - 1, 'column': start_col }, 'end': { 'line': line - 1, 'column': end_col } } } return location else: log('No referenced information') return None loc = referenced.location log('Returning {0}:{1}:{2}'.format(os.path.basename(loc.file.name), loc.line, loc.column)) # An extent has a `start` and `end` property, each of which have a `line` # and `column` property. extent = cursor.extent type = None try: type = cursor.type and cursor.type.spelling except: logger.warn('Was not able to get cursor type') pass location = location_dict(loc) location['spelling'] = cursor.spelling location['type'] = type location['extent'] = range_dict(extent) return location
def get_declaration_location_and_spelling(translation_unit, contents, flags, absolute_path, line, column): def log(s): logger.info('%s:%d:%d - %s', os.path.basename(absolute_path), line, column, s) source_location = translation_unit.get_location( absolute_path, (line, column)) cursor = Cursor.from_location(translation_unit, source_location) if cursor is None: log('No cursor') return None # Don't allow clicks/tooltips on most declarations, as their content is usually obvious. # Make an exception for variable declarations, as these can often have auto types. if cursor.kind != CursorKind.VAR_DECL and cursor.kind.is_declaration(): log('Ignoring declaration') return None referenced = cursor.referenced if referenced is None or referenced.location is None or referenced.location.file is None: # If cursor is over an include statement, attempt to resolve the location # of the included file. line_text = get_line(contents, line) bounds = get_include_path_extent(line_text) if bounds is not None: start_col, end_col = bounds # Don't allow hyperclick if cursor is not over the include name, i.e.: # #include "header.h" # ^^^^^^^^ if column < start_col or column > end_col: return None filename = resolve_include(absolute_path, line_text, flags) if filename is None: return None # Point location to beginning of the found included file (line 0, column 0) location = { 'file': filename, 'point': { 'row': 0, 'column': 0, }, # Show destination file of hyperclick in hover popover 'type': filename, 'spelling': None, 'extent': { 'start': {'row': line - 1, 'column': start_col}, 'end': {'row': line - 1, 'column': end_col} } } return location else: log('No referenced information') return None loc = referenced.location log('Returning {0}:{1}:{2}'.format( os.path.basename(loc.file.name), loc.line, loc.column)) # An extent has a `start` and `end` property, each of which have a `line` # and `column` property. extent = cursor.extent type = None try: type = cursor.type and cursor.type.spelling except: logger.warn('Was not able to get cursor type') pass location = location_dict(loc) location['spelling'] = cursor.spelling location['type'] = type location['extent'] = range_dict_relative(extent) return location