Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce new Renderer #1995

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

kounoike
Copy link
Contributor

@kounoike kounoike commented May 4, 2018

Introduce new rendering approach

This PR introduce EnhancedRenderer and some extended classes.

Changes

All files rendered by EnhancedRenderer.

  • It makes more simple logic for blob/diff view.
  • Previous Renderer assumes used for render Markup. So, image/video/audio renderer is tricky.

correct handling image files

Previously, GitBucket detects image files by MIME-type. But, some image/xxx files can't render by browser. for example tiff file can't render by img tag.

EnhancedRenderer provides ImageRenderer trait. It represents "file can render by img tag".

Image file is not limited to binary

For example, SVG file is text but it can rendered by img tag. So this PR introduce TextImageRenderer. It extends TextRenderer and ImageRenderer. For TextImageRenderer repository viewer and diff viewer render both image and source text .

image

image

Allow read file as string or binary for EnhancedRenderer

EnhancedRenderer can read file as binary. So, it can render with format converter (for example, tiff to png).

It enables direct serving image file with data-URI. It reduce number of connections.

20180503-132639

Diffs rendered by EnhancedRenderer.

If plugin has new renderer which extends ImageRenderer, automatically rendered with Image Diff tool.

or, If renderer extends DiffRenderer, diffs are rendered with custom diff rendering.

for example: JSON diff by benjamine/JsonDiffPatch

image

Compatibility

The RendererWrapper wraps old-style Renderer to new-style EnhancedRenderer. So, no changes required for old-style Renderer plugins.

Before submitting a pull-request to GitBucket I have first:

  • read the contribution guidelines
  • rebased my branch over master
  • verified that project is compiling
  • verified that tests are passing
  • squashed my commits as appropriate (keep several commits if it is relevant to understand the PR)
  • marked as closed using commit message all issue ID that this PR should correct

Copy link
Member

@takezoe takezoe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically, this extension looks good to enrich GitBucket presentation.

Since this is a much big pull request, I can't review perfectly at once. So I commented based on my first impression as a first review.


trait DiffRenderer extends EnhancedRenderer {
def renderDiff(oldRequest: EnhancedRenderRequest, newRequest: EnhancedRenderRequest, diffIndex: Int): Html
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why DiffRenderer is extending EnhancedRenderer.

/**
* New style Renderer
*/
trait EnhancedRenderer {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's might better that EnhancedRenderer has only renderContent method and implement the method in concrete classes. While it looks to switch diff presentation in the template by checking type of renderer, I wonder if it can be generalized instead of branching off in the template.

On the other hand, it's understandable that TextRenderer has further rendererMarkup method.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well... My opinion is the a parent class shouldn't know sub classes.

renderContent() isn't necessary to be implemented at EnhancedRenderer. This method should be only declared at EnhancedRenderer, and you can give implementation at TextRemderer, ImageRenderer and ObjectRenderer.

<input type="hidden" id="content" name="content" value=""/>
<input type="hidden" id="initial" value="@content.content"/>
<input type="hidden" id="initial" value="@content.map(_.content).getOrElse("")"/>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.getOrElse("") is unnecessary. Twirl renderes None as an empty string.

}
}

def openFile[T](default: T)(f: InputStream => T): T = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method shold be private or protected at least.

}
}

def content: String = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method can be implemented using contentBytes.

val fileName = filePath.last.toLowerCase
val extension = FileUtil.getExtension(fileName)
val renderer = PluginRegistry().getRenderer(extension)
if (renderer.isInstanceOf[TextRenderer]) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically, pattern match is better than the combination of isInstanceOf and cast, but adding a method to return TextRenderer might be more better.

case (None, None) => {
<div>Error???</div>
}
case (Some(oldCI: ContentInfo), Some(newCI: ContentInfo)) if oldCI.renderer.isInstanceOf[DiffRenderer] && newCI.renderer.isInstanceOf[DiffRenderer] => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oldCI and newCI are a little confusing because I didn't know what is CI at first. In my opinion, oldContentInfo and newContentInfo are better in this case.

} else {
@if(diff.changeType != ChangeType.ADD){
<div style="padding: 12px;">Not supported</div>
@* TODO: too Large check *@
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While TODO comment is remaining, is this pull request ready to be merged?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I didn't remove it. This TODO is fixed by introducing LargeFileRenderer

@kounoike
Copy link
Contributor Author

@takezoe Thanks for your review comments. Sorry for large PR. But I can't split to smaller PR.

I fixed this PR with your comments except this comment. It is essential part of this PR. I think we need to discuss it carefully. I think renderAsXXX is easily understandable for plugin developers. Especially its target file format can render as both text and image, like SVG.

loader: ObjectLoader,
branch: String,
filePath: String
) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically, case classes shouldn't cause (or depend on) side effect in the constructor. These properties should be set at the outside of case classes (e.g. You can create a factory method in the companion object to do it).

This also makes possible to extract a renderer using pattern matching, not cast of the renderer property.

/**
* New style Renderer
*/
trait EnhancedRenderer {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well... My opinion is the a parent class shouldn't know sub classes.

renderContent() isn't necessary to be implemented at EnhancedRenderer. This method should be only declared at EnhancedRenderer, and you can give implementation at TextRemderer, ImageRenderer and ObjectRenderer.

case (None, None) => {
<div>Error???</div>
}
case (Some(oldContentInfo: ContentInfo), Some(newContentInfo: ContentInfo)) if oldContentInfo.renderer.isInstanceOf[DiffRenderer] && newContentInfo.renderer.isInstanceOf[DiffRenderer] => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if renderer can be extracted from ContentInfo using pattern matching, not type casting. See also above commet for ContentInfo.

@takezoe
Copy link
Member

takezoe commented Jun 23, 2018

@kounoike I added some comments to the design of your new rederer framework. I hope that you will check them.

@ExtArtQ
Copy link

ExtArtQ commented Jan 24, 2023

And how did it all end?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging this pull request may close these issues.

None yet

3 participants