Commit 61748c993de8a38300c0c038cec5a07e6c324cd6

Authored by Ciro Santillli
1 parent 1284f21c

Headers have ids and link to their own id.

CHANGELOG
... ... @@ -12,6 +12,7 @@ v 6.6.0
12 12 - Mobile UI improvements (Drew Blessing)
13 13 - Fix block/remove UI for admin::users#show page
14 14 - Show users' group membership on users' activity page
  15 + - Markdown rendered headers have id derived from their name and link to their id
15 16  
16 17 v 6.5.1
17 18 - Fix branch selectbox when create merge request from fork
... ...
app/assets/images/icon-link.png 0 → 100644

1019 Bytes

app/assets/stylesheets/generic/files.scss
... ... @@ -50,7 +50,6 @@
50 50 }
51 51  
52 52 &.wiki {
53   - padding: 20px;
54 53 font-size: 14px;
55 54 line-height: 1.6;
56 55  
... ...
app/assets/stylesheets/generic/issue_box.scss
... ... @@ -23,11 +23,12 @@
23 23 line-height: 28px;
24 24 margin: 0;
25 25 color: #444;
  26 + border-bottom: 1px solid #eee;
26 27 }
27 28  
28 29 .context {
29 30 border: none;
30   - border-top: 1px solid #eee;
  31 + border-bottom: 1px solid #eee;
31 32 }
32 33  
33 34 .description {
... ... @@ -35,7 +36,7 @@
35 36 }
36 37  
37 38 .title, .context, .description {
38   - padding: 15px;
  39 + padding: 15px 15px 15px 30px;
39 40  
40 41 .clearfix {
41 42 margin: 0;
... ...
app/assets/stylesheets/generic/typography.scss
... ... @@ -90,6 +90,27 @@ a:focus {
90 90  
91 91 font-size: 14px;
92 92 line-height: 1.6;
  93 +
  94 + /* Link to current header. */
  95 + h1, h2, h3, h4, h5, h6 {
  96 + position: relative;
  97 + &:hover > :last-child {
  98 + $size: 16px;
  99 + position: absolute;
  100 + right: 100%;
  101 + top: 50%;
  102 + margin-top: -$size/2;
  103 + margin-right: 0px;
  104 + padding-right: 20px;
  105 + display: inline-block;
  106 + width: $size;
  107 + height: $size;
  108 + background-image: url("icon-link.png");
  109 + background-size: contain;
  110 + background-repeat: no-repeat;
  111 + }
  112 + }
  113 +
93 114 ul {
94 115 padding: 0;
95 116 margin: 0 0 9px 25px !important;
... ...
app/assets/stylesheets/main/mixins.scss
... ... @@ -114,6 +114,10 @@
114 114 font-size: 1.2em;
115 115 }
116 116  
  117 + // Larger 30px left margin is required for the header link icon.
  118 + // Use on all markdown including those without header links for uniformity.
  119 + margin: 20px 20px 20px 30px;
  120 +
117 121 blockquote p {
118 122 color: #888;
119 123 font-size: 14px;
... ...
app/helpers/gitlab_markdown_helper.rb
... ... @@ -28,14 +28,16 @@ module GitlabMarkdownHelper
28 28 link_to(gfm_body.html_safe, url, html_options)
29 29 end
30 30  
31   - def markdown(text)
32   - unless @markdown
33   - gitlab_renderer = Redcarpet::Render::GitlabHTML.new(self,
34   - # see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch-
35   - filter_html: true,
36   - with_toc_data: true,
37   - hard_wrap: true,
38   - safe_links_only: true)
  31 + def markdown(text, options={})
  32 + unless (@markdown and options == @options)
  33 + @options = options
  34 + gitlab_renderer = Redcarpet::Render::GitlabHTML.new(self, {
  35 + # see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch-
  36 + filter_html: true,
  37 + with_toc_data: true,
  38 + hard_wrap: true,
  39 + safe_links_only: true
  40 + }.merge(options))
39 41 @markdown = Redcarpet::Markdown.new(gitlab_renderer,
40 42 # see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
41 43 no_intra_emphasis: true,
... ... @@ -47,7 +49,6 @@ module GitlabMarkdownHelper
47 49 space_after_headers: true,
48 50 superscript: true)
49 51 end
50   -
51 52 @markdown.render(text).html_safe
52 53 end
53 54  
... ...
app/views/help/_layout.html.haml
... ... @@ -8,4 +8,5 @@
8 8 = link_to title, path
9 9  
10 10 .col-md-9
11   - = yield
  11 + .wiki
  12 + = yield
... ...
app/views/projects/issues/show.html.haml
... ... @@ -46,10 +46,9 @@
46 46 = render partial: 'issue_context', locals: { issue: @issue }
47 47  
48 48 - if @issue.description.present?
49   - .description
50   - .wiki
51   - = preserve do
52   - = markdown @issue.description
  49 + .wiki
  50 + = preserve do
  51 + = markdown @issue.description
53 52  
54 53 - content_for :note_actions do
55 54 - if can?(current_user, :modify_issue, @issue)
... ...
app/views/projects/merge_requests/show/_mr_box.html.haml
... ... @@ -15,10 +15,9 @@
15 15  
16 16  
17 17 - if @merge_request.description.present?
18   - .description
19   - .wiki
20   - = preserve do
21   - = markdown @merge_request.description
  18 + .wiki
  19 + = preserve do
  20 + = markdown @merge_request.description
22 21  
23 22 - if @merge_request.closed?
24 23 .description.alert-danger
... ...
app/views/projects/milestones/show.html.haml
... ... @@ -42,13 +42,11 @@
42 42 .progress.progress-info
43 43 .progress-bar{style: "width: #{@milestone.percent_complete}%;"}
44 44  
45   -
46 45 - if @milestone.description.present?
47   - .description
  46 + .wiki
48 47 = preserve do
49 48 = markdown @milestone.description
50 49  
51   -
52 50 %ul.nav.nav-tabs.append-bottom-10
53 51 %li.active
54 52 = link_to '#tab-issues', 'data-toggle' => 'tab' do
... ...
app/views/projects/notes/_note.html.haml
... ... @@ -31,7 +31,7 @@
31 31 .note-body
32 32 .note-text
33 33 = preserve do
34   - = markdown(note.note)
  34 + = markdown(note.note, {no_header_anchors: true})
35 35  
36 36 .note-edit-form
37 37 = form_for note, url: project_note_path(@project, note), method: :put, remote: true, authenticity_token: true do |f|
... ...
doc/markdown/markdown.md
1 1 ----------------------------------------------
2 2  
3   -Table of Contents
  3 +Table of Contents
4 4 =================
5 5  
6 6 ----------------------------------------------
7 7  
8   -[GitLab Flavored Markdown](#toc_3)
9   --------------------------------
10   -[Newlines](#toc_4)
11   -[Multiple underscores in words](#toc_5)
12   -[URL autolinking](#toc_6)
13   -[Code and Syntax Highlighting](#toc_7)
14   -[Emoji](#toc_8)
15   -[Special GitLab references](#toc_9)
16   -
17   -
18   -
19   -[Standard Markdown](#toc_10)
20   -------------------------------
21   -[Headers](#toc_11)
22   -[Emphasis](#toc_20)
23   -[Lists](#toc_21)
24   -[Links](#toc_22)
25   -[Images](#toc_23)
26   -[Blockquotes](#toc_24)
27   -[Inline HTML](#toc_25)
28   -[Horizontal Rule](#toc_26)
29   -[Line Breaks](#toc_27)
30   -[Tables](#toc_28)
31   -
32   -[References](#toc_29)
33   ----------------------
  8 +**[GitLab Flavored Markdown](#gitlab-flavored-markdown-gfm)**
  9 +
  10 +[Newlines](#newlines)
  11 +[Multiple underscores in words](#multiple-underscores-in-words)
  12 +[URL autolinking](#url-autolinking)
  13 +[Code and Syntax Highlighting](#code-and-syntax-highlighting)
  14 +[Emoji](#emoji)
  15 +[Special GitLab references](#special-gitlab-references)
  16 +
  17 +**[Standard Markdown](#standard-markdown)**
  18 +
  19 +[Headers](#headers)
  20 +[Emphasis](#emphasis)
  21 +[Lists](#lists)
  22 +[Links](#links)
  23 +[Images](#images)
  24 +[Blockquotes](#blockquotes)
  25 +[Inline HTML](#inline-html)
  26 +[Horizontal Rule](#horizontal-rule)
  27 +[Line Breaks](#line-breaks)
  28 +[Tables](#tables)
  29 +
  30 +**[References](#references)**
34 31  
35 32 ----------------------------------------------
36 33  
37   -<a name="gfm" />
38   -GitLab Flavored Markdown (GFM)
  34 +GitLab Flavored Markdown (GFM)
39 35 ==============================
40 36 For GitLab we developed something we call "GitLab Flavored Markdown" (GFM). It extends the standard Markdown in a few significant ways to add some useful functionality.
41 37  
... ... @@ -49,7 +45,6 @@ You can use GFM in
49 45 * milestones
50 46 * wiki pages
51 47  
52   -<a name="newlines" />
53 48 Newlines
54 49 --------
55 50 The biggest difference that GFM introduces is in the handling of linebreaks. With traditional Markdown you can hard wrap paragraphs of text and they will be combined into a single paragraph. We find this to be the cause of a huge number of unintentional formatting errors. GFM treats newlines in paragraph-like content as real line breaks, which is probably what you intended.
... ... @@ -61,8 +56,7 @@ The next paragraph contains two phrases separated by a single newline character:
61 56  
62 57 Roses are red
63 58 Violets are blue
64   -
65   -<a name="underscores" />
  59 +
66 60 Multiple underscores in words
67 61 -----------------------------
68 62 It is not reasonable to italicize just _part_ of a word, especially when you're dealing with code and names that often appear with multiple underscores. Therefore, GFM ignores multiple underscores in words.
... ... @@ -73,7 +67,6 @@ It is not reasonable to italicize just _part_ of a word, especially when you&#39;re
73 67 perform_complicated_task
74 68 do_this_and_do_that_and_another_thing
75 69  
76   -<a name="autolink" />
77 70 URL autolinking
78 71 ---------------
79 72 GFM will autolink standard URLs you copy and paste into your text.
... ... @@ -83,12 +76,10 @@ So if you want to link to a URL (instead of a textural link), you can simply put
83 76  
84 77 http://www.google.com
85 78  
86   -<a name="code"/>
87 79 ## Code and Syntax Highlighting
88 80  
89 81 Blocks of code are either fenced by lines with three back-ticks <code>```</code>, or are indented with four spaces. Only the fenced code blocks support syntax highlighting.
90 82  
91   -
92 83 ```no-highlight
93 84 Inline `code` has `back-ticks around` it.
94 85 ```
... ... @@ -101,14 +92,14 @@ Example:
101 92 var s = "JavaScript syntax highlighting";
102 93 alert(s);
103 94 ```
104   -
  95 +
105 96 ```python
106 97 def function():
107 98 #indenting works just fine in the fenced code block
108 99 s = "Python syntax highlighting"
109 100 print s
110 101 ```
111   -
  102 +
112 103 ```ruby
113 104 require 'redcarpet'
114 105 markdown = Redcarpet.new("Hello World!")
... ... @@ -116,7 +107,7 @@ Example:
116 107 ```
117 108  
118 109 ```
119   - No language indicated, so no syntax highlighting.
  110 + No language indicated, so no syntax highlighting.
120 111 s = "There is no highlighting for this."
121 112 But let's throw in a <b>tag</b>.
122 113 ```
... ... @@ -147,7 +138,6 @@ s = &quot;There is no highlighting for this.&quot;
147 138 But let's throw in a <b>tag</b>.
148 139 ```
149 140  
150   -<a name="emoji"/>
151 141 Emoji
152 142 -----
153 143  
... ... @@ -159,7 +149,7 @@ Emoji
159 149  
160 150 If you are :new: to this, don't be :fearful:. You can easily join the emoji :circus_tent:. All you need to do is to :book: up on the supported codes.
161 151  
162   - Consult the [Emoji Cheat Sheet](http://www.emoji-cheat-sheet.com/) for a list of all supported emoji codes. :thumbsup:
  152 + Consult the [Emoji Cheat Sheet](http://www.emoji-cheat-sheet.com/) for a list of all supported emoji codes. :thumbsup:
163 153  
164 154 Sometimes you want to be :cool: and add some :sparkles: to your :speech_balloon:. Well we have a :gift: for you:
165 155  
... ... @@ -169,9 +159,8 @@ You can use it to point out a :bug: or warn about :monkey:patches. And if someon
169 159  
170 160 If you are :new: to this, don't be :fearful:. You can easily join the emoji :circus_tent:. All you need to do is to :book: up on the supported codes.
171 161  
172   -Consult the [Emoji Cheat Sheet](http://www.emoji-cheat-sheet.com/) for a list of all supported emoji codes. :thumbsup:
  162 +Consult the [Emoji Cheat Sheet](http://www.emoji-cheat-sheet.com/) for a list of all supported emoji codes. :thumbsup:
173 163  
174   -<a name="special"/>
175 164 Special GitLab References
176 165 -----
177 166  
... ... @@ -179,7 +168,6 @@ GFM recognized special references.
179 168 You can easily reference e.g. a team member, an issue, or a commit within a project.
180 169 GFM will turn that reference into a link so you can navigate between them easily.
181 170  
182   -
183 171 GFM will recognize the following:
184 172  
185 173 * @foo : for team members
... ... @@ -189,13 +177,10 @@ GFM will recognize the following:
189 177 * 1234567 : for commits
190 178 * \[file\](path/to/file) : for file references
191 179  
192   -<a name="standard"/>
193   -
194 180 ----------------------------------
195 181 # Standard Markdown
196 182  
197 183 ----------------------------------
198   -<a name="headers"/>
199 184 ## Headers
200 185  
201 186 ```no-highlight
... ... @@ -230,7 +215,54 @@ Alt-H1
230 215 Alt-H2
231 216 ------
232 217  
233   -<a name="emphasis"/>
  218 +### Header IDs and links
  219 +
  220 +All markdown rendered headers automatically get IDs, except for comments.
  221 +
  222 +On hover a link to those IDs becomes visible to make it easier to copy the link to the header to give it to someone else.
  223 +
  224 +The IDs are generated from the content of the header according to the following rules:
  225 +
  226 +1) remove the heading hashes `#` and process the rest of the line as it would be processed if it were not a header
  227 +2) from the result, remove all HTML tags, but keep their inner content
  228 +3) convert all characters to lowercase
  229 +4) convert all characters except `[a-z0-9_-]` into hyphens `-`
  230 +5) transform multiple adjacent hyphens into a single hyphen
  231 +6) remove trailing and heading hyphens
  232 +
  233 +For example:
  234 +
  235 +```
  236 +###### ..Ab_c-d. e [anchor](url) ![alt text](url)..
  237 +```
  238 +
  239 +which renders as:
  240 +
  241 +###### ..Ab_c-d. e [anchor](url) ![alt text](url)..
  242 +
  243 +will first be converted by step 1) into a string like:
  244 +
  245 +```
  246 +..Ab_c-d. e &lt;a href="url">anchor&lt;/a> &lt;img src="url" alt="alt text"/>..
  247 +```
  248 +
  249 +After removing the tags in step 2) we get:
  250 +
  251 +```
  252 +..Ab_c-d. e anchor ..
  253 +```
  254 +
  255 +And applying all the other steps gives the id:
  256 +
  257 +```
  258 +ab_c-d-e-anchor
  259 +```
  260 +
  261 +Note in particular how:
  262 +
  263 +- for markdown anchors `[text](url)`, only the `text` is used
  264 +- markdown images `![alt](url)` are completely ignored
  265 +
234 266 ## Emphasis
235 267  
236 268 ```no-highlight
... ... @@ -251,18 +283,16 @@ Combined emphasis with **asterisks and _underscores_**.
251 283  
252 284 Strikethrough uses two tildes. ~~Scratch this.~~
253 285  
254   -
255   -<a name="lists"/>
256 286 ## Lists
257 287  
258 288 ```no-highlight
259 289 1. First ordered list item
260 290 2. Another item
261   - * Unordered sub-list.
  291 + * Unordered sub-list.
262 292 1. Actual numbers don't matter, just that it's a number
263 293 1. Ordered sub-list
264   -4. And another item.
265   -
  294 +4. And another item.
  295 +
266 296 Some text that should be aligned with the above item.
267 297  
268 298 * Unordered list can use asterisks
... ... @@ -272,18 +302,17 @@ Strikethrough uses two tildes. ~~Scratch this.~~
272 302  
273 303 1. First ordered list item
274 304 2. Another item
275   - * Unordered sub-list.
  305 + * Unordered sub-list.
276 306 1. Actual numbers don't matter, just that it's a number
277 307 1. Ordered sub-list
278   -4. And another item.
279   -
  308 +4. And another item.
  309 +
280 310 Some text that should be aligned with the above item.
281 311  
282 312 * Unordered list can use asterisks
283 313 - Or minuses
284 314 + Or pluses
285 315  
286   -<a name="links"/>
287 316 ## Links
288 317  
289 318 There are two ways to create links.
... ... @@ -320,30 +349,28 @@ Some text to show that the reference links can follow later.
320 349 [1]: http://slashdot.org
321 350 [link text itself]: http://www.reddit.com
322 351  
323   -<a name="images"/>
324 352 ## Images
325 353  
326 354 Here's our logo (hover to see the title text):
327 355  
328   - Inline-style:
  356 + Inline-style:
329 357 ![alt text](assets/logo-white.png)
330 358  
331   - Reference-style:
  359 + Reference-style:
332 360 ![alt text1][logo]
333 361  
334 362 [logo]: assets/logo-white.png
335 363  
336 364 Here's our logo (hover to see the title text):
337 365  
338   -Inline-style:
  366 +Inline-style:
339 367 ![alt text](/assets/logo-white.png "Logo Title Text 1")
340 368  
341   -Reference-style:
  369 +Reference-style:
342 370 ![alt text][logo]
343 371  
344 372 [logo]: /assets/logo-white.png "Logo Title Text 2"
345 373  
346   -<a name="blockquotes"/>
347 374 ## Blockquotes
348 375  
349 376 ```no-highlight
... ... @@ -352,7 +379,7 @@ Reference-style:
352 379  
353 380 Quote break.
354 381  
355   -> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote.
  382 +> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote.
356 383 ```
357 384  
358 385 > Blockquotes are very handy in email to emulate reply text.
... ... @@ -360,12 +387,11 @@ Quote break.
360 387  
361 388 Quote break.
362 389  
363   -> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote.
  390 +> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote.
364 391  
365   -<a name="html"/>
366 392 ## Inline HTML
367 393  
368   -You can also use raw HTML in your Markdown, and it'll mostly work pretty well.
  394 +You can also use raw HTML in your Markdown, and it'll mostly work pretty well.
369 395  
370 396 ```no-highlight
371 397 <dl>
... ... @@ -385,7 +411,6 @@ You can also use raw HTML in your Markdown, and it&#39;ll mostly work pretty well.
385 411 <dd>Does *not* work **very** well. Use HTML <em>tags</em>.</dd>
386 412 </dl>
387 413  
388   -<a name="hr"/>
389 414 ## Horizontal Rule
390 415  
391 416 ```
... ... @@ -418,10 +443,9 @@ ___
418 443  
419 444 Underscores
420 445  
421   -<a name="lines"/>
422 446 ## Line Breaks
423 447  
424   -My basic recommendation for learning how line breaks work is to experiment and discover -- hit &lt;Enter&gt; once (i.e., insert one newline), then hit it twice (i.e., insert two newlines), see what happens. You'll soon learn to get what you want. "Markdown Toggle" is your friend.
  448 +My basic recommendation for learning how line breaks work is to experiment and discover -- hit &lt;Enter&gt; once (i.e., insert one newline), then hit it twice (i.e., insert two newlines), see what happens. You'll soon learn to get what you want. "Markdown Toggle" is your friend.
425 449  
426 450 Here are some things to try out:
427 451  
... ... @@ -438,11 +462,9 @@ Here&#39;s a line for us to start with.
438 462  
439 463 This line is separated from the one above by two newlines, so it will be a *separate paragraph*.
440 464  
441   -This line is also begins a separate paragraph, but...
  465 +This line is also begins a separate paragraph, but...
442 466 This line is only separated by a single newline, so it's a separate line in the *same paragraph*.
443 467  
444   -
445   -<a name="tables"/>
446 468 ## Tables
447 469  
448 470 Tables aren't part of the core Markdown spec, but they are part of GFM and Markdown Here supports them.
... ... @@ -461,10 +483,8 @@ Code above produces next output:
461 483 | cell 1 | cell 2 |
462 484 | cell 3 | cell 4 |
463 485  
464   -
465 486 ------------
466 487  
467   -<a name="references"/>
468 488 ## References
469 489  
470 490 * This document leveraged heavily from the [Markdown-Cheatsheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet).
... ...
features/dashboard/help.feature 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +Feature: Help
  2 + Background:
  3 + Given I sign in as a user
  4 + And I visit the "Rake Tasks" help page
  5 +
  6 + Scenario: The markdown should be rendered correctly
  7 + Then I should see "Rake Tasks" page markdown rendered
  8 + And Header "Rebuild project satellites" should have correct ids and links
... ...
features/project/issues/issues.feature
... ... @@ -55,3 +55,15 @@ Feature: Project Issues
55 55 And I fill in issue search with ".3"
56 56 Then I should see "Release 0.3" in issues
57 57 And I should not see "Release 0.4" in issues
  58 +
  59 + # Markdown
  60 +
  61 + Scenario: Headers inside the description should have ids generated for them.
  62 + Given I visit issue page "Release 0.4"
  63 + Then Header "Description header" should have correct id and link
  64 +
  65 + @javascript
  66 + Scenario: Headers inside comments should not have ids generated for them.
  67 + Given I visit issue page "Release 0.4"
  68 + And I leave a comment with a header containing "Comment with a header"
  69 + Then The comment with the header should not have an ID
... ...
features/project/issues/milestones.feature
... ... @@ -22,3 +22,9 @@ Feature: Project Milestones
22 22 Given the milestone has open and closed issues
23 23 And I click link "v2.2"
24 24 Then I should see 3 issues
  25 +
  26 + # Markdown
  27 +
  28 + Scenario: Headers inside the description should have ids generated for them.
  29 + Given I click link "v2.2"
  30 + Then Header "Description header" should have correct id and link
... ...
features/project/merge_requests.feature
... ... @@ -77,3 +77,15 @@ Feature: Project Merge Requests
77 77 Then I modify merge commit message
78 78 And I accept this merge request
79 79 Then I should see merged request
  80 +
  81 + # Markdown
  82 +
  83 + Scenario: Headers inside the description should have ids generated for them.
  84 + When I visit merge request page "Bug NS-04"
  85 + Then Header "Description header" should have correct id and link
  86 +
  87 + @javascript
  88 + Scenario: Headers inside comments should not have ids generated for them.
  89 + Given I visit merge request page "Bug NS-04"
  90 + And I leave a comment with a header containing "Comment with a header"
  91 + Then The comment with the header should not have an ID
... ...
features/project/source/markdown_render.feature
... ... @@ -4,6 +4,15 @@ Feature: Project markdown render
4 4 And I own project "Delta"
5 5 Given I visit project source page
6 6  
  7 + # -------------------------------------------
  8 + # README
  9 + # -------------------------------------------
  10 +
  11 + Scenario: Tree view should have correct links in README
  12 + Given I go directory which contains README file
  13 + And I click on a relative link in README
  14 + Then I should see the correct markdown
  15 +
7 16 Scenario: I browse files from master branch
8 17 Then I should see files from repository in master
9 18 And I should see rendered README which contains correct links
... ... @@ -28,6 +37,14 @@ Feature: Project markdown render
28 37 And I click on Maintenance in README
29 38 Then I should see correct maintenance file rendered
30 39  
  40 + Scenario: README headers should have header links
  41 + Then I should see rendered README which contains correct links
  42 + And Header "Application details" should have correct id and link
  43 +
  44 + # -------------------------------------------
  45 + # File content
  46 + # -------------------------------------------
  47 +
31 48 Scenario: I navigate to doc directory to view documentation in master
32 49 And I navigate to the doc/api/README
33 50 And I see correct file rendered
... ... @@ -40,6 +57,14 @@ Feature: Project markdown render
40 57 And I click on raketasks in doc/api/README
41 58 Then I should see correct directory rendered
42 59  
  60 + Scenario: I navigate to doc directory to view user doc in master
  61 + And I navigate to the doc/api/README
  62 + And Header "GitLab API" should have correct id and link
  63 +
  64 + # -------------------------------------------
  65 + # Markdown branch README
  66 + # -------------------------------------------
  67 +
43 68 Scenario: I browse files from markdown branch
44 69 When I visit markdown branch
45 70 Then I should see files from repository in markdown branch
... ... @@ -68,6 +93,10 @@ Feature: Project markdown render
68 93 And I click on raketasks in doc/api/README
69 94 Then I should see correct directory rendered for markdown branch
70 95  
  96 + # -------------------------------------------
  97 + # Wiki
  98 + # -------------------------------------------
  99 +
71 100 Scenario: I create a wiki page with different links
72 101 Given I go to wiki page
73 102 And I add various links to the wiki page
... ... @@ -81,12 +110,7 @@ Feature: Project markdown render
81 110 And I click on Rake tasks link
82 111 Then I see Rake tasks directory
83 112  
84   - Scenario: I visit the help page with markdown
85   - Given I visit to the help page
86   - And I select a page with markdown
87   - Then I should see a help page with markdown
88   -
89   - Scenario: Tree view should have correct links in README
90   - Given I go directory which contains README file
91   - And I click on a relative link in README
92   - Then I should see the correct markdown
  113 + Scenario: Wiki headers should have should have ids generated for them.
  114 + Given I go to wiki page
  115 + And I add a header to the wiki page
  116 + Then Wiki header should have correct id and link
... ...
features/steps/help.rb 0 → 100644
... ... @@ -0,0 +1,21 @@
  1 +class Spinach::Features::Help < Spinach::FeatureSteps
  2 + include SharedAuthentication
  3 + include SharedPaths
  4 + include SharedMarkdown
  5 +
  6 + step 'I visit the help page' do
  7 + visit help_path
  8 + end
  9 +
  10 + step 'I visit the "Rake Tasks" help page' do
  11 + visit help_raketasks_path
  12 + end
  13 +
  14 + step 'I should see "Rake Tasks" page markdown rendered' do
  15 + page.should have_content "GitLab provides some specific rake tasks to enable special features or perform maintenance tasks"
  16 + end
  17 +
  18 + step 'Header "Rebuild project satellites" should have correct ids and links' do
  19 + header_should_have_correct_id_and_link(3, 'Rebuild project satellites', 'rebuild-project-satellites')
  20 + end
  21 +end
... ...
features/steps/project/project_issues.rb
... ... @@ -3,6 +3,7 @@ class ProjectIssues &lt; Spinach::FeatureSteps
3 3 include SharedProject
4 4 include SharedNote
5 5 include SharedPaths
  6 + include SharedMarkdown
6 7  
7 8 Given 'I should see "Release 0.4" in issues' do
8 9 page.should have_content "Release 0.4"
... ... @@ -121,7 +122,9 @@ class ProjectIssues &lt; Spinach::FeatureSteps
121 122 create(:issue,
122 123 title: "Release 0.4",
123 124 project: project,
124   - author: project.users.first)
  125 + author: project.users.first,
  126 + description: "# Description header"
  127 + )
125 128 end
126 129  
127 130 And 'project "Shop" have "Tweet control" open issue' do
... ...
features/steps/project/project_markdown_render.rb
1 1 class Spinach::Features::ProjectMarkdownRender < Spinach::FeatureSteps
2 2 include SharedAuthentication
3 3 include SharedPaths
  4 + include SharedMarkdown
4 5  
5 6 And 'I own project "Delta"' do
6 7 @project = Project.find_by(name: "Delta")
... ... @@ -44,7 +45,6 @@ class Spinach::Features::ProjectMarkdownRender &lt; Spinach::FeatureSteps
44 45 page.should have_content "maintenance.md"
45 46 end
46 47  
47   -
48 48 And 'I click on GitLab API doc directory in README' do
49 49 click_link "GitLab API doc directory"
50 50 end
... ... @@ -140,6 +140,16 @@ class Spinach::Features::ProjectMarkdownRender &lt; Spinach::FeatureSteps
140 140 page.should have_content "test GitLab API doc Rake tasks"
141 141 end
142 142  
  143 + step 'I add a header to the wiki page' do
  144 + fill_in "wiki[content]", with: "# Wiki header\n"
  145 + fill_in "wiki[message]", with: "Add header to wiki"
  146 + click_button "Create page"
  147 + end
  148 +
  149 + step 'Wiki header should have correct id and link' do
  150 + header_should_have_correct_id_and_link(1, 'Wiki header', 'wiki-header')
  151 + end
  152 +
143 153 And 'I click on test link' do
144 154 click_link "test"
145 155 end
... ... @@ -173,18 +183,6 @@ class Spinach::Features::ProjectMarkdownRender &lt; Spinach::FeatureSteps
173 183 page.should have_content "maintenance.md"
174 184 end
175 185  
176   - Given 'I visit to the help page' do
177   - visit help_path
178   - end
179   -
180   - And 'I select a page with markdown' do
181   - click_link "Rake Tasks"
182   - end
183   -
184   - Then 'I should see a help page with markdown' do
185   - page.should have_content "GitLab provides some specific rake tasks to enable special features or perform maintenance tasks"
186   - end
187   -
188 186 Given 'I go directory which contains README file' do
189 187 visit project_tree_path(@project, "master/doc/api")
190 188 current_path.should == project_tree_path(@project, "master/doc/api")
... ... @@ -198,4 +196,12 @@ class Spinach::Features::ProjectMarkdownRender &lt; Spinach::FeatureSteps
198 196 current_path.should == project_blob_path(@project, "master/doc/api/users.md")
199 197 page.should have_content "List users"
200 198 end
  199 +
  200 + step 'Header "Application details" should have correct id and link' do
  201 + header_should_have_correct_id_and_link(2, 'Application details', 'application-details')
  202 + end
  203 +
  204 + step 'Header "GitLab API" should have correct id and link' do
  205 + header_should_have_correct_id_and_link(1, 'GitLab API', 'gitlab-api')
  206 + end
201 207 end
... ...
features/steps/project/project_merge_requests.rb
... ... @@ -3,6 +3,7 @@ class ProjectMergeRequests &lt; Spinach::FeatureSteps
3 3 include SharedProject
4 4 include SharedNote
5 5 include SharedPaths
  6 + include SharedMarkdown
6 7  
7 8 step 'I click link "New Merge Request"' do
8 9 click_link "New Merge Request"
... ... @@ -83,7 +84,9 @@ class ProjectMergeRequests &lt; Spinach::FeatureSteps
83 84 target_project: project,
84 85 source_branch: 'stable',
85 86 target_branch: 'master',
86   - author: project.users.first)
  87 + author: project.users.first,
  88 + description: "# Description header"
  89 + )
87 90 end
88 91  
89 92 step 'project "Shop" have "Bug NS-05" open merge request with diffs inside' do
... ...
features/steps/project/project_milestones.rb
... ... @@ -2,6 +2,7 @@ class ProjectMilestones &lt; Spinach::FeatureSteps
2 2 include SharedAuthentication
3 3 include SharedProject
4 4 include SharedPaths
  5 + include SharedMarkdown
5 6  
6 7 Then 'I should see milestone "v2.2"' do
7 8 milestone = @project.milestones.find_by(title: "v2.2")
... ... @@ -32,8 +33,11 @@ class ProjectMilestones &lt; Spinach::FeatureSteps
32 33  
33 34 And 'project "Shop" has milestone "v2.2"' do
34 35 project = Project.find_by(name: "Shop")
35   - milestone = create(:milestone, title: "v2.2", project: project)
36   -
  36 + milestone = create(:milestone,
  37 + title: "v2.2",
  38 + project: project,
  39 + description: "# Description header"
  40 + )
37 41 3.times { create(:issue, project: project, milestone: milestone) }
38 42 end
39 43  
... ...
features/steps/shared/markdown.rb 0 → 100644
... ... @@ -0,0 +1,12 @@
  1 +module SharedMarkdown
  2 + include Spinach::DSL
  3 +
  4 + def header_should_have_correct_id_and_link(level, text, id, parent = ".wiki")
  5 + page.find(:css, "#{parent} h#{level}##{id}").text.should == text
  6 + page.find(:css, "#{parent} h#{level}##{id} > :last-child")[:href].should =~ /##{id}$/
  7 + end
  8 +
  9 + step 'Header "Description header" should have correct id and link' do
  10 + header_should_have_correct_id_and_link(1, 'Description header', 'description-header')
  11 + end
  12 +end
... ...
features/steps/shared/note.rb
... ... @@ -102,4 +102,21 @@ module SharedNote
102 102 page.should have_content("XML attached")
103 103 end
104 104 end
  105 +
  106 + # Markdown
  107 +
  108 + step 'I leave a comment with a header containing "Comment with a header"' do
  109 + within(".js-main-target-form") do
  110 + fill_in "note[note]", with: "# Comment with a header"
  111 + click_button "Add Comment"
  112 + sleep 0.05
  113 + end
  114 + end
  115 +
  116 + step 'The comment with the header should not have an ID' do
  117 + within(".note-text") do
  118 + page.should have_content("Comment with a header")
  119 + page.should_not have_css("#comment-with-a-header")
  120 + end
  121 + end
105 122 end
... ...
lib/redcarpet/render/gitlab_html.rb
... ... @@ -8,6 +8,7 @@ class Redcarpet::Render::GitlabHTML &lt; Redcarpet::Render::HTML
8 8 @project = @template.instance_variable_get("@project")
9 9 @ref = @template.instance_variable_get("@ref")
10 10 @request_path = @template.instance_variable_get("@path")
  11 + @options = options.dup
11 12 super options
12 13 end
13 14  
... ... @@ -34,6 +35,16 @@ class Redcarpet::Render::GitlabHTML &lt; Redcarpet::Render::HTML
34 35 h.link_to_gfm(content, link, title: title)
35 36 end
36 37  
  38 + def header(text, level)
  39 + if @options[:no_header_anchors]
  40 + "<h#{level}>#{text}</h#{level}>"
  41 + else
  42 + id = ActionController::Base.helpers.strip_tags(h.gfm(text)).downcase() \
  43 + .gsub(/[^a-z0-9_-]/, '-').gsub(/-+/, '-').gsub(/^-/, '').gsub(/-$/, '')
  44 + "<h#{level} id=\"#{id}\">#{text}<a href=\"\##{id}\"></a></h#{level}>"
  45 + end
  46 + end
  47 +
37 48 def preprocess(full_document)
38 49 if @project
39 50 h.create_relative_links(full_document, @project, @ref, @request_path, is_wiki?)
... ...
spec/helpers/gitlab_markdown_helper_spec.rb
... ... @@ -348,8 +348,21 @@ describe GitlabMarkdownHelper do
348 348 it "should handle references in headers" do
349 349 actual = "\n# Working around ##{issue.iid}\n## Apply !#{merge_request.iid}"
350 350  
351   - markdown(actual).should match(%r{<h1[^<]*>Working around <a.+>##{issue.iid}</a></h1>})
352   - markdown(actual).should match(%r{<h2[^<]*>Apply <a.+>!#{merge_request.iid}</a></h2>})
  351 + markdown(actual, {no_header_anchors:true}).should match(%r{<h1[^<]*>Working around <a.+>##{issue.iid}</a></h1>})
  352 + markdown(actual, {no_header_anchors:true}).should match(%r{<h2[^<]*>Apply <a.+>!#{merge_request.iid}</a></h2>})
  353 + end
  354 +
  355 + it "should add ids and links to headers" do
  356 + # Test every rule except nested tags.
  357 + text = '..Ab_c-d. e..'
  358 + id = 'ab_c-d-e'
  359 + markdown("# #{text}").should match(%r{<h1 id="#{id}">#{text}<a href="[^"]*##{id}"></a></h1>})
  360 + markdown("# #{text}", {no_header_anchors:true}).should == "<h1>#{text}</h1>"
  361 +
  362 + id = 'link-text'
  363 + markdown("# [link text](url) ![img alt](url)").should match(
  364 + %r{<h1 id="#{id}"><a href="[^"]*url">link text</a> <img[^>]*><a href="[^"]*##{id}"></a></h1>}
  365 + )
353 366 end
354 367  
355 368 it "should handle references in lists" do
... ...