]> source.dussan.org Git - sonarqube.git/commitdiff
Component Viewer: duplications
authorStas Vilchik <vilchiks@gmail.com>
Mon, 28 Apr 2014 10:57:34 +0000 (16:57 +0600)
committerStas Vilchik <vilchiks@gmail.com>
Mon, 28 Apr 2014 10:57:44 +0000 (16:57 +0600)
sonar-server/src/main/coffee/component-viewer/coverage-popup.coffee
sonar-server/src/main/coffee/component-viewer/duplication-popup.coffee [new file with mode: 0644]
sonar-server/src/main/coffee/component-viewer/main.coffee
sonar-server/src/main/coffee/component-viewer/popup.coffee [new file with mode: 0644]
sonar-server/src/main/coffee/component-viewer/source.coffee
sonar-server/src/main/hbs/component-viewer/duplicationPopup.hbs [new file with mode: 0644]
sonar-server/src/main/hbs/component-viewer/source.hbs
sonar-server/src/main/less/component-viewer.less
sonar-server/src/main/less/variables.less
sonar-server/src/main/webapp/WEB-INF/app/views/issues/search.html.erb

index 7623eebcd576360e4791827e8f39cb8ea93d5675..63d38c8b7024b7fb6acb9cbb4c4077c4c7d0d4c5 100644 (file)
@@ -1,16 +1,17 @@
 define [
   'backbone.marionette'
   'templates/component-viewer'
+  'component-viewer/popup'
 ], (
   Marionette
   Templates
+  Popup
 ) ->
 
   $ = jQuery
 
 
-  class CoveragePopupView extends Marionette.ItemView
-    className: 'component-viewer-popup'
+  class CoveragePopupView extends Popup
     template: Templates['coveragePopup']
 
 
@@ -18,17 +19,6 @@ define [
       'click a[data-key]': 'goToFile'
 
 
-    onRender: ->
-      @$el.detach().appendTo $('body')
-      @$el.css
-        top: @options.triggerEl.offset().top
-        left: @options.triggerEl.offset().left + @options.triggerEl.outerWidth()
-
-      $('body').on 'click.coverage-popup', =>
-        $('body').off 'click.coverage-popup'
-        @close()
-
-
     goToFile: (e) ->
       key = $(e.currentTarget).data 'key'
       test = $(e.currentTarget).data 'test'
diff --git a/sonar-server/src/main/coffee/component-viewer/duplication-popup.coffee b/sonar-server/src/main/coffee/component-viewer/duplication-popup.coffee
new file mode 100644 (file)
index 0000000..5a22068
--- /dev/null
@@ -0,0 +1,36 @@
+define [
+  'backbone.marionette'
+  'templates/component-viewer'
+  'component-viewer/popup'
+], (
+  Marionette
+  Templates
+  Popup
+) ->
+
+  $ = jQuery
+
+
+  class DuplicationPopupView extends Popup
+    template: Templates['duplicationPopup']
+
+
+    events:
+      'click a[data-key]': 'goToFile'
+
+
+    goToFile: (e) ->
+      key = $(e.currentTarget).data 'key'
+      @options.main.addTransition key, 'duplication', [
+        {
+          key: 'org.codehaus.sonar:sonar-plugin-api:src/test/java/org/sonar/api/resources/ResourceTypeTree.java'
+          name: 'ResourceTypeTree.java'
+          active: true
+        }
+        {
+          key: 'org.codehaus.sonar:sonar-batch:src/main/java/org/sonar/batch/phases/PhaseExecutor.java'
+          name: 'PhaseExecutor.java'
+          active: false
+        }
+      ], []
+
index 5601a7b88d1942120a13878fd2f39c090ac0710b..0f5406188495f869ac3513e7c012df678ed452ba 100644 (file)
@@ -25,6 +25,8 @@ define [
 
   ISSUES_METRIC_LIST = 'blocker_violations,critical_violations,major_violations,minor_violations,info_violations'
 
+  DUPLICATIONS_METRIC_LIST = 'duplicated_lines_density,duplicated_blocks,duplicated_files,duplicated_lines'
+
 
 
   class ComponentViewer extends Marionette.Layout
@@ -52,7 +54,7 @@ define [
       @settings = new Backbone.Model
         issues: false
         coverage: false
-        duplications: false
+        duplications: true
         scm: false
         workspace: false
 
@@ -79,6 +81,10 @@ define [
       $.get API_RESOURCES, resource: key, metrics: ISSUES_METRIC_LIST
 
 
+    requestComponentDuplications: (key) ->
+      $.get API_RESOURCES, resource: key, metrics: DUPLICATIONS_METRIC_LIST
+
+
     requestSource: (key) ->
       $.get API_SOURCES, resource: key, (data) =>
         @source.set source: data[0]
@@ -87,15 +93,15 @@ define [
     extractCoverage: (data) ->
       toObj = (d) ->
         q = {}
-        d.split(';').forEach (item) ->
+        d?.split(';').forEach (item) ->
           tokens = item.split '='
           q[tokens[0]] = tokens[1]
         q
 
       msr = data[0].msr
-      coverage = toObj _.findWhere(msr, key: 'coverage_line_hits_data').data
-      coverageConditions = toObj _.findWhere(msr, key: 'covered_conditions_by_line').data
-      conditions = toObj _.findWhere(msr, key: 'conditions_by_line').data
+      coverage = toObj _.findWhere(msr, key: 'coverage_line_hits_data')?.data
+      coverageConditions = toObj _.findWhere(msr, key: 'covered_conditions_by_line')?.data
+      conditions = toObj _.findWhere(msr, key: 'conditions_by_line')?.data
       @source.set
         coverage: coverage
         coverageConditions: coverageConditions
@@ -105,6 +111,18 @@ define [
       @component.set 'coverageMeasures', coverageMeasures
 
 
+    extractDuplications: (data) ->
+      @source.set
+        duplications: [
+          { from: 18, count: 33 }
+          { from: 24, count: 23 }
+          { from: 62, count: 33 }
+        ]
+      duplicationsMeasures = _.sortBy data[0].msr, (item) -> -(item.key == 'duplicated_lines_density')
+      console.log data
+      @component.set 'duplicationsMeasures', duplicationsMeasures
+
+
     extractIssues: (data) ->
       issuesMeasures = {}
       data[0].msr.forEach (q) ->
@@ -127,6 +145,7 @@ define [
           model.set 'component': @component.toJSON()
         @render()
         if @settings.get('coverage') then @showCoverage() else @hideCoverage()
+        if @settings.get('duplications') then @showDuplications() else @hideDuplications()
 
 
     showCoverage: ->
@@ -172,6 +191,21 @@ define [
       @sourceView.render()
 
 
+    showDuplications: ->
+      @settings.set 'duplications', true
+      unless @source.has 'duplications'
+        @requestComponentDuplications(@key).done (data) =>
+          @extractDuplications data
+          @sourceView.render()
+      else
+        @sourceView.render()
+
+
+    hideDuplications: ->
+      @settings.set 'duplications', false
+      @sourceView.render()
+
+
     addTransition: (key, transition, optionsForCurrent, options) ->
       if optionsForCurrent?
         last = @workspace.at(@workspace.length - 1)
diff --git a/sonar-server/src/main/coffee/component-viewer/popup.coffee b/sonar-server/src/main/coffee/component-viewer/popup.coffee
new file mode 100644 (file)
index 0000000..bd80680
--- /dev/null
@@ -0,0 +1,22 @@
+define [
+  'backbone.marionette'
+], (
+  Marionette
+) ->
+
+  $ = jQuery
+
+
+  class CoveragePopupView extends Marionette.ItemView
+    className: 'component-viewer-popup'
+
+
+    onRender: ->
+      @$el.detach().appendTo $('body')
+      @$el.css
+        top: @options.triggerEl.offset().top
+        left: @options.triggerEl.offset().left + @options.triggerEl.outerWidth()
+
+      $('body').on 'click.coverage-popup', =>
+        $('body').off 'click.coverage-popup'
+        @close()
\ No newline at end of file
index 74b37753166822e77b6db9fef3c1d778d8dfced1..e54ec45efd83a5d5d8abba2149c0ea71f455aa02 100644 (file)
@@ -2,6 +2,7 @@ define [
   'backbone.marionette'
   'templates/component-viewer'
   'component-viewer/coverage-popup'
+  'component-viewer/duplication-popup'
   'issues/issue-view'
   'issues/models/issue'
   'common/handlebars-extensions'
@@ -9,6 +10,7 @@ define [
   Marionette
   Templates
   CoveragePopupView
+  DuplicationPopupView
   IssueView
   Issue
 ) ->
@@ -24,9 +26,14 @@ define [
       'click .js-toggle-settings': 'toggleSettings'
       'click .js-toggle-measures': 'toggleMeasures'
       'change #source-coverage': 'toggleCoverage'
+      'change #source-duplications': 'toggleDuplications'
       'change #source-workspace': 'toggleWorkspace'
       'click .coverage-tests': 'showCoveragePopup'
 
+      'click .duplication-exists': 'showDuplicationPopup'
+      'mouseenter .duplication-exists': 'duplicationMouseEnter'
+      'mouseleave .duplication-exists': 'duplicationMouseLeave'
+
 
     onRender: ->
       @delegateEvents()
@@ -64,6 +71,12 @@ define [
       if active then @options.main.showCoverage() else @options.main.hideCoverage()
 
 
+    toggleDuplications: (e) ->
+      active = $(e.currentTarget).is ':checked'
+      @showSettings = true
+      if active then @options.main.showDuplications() else @options.main.hideDuplications()
+
+
     toggleWorkspace: (e) ->
       active = $(e.currentTarget).is ':checked'
       @showSettings = true
@@ -79,11 +92,36 @@ define [
       popup.render()
 
 
+    showDuplicationPopup: (e) ->
+      e.stopPropagation()
+      $('body').click()
+      popup = new DuplicationPopupView
+        triggerEl: $(e.currentTarget)
+        main: @options.main
+      popup.render()
+
+
+    duplicationMouseEnter: (e) ->
+      @toggleDuplicationHover e, true
+
+
+    duplicationMouseLeave: (e) ->
+      @toggleDuplicationHover e, false
+
+
+    toggleDuplicationHover: (e, add) ->
+      bar = $(e.currentTarget)
+      index = bar.parent().children('.duplication').index bar
+      @$('.duplications').each ->
+        $(".duplication", @).eq(index).filter('.duplication-exists').toggleClass 'duplication-hover', add
+
+
     prepareSource: ->
       source = @model.get 'source'
       coverage = @model.get 'coverage'
       coverageConditions = @model.get 'coverageConditions'
       conditions = @model.get 'conditions'
+      duplications = @model.get('duplications') || []
       _.map source, (code, line) ->
         lineCoverage = coverage? && coverage[line]? && coverage[line]
         lineCoverage = +lineCoverage if _.isString lineCoverage
@@ -103,6 +141,9 @@ define [
           lineCoverageConditionsStatus = 'orange' if lineCoverageConditions > 0 && lineCoverageConditions < lineConditions
           lineCoverageConditionsStatus = 'green' if lineCoverageConditions == lineConditions
 
+        lineDuplications = duplications.map (d) ->
+          d.from <= line && (d.from + d.count) > line
+
         lineNumber: line
         code: code
         coverage: lineCoverage
@@ -110,6 +151,7 @@ define [
         coverageConditions: lineCoverageConditions
         conditions: lineConditions
         coverageConditionsStatus: lineCoverageConditionsStatus || lineCoverageStatus
+        duplications: lineDuplications
 
 
     serializeData: ->
diff --git a/sonar-server/src/main/hbs/component-viewer/duplicationPopup.hbs b/sonar-server/src/main/hbs/component-viewer/duplicationPopup.hbs
new file mode 100644 (file)
index 0000000..3be9afb
--- /dev/null
@@ -0,0 +1,19 @@
+<div class="component-viewer-popup-section">
+  <div class="component-viewer-popup-section-title">
+    2 duplications
+  </div>
+  <ul class="component-viewer-popup-list">
+    <li>
+      <a data-key="org.codehaus.sonar:sonar-plugin-api:src/main/java/org/sonar/api/resources/ResourceTypeTree.java">
+        <span>ResourceTypeTree.java</span>
+      </a>
+    </li>
+    <li>
+      <a data-key="org.codehaus.sonar:sonar-batch:src/main/java/org/sonar/batch/phases/PhaseExecutor.java">
+        <span>PhaseExecutor.java</span>
+      </a>
+    </li>
+  </ul>
+</div>
+
+<div class="component-viewer-popup-arrow"></div>
\ No newline at end of file
index c6372ef5da1c4e448a5893564c64780788af14e5..ba11ec0cf527f79011cf04dccaf9ad767d4de996 100644 (file)
@@ -5,6 +5,9 @@
       <th class="stat"></th>
       <th class="stat"></th>
     {{/if}}
+    {{#if settings.duplications}}
+      <th class="stat"></th>
+    {{/if}}
     <th class="stat settings-toggle">
       <button class="button-clean js-toggle-settings">
         <i class="icon-settings"></i></i>
@@ -42,6 +45,9 @@
       <th class="stat"></th>
       <th class="stat"></th>
     {{/if}}
+    {{#if settings.duplications}}
+      <th class="stat"></th>
+    {{/if}}
     <th class="stat settings-toggle">
       <button class="button-clean js-toggle-measures"><i class="icon-list"></i></button>
     </th>
             {{/if}}
           </li>
         {{/if}}
-        <li class="component-viewer-measures-section">
-        </li>
+        {{#if settings.duplications}}
+          <li class="component-viewer-measures-section brief">
+            {{component.duplicationsMeasures}}
+            {{#if component.duplicationsMeasures}}
+              <dl>
+                {{#each component.duplicationsMeasures}}
+                  <dt>{{t 'metric' key 'name'}}</dt><dd>{{frmt_val}}</dd>
+                {{/each}}
+              </dl>
+            {{/if}}
+          </li>
+        {{/if}}
       </ul>
     </th>
   </tr>
         <td class="stat coverage-tests"></td>
         <td class="stat coverage-conditions"></td>
       {{/if}}
+      {{#if settings.duplications}}
+        <td class="stat"></td>
+      {{/if}}
       <td class="stat lid"></td>
       <td class="line"></td>
     </tr>
           </td>
         {{/if}}
 
+        {{#if ../settings.duplications}}
+          <td class="stat duplications">
+            {{#each duplications}}
+              <span class="duplication {{#if this}}duplication-exists{{/if}}"></span>
+            {{/each}}
+          </td>
+        {{/if}}
+
         <td class="stat lid">{{lineNumber}}</td>
 
         <td class="line"><pre>{{{code}}}</pre></td>
index 6f1d21d59e2c6f25030a6b19d6fd22069a69d6c2..7f2f99799117c9c266e8ae22bb13f287a886ce72 100644 (file)
     font-size: 11px;
     text-align: right;
     cursor: default;
+    white-space: nowrap;
   }
 
   .sources2 .lid {
     cursor: pointer;
   }
 
+  .sources2 .duplications {
+    padding-top: 0;
+    padding-bottom: 0;
+    font-size: 0;
+
+    .duplication {
+      display: inline-block;
+      vertical-align: top;
+      .size(5px, 100%);
+    }
+
+    .duplication + .duplication { margin-left: 2px; }
+
+    .duplication-exists {
+      background-color: @lightOrange;
+      cursor: pointer;
+      .trans;
+    }
+
+    .duplication-hover {
+      background-color: darken(@lightOrange, 10%);
+    }
+  }
+
   .sources2 .measures {
     padding: 4px 5px;
     border-left: 1px solid @barBorderColor;
index 659091e4ba45bee3d120bad9c42ed28a79f50b35..ea62c4aaba9a5e3e7a62b0150308a94da3a6ab00 100644 (file)
@@ -24,6 +24,7 @@
 @green: #85bb43;
 @yellow: #fede06;
 @orange: #f90;
+@lightOrange: #f3ca8e;
 @purple: #9139d4;
 
 @darkBlue: darken(@blue, 20%);
index d09c9d319c3c0d4364d589639a37ee4e27ed1c09..758be6eae54045ebbc138abfc3607b5738991664 100644 (file)
     'metric.file_complexity_distribution.name': '<%= escape_javascript message('metric.file_complexity_distribution.name') -%>',
     'metric.coverage.name': '<%= escape_javascript message('metric.coverage.name') -%>',
     'metric.line_coverage.name': '<%= escape_javascript message('metric.line_coverage.name') -%>',
-    'metric.branch_coverage.name': '<%= escape_javascript message('metric.branch_coverage.name') -%>'
+    'metric.branch_coverage.name': '<%= escape_javascript message('metric.branch_coverage.name') -%>',
+    'metric.duplicated_blocks.name': '<%= escape_javascript message('metric.duplicated_blocks.name') -%>',
+    'metric.duplicated_files.name': '<%= escape_javascript message('metric.duplicated_files.name') -%>',
+    'metric.duplicated_lines.name': '<%= escape_javascript message('metric.duplicated_lines.name') -%>',
+    'metric.duplicated_lines_density.name': '<%= escape_javascript message('metric.duplicated_lines_density.name') -%>'
   };
 </script>