Commit a9cce9358ae2c264bad1fc61c6e1d20b9e3a23c7
Exists in
master
and in
4 other branches
Merge pull request #1627 from tsigo/tree_performance
Tree performance improvements
Showing
10 changed files
with
83 additions
and
95 deletions
Show diff stats
app/assets/javascripts/tree.js
@@ -1,30 +0,0 @@ | @@ -1,30 +0,0 @@ | ||
1 | -/** | ||
2 | - * Tree slider for code browse | ||
3 | - * | ||
4 | - */ | ||
5 | -var Tree = { | ||
6 | - init: | ||
7 | - function() { | ||
8 | - $('#tree-slider .tree-item-file-name a, .breadcrumb li > a').live("click", function() { | ||
9 | - $("#tree-content-holder").hide("slide", { direction: "left" }, 150) | ||
10 | - }) | ||
11 | - | ||
12 | - $('.project-refs-form').live({ | ||
13 | - "ajax:beforeSend": function() { | ||
14 | - $("#tree-content-holder").hide("slide", { direction: "left" }, 150); | ||
15 | - } | ||
16 | - }) | ||
17 | - | ||
18 | - $("#tree-slider .tree-item").live('click', function(e){ | ||
19 | - if(e.target.nodeName != "A") { | ||
20 | - link = $(this).find(".tree-item-file-name a"); | ||
21 | - link.trigger("click"); | ||
22 | - } | ||
23 | - }); | ||
24 | - | ||
25 | - $('#tree-slider .tree-item-file-name a, .breadcrumb a, .project-refs-form').live({ | ||
26 | - "ajax:beforeSend": function() { $('.tree_progress').addClass("loading"); }, | ||
27 | - "ajax:complete": function() { $('.tree_progress').removeClass("loading"); } | ||
28 | - }); | ||
29 | - } | ||
30 | -} |
@@ -0,0 +1,21 @@ | @@ -0,0 +1,21 @@ | ||
1 | +# Code browser tree slider | ||
2 | + | ||
3 | +$ -> | ||
4 | + if $('#tree-slider').length > 0 | ||
5 | + # Show the "Loading commit data" for only the first element | ||
6 | + $('span.log_loading:first').removeClass('hide') | ||
7 | + | ||
8 | + $('#tree-slider .tree-item-file-name a, .breadcrumb li > a').live "click", -> | ||
9 | + $("#tree-content-holder").hide("slide", { direction: "left" }, 150) | ||
10 | + | ||
11 | + $('.project-refs-form').live | ||
12 | + "ajax:beforeSend": -> $("#tree-content-holder").hide("slide", { direction: "left" }, 150) | ||
13 | + | ||
14 | + # Make the entire tree-item row clickable, but not if clicking another link (like a commit message) | ||
15 | + $("#tree-slider .tree-item").live 'click', (e) -> | ||
16 | + $('.tree-item-file-name a', this).trigger('click') if (e.target.nodeName != "A") | ||
17 | + | ||
18 | + # Show/Hide the loading spinner | ||
19 | + $('#tree-slider .tree-item-file-name a, .breadcrumb a, .project-refs-form').live | ||
20 | + "ajax:beforeSend": -> $('.tree_progress').addClass("loading") | ||
21 | + "ajax:complete": -> $('.tree_progress').removeClass("loading") |
app/decorators/tree_decorator.rb
@@ -8,14 +8,14 @@ class TreeDecorator < ApplicationDecorator | @@ -8,14 +8,14 @@ class TreeDecorator < ApplicationDecorator | ||
8 | 8 | ||
9 | #parts = parts[0...-1] if is_blob? | 9 | #parts = parts[0...-1] if is_blob? |
10 | 10 | ||
11 | - yield(h.link_to("..", "#", remote: :true)) if parts.count > max_links | 11 | + yield(h.link_to("..", "#", remote: true)) if parts.count > max_links |
12 | 12 | ||
13 | parts.each do |part| | 13 | parts.each do |part| |
14 | part_path = File.join(part_path, part) unless part_path.empty? | 14 | part_path = File.join(part_path, part) unless part_path.empty? |
15 | part_path = part if part_path.empty? | 15 | part_path = part if part_path.empty? |
16 | 16 | ||
17 | next unless parts.last(2).include?(part) if parts.count > max_links | 17 | next unless parts.last(2).include?(part) if parts.count > max_links |
18 | - yield(h.link_to(h.truncate(part, length: 40), h.project_tree_path(project, h.tree_join(ref, part_path)), remote: :true)) | 18 | + yield(h.link_to(h.truncate(part, length: 40), h.project_tree_path(project, h.tree_join(ref, part_path)), remote: true)) |
19 | end | 19 | end |
20 | end | 20 | end |
21 | end | 21 | end |
app/helpers/tree_helper.rb
1 | module TreeHelper | 1 | module TreeHelper |
2 | - def tree_icon(content) | ||
3 | - if content.is_a?(Grit::Blob) | ||
4 | - if content.text? | ||
5 | - image_tag "file_txt.png" | ||
6 | - elsif content.image? | ||
7 | - image_tag "file_img.png" | 2 | + # Sorts a repository's tree so that folders are before files and renders |
3 | + # their corresponding partials | ||
4 | + # | ||
5 | + # contents - A Grit::Tree object for the current tree | ||
6 | + def render_tree(contents) | ||
7 | + # Render Folders before Files/Submodules | ||
8 | + folders, files = contents.partition { |v| v.kind_of?(Grit::Tree) } | ||
9 | + | ||
10 | + tree = "" | ||
11 | + | ||
12 | + # Render folders if we have any | ||
13 | + tree += render partial: 'tree/tree_item', collection: folders, locals: {type: 'folder'} if folders.present? | ||
14 | + | ||
15 | + files.each do |f| | ||
16 | + if f.respond_to?(:url) | ||
17 | + # Object is a Submodule | ||
18 | + tree += render partial: 'tree/submodule_item', object: f | ||
8 | else | 19 | else |
9 | - image_tag "file_bin.png" | 20 | + # Object is a Blob |
21 | + tree += render partial: 'tree/tree_item', object: f, locals: {type: 'file'} | ||
10 | end | 22 | end |
11 | - else | ||
12 | - image_tag "file_dir.png" | ||
13 | end | 23 | end |
24 | + | ||
25 | + tree.html_safe | ||
14 | end | 26 | end |
15 | 27 | ||
16 | - def tree_hex_class(content) | ||
17 | - "file_#{hexdigest(content.name)}" | 28 | + # Return an image icon depending on the file type |
29 | + # | ||
30 | + # type - String type of the tree item; either 'folder' or 'file' | ||
31 | + def tree_icon(type) | ||
32 | + image = type == 'folder' ? 'file_dir.png' : 'file_txt.png' | ||
33 | + image_tag(image, size: '16x16') | ||
18 | end | 34 | end |
19 | 35 | ||
20 | - def tree_full_path(content) | ||
21 | - content.name.force_encoding('utf-8') | ||
22 | - if params[:path] | ||
23 | - File.join(params[:path], content.name) | ||
24 | - else | ||
25 | - content.name | ||
26 | - end | 36 | + def tree_hex_class(content) |
37 | + "file_#{hexdigest(content.name)}" | ||
27 | end | 38 | end |
28 | 39 | ||
29 | # Public: Determines if a given filename is compatible with GitHub::Markup. | 40 | # Public: Determines if a given filename is compatible with GitHub::Markup. |
app/views/tree/_submodule_item.html.haml
1 | -- url = content.url(@ref) rescue nil | ||
2 | -- name = content.basename | 1 | +- url = submodule_item.url(@ref) rescue nil |
2 | +- name = submodule_item.basename | ||
3 | - return unless url | 3 | - return unless url |
4 | %tr{ class: "tree-item", url: url } | 4 | %tr{ class: "tree-item", url: url } |
5 | %td.tree-item-file-name | 5 | %td.tree-item-file-name |
6 | = image_tag "submodule.png" | 6 | = image_tag "submodule.png" |
7 | %strong= truncate(name, length: 40) | 7 | %strong= truncate(name, length: 40) |
8 | %td | 8 | %td |
9 | - %code= content.id[0..10] | 9 | + %code= submodule_item.id[0..10] |
10 | %td | 10 | %td |
11 | = link_to truncate(url, length: 40), url | 11 | = link_to truncate(url, length: 40), url |
12 | - | ||
13 | - |
app/views/tree/_tree.html.haml
@@ -6,13 +6,14 @@ | @@ -6,13 +6,14 @@ | ||
6 | - tree.breadcrumbs(6) do |link| | 6 | - tree.breadcrumbs(6) do |link| |
7 | \/ | 7 | \/ |
8 | %li= link | 8 | %li= link |
9 | + | ||
9 | .clear | 10 | .clear |
10 | %div.tree_progress | 11 | %div.tree_progress |
12 | + | ||
11 | %div#tree-content-holder.tree-content-holder | 13 | %div#tree-content-holder.tree-content-holder |
12 | - if tree.is_blob? | 14 | - if tree.is_blob? |
13 | - = render partial: "tree/tree_file", locals: { name: tree.name, content: tree.data, file: tree } | 15 | + = render partial: "tree/tree_file", object: tree |
14 | - else | 16 | - else |
15 | - - contents = tree.contents | ||
16 | %table#tree-slider{class: "table_#{@hex_path} tree-table" } | 17 | %table#tree-slider{class: "table_#{@hex_path} tree-table" } |
17 | %thead | 18 | %thead |
18 | %th Name | 19 | %th Name |
@@ -22,22 +23,16 @@ | @@ -22,22 +23,16 @@ | ||
22 | = link_to "History", tree.history_path, class: "right" | 23 | = link_to "History", tree.history_path, class: "right" |
23 | 24 | ||
24 | - if tree.up_dir? | 25 | - if tree.up_dir? |
25 | - %tr{ class: "tree-item", url: tree.up_dir_path } | 26 | + %tr.tree-item |
26 | %td.tree-item-file-name | 27 | %td.tree-item-file-name |
27 | - = image_tag "file_empty.png" | ||
28 | - = link_to "..", tree.up_dir_path, remote: :true | 28 | + = image_tag "file_empty.png", size: '16x16' |
29 | + = link_to "..", tree.up_dir_path, remote: true | ||
29 | %td | 30 | %td |
30 | %td | 31 | %td |
31 | 32 | ||
32 | - - index = 0 | ||
33 | - - contents.select{ |i| i.is_a?(Grit::Tree)}.each do |content| | ||
34 | - = render partial: "tree/tree_item", locals: { content: content, index: (index += 1) } | ||
35 | - - contents.select{ |i| i.is_a?(Grit::Blob)}.each do |content| | ||
36 | - = render partial: "tree/tree_item", locals: { content: content, index: (index += 1) } | ||
37 | - - contents.select{ |i| i.is_a?(Grit::Submodule)}.each do |content| | ||
38 | - = render partial: "tree/submodule_item", locals: { content: content, index: (index += 1) } | 33 | + = render_tree(tree.contents) |
39 | 34 | ||
40 | - - if content = contents.select{ |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i }.first | 35 | + - if content = tree.contents.find { |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i } |
41 | .file_holder#README | 36 | .file_holder#README |
42 | .file_title | 37 | .file_title |
43 | %i.icon-file | 38 | %i.icon-file |
app/views/tree/_tree_file.html.haml
@@ -2,32 +2,32 @@ | @@ -2,32 +2,32 @@ | ||
2 | .file_title | 2 | .file_title |
3 | %i.icon-file | 3 | %i.icon-file |
4 | %span.file_name | 4 | %span.file_name |
5 | - = name.force_encoding('utf-8') | ||
6 | - %small #{file.mode} | 5 | + = tree_file.name.force_encoding('utf-8') |
6 | + %small #{tree_file.mode} | ||
7 | %span.options | 7 | %span.options |
8 | = link_to "raw", project_blob_path(@project, @id), class: "btn very_small", target: "_blank" | 8 | = link_to "raw", project_blob_path(@project, @id), class: "btn very_small", target: "_blank" |
9 | = link_to "history", project_commits_path(@project, @id), class: "btn very_small" | 9 | = link_to "history", project_commits_path(@project, @id), class: "btn very_small" |
10 | = link_to "blame", project_blame_path(@project, @id), class: "btn very_small" | 10 | = link_to "blame", project_blame_path(@project, @id), class: "btn very_small" |
11 | - - if file.text? | ||
12 | - - if gitlab_markdown?(name) | 11 | + - if tree_file.text? |
12 | + - if gitlab_markdown?(tree_file.name) | ||
13 | .file_content.wiki | 13 | .file_content.wiki |
14 | = preserve do | 14 | = preserve do |
15 | - = markdown(file.data) | ||
16 | - - elsif markup?(name) | 15 | + = markdown(tree_file.data) |
16 | + - elsif markup?(tree_file.name) | ||
17 | .file_content.wiki | 17 | .file_content.wiki |
18 | - = raw GitHub::Markup.render(name, file.data) | 18 | + = raw GitHub::Markup.render(tree_file.name, tree_file.data) |
19 | - else | 19 | - else |
20 | .file_content.code | 20 | .file_content.code |
21 | - - unless file.empty? | 21 | + - unless tree_file.empty? |
22 | %div{class: current_user.dark_scheme ? "black" : "white"} | 22 | %div{class: current_user.dark_scheme ? "black" : "white"} |
23 | = preserve do | 23 | = preserve do |
24 | - = raw file.colorize(options: { linenos: 'True'}) | 24 | + = raw tree_file.colorize(options: { linenos: 'True'}) |
25 | - else | 25 | - else |
26 | %h4.nothing_here_message Empty file | 26 | %h4.nothing_here_message Empty file |
27 | 27 | ||
28 | - - elsif file.image? | 28 | + - elsif tree_file.image? |
29 | .file_content.image_file | 29 | .file_content.image_file |
30 | - %img{ src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} | 30 | + %img{ src: "data:#{tree_file.mime_type};base64,#{Base64.encode64(tree_file.data)}"} |
31 | 31 | ||
32 | - else | 32 | - else |
33 | .file_content.blob_file | 33 | .file_content.blob_file |
@@ -37,4 +37,4 @@ | @@ -37,4 +37,4 @@ | ||
37 | %br | 37 | %br |
38 | = image_tag "download.png", width: 64 | 38 | = image_tag "download.png", width: 64 |
39 | %h3 | 39 | %h3 |
40 | - Download (#{file.mb_size}) | 40 | + Download (#{tree_file.mb_size}) |
app/views/tree/_tree_item.html.haml
1 | -- file = tree_full_path(content) | ||
2 | -%tr{ class: "tree-item #{tree_hex_class(content)}", url: project_tree_path(@project, tree_join(@id, file)) } | 1 | +%tr{ class: "tree-item #{tree_hex_class(tree_item)}" } |
3 | %td.tree-item-file-name | 2 | %td.tree-item-file-name |
4 | - = tree_icon(content) | ||
5 | - %strong= link_to truncate(content.name, length: 40), project_tree_path(@project, tree_join(@id || @commit.id, file)), remote: :true | 3 | + = tree_icon(type) |
4 | + %strong= link_to truncate(tree_item.name, length: 40), project_tree_path(@project, tree_join(@id || @commit.id, tree_item.name)), remote: true | ||
6 | %td.tree_time_ago.cgray | 5 | %td.tree_time_ago.cgray |
7 | - - if index == 1 | ||
8 | - %span.log_loading | ||
9 | - Loading commit data.. | ||
10 | - = image_tag "ajax_loader_tree.gif", width: 14 | 6 | + %span.log_loading.hide |
7 | + Loading commit data... | ||
8 | + = image_tag "ajax_loader_tree.gif", width: 14 | ||
11 | %td.tree_commit | 9 | %td.tree_commit |
app/views/tree/show.html.haml
app/views/tree/show.js.haml
1 | :plain | 1 | :plain |
2 | // Load Files list | 2 | // Load Files list |
3 | - $("#tree-holder").html("#{escape_javascript(render(partial: "tree", locals: {commit: @commit, tree: @tree}))}"); | 3 | + $("#tree-holder").html("#{escape_javascript(render(partial: "tree", locals: {tree: @tree}))}"); |
4 | $("#tree-content-holder").show("slide", { direction: "right" }, 150); | 4 | $("#tree-content-holder").show("slide", { direction: "right" }, 150); |
5 | $('.project-refs-form #path').val("#{@path}"); | 5 | $('.project-refs-form #path').val("#{@path}"); |
6 | 6 |