diff --git a/Gemfile b/Gemfile index 4ab1ab50eb9c0f4b5fa46a9fd8ab88a5f8c70b75..2f1347879cd4da552812881a166ff5be19d0b84b 100644 --- a/Gemfile +++ b/Gemfile @@ -82,6 +82,9 @@ gem "seed-fu" gem "redcarpet", "~> 2.2.2" gem "github-markup" +# Diffs +gem 'diffy', '~> 3.0.3' + # Asciidoc to HTML gem "asciidoctor" diff --git a/Gemfile.lock b/Gemfile.lock index 7682540eba07c1f95b38ebb9e51e3dc833ec9cd1..60329b40a62d2123fada2ab3cec6f5ddb4cbff81 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -101,6 +101,7 @@ GEM devise-async (0.8.0) devise (>= 2.2, < 3.2) diff-lcs (1.2.5) + diffy (3.0.3) docile (1.1.1) dotenv (0.9.0) email_spec (1.5.0) @@ -574,6 +575,7 @@ DEPENDENCIES default_value_for (~> 3.0.0) devise (= 3.0.4) devise-async (= 0.8.0) + diffy (~> 3.0.3) email_spec email_validator (~> 1.4.0) enumerize @@ -644,7 +646,7 @@ DEPENDENCIES simplecov sinatra six - slack-notifier (~> 0.2.0) + slack-notifier (~> 0.3.2) slim spinach-rails spring (= 1.1.1) @@ -662,4 +664,4 @@ DEPENDENCIES unicorn (~> 4.6.3) unicorn-worker-killer version_sorter - webmock \ No newline at end of file + webmock diff --git a/app/assets/stylesheets/generic/files.scss b/app/assets/stylesheets/generic/files.scss index 12559f76051da729808892a6e78fcff706dcdefd..6418f24d97f34bac6ac0ed1aaa0f3d06f637a820 100644 --- a/app/assets/stylesheets/generic/files.scss +++ b/app/assets/stylesheets/generic/files.scss @@ -26,6 +26,10 @@ margin-top: -5px; } + .left-options { + margin-top: -3px; + } + .file_name { color: $style_color; font-size: 14px; diff --git a/app/controllers/projects/edit_tree_controller.rb b/app/controllers/projects/edit_tree_controller.rb index ff5206b6fa1e56a5edddd03e4a160b4f41489f4d..be611892bb07140abcfb71c44a383961965db09b 100644 --- a/app/controllers/projects/edit_tree_controller.rb +++ b/app/controllers/projects/edit_tree_controller.rb @@ -26,6 +26,18 @@ def update end end + def preview + @content = params[:content] + #FIXME workaround https://github.com/gitlabhq/gitlabhq/issues/5936 + @content += "\n" if @blob.data.end_with?("\n") + + diffy = Diffy::Diff.new(@blob.data, @content, diff: '-U 3', + include_diff_info: true) + @diff = Gitlab::DiffParser.new(diffy.diff.scan(/.*\n/)) + + render layout: false + end + private def blob diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index c6e4f574b67805c48353ea4544fc132f210d11cd..de081acc2ba146eafbb2492ebf8c02540d53e4c8 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -16,9 +16,10 @@ def commit_committer_link(commit, options = {}) end def each_diff_line(diff, index) - Gitlab::DiffParser.new(diff).each do |full_line, type, line_code, line_new, line_old| - yield(full_line, type, line_code, line_new, line_old) - end + Gitlab::DiffParser.new(diff.diff.lines.to_a, diff.new_path) + .each do |full_line, type, line_code, line_new, line_old| + yield(full_line, type, line_code, line_new, line_old) + end end def each_diff_line_near(diff, index, expected_line_code) diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index 50501dffefb54055d5b2ddbe28079f21fce9f051..f39d0081dce4fa1b75f421bb6c1c9f4143ead5ef 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -91,4 +91,12 @@ def up_dir_path tree def leave_edit_message "Leave edit mode?\nAll unsaved changes will be lost." end + + def editing_preview_title(filename) + if gitlab_markdown?(filename) || markup?(filename) + 'Preview' + else + 'Diff' + end + end end diff --git a/app/models/note.rb b/app/models/note.rb index 6f7afcd1f9f7fe1f5cd47d2432b1a87a3321da23..cee10ec90d2a59843e0294d3b6673dc190322b9a 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -184,9 +184,10 @@ def diff_line return @diff_line if @diff_line if diff - Gitlab::DiffParser.new(diff).each do |full_line, type, line_code, line_new, line_old| - @diff_line = full_line if line_code == self.line_code - end + Gitlab::DiffParser.new(diff.diff.lines.to_a, diff.new_path) + .each do |full_line, type, line_code, line_new, line_old| + @diff_line = full_line if line_code == self.line_code + end end @diff_line diff --git a/app/views/projects/edit_tree/_diff.html.haml b/app/views/projects/edit_tree/_diff.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..cf044feb9a440b8a010dda01f67d5ea8eb4c705c --- /dev/null +++ b/app/views/projects/edit_tree/_diff.html.haml @@ -0,0 +1,13 @@ +%table.text-file + - each_diff_line(diff, 1) do |line, type, line_code, line_new, line_old, raw_line| + %tr.line_holder{ id: line_code, class: "#{type}" } + - if type == "match" + %td.old_line= "..." + %td.new_line= "..." + %td.line_content.matched= line + - else + %td.old_line + = link_to raw(type == "new" ? " " : line_old), "##{line_code}", id: line_code + %td.new_line= link_to raw(type == "old" ? " " : line_new) , "##{line_code}", id: line_code + %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line) + diff --git a/app/views/projects/edit_tree/preview.html.haml b/app/views/projects/edit_tree/preview.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..fc6d3bfbc2413374c93fb912de7deb686c1f294c --- /dev/null +++ b/app/views/projects/edit_tree/preview.html.haml @@ -0,0 +1,26 @@ +.diff-file + .diff-content + - if gitlab_markdown?(@blob.name) + .file-content.wiki + = preserve do + = markdown(@content) + - elsif markup?(@blob.name) + .file-content.wiki + = raw GitHub::Markup.render(@blob.name, @content) + - else + .file-content.code + - unless @diff.empty? + %table.text-file + - @diff.each do |line, type, line_code, line_new, line_old, raw_line| + %tr.line_holder{ id: line_code, class: "#{type}" } + - if type == "match" + %td.old_line= "..." + %td.new_line= "..." + %td.line_content.matched= line + - else + %td.old_line + = link_to raw(type == "new" ? " " : line_old), "##{line_code}", id: line_code + %td.new_line= link_to raw(type == "old" ? " " : line_new) , "##{line_code}", id: line_code + %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line) + - else + %p.nothing_here_message No changes. diff --git a/app/views/projects/edit_tree/show.html.haml b/app/views/projects/edit_tree/show.html.haml index 3f2e98f3a7f634778cc6bd794e0426d0862ed716..48babb43aaf793190a18e14df8170ef8440b96e7 100644 --- a/app/views/projects/edit_tree/show.html.haml +++ b/app/views/projects/edit_tree/show.html.haml @@ -1,8 +1,11 @@ %h3.page-title Edit mode .file-editor = form_tag(project_edit_tree_path(@project, @id), method: :put, class: "form-horizontal") do - .file-holder + .file-holder.file .file-title + .btn-group.js-edit-mode.left-options + = link_to 'Edit', '#editor', class: 'active hover btn btn-tiny' + = link_to editing_preview_title(@blob.name), '#preview', class: 'btn btn-tiny', 'data-preview-url' => preview_project_edit_tree_path(@project, @id) %i.icon-file %span.file_name = @path @@ -13,7 +16,8 @@ .btn-group.tree-btn-group = link_to "Cancel", @after_edit_path, class: "btn btn-tiny btn-cancel", data: { confirm: leave_edit_message } .file-content.code - %pre#editor= @blob.data + %pre.js-edit-mode-pane#editor= @blob.data + .js-edit-mode-pane#preview.hide .form-group.commit_message-group = label_tag 'commit_message', class: "control-label" do @@ -45,3 +49,28 @@ $("#file-content").val(editor.getValue()); $(".file-editor form").submit(); }); + + var editModePanes = $('.js-edit-mode-pane'), + editModeLinks = $('.js-edit-mode a'); + + editModeLinks.click(function(event) { + event.preventDefault(); + + var currentLink = $(this), + paneId = currentLink.attr('href'), + currentPane = editModePanes.filter(paneId); + + editModeLinks.removeClass('active hover'); + currentLink.addClass('active hover'); + editModePanes.hide(); + + if (paneId == '#preview') { + $.post(currentLink.data('preview-url'), { content: editor.getValue() }, function(response) { + currentPane.empty().append(response); + currentPane.fadeIn(200); + }) + } else { + currentPane.fadeIn(200); + editor.focus() + } + }) diff --git a/config/routes.rb b/config/routes.rb index f23542cc8931aa5bab6780f75bd4afe6f856b9be..910c9ec23937a669e4a146ba14d991d67bf7aaac 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -187,7 +187,9 @@ resources :blob, only: [:show, :destroy], constraints: {id: /.+/} resources :raw, only: [:show], constraints: {id: /.+/} resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ } - resources :edit_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'edit' + resources :edit_tree, only: [:show, :update], constraints: { id: /.+/ }, path: 'edit' do + post :preview, on: :member + end resources :new_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'new' resources :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/} resources :commits, only: [:show], constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/} diff --git a/db/schema.rb b/db/schema.rb index 265d556bd277a7a1f3fc916061b3666603589a88..dbd489335db5687f8ba6d6f9b9e86514a15a2e05 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -319,7 +319,6 @@ t.integer "notification_level", default: 1, null: false t.datetime "password_expires_at" t.integer "created_by_id" - t.datetime "last_credential_check_at" t.string "avatar" t.string "confirmation_token" t.datetime "confirmed_at" @@ -327,6 +326,7 @@ t.string "unconfirmed_email" t.boolean "hide_no_ssh_key", default: false t.string "website_url", default: "", null: false + t.datetime "last_credential_check_at" end add_index "users", ["admin"], name: "index_users_on_admin", using: :btree diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature index fd9a2f01a28c619ce85b4865003e5f547eaf24c4..a204c3e10c7d08105c7c833b6c273a57e2cf9b14 100644 --- a/features/project/source/browse_files.feature +++ b/features/project/source/browse_files.feature @@ -29,3 +29,13 @@ Feature: Project Browse files Given I click on "Gemfile.lock" file in repo And I click button "edit" Then I can edit code + + @javascript + Scenario: I can see editing preview + Given I click on "Gemfile.lock" file in repo + And I click button "edit" + And I edit code + And I click link "Diff" + Then I see diff + + diff --git a/features/steps/project/browse_files.rb b/features/steps/project/browse_files.rb index 069086d5eac996d3c591d698ee0e2b8af101f985..7cdd1101ac5e7d6e3d51c7097876941cbba510d3 100644 --- a/features/steps/project/browse_files.rb +++ b/features/steps/project/browse_files.rb @@ -41,6 +41,18 @@ class ProjectBrowseFiles < Spinach::FeatureSteps page.evaluate_script('editor.getValue()').should == "GitlabFileEditor" end + step 'I edit code' do + page.execute_script('editor.setValue("GitlabFileEditor")') + end + + step 'I click link "Diff"' do + click_link 'Diff' + end + + step 'I see diff' do + page.should have_css '.line_holder.new' + end + step 'I click on "new file" link in repo' do click_link 'new-file-link' end diff --git a/lib/gitlab/diff_parser.rb b/lib/gitlab/diff_parser.rb index fb27280c4a4d403b7183c7c5c370586ac536c0cf..14bbb328637bc5acdb8051f52be7ec521587ef19 100644 --- a/lib/gitlab/diff_parser.rb +++ b/lib/gitlab/diff_parser.rb @@ -4,9 +4,9 @@ class DiffParser attr_reader :lines, :new_path - def initialize(diff) - @lines = diff.diff.lines.to_a - @new_path = diff.new_path + def initialize(lines, new_path = '') + @lines = lines + @new_path = new_path end def each @@ -18,10 +18,7 @@ def each lines_arr.each do |line| raw_line = line.dup - next if line.match(/^\-\-\- \/dev\/null/) - next if line.match(/^\+\+\+ \/dev\/null/) - next if line.match(/^\-\-\- a/) - next if line.match(/^\+\+\+ b/) + next if filename?(line) full_line = html_escape(line.gsub(/\n/, '')) full_line = ::Gitlab::InlineDiff.replace_markers full_line @@ -53,8 +50,17 @@ def each end end + def empty? + @lines.empty? + end + private + def filename?(line) + line.start_with?('--- /dev/null', '+++ /dev/null', '--- a', '+++ b', + '--- /tmp/diffy', '+++ /tmp/diffy') + end + def identification_type(line) if line[0] == "+" "new"