def _get_requested_diff_file(self, get_chunks=True): """Fetches information on the requested diff. This will look up information on the diff that's to be rendered and return it, if found. It may also augment it with additional data. If get_chunks is True, the diff file information will include chunks for rendering. Otherwise, it will just contain generic information from the database. """ files = get_diff_files(self.diffset, self.filediff, self.interdiffset, request=self.request) if get_chunks: populate_diff_chunks(files, self.highlighting, request=self.request) if files: assert len(files) == 1 file = files[0] if 'index' in self.request.GET: file['index'] = self.request.GET.get('index') return file return None
def get_requested_diff_file(get_chunks=True): files = get_diff_files(diffset, filediff, interdiffset) if get_chunks: populate_diff_chunks(files, highlighting) if files: assert len(files) == 1 file = files[0] if 'index' in request.GET: file['index'] = request.GET.get('index') return file return None
def _get_diff_data(self, request, mimetype, *args, **kwargs): try: resources.review_request.get_object(request, *args, **kwargs) filediff = self.get_object(request, *args, **kwargs) except ObjectDoesNotExist: return DOES_NOT_EXIST highlighting = request.GET.get('syntax-highlighting', False) files = get_diff_files(diffset=filediff.diffset, filediff=filediff, request=request) populate_diff_chunks(files, highlighting, request=request) if not files: # This may not be the right error here. return DOES_NOT_EXIST assert len(files) == 1 f = files[0] payload = { 'diff_data': { 'binary': f['binary'], 'chunks': f['chunks'], 'num_changes': f['num_changes'], 'changed_chunk_indexes': f['changed_chunk_indexes'], 'new_file': f['newfile'], } } # XXX: Kind of a hack. api_format = mimetype.split('+')[-1] resp = WebAPIResponse(request, payload, api_format=api_format) set_last_modified(resp, filediff.diffset.timestamp) return resp
def render_to_string_uncached(self, request): """Renders a diff to a string without caching. This is a potentially expensive operation, and so is meant to be called only as often as necessary. render_to_string will call this if it's not already in the cache. """ if not self.diff_file.get('chunks_loaded', False): populate_diff_chunks([self.diff_file], self.highlighting, request=request) if self.chunk_index is not None: assert not self.lines_of_context or self.collapse_all self.num_chunks = len(self.diff_file['chunks']) if self.chunk_index < 0 or self.chunk_index >= self.num_chunks: raise UserVisibleError( _('Invalid chunk index %s specified.') % self.chunk_index) return render_to_string(template_name=self.template_name, context=self.make_context())
def render_to_string_uncached(self, request): """Renders a diff to a string without caching. This is a potentially expensive operation, and so is meant to be called only as often as necessary. render_to_string will call this if it's not already in the cache. """ if not self.diff_file.get('chunks_loaded', False): populate_diff_chunks([self.diff_file], self.highlighting, request=request) if self.chunk_index is not None: assert not self.lines_of_context or self.collapse_all self.num_chunks = len(self.diff_file['chunks']) if self.chunk_index < 0 or self.chunk_index >= self.num_chunks: raise UserVisibleError( _('Invalid chunk index %s specified.') % self.chunk_index) return render_to_string(self.template_name, Context(self.make_context()))
def view_diff(request, diffset, interdiffset=None, extra_context={}, template_name='diffviewer/view_diff.html'): highlighting = get_enable_highlighting(request.user) try: if interdiffset: logging.debug("Generating diff viewer page for interdiffset ids " "%s-%s", diffset.id, interdiffset.id, request=request) else: logging.debug("Generating diff viewer page for filediff id %s", diffset.id, request=request) files = get_diff_files(diffset, None, interdiffset, request=request) # Break the list of files into pages siteconfig = SiteConfiguration.objects.get_current() paginator = Paginator(files, siteconfig.get("diffviewer_paginate_by"), siteconfig.get("diffviewer_paginate_orphans")) page_num = int(request.GET.get('page', 1)) if request.GET.get('file', False): file_id = int(request.GET['file']) for i, f in enumerate(files): if f['filediff'].id == file_id: page_num = i // paginator.per_page + 1 if page_num > paginator.num_pages: page_num = paginator.num_pages break page = paginator.page(page_num) collapse_diffs = get_collapse_diff(request) context = { 'diffset': diffset, 'interdiffset': interdiffset, 'diffset_pair': (diffset, interdiffset), 'files': page.object_list, 'collapseall': collapse_diffs, # Add the pagination context 'is_paginated': page.has_other_pages(), 'page': page.number, 'pages': paginator.num_pages, 'page_numbers': paginator.page_range, 'has_next': page.has_next(), 'next_page': page.next_page_number(), 'has_previous': page.has_previous(), 'previous_page': page.previous_page_number(), 'page_start_index': page.start_index(), } context.update(extra_context) # Attempt to preload the first file before rendering any part of # the page. This helps to remove the perception that the diff viewer # takes longer to load now that we have progressive diffs. Users were # seeing the page itself load quickly, but didn't see that first # diff immediately and instead saw a spinner, making them feel it was # taking longer than it used to to load a page. We just trick the # user by providing that first file. if page.object_list: first_file = page.object_list[0] else: first_file = None if first_file: filediff = first_file['filediff'] if filediff.diffset == interdiffset: temp_files = get_diff_files(interdiffset, filediff, None, request=request) else: temp_files = get_diff_files(diffset, filediff, interdiffset, request=request) if temp_files: try: populate_diff_chunks(temp_files, highlighting, request=request) except Exception, e: file_temp = temp_files[0] file_temp['index'] = first_file['index'] first_file['fragment'] = \ exception_traceback(request, e, 'diffviewer/diff_fragment_error.html', extra_context={'file': file_temp}) else: file_temp = temp_files[0] file_temp['index'] = first_file['index'] first_file['fragment'] = build_diff_fragment( request, file_temp, None, highlighting, collapse_diffs, None, context=context, template_name='diffviewer/diff_file_fragment.html') response = render_to_response(template_name, RequestContext(request, context)) response.set_cookie('collapsediffs', collapse_diffs) if interdiffset: logging.debug("Done generating diff viewer page for interdiffset " "ids %s-%s", diffset.id, interdiffset.id, request=request) else: logging.debug("Done generating diff viewer page for filediff " "id %s", diffset.id, request=request) return response
def get_file_chunks_in_range_custom(context, filediff, interfilediff, first_line, num_lines): """ A generator that yields chunks within a range of lines in the specified filediff/interfilediff. This function was mostly copied from the Review Board source code. The main modification is to always set syntax highlighting to False so that plain code is returned in the chunks instead of html. This is primarily intended for use with templates. It takes a RequestContext for looking up the user and for caching file lists, in order to improve performance and reduce lookup times for files that have already been fetched. Each returned chunk is a dictionary with the following fields: ============= ======================================================== Variable Description ============= ======================================================== ``change`` The change type ("equal", "replace", "insert", "delete") ``numlines`` The number of lines in the chunk. ``lines`` The list of lines in the chunk. ``meta`` A dictionary containing metadata on the chunk ============= ======================================================== Each line in the list of lines is an array with the following data: ======== ============================================================= Index Description ======== ============================================================= 0 Virtual line number (union of the original and patched files) 1 Real line number in the original file 2 HTML markup of the original file 3 Changed regions of the original line (for "replace" chunks) 4 Real line number in the patched file 5 HTML markup of the patched file 6 Changed regions of the patched line (for "replace" chunks) 7 True if line consists of only whitespace changes ======== ============================================================= """ def find_header(headers): for header in reversed(headers): if header[0] < first_line: return { 'line': header[0], 'text': header[1], } interdiffset = None key = "_diff_files_%s_%s" % (filediff.diffset.id, filediff.id) if interfilediff: key += "_%s" % (interfilediff.id) interdiffset = interfilediff.diffset if key in context: files = context[key] else: assert 'user' in context request = context.get('request', None) files = get_diff_files(filediff.diffset, filediff, interdiffset, request=request) populate_diff_chunks(files, False, request=request) context[key] = files if not files: raise StopIteration assert len(files) == 1 last_header = [None, None] for chunk in files[0]['chunks']: if ('headers' in chunk['meta'] and (chunk['meta']['headers'][0] or chunk['meta']['headers'][1])): last_header = chunk['meta']['headers'] lines = chunk['lines'] if lines[-1][0] >= first_line >= lines[0][0]: start_index = first_line - lines[0][0] if first_line + num_lines <= lines[-1][0]: last_index = start_index + num_lines else: last_index = len(lines) new_chunk = { 'lines': chunk['lines'][start_index:last_index], 'numlines': last_index - start_index, 'change': chunk['change'], 'meta': chunk.get('meta', {}), } if 'left_headers' in chunk['meta']: left_header = find_header(chunk['meta']['left_headers']) right_header = find_header(chunk['meta']['right_headers']) del new_chunk['meta']['left_headers'] del new_chunk['meta']['right_headers'] if left_header or right_header: header = (left_header, right_header) else: header = last_header new_chunk['meta']['headers'] = header yield new_chunk first_line += new_chunk['numlines'] num_lines -= new_chunk['numlines'] assert num_lines >= 0 if num_lines == 0: break