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 | -/** | |
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 @@ |
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 | 8 | |
9 | 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 | 13 | parts.each do |part| |
14 | 14 | part_path = File.join(part_path, part) unless part_path.empty? |
15 | 15 | part_path = part if part_path.empty? |
16 | 16 | |
17 | 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 | 19 | end |
20 | 20 | end |
21 | 21 | end | ... | ... |
app/helpers/tree_helper.rb
1 | 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 | 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 | 22 | end |
11 | - else | |
12 | - image_tag "file_dir.png" | |
13 | 23 | end |
24 | + | |
25 | + tree.html_safe | |
14 | 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 | 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 | 38 | end |
28 | 39 | |
29 | 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 | 3 | - return unless url |
4 | 4 | %tr{ class: "tree-item", url: url } |
5 | 5 | %td.tree-item-file-name |
6 | 6 | = image_tag "submodule.png" |
7 | 7 | %strong= truncate(name, length: 40) |
8 | 8 | %td |
9 | - %code= content.id[0..10] | |
9 | + %code= submodule_item.id[0..10] | |
10 | 10 | %td |
11 | 11 | = link_to truncate(url, length: 40), url |
12 | - | |
13 | - | ... | ... |
app/views/tree/_tree.html.haml
... | ... | @@ -6,13 +6,14 @@ |
6 | 6 | - tree.breadcrumbs(6) do |link| |
7 | 7 | \/ |
8 | 8 | %li= link |
9 | + | |
9 | 10 | .clear |
10 | 11 | %div.tree_progress |
12 | + | |
11 | 13 | %div#tree-content-holder.tree-content-holder |
12 | 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 | 16 | - else |
15 | - - contents = tree.contents | |
16 | 17 | %table#tree-slider{class: "table_#{@hex_path} tree-table" } |
17 | 18 | %thead |
18 | 19 | %th Name |
... | ... | @@ -22,22 +23,16 @@ |
22 | 23 | = link_to "History", tree.history_path, class: "right" |
23 | 24 | |
24 | 25 | - if tree.up_dir? |
25 | - %tr{ class: "tree-item", url: tree.up_dir_path } | |
26 | + %tr.tree-item | |
26 | 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 | 30 | %td |
30 | 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 | 36 | .file_holder#README |
42 | 37 | .file_title |
43 | 38 | %i.icon-file | ... | ... |
app/views/tree/_tree_file.html.haml
... | ... | @@ -2,32 +2,32 @@ |
2 | 2 | .file_title |
3 | 3 | %i.icon-file |
4 | 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 | 7 | %span.options |
8 | 8 | = link_to "raw", project_blob_path(@project, @id), class: "btn very_small", target: "_blank" |
9 | 9 | = link_to "history", project_commits_path(@project, @id), class: "btn very_small" |
10 | 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 | 13 | .file_content.wiki |
14 | 14 | = preserve do |
15 | - = markdown(file.data) | |
16 | - - elsif markup?(name) | |
15 | + = markdown(tree_file.data) | |
16 | + - elsif markup?(tree_file.name) | |
17 | 17 | .file_content.wiki |
18 | - = raw GitHub::Markup.render(name, file.data) | |
18 | + = raw GitHub::Markup.render(tree_file.name, tree_file.data) | |
19 | 19 | - else |
20 | 20 | .file_content.code |
21 | - - unless file.empty? | |
21 | + - unless tree_file.empty? | |
22 | 22 | %div{class: current_user.dark_scheme ? "black" : "white"} |
23 | 23 | = preserve do |
24 | - = raw file.colorize(options: { linenos: 'True'}) | |
24 | + = raw tree_file.colorize(options: { linenos: 'True'}) | |
25 | 25 | - else |
26 | 26 | %h4.nothing_here_message Empty file |
27 | 27 | |
28 | - - elsif file.image? | |
28 | + - elsif tree_file.image? | |
29 | 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 | 32 | - else |
33 | 33 | .file_content.blob_file |
... | ... | @@ -37,4 +37,4 @@ |
37 | 37 | %br |
38 | 38 | = image_tag "download.png", width: 64 |
39 | 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 | 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 | 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 | 9 | %td.tree_commit | ... | ... |
app/views/tree/show.html.haml
app/views/tree/show.js.haml
1 | 1 | :plain |
2 | 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 | 4 | $("#tree-content-holder").show("slide", { direction: "right" }, 150); |
5 | 5 | $('.project-refs-form #path').val("#{@path}"); |
6 | 6 | ... | ... |