diff options
author | Fabrice Bellingard <bellingard@gmail.com> | 2011-10-26 19:18:08 +0200 |
---|---|---|
committer | Fabrice Bellingard <bellingard@gmail.com> | 2011-10-26 19:18:56 +0200 |
commit | af58e35a58579484d6521bd3e675c90e43c18623 (patch) | |
tree | f41f4dbd6540d223a2767134837340104f4c3a19 /sonar-server | |
parent | a589e2539c1d163337daf23b682c95d492b3fbb9 (diff) | |
download | sonarqube-af58e35a58579484d6521bd3e675c90e43c18623.tar.gz sonarqube-af58e35a58579484d6521bd3e675c90e43c18623.zip |
SONAR-2733 Display duplicated blocks by group in the resource viewer
First shot, needs improvements.
Diffstat (limited to 'sonar-server')
8 files changed, 225 insertions, 4 deletions
diff --git a/sonar-server/src/main/java/org/sonar/server/ui/DefaultPages.java b/sonar-server/src/main/java/org/sonar/server/ui/DefaultPages.java index ed976159312..a68cbc86c19 100644 --- a/sonar-server/src/main/java/org/sonar/server/ui/DefaultPages.java +++ b/sonar-server/src/main/java/org/sonar/server/ui/DefaultPages.java @@ -28,7 +28,7 @@ import org.sonar.api.web.*; */ public final class DefaultPages { - private static final View[] PAGES = { new SourceTab(), new CoverageTab(), new ViolationsTab() }; + private static final View[] PAGES = { new SourceTab(), new CoverageTab(), new ViolationsTab(), new DuplicationsTab() }; private DefaultPages() { } @@ -103,4 +103,23 @@ public final class DefaultPages { return "Violations"; } } + + @NavigationSection(NavigationSection.RESOURCE_TAB) + @DefaultTab(metrics = {CoreMetrics.DUPLICATED_LINES_KEY, CoreMetrics.DUPLICATED_BLOCKS_KEY, CoreMetrics.DUPLICATED_FILES_KEY, CoreMetrics.DUPLICATED_LINES_DENSITY_KEY}) + @ResourceQualifier({Qualifiers.FILE, Qualifiers.CLASS}) + @UserRole(UserRole.CODEVIEWER) + private static final class DuplicationsTab implements RubyRailsPage { + public String getTemplate() { + //not used, hardcoded in BrowseController + return "browse/index"; + } + + public String getId() { + return "duplications"; + } + + public String getTitle() { + return "Duplications"; + } + } } diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/resource_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/resource_controller.rb index 2ca2eadfa51..61c30b9aee9 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/resource_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/resource_controller.rb @@ -17,10 +17,14 @@ # License along with Sonar; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 # +require "rexml/document" + class ResourceController < ApplicationController + include REXML + SECTION=Navigation::SECTION_RESOURCE - helper :dashboard + helper :dashboard, SourceHelper def index @resource = Project.by_key(params[:id]) @@ -38,6 +42,8 @@ class ResourceController < ApplicationController render_coverage() elsif (@extension.getId()=='source') render_source() + elsif (@extension.getId()=='duplications') + render_duplications() else render_extension() end @@ -48,6 +54,15 @@ class ResourceController < ApplicationController access_denied end end + + def show_duplication_snippet + @resource = Project.by_key(params[:id]) + if (@resource && has_role?(:user, @resource)) + render :partial => 'duplications_source_snippet', :locals => {:resource => @resource, :from_line => params[:from_line].to_i, :to_line => params[:to_line].to_i} + else + access_denied + end + end private @@ -165,7 +180,24 @@ class ResourceController < ApplicationController end render :action => 'index', :layout => !request.xhr? end - + + + def render_duplications + duplications_data = @snapshot.measure('duplications_data'); + + @duplication_groups = [] + if duplications_data && duplications_data.measure_data && duplications_data.measure_data.data + dups = Document.new duplications_data.measure_data.data.to_s + dups.elements.each("duplications/duplication") do |dup| + group = [] + group << {:lines_count => dup.attributes['lines'], :from_line => dup.attributes['start'], :resource => @resource} + group << {:lines_count => dup.attributes['lines'], :from_line => dup.attributes['target-start'], :resource => Project.by_key(dup.attributes['target-resource'])} + @duplication_groups << group + end + end + + render :action => 'index', :layout => !request.xhr? + end def render_violations diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_duplications.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_duplications.html.erb new file mode 100644 index 00000000000..fc9bbb1b809 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_duplications.html.erb @@ -0,0 +1,90 @@ +<% if @duplication_groups.empty? %> + + <%= message('duplications.no_duplicated_block') -%> + +<% else %> + + <table id="duplicationsTable" class="data max-width"> + <thead> + <tr> + <th class="thin nowrap"><%= message('duplications.blocks') -%></th> + <th class="thin nowrap"><%= message('duplications.number_of_lines') -%></th> + <th class="thin nowrap"><%= message('duplications.from_line') -%></th> + <th class="thin nowrap"><%= message('duplications.file') -%></th> + <th><%= message('duplications.details') -%></th> + <th class="thin nowrap"></th> + </tr> + </thead> + + <tbody> + <% + @duplication_groups.each_with_index do |group, group_index| + group_size = group.size() + row_style = cycle 'even','odd', :name => ('duplications') + %> + <tr class="<%= row_style -%>"> + <td rowspan="<%= group_size+2 -%>" class="center"> + <br/><b><%= group_size -%></b> + </td> + <td colspan="4" style="height:2px"></td> + <td rowspan="<%= group_size+2 -%>"></td> + </tr> + <% + group.each_with_index do |dup, index| + resource = dup[:resource] + from_line = dup[:from_line].to_i + to_line = from_line+5 + update_snippet_script = "new Ajax.Updater('source-#{group_index}', '#{url_for :action => :show_duplication_snippet, :params => {:id => resource.id, :from_line => from_line, :to_line => to_line}}', {asynchronous:true, evalScripts:true}); return false;" + groupClass = "group" + group_index.to_s + groupRowClass = "row" + group_index.to_s + "-" + index.to_s + %> + <tr class="hoverable <%= row_style -%>"> + <td class="center group-item"> + <div class="<%= groupClass -%> <%= groupRowClass -%> clickable <%= 'selected' if resource==@resource -%>" + onclick="updateDuplicationLines(this, '<%= groupClass -%>', '<%= groupRowClass -%>'); <%= update_snippet_script -%>" style="border-right-width: 0px;"> + <%= dup[:lines_count] -%> + <div> + </td> + <td class="center group-item"> + <div class="group<%= group_index -%> <%= groupRowClass -%> clickable <%= 'selected' if resource==@resource -%>" + onclick="updateDuplicationLines(this, '<%= groupClass -%>', '<%= groupRowClass -%>'); <%= update_snippet_script -%>" style="border-right-width: 0px;border-left-width: 0px;"> + <%= from_line -%> + </div> + </td> + <td class="left group-item"> + <% if resource==@resource + cell_content = resource.name + else + cell_content = link_to_resource(resource, resource.name) + end + %> + <div class="group<%= group_index -%> <%= groupRowClass -%> clickable <%= 'selected' if resource==@resource -%>" + onclick="updateDuplicationLines(this, '<%= groupClass -%>', '<%= groupRowClass -%>'); <%= update_snippet_script -%>" style="padding-right: 20px; border-right-width: 0px;border-left-width: 0px;"> + <%= cell_content -%> + <div> + </td> + + <% if index==0 %> + <td rowspan="<%= group_size+1 -%>" class="source-snippet"> + <div id="source-<%= group_index -%>"> + <%= render :partial => 'duplications_source_snippet', :locals => {:resource => resource, :from_line => from_line, :to_line => to_line} -%> + </div> + </td> + <% end %> + </tr> + <% + end + %> + <tr class="<%= row_style -%>" style="padding-bottom:100px"> + <td colspan="3"></td> + </tr> + <tr class="<%= row_style -%>"> + <td colspan="6" style="height:4px"></td> + </tr> + <% + end + %> + </tbody> + </table> + +<% end %>
\ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_duplications_source_snippet.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_duplications_source_snippet.html.erb new file mode 100644 index 00000000000..7b4faa9a565 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_duplications_source_snippet.html.erb @@ -0,0 +1,2 @@ +<%= resource.key -%> +<%= snapshot_source_to_html(resource.last_snapshot, {:line_range => (from_line)..(to_line)}) -%> diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_header_duplications.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_header_duplications.html.erb new file mode 100644 index 00000000000..99282485557 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_header_duplications.html.erb @@ -0,0 +1,22 @@ +<div id="source_header" class="tab_header"> + <table class="metrics"> + <tr> + <% m=measure('duplicated_lines_density') %> + <td class="value big"><%= m ? format_measure(m) : "0%" -%></td> + <td class="sep"> </td> + <% m=measure('lines') %> + <td class="name"><%= message('metric.lines.name') -%>:</td> + <td class="value"><%= m ? format_measure(m) : "0" -%></td> + <td class="sep"> </td> + <% m=measure('duplicated_lines') %> + <td class="name"><%= message('metric.duplicated_lines.name') -%>:</td> + <td class="value"><%= m ? format_measure(m) : "0" -%></td> + <td class="sep"> </td> + <% m=measure('duplicated_blocks') %> + <td class="name"><%= message('metric.duplicated_blocks.name') -%>:</td> + <td class="value"><%= m ? format_measure(m) : "0" -%></td> + <td class="sep"> </td> + </tr> + </table> +</div> + diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/resource/index.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/resource/index.html.erb index 7ec54ed21cd..31c2aa66209 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/resource/index.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/resource/index.html.erb @@ -13,7 +13,9 @@ </table> <% end %> + <% if @lines && @lines.size>0 %> + <table id="sources" class="sources2 code" cellpadding="0" cellspacing="0" border="0"> <% current_revision=nil @@ -120,4 +122,9 @@ <% if @filtered && !has_displayed_lines %> <p><%= message('no_lines_match_your_filter_criteria') -%></p> <% end %> + +<% else %> + + <%= render :partial => 'duplications' -%> + <% end %>
\ No newline at end of file diff --git a/sonar-server/src/main/webapp/javascripts/application.js b/sonar-server/src/main/webapp/javascripts/application.js index 4de5a61ed91..5927f31860f 100644 --- a/sonar-server/src/main/webapp/javascripts/application.js +++ b/sonar-server/src/main/webapp/javascripts/application.js @@ -90,6 +90,17 @@ function hideElement( elementId ) { return false; } +function updateDuplicationLines(selectedDiv, groupClass, groupRowClass) { + divs = $$('.'+groupClass); + for ( i = 0; i < divs.size(); i++) { + divs[i].removeClassName('selected'); + } + divs = $$('.'+groupRowClass); + for ( i = 0; i < divs.size(); i++) { + divs[i].addClassName('selected'); + } +} + var projects; function autocompleteProjects(APIURL, projectURL, searchInput, searchResult) { if (projects != null) return; diff --git a/sonar-server/src/main/webapp/stylesheets/style.css b/sonar-server/src/main/webapp/stylesheets/style.css index 037c14f70f7..24ed0b4dd7e 100644 --- a/sonar-server/src/main/webapp/stylesheets/style.css +++ b/sonar-server/src/main/webapp/stylesheets/style.css @@ -917,6 +917,44 @@ span.rulename a:hover { +/* DUPLICATIONS */ + +#duplicationsTable > tbody > tr { + padding-top: 2px; +} + +#duplicationsTable td.group-item { + padding: 0px; + height: 1px; +} + +#duplicationsTable td.group-item > div { + padding: 3px; + margin: 2px 0px; +} + +#duplicationsTable td.group-item > div.selected { + background-color: #fff; + border: 1px solid #DDDDDD; +} + +#duplicationsTable td.source-snippet { + background-color: #ddd; + border: 1px solid #DDDDDD; + padding: 0px; +} + +#duplicationsTable td.source-snippet > div { + padding: 2px; + background-color: #fff; +} + +.clickable { + cursor: pointer; +} + + + /* REVIEWS */ div#review .actions{ visibility: hidden; @@ -1903,7 +1941,7 @@ ul.horizontal li { float: left; position: relative; } -table.nowrap td, td.nowrap { +table.nowrap td, td.nowrap, th.nowrap { white-space: nowrap; } .background-gray { |