Пример #1
0
        def processRange(offset, count, context):
            if context: context = getContext(offset)
            else: context = None

            # Offset is a 1-based line number.
            start = offset - 1
            # If count is -1, fetch all lines.
            end = start + count if count > -1 else None

            lines = file.newLines(highlighted=True)[start:end]

            if tabify:
                lines = [htmlutils.tabify(line, tabwidth, indenttabsmode) for line in lines]

            return { "lines": lines, "context": context }
Пример #2
0
        def processRange(offset, count, context):
            if context: context = getContext(offset)
            else: context = None

            # Offset is a 1-based line number.
            start = offset - 1
            # If count is -1, fetch all lines.
            end = start + count if count > -1 else None

            lines = file.newLines(highlighted=True)[start:end]

            if tabify:
                lines = [
                    htmlutils.tabify(line, tabwidth, indenttabsmode)
                    for line in lines
                ]

            return {"lines": lines, "context": context}
Пример #3
0
def renderFile(db, target, user, review, file, first_file=False, options={}, conflicts=False, add_resources=True):
    if add_resources:
        addResources(db, user, review, options.get("compact", False), options.get("tabify"), target)

    if options.get("count_chunks"):
        deleted = 0
        inserted = 0
        if file.wasRemoved():
            file.loadOldLines(False)
            deleted = file.oldCount()
        else:
            for macro_chunk in file.macro_chunks:
                for chunk in macro_chunk.chunks:
                    deleted += chunk.delete_count
                    inserted += chunk.insert_count
        chunksText = "-%d/+%d lines" % (deleted, inserted)
    else:
        chunksText = ""

    compact = options.get("compact", False)

    file_table_class = "file sourcefont"
    compact = options.get("compact", False)

    if options.get("show"):
        file_table_class += " show"
    if options.get("expand"):
        file_table_class += " expanded"
        compact = False

    if first_file:
        file_table_class += " first"

    file_id = "f%d" % file.id
    customFileId = options.get("file_id")
    if customFileId:
        file_id = customFileId(file_id)

    target.script(type="text/javascript").text("calculateTabWidth();")

    table = target.table(file_table_class, width='100%', cellspacing=0, cellpadding=0, id=file_id, critic_file_id=file.id, critic_parent_index=options.get("parent_index"))

    if not compact:
        columns = table.colgroup()
        columns.col('edge').empty()
        columns.col('linenr').empty()
        columns.col('line').empty()
        columns.col('middle').empty()
        columns.col('middle').empty()
        columns.col('line').empty()
        columns.col('linenr').empty()
        columns.col('edge').empty()

    row = table.thead().tr()

    header_left = options.get("header_left")
    header_right = options.get("header_right")

    if header_left:
        header_left(db, row.td('left', colspan=4, align='left'), file)
    else:
        cell = row.td('left', colspan=4, align='left')

        commit = options.get("commit")
        if commit:
            review_arg = "&review=%d" % review.id if review else ""
            repository_arg = "&repository=%d" % file.repository.id if not review else ""

            cell.a("showtree root", href="showtree?sha1=%s&path=/%s%s" % (commit.sha1, review_arg, repository_arg)).text("root")
            cell.span("slash").text('/')

            components = file.path.split("/")
            for index, component in enumerate(components[:-1]):
                cell.a("showtree", href="showtree?sha1=%s&path=%s%s%s" % (commit.sha1, "/".join(components[:index + 1]), review_arg, repository_arg)).text(component)
                cell.span("slash").text('/')

            cell.a("showtree", href="showfile?sha1=%s&path=%s%s%s" % (commit.sha1, "/".join(components), review_arg, repository_arg)).text(components[-1])
        else:
            cell.text(file.path)

        if not compact:
            cell.comment("sha1: %s to %s" % (file.old_sha1, file.new_sha1))

    if header_right:
        header_right(db, row.td('right', colspan=4, align='right'), file)
    else:
        row.td('right', colspan=4, align='right').text(chunksText)

    next_old_offset = 1
    next_new_offset = 1

    display_type = options.get("display_type", "both")
    deleted_file = False
    added_file = False

    if not file.isBinaryChanges():
        if file.old_sha1 == 40 * '0':
            display_type = "new"

            if file.getLanguage() is None:
                limit = configuration.limits.MAXIMUM_ADDED_LINES_UNRECOGNIZED
            else:
                limit = configuration.limits.MAXIMUM_ADDED_LINES_RECOGNIZED

            count = file.newCount()
            if count > limit and len(file.macro_chunks) == 1 and len(file.macro_chunks[0].lines) == count:
                added_file = True
        elif file.new_sha1 == 40 * '0':
            display_type = "old"
            deleted_file = not options.get("include_deleted", False)

    def baseLineId(file, line, index):
        if line.type == diff.Line.DELETED:
            return "f%do%dn0" % (file.id, line.old_offset)
        elif line.type == diff.Line.INSERTED:
            return "f%do0n%d" % (file.id, line.new_offset)
        else:
            return "f%do%dn%d" % (file.id, line.old_offset, line.new_offset)

    def baseLineCellId(file, version, line):
        if line: return "f%d%s%d" % (file.id, version, line)
        else: return None

    customLineId = options.get("line_id")
    if customLineId:
        lineId = lambda file, line, index: customLineId(baseLineId(file, line, index))
    else:
        lineId = baseLineId

    customLineCellId = options.get("line_cell_id")
    if customLineCellId:
        lineCellId = lambda file, version, line: customLineCellId(baseLineCellId(file, version, line))
    else:
        lineCellId = baseLineCellId

    def lineType(line, index):
        type = line.type
        if type == diff.Line.DELETED: return "deleted"
        elif type == diff.Line.INSERTED: return "inserted"
        elif type == diff.Line.MODIFIED: return "modified whitespace" if line.is_whitespace else "modified"
        elif type == diff.Line.REPLACED: return "replaced"
        else: return "context"

    support_expand = options.get("support_expand", True)
    style = options.get("style", "horizontal")
    collapse_simple_hunks = user.getPreference(db, 'commit.diff.collapseSimpleHunks')

    content_before = options.get("content_before")
    if content_before:
        content = table.tbody('content')

        row = content.tr('content')
        row.td(colspan=2).text()
        content_before(db, row.td(colspan=4))
        row.td(colspan=2).text()

    if added_file or deleted_file:
        table.tbody('spacer').tr('spacer').td(colspan=8).text()

        verb = "added" if added_file else "deleted"
        side = "new" if added_file else "old"

        if added_file: count = file.newCount()
        else: count = file.oldCount()

        tbody = table.tbody('deleted')

        row = tbody.tr('deleted')
        row.td(colspan=2).text()
        row.td(colspan=4).h2().text("File was %s." % verb)
        row.td(colspan=2).text()

        row = tbody.tr('deleted')
        row.td(colspan=2).text()
        row.td(colspan=4).button(onclick="fetchFile(%d, '%s', event.currentTarget.parentNode.parentNode.parentNode);" % (file.id, side)).text("Fetch %d %s Lines" % (count, verb.capitalize()))
        row.td(colspan=2).text()

        table.tbody('spacer').tr('spacer').td(colspan=8).text()
    elif file.isBinaryChanges():
        table.tbody('spacer').tr('spacer').td(colspan=8).text()

        binary = table.tbody('binary')

        if file.wasAdded():
            title = "Binary file added."
        elif file.wasRemoved():
            title = "Binary file removed."
        else:
            title = "Binary file modified."

        row = binary.tr('binary')
        row.td(colspan=2).text()
        row.td(colspan=4).h2().text(title)
        row.td(colspan=2).text()

        row = binary.tr('download')
        row.td(colspan=2).text()
        cell = row.td(colspan=4)

        def linkToFile(target, file, sha1):
            is_image = False

            try:
                base, extension = file.path.rsplit(".")
                if configuration.mimetypes.MIMETYPES.get(extension, "").startswith("image/"):
                    is_image = True
            except:
                pass

            url = "download/%s?sha1=%s&repository=%d" % (file.path, sha1, file.repository.id)
            link = target.a(href=url)

            if is_image: link.img(src=url)
            else: link.text(sha1)

        if file.wasAdded():
            linkToFile(cell, file, file.new_sha1)
        elif file.wasRemoved():
            linkToFile(cell, file, file.old_sha1)
        else:
            linkToFile(cell, file, file.old_sha1)
            cell.innerHTML(" → ")
            linkToFile(cell, file, file.new_sha1)

        row.td(colspan=2).text()

        table.tbody('spacer').tr('spacer').td(colspan=8).text()
    else:
        if options.get("tabify"):
            tabwidth = file.getTabWidth()
            indenttabsmode = file.getIndentTabsMode()
            tabify = lambda line: htmlutils.tabify(line, tabwidth, indenttabsmode)
        else:
            tabify = lambda line: line

        code_contexts = CodeContexts(db, file.new_sha1,
                                     file.macro_chunks[0].lines[0].new_offset,
                                     file.macro_chunks[-1].lines[-1].new_offset)

        blocks = [("[%d,%d]" % (macro_chunk.lines[0].new_offset, macro_chunk.lines[-1].new_offset))
                  for macro_chunk in file.macro_chunks]

        target.script(type="text/javascript").text("blocks[%d] = [%s];" % (file.id, ",".join(blocks)))

        for index, macro_chunk in enumerate(file.macro_chunks):
            first_line = macro_chunk.lines[0]
            last_line = macro_chunk.lines[-1]

            spacer = table.tbody('spacer')

            if support_expand and next_old_offset < first_line.old_offset and next_new_offset < first_line.new_offset:
                row = spacer.tr('expand').td(colspan='8')
                expandHTML(db, file, next_old_offset, next_new_offset, first_line.old_offset - next_old_offset, row)

            code_context = code_contexts.find(first_line.new_offset)
            if code_context: spacer.tr('context').td(colspan='8').text(code_context)

            spacer.tr('spacer').td(colspan='8').text()

            lines = table.tbody('lines')

            local_display_type = display_type

            for line in macro_chunk.lines:
                if line.type != diff.Line.INSERTED:
                    match = re_tailws.match(line.old_value)
                    if match:
                        line.old_value = match.group(1) + "<i class='tailws'>" + match.group(2) + "</i>" + match.group(3)
                if line.type != diff.Line.DELETED:
                    match = re_tailws.match(line.new_value)
                    if match:
                        line.new_value = match.group(1) + "<i class='tailws'>" + match.group(2) + "</i>" + match.group(3)

                if line.old_value:
                    line.old_value = line.old_value.replace("\r", "<i class='cr'></i>")
                if line.new_value:
                    line.new_value = line.new_value.replace("\r", "<i class='cr'></i>")

            if collapse_simple_hunks:
                if local_display_type == "both":
                    deleted = False
                    inserted = False

                    for line in macro_chunk.lines:
                        if line.type == diff.Line.MODIFIED or line.type == diff.Line.REPLACED:
                            break
                        elif line.type == diff.Line.DELETED:
                            if inserted: break
                            deleted = True
                        elif line.type == diff.Line.INSERTED:
                            if deleted: break
                            inserted = True
                    else:
                        if deleted: local_display_type = "old"
                        if inserted: local_display_type = "new"

            if compact:
                def packSyntaxHighlighting(line):
                    return re_tag.sub(lambda m: "<%s%s>" % (m.group(1), m.group(2)), line)

                items = []
                for line in macro_chunk.lines:
                    if line.type == diff.Line.MODIFIED and line.is_whitespace:
                        line_type = diff.Line.WHITESPACE
                    elif conflicts and line.type == diff.Line.DELETED and line.isConflictMarker():
                        line_type = diff.Line.CONFLICT
                    else:
                        line_type = line.type
                    data = [str(line_type)]
                    if line.type != diff.Line.INSERTED:
                        data.append(jsify(packSyntaxHighlighting(tabify(line.old_value)), json=True))
                    if line.type != diff.Line.DELETED:
                        data.append(jsify(packSyntaxHighlighting(tabify(line.new_value)), json=True))
                    items.append("[%s]" % ",".join(data))
                data = "[%d,%d,%d,%d,%s]" % (file.id,
                                             2 if local_display_type == "both" else 1,
                                             macro_chunk.lines[0].old_offset,
                                             macro_chunk.lines[0].new_offset,
                                             "[%s]" % ",".join(items))
                lines.comment(data.replace("--", "-\u002d"))
            elif style == "vertical" or local_display_type != "both":
                linesIterator = iter(macro_chunk.lines)
                line = linesIterator.next()

                def lineHTML(what, file, line, is_whitespace, target):
                    line_class = what

                    if is_whitespace and line.type == diff.Line.MODIFIED:
                        line_class = "modified"

                    if what == "deleted":
                        linenr = line.old_offset
                    else:
                        linenr = line.new_offset

                    row = target.tr("line " + line_class, id=lineId(file, line, 0))
                    row.td("edge").text()
                    row.td("linenr old").text(linenr)

                    if what == "deleted" or local_display_type == "old":
                        code = line.old_value
                        lineClass = "old"
                    else:
                        code = line.new_value
                        lineClass = "new"

                    if not code: code = "&nbsp;"

                    row.td('line single ' + lineClass, colspan=4, id=lineCellId(file, lineClass[0], linenr)).innerHTML(tabify(code))
                    row.td('linenr new').text(linenr)

                    row.td("edge").text()

                try:
                    while line:
                        while line.type == diff.Line.CONTEXT:
                            lineHTML("context", file, line, False, lines)
                            line = linesIterator.next()

                        deleted = []
                        inserted = []

                        while line.is_whitespace:
                            lineHTML("modified", file, line, True, lines)
                            line = linesIterator.next()

                        previous_type = diff.Line.DELETED

                        try:
                            while line.type >= previous_type and not line.is_whitespace:
                                if line.type != diff.Line.INSERTED: deleted.append(line)
                                if line.type != diff.Line.DELETED: inserted.append(line)
                                previous_type = line.type
                                line = None
                                line = linesIterator.next()
                        except StopIteration:
                            line = None

                        for deletedLine in deleted:
                            lineHTML("deleted", file, deletedLine, False, lines)
                        for insertedLine in inserted:
                            lineHTML("inserted", file, insertedLine, False, lines)
                except StopIteration:
                    pass
            elif style == "horizontal":
                for line in macro_chunk.lines:
                    old_offset = None
                    new_offset = None
                    old_line = None
                    new_line = None

                    if line.type != diff.Line.INSERTED:
                        old_offset = line.old_offset
                        old_line = tabify(line.old_value)

                    if line.type != diff.Line.DELETED:
                        new_offset = line.new_offset
                        new_line = tabify(line.new_value)

                    if not old_line: old_line = "&nbsp;"
                    if old_line is None: old_offset = None
                    if not new_line: new_line = "&nbsp;"
                    if new_line is None: new_offset = None

                    line_type = lineType(line, 0)

                    if conflicts and line.isConflictMarker():
                        line_type += " conflict"

                    row = ("<tr class='line %s' id='%s'>"
                             "<td class='edge'>&nbsp;</td>"
                             "<td class='linenr old'>%s</td>"
                             "<td class='line old'%s>%s</td>"
                             "<td class='middle' colspan=2>&nbsp;</td>"
                             "<td class='line new'%s>%s</td>"
                             "<td class='linenr new'>%s</td>"
                             "<td class='edge'>&nbsp;</td>"
                           "</tr>\n") % (line_type, lineId(file, line, 0),
                                         str(old_offset) if old_offset else "&nbsp;",
                                         " id='%s'" % lineCellId(file, "o", old_offset) if old_offset else "", old_line,
                                         " id='%s'" % lineCellId(file, "n", new_offset) if new_offset else "", new_line,
                                         str(new_offset) if new_offset else "&nbsp;")

                    lines.innerHTML(row)

            next_old_offset = last_line.old_offset + 1
            next_new_offset = last_line.new_offset + 1

        spacer = table.tbody('spacer')

        if support_expand and next_old_offset < file.oldCount() + 1 and next_new_offset < file.newCount() + 1:
            row = spacer.tr('expand').td(colspan='8')
            expandHTML(db, file, next_old_offset, next_new_offset, 1 + file.oldCount() - next_old_offset, row)

        spacer.tr('spacer').td(colspan='8').text()

    content_after = options.get("content_after")
    if content_after:
        content = table.tbody('content')

        row = content.tr('content')
        row.td(colspan=2).text()
        content_after(db, row.td(colspan=4), file=file)
        row.td(colspan=2).text()

        content.tr('spacer').td(colspan=8).text()

    row = table.tfoot().tr()
    cell = row.td('left', colspan=4)

    commit = options.get("commit")
    if commit:
        review_arg = "&review=%d" % review.id if review else ""
        repository_arg = "&repository=%d" % file.repository.id if not review else ""

        cell.a("showtree root", href="showtree?sha1=%s&path=/%s%s" % (commit.sha1, review_arg, repository_arg)).text("root")
        cell.span("slash").text('/')

        components = file.path.split("/")
        for index, component in enumerate(components[:-1]):
            cell.a("showtree", href="showtree?sha1=%s&path=%s%s%s" % (commit.sha1, "/".join(components[:index + 1]), review_arg, repository_arg)).text(component)
            cell.span("slash").text('/')

        cell.a("showtree", href="showfile?sha1=%s&path=%s%s%s" % (commit.sha1, "/".join(components), review_arg, repository_arg)).text(components[-1])
    else:
        cell.text(file.path)

    row.td('right', colspan=4).text(chunksText)
Пример #4
0
def renderShowFile(req, db, user):
    cursor = db.cursor()

    sha1 = req.getParameter("sha1")
    path = req.getParameter("path")
    line = req.getParameter("line", None)
    review_id = req.getParameter("review", None, filter=int)

    default_tabify = "yes" if user.getPreference(db, "commit.diff.visualTabs") else "no"
    tabify = req.getParameter("tabify", default_tabify) == "yes"

    if line is None:
        first, last = None, None
    else:
        if "-" in line:
            first, last = map(int, line.split("-"))
        else:
            first = last = int(line)

        context = req.getParameter("context", user.getPreference(db, "commit.diff.contextLines"), int)

        first_with_context = max(1, first - context)
        last_with_context = last + context

    if user.getPreference(db, "commit.diff.compactMode"): default_compact = "yes"
    else: default_compact = "no"

    compact = req.getParameter("compact", default_compact) == "yes"

    if len(path) == 0 or path[-1:] == "/":
        raise page.utils.DisplayMessage(
            title="Invalid path parameter",
            body="<p>The path must be non-empty and must not end with a <code>/</code>.</p>",
            html=True)
    if path[0] == '/':
        full_path = path
        if path != "/": path = path[1:]
    else:
        full_path = "/" + path
        if not path: path = "/"

    if review_id is None:
        review = None
        repository_arg = req.getParameter("repository", "")
        if repository_arg:
            repository = gitutils.Repository.fromParameter(db, repository_arg)
        else:
            repository = gitutils.Repository.fromSHA1(db, sha1)
    else:
        review = dbutils.Review.fromId(db, review_id)
        repository = review.repository

    document = htmlutils.Document(req)

    html = document.html()
    head = html.head()
    body = html.body()

    if review:
        page.utils.generateHeader(body, db, user, lambda target: review_utils.renderDraftItems(db, user, review, target), extra_links=[("r/%d" % review.id, "Back to Review")])
    else:
        page.utils.generateHeader(body, db, user)

    document.addExternalStylesheet("resource/showfile.css")
    document.addInternalStylesheet(htmlutils.stripStylesheet(user.getResource(db, "syntax.css")[1], compact))

    commit = gitutils.Commit.fromSHA1(db, repository, sha1)
    file_sha1 = commit.getFileSHA1(full_path)
    file_id = dbutils.find_file(db, path=path)

    if file_sha1 is None:
        raise page.utils.DisplayMessage(
            title="File does not exist",
            body=("<p>There is no file named <code>%s</code> in the commit "
                  "<a href='/showcommit?repository=%s&amp;sha1=%s'>"
                  "<code>%s</code></a>.</p>"
                  % (htmlutils.htmlify(textutils.escape(full_path)),
                     htmlutils.htmlify(repository.name),
                     htmlutils.htmlify(sha1), htmlutils.htmlify(sha1[:8]))),
            html=True)

    file = diff.File(file_id, path, None, file_sha1, repository)

    # A new file ID might have been added to the database, so need to commit.
    db.commit()

    if file.canHighlight():
        requestHighlights(repository, { file.new_sha1: (file.path, file.getLanguage()) })

    file.loadNewLines(True, request_highlight=True)

    if review:
        document.addInternalScript(user.getJS())
        document.addInternalScript(review.getJS())
        document.addInternalScript("var changeset = { parent: { id: %(id)d, sha1: %(sha1)r }, child: { id: %(id)d, sha1: %(sha1)r } };" % { 'id': commit.getId(db), 'sha1': commit.sha1 })
        document.addInternalScript("var files = { %(id)d: { new_sha1: %(sha1)r }, %(sha1)r: { id: %(id)d, side: 'n' } };" % { 'id': file_id, 'sha1': file_sha1 })
        document.addExternalStylesheet("resource/review.css")
        document.addExternalScript("resource/review.js")

        cursor.execute("""SELECT DISTINCT commentchains.id
                            FROM commentchains
                            JOIN commentchainlines ON (commentchainlines.chain=commentchains.id)
                           WHERE commentchains.review=%s
                             AND commentchains.file=%s
                             AND commentchainlines.sha1=%s
                             AND ((commentchains.state!='draft' OR commentchains.uid=%s)
                              AND commentchains.state!='empty')
                        GROUP BY commentchains.id""",
                       (review.id, file_id, file_sha1, user.id))

        comment_chain_script = ""

        for (chain_id,) in cursor.fetchall():
            chain = review_comment.CommentChain.fromId(db, chain_id, user, review=review)
            chain.loadComments(db, user)

            comment_chain_script += "commentChains.push(%s);\n" % chain.getJSConstructor(file_sha1)

        if comment_chain_script:
            document.addInternalScript(comment_chain_script)

    document.addExternalStylesheet("resource/comment.css")
    document.addExternalScript("resource/comment.js")
    document.addExternalScript("resource/showfile.js")

    if tabify:
        document.addExternalStylesheet("resource/tabify.css")
        document.addExternalScript("resource/tabify.js")
        tabwidth = file.getTabWidth()
        indenttabsmode = file.getIndentTabsMode()

    if user.getPreference(db, "commit.diff.highlightIllegalWhitespace"):
        document.addInternalStylesheet(user.getResource(db, "whitespace.css")[1], compact)

    if first is not None:
        document.addInternalScript("var firstSelectedLine = %d, lastSelectedLine = %d;" % (first, last))

    target = body.div("main")

    if tabify:
        target.script(type="text/javascript").text("calculateTabWidth();")

    table = target.table('file show expanded paleyellow', align='center', cellspacing=0)

    columns = table.colgroup()
    columns.col('edge')
    columns.col('linenr')
    columns.col('line')
    columns.col('middle')
    columns.col('middle')
    columns.col('line')
    columns.col('linenr')
    columns.col('edge')

    thead = table.thead()
    cell = thead.tr().td('h1', colspan=8)
    h1 = cell.h1()

    def make_url(url_path, path):
        params = { "sha1": sha1,
                   "path": path }
        if review is None:
            params["repository"] = str(repository.id)
        else:
            params["review"] = str(review.id)
        return "%s?%s" % (url_path, urllib.urlencode(params))

    h1.a("root", href=make_url("showtree", "/")).text("root")
    h1.span().text('/')

    components = path.split("/")
    for index, component in enumerate(components[:-1]):
        h1.a(href=make_url("showtree", "/".join(components[:index + 1]))).text(component, escape=True)
        h1.span().text('/')

    if first is not None:
        h1.a(href=make_url("showfile", "/".join(components))).text(components[-1], escape=True)
    else:
        h1.text(components[-1], escape=True)

    h1.span("right").a(href=("/download/%s?repository=%s&sha1=%s"
                             % (urllib.quote(path), repository.name, file_sha1)),
                       download=urllib.quote(path)).text("[download]")
    h1.span("right").a(href=("/download/%s?repository=%s&sha1=%s"
                             % (urllib.quote(path), repository.name, file_sha1))).text("[view]")

    table.tbody('spacer top').tr('spacer top').td(colspan=8).text()

    tbody = table.tbody("lines")

    yield document.render(stop=tbody, pretty=not compact)

    for linenr, line in enumerate(file.newLines(True)):
        linenr = linenr + 1
        highlight_class = ""

        if first is not None:
            if not (first_with_context <= linenr <= last_with_context): continue
            if linenr == first:
                highlight_class += " first-selected"
            if linenr == last:
                highlight_class += " last-selected"

        if tabify:
            line = htmlutils.tabify(line, tabwidth, indenttabsmode)

        line = line.replace("\r", "<i class='cr'></i>")

        row = tbody.tr("line context single", id="f%do%dn%d" % (file.id, linenr, linenr))
        row.td("edge").text()
        row.td("linenr old").text(linenr)
        row.td("line single whole%s" % highlight_class, id="f%dn%d" % (file.id, linenr), colspan=4).innerHTML(line)
        row.td("linenr new").text(linenr)
        row.td("edge").text()

        if linenr % 500:
            yield document.render(stop=tbody, pretty=not compact)

    table.tbody('spacer bottom').tr('spacer bottom').td(colspan=8).text()

    yield document.render(pretty=not compact)
Пример #5
0
def renderShowFile(req, db, user):
    cursor = db.cursor()

    sha1 = req.getParameter("sha1")
    path = req.getParameter("path")
    line = req.getParameter("line", None)
    review_id = req.getParameter("review", None, filter=int)

    default_tabify = "yes" if user.getPreference(
        db, "commit.diff.visualTabs") else "no"
    tabify = req.getParameter("tabify", default_tabify) == "yes"

    if line is None:
        first, last = None, None
    else:
        if "-" in line:
            first, last = map(int, line.split("-"))
        else:
            first = last = int(line)

        context = req.getParameter(
            "context", user.getPreference(db, "commit.diff.contextLines"), int)

        first_with_context = max(1, first - context)
        last_with_context = last + context

    if user.getPreference(db, "commit.diff.compactMode"):
        default_compact = "yes"
    else:
        default_compact = "no"

    compact = req.getParameter("compact", default_compact) == "yes"

    if len(path) == 0 or path[-1:] == "/":
        raise page.utils.DisplayMessage(
            title="Invalid path parameter",
            body=
            "<p>The path must be non-empty and must not end with a <code>/</code>.</p>",
            html=True)
    if path[0] == '/':
        full_path = path
        if path != "/": path = path[1:]
    else:
        full_path = "/" + path
        if not path: path = "/"

    if review_id is None:
        review = None
        repository_arg = req.getParameter("repository", "")
        if repository_arg:
            repository = gitutils.Repository.fromParameter(db, repository_arg)
        else:
            repository = gitutils.Repository.fromSHA1(db, sha1)
    else:
        review = dbutils.Review.fromId(db, review_id)
        repository = review.repository

    document = htmlutils.Document(req)

    html = document.html()
    head = html.head()
    body = html.body()

    if review:
        page.utils.generateHeader(body,
                                  db,
                                  user,
                                  lambda target: review_utils.renderDraftItems(
                                      db, user, review, target),
                                  extra_links=[("r/%d" % review.id,
                                                "Back to Review")])
    else:
        page.utils.generateHeader(body, db, user)

    document.addExternalStylesheet("resource/showfile.css")
    document.addInternalStylesheet(
        htmlutils.stripStylesheet(
            user.getResource(db, "syntax.css")[1], compact))

    commit = gitutils.Commit.fromSHA1(db, repository, sha1)
    file_sha1 = commit.getFileSHA1(full_path)
    file_id = dbutils.find_file(db, path=path)

    if file_sha1 is None:
        raise page.utils.DisplayMessage(
            title="File does not exist",
            body=("<p>There is no file named <code>%s</code> in the commit "
                  "<a href='/showcommit?repository=%s&amp;sha1=%s'>"
                  "<code>%s</code></a>.</p>" %
                  (htmlutils.htmlify(textutils.escape(full_path)),
                   htmlutils.htmlify(repository.name), htmlutils.htmlify(sha1),
                   htmlutils.htmlify(sha1[:8]))),
            html=True)

    file = diff.File(file_id, path, None, file_sha1, repository)

    # A new file ID might have been added to the database, so need to commit.
    db.commit()

    if file.canHighlight():
        requestHighlights(repository,
                          {file.new_sha1: (file.path, file.getLanguage())})

    file.loadNewLines(True, request_highlight=True)

    if review:
        document.addInternalScript(user.getJS())
        document.addInternalScript(review.getJS())
        document.addInternalScript(
            "var changeset = { parent: { id: %(id)d, sha1: %(sha1)r }, child: { id: %(id)d, sha1: %(sha1)r } };"
            % {
                'id': commit.getId(db),
                'sha1': commit.sha1
            })
        document.addInternalScript(
            "var files = { %(id)d: { new_sha1: %(sha1)r }, %(sha1)r: { id: %(id)d, side: 'n' } };"
            % {
                'id': file_id,
                'sha1': file_sha1
            })
        document.addExternalStylesheet("resource/review.css")
        document.addExternalScript("resource/review.js")

        cursor.execute(
            """SELECT DISTINCT commentchains.id
                            FROM commentchains
                            JOIN commentchainlines ON (commentchainlines.chain=commentchains.id)
                           WHERE commentchains.review=%s
                             AND commentchains.file=%s
                             AND commentchainlines.sha1=%s
                             AND ((commentchains.state!='draft' OR commentchains.uid=%s)
                              AND commentchains.state!='empty')
                        GROUP BY commentchains.id""",
            (review.id, file_id, file_sha1, user.id))

        comment_chain_script = ""

        for (chain_id, ) in cursor.fetchall():
            chain = review_comment.CommentChain.fromId(db,
                                                       chain_id,
                                                       user,
                                                       review=review)
            chain.loadComments(db, user)

            comment_chain_script += "commentChains.push(%s);\n" % chain.getJSConstructor(
                file_sha1)

        if comment_chain_script:
            document.addInternalScript(comment_chain_script)

    document.addExternalStylesheet("resource/comment.css")
    document.addExternalScript("resource/comment.js")
    document.addExternalScript("resource/showfile.js")

    if tabify:
        document.addExternalStylesheet("resource/tabify.css")
        document.addExternalScript("resource/tabify.js")
        tabwidth = file.getTabWidth()
        indenttabsmode = file.getIndentTabsMode()

    if user.getPreference(db, "commit.diff.highlightIllegalWhitespace"):
        document.addInternalStylesheet(
            user.getResource(db, "whitespace.css")[1], compact)

    if first is not None:
        document.addInternalScript(
            "var firstSelectedLine = %d, lastSelectedLine = %d;" %
            (first, last))

    target = body.div("main")

    if tabify:
        target.script(type="text/javascript").text("calculateTabWidth();")

    table = target.table('file show expanded paleyellow',
                         align='center',
                         cellspacing=0)

    columns = table.colgroup()
    columns.col('edge')
    columns.col('linenr')
    columns.col('line')
    columns.col('middle')
    columns.col('middle')
    columns.col('line')
    columns.col('linenr')
    columns.col('edge')

    thead = table.thead()
    cell = thead.tr().td('h1', colspan=8)
    h1 = cell.h1()

    def make_url(url_path, path):
        params = {"sha1": sha1, "path": path}
        if review is None:
            params["repository"] = str(repository.id)
        else:
            params["review"] = str(review.id)
        return "%s?%s" % (url_path, urllib.urlencode(params))

    h1.a("root", href=make_url("showtree", "/")).text("root")
    h1.span().text('/')

    components = path.split("/")
    for index, component in enumerate(components[:-1]):
        h1.a(href=make_url("showtree", "/".join(components[:index + 1]))).text(
            component, escape=True)
        h1.span().text('/')

    if first is not None:
        h1.a(href=make_url("showfile", "/".join(components))).text(
            components[-1], escape=True)
    else:
        h1.text(components[-1], escape=True)

    h1.span("right").a(href=("/download/%s?repository=%s&sha1=%s" %
                             (urllib.quote(path), repository.name, file_sha1)),
                       download=urllib.quote(path)).text("[download]")
    h1.span("right").a(
        href=("/download/%s?repository=%s&sha1=%s" %
              (urllib.quote(path), repository.name, file_sha1))).text("[view]")

    table.tbody('spacer top').tr('spacer top').td(colspan=8).text()

    tbody = table.tbody("lines")

    yield document.render(stop=tbody, pretty=not compact)

    for linenr, line in enumerate(file.newLines(True)):
        linenr = linenr + 1
        highlight_class = ""

        if first is not None:
            if not (first_with_context <= linenr <= last_with_context):
                continue
            if linenr == first:
                highlight_class += " first-selected"
            if linenr == last:
                highlight_class += " last-selected"

        if tabify:
            line = htmlutils.tabify(line, tabwidth, indenttabsmode)

        line = line.replace("\r", "<i class='cr'></i>")

        row = tbody.tr("line context single",
                       id="f%do%dn%d" % (file.id, linenr, linenr))
        row.td("edge").text()
        row.td("linenr old").text(linenr)
        row.td("line single whole%s" % highlight_class,
               id="f%dn%d" % (file.id, linenr),
               colspan=4).innerHTML(line)
        row.td("linenr new").text(linenr)
        row.td("edge").text()

        if linenr % 500:
            yield document.render(stop=tbody, pretty=not compact)

    table.tbody('spacer bottom').tr('spacer bottom').td(colspan=8).text()

    yield document.render(pretty=not compact)