##// END OF EJS Templates
Added code highlighting support in wiki, using this syntax:...
Jean-Philippe Lang -
r699:42db3cac06ad
parent child
Show More
@@ -1,27 +1,31
1 1 <div class="contextual">
2 2 <%= link_to(l(:label_page_index), {:action => 'special', :page => 'Page_index'}, :class => 'icon icon-index') %>
3 3 </div>
4 4
5 5 <h2><%= @page.pretty_title %></h2>
6 6
7 7 <% form_for :content, @content, :url => {:action => 'edit', :page => @page.title}, :html => {:id => 'wiki_form'} do |f| %>
8 8 <%= f.hidden_field :version %>
9 9 <%= error_messages_for 'content' %>
10 10 <div class="contextual">
11 11 <%= l(:setting_text_formatting) %>:
12 12 <%= link_to l(:label_help), {:controller => 'help', :ctrl => 'wiki', :page => 'syntax' },
13 13 :onclick => "window.open('#{ url_for :controller => 'help', :ctrl => 'wiki', :page => 'syntax' }', '', 'resizable=yes, location=no, width=300, height=500, menubar=no, status=no, scrollbars=yes'); return false;" %>
14 14 </div>
15 15 <p><%= f.text_area :text, :cols => 100, :rows => 25, :class => 'wiki-edit' %></p>
16 16 <p><label><%= l(:field_comments) %></label><br /><%= f.text_field :comments, :size => 120 %></p>
17 17 <p><%= submit_tag l(:button_save) %>
18 18 <%= link_to_remote l(:label_preview),
19 19 { :url => { :controller => 'wiki', :action => 'preview', :id => @project, :page => @page.title },
20 20 :method => 'post',
21 21 :update => 'preview',
22 22 :with => "Form.serialize('wiki_form')"
23 23 } %></p>
24 24 <%= wikitoolbar_for 'content_text' %>
25 25 <% end %>
26 26
27 27 <div id="preview" class="wiki"></div>
28
29 <% content_for :header_tags do %>
30 <%= stylesheet_link_tag 'scm' %>
31 <% end %>
@@ -1,39 +1,43
1 1 <div class="contextual">
2 2 <%= link_to_if_authorized(l(:button_edit), {:action => 'edit', :page => @page.title}, :class => 'icon icon-edit') if @content.version == @page.content.version %>
3 3 <%= link_to_if_authorized(l(:button_delete), {:action => 'destroy', :page => @page.title}, :method => :post, :confirm => l(:text_are_you_sure), :class => 'icon icon-del') %>
4 4 <%= link_to_if_authorized(l(:button_rollback), {:action => 'edit', :page => @page.title, :version => @content.version }, :class => 'icon icon-cancel') if @content.version < @page.content.version %>
5 5 <%= link_to(l(:label_history), {:action => 'history', :page => @page.title}, :class => 'icon icon-history') %>
6 6 <%= link_to(l(:label_page_index), {:action => 'special', :page => 'Page_index'}, :class => 'icon icon-index') %>
7 7 </div>
8 8
9 9 <% if @content.version != @page.content.version %>
10 10 <p>
11 11 <%= link_to(('&#171; ' + l(:label_previous)), :action => 'index', :page => @page.title, :version => (@content.version - 1)) + " - " if @content.version > 1 %>
12 12 <%= "#{l(:label_version)} #{@content.version}/#{@page.content.version}" %>
13 13 <%= '(' + link_to('diff', :controller => 'wiki', :action => 'diff', :page => @page.title, :version => @content.version) + ')' if @content.version > 1 %> -
14 14 <%= link_to((l(:label_next) + ' &#187;'), :action => 'index', :page => @page.title, :version => (@content.version + 1)) + " - " if @content.version < @page.content.version %>
15 15 <%= link_to(l(:label_current_version), :action => 'index', :page => @page.title) %>
16 16 <br />
17 17 <em><%= @content.author ? @content.author.name : "anonyme" %>, <%= format_time(@content.updated_on) %> </em><br />
18 18 <%=h @content.comments %>
19 19 </p>
20 20 <hr />
21 21 <% end %>
22 22
23 23 <%= render(:partial => "wiki/content", :locals => {:content => @content}) %>
24 24
25 25 <%= link_to_attachments @page.attachments, :delete_url => (authorize_for('wiki', 'destroy_attachment') ? {:controller => 'wiki', :action => 'destroy_attachment', :page => @page.title} : nil) %>
26 26
27 27 <div class="contextual">
28 28 <%= l(:label_export_to) %>
29 29 <%= link_to 'HTML', {:export => 'html', :version => @content.version}, :class => 'icon icon-html' %>,
30 30 <%= link_to 'TXT', {:export => 'txt', :version => @content.version}, :class => 'icon icon-txt' %>
31 31 </div>
32 32
33 33 <% if authorize_for('wiki', 'add_attachment') %>
34 34 <p><%= toggle_link l(:label_attachment_new), "add_attachment_form" %></p>
35 35 <% form_tag({ :controller => 'wiki', :action => 'add_attachment', :page => @page.title }, :multipart => true, :class => "tabular", :id => "add_attachment_form", :style => "display:none;") do %>
36 36 <%= render :partial => 'attachments/form' %>
37 37 <%= submit_tag l(:button_add) %>
38 38 <% end %>
39 39 <% end %>
40
41 <% content_for :header_tags do %>
42 <%= stylesheet_link_tag 'scm' %>
43 <% end %>
@@ -1,79 +1,95
1 1 require 'redcloth'
2 require 'coderay'
2 3
3 4 module Redmine
4 5 module WikiFormatting
5 6
6 7 private
7 8
8 9 class TextileFormatter < RedCloth
9 10 RULES = [:inline_auto_link, :inline_auto_mailto, :textile ]
10 11
11 12 def initialize(*args)
12 13 super
13 14 self.hard_breaks=true
14 15 end
15 16
16 17 def to_html
17 18 super(*RULES).to_s
18 19 end
19 20
20 21 private
21 22
22 23 # Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet.
23 24 # <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a>
24 25 def hard_break( text )
25 26 text.gsub!( /(.)\n(?!\n|\Z| *([#*=]+(\s|$)|[{|]))/, "\\1<br />" ) if hard_breaks
26 27 end
27 28
29 # Patch to add code highlighting support to RedCloth
30 def smooth_offtags( text )
31 unless @pre_list.empty?
32 ## replace <pre> content
33 text.gsub!(/<redpre#(\d+)>/) do
34 content = @pre_list[$1.to_i]
35 if content.match(/<code\s+class="(\w+)">\s?(.+)/m)
36 content = "<code class=\"#{$1} CodeRay\">" +
37 CodeRay.scan($2, $1).html(:escape => false, :line_numbers => :inline)
38 end
39 content
40 end
41 end
42 end
43
28 44 AUTO_LINK_RE = %r{
29 45 ( # leading text
30 46 <\w+.*?>| # leading HTML tag, or
31 47 [^=<>!:'"/]| # leading punctuation, or
32 48 ^ # beginning of line
33 49 )
34 50 (
35 51 (?:https?://)| # protocol spec, or
36 52 (?:www\.) # www.*
37 53 )
38 54 (
39 55 [-\w]+ # subdomain or domain
40 56 (?:\.[-\w]+)* # remaining subdomains or domain
41 57 (?::\d+)? # port
42 58 (?:/(?:(?:[~\w\+%-]|(?:[,.;:][^\s$]))+)?)* # path
43 59 (?:\?[\w\+%&=.;-]+)? # query string
44 60 (?:\#[\w\-]*)? # trailing anchor
45 61 )
46 62 ([[:punct:]]|\s|<|$) # trailing text
47 63 }x unless const_defined?(:AUTO_LINK_RE)
48 64
49 65 # Turns all urls into clickable links (code from Rails).
50 66 def inline_auto_link(text)
51 67 text.gsub!(AUTO_LINK_RE) do
52 68 all, a, b, c, d = $&, $1, $2, $3, $4
53 69 if a =~ /<a\s/i || a =~ /![<>=]?/
54 70 # don't replace URL's that are already linked
55 71 # and URL's prefixed with ! !> !< != (textile images)
56 72 all
57 73 else
58 74 text = b + c
59 75 %(#{a}<a href="#{b=="www."?"http://www.":b}#{c}">#{text}</a>#{d})
60 76 end
61 77 end
62 78 end
63 79
64 80 # Turns all email addresses into clickable links (code from Rails).
65 81 def inline_auto_mailto(text)
66 82 text.gsub!(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
67 83 text = $1
68 84 %{<a href="mailto:#{$1}" class="email">#{text}</a>}
69 85 end
70 86 end
71 87 end
72 88
73 89 public
74 90
75 91 def self.to_html(text, options = {})
76 92 TextileFormatter.new(text).to_html
77 93 end
78 94 end
79 95 end
@@ -1,123 +1,123
1 1
2 2 div.action_M { background: #fd8 }
3 3 div.action_D { background: #f88 }
4 4 div.action_A { background: #bfb }
5 5
6 6
7 7 tr.spacing {
8 8 border: 1px solid #d7d7d7;
9 9 }
10 10
11 11 .line-num {
12 12 border: 1px solid #d7d7d7;
13 13 font-size: 0.8em;
14 14 text-align: right;
15 15 width: 3em;
16 16 padding-right: 3px;
17 17 }
18 18
19 19 .line-code {
20 20 font-size: 1.4em;
21 21 }
22 22
23 23 table.list thead th.list-filename {
24 24 background-color: #ddc;
25 25 font-weight: bolder;
26 26 text-align: left;
27 27 }
28 28
29 29
30 30 /************* Coderay styles *************/
31 31
32 .CodeRay {
32 table.CodeRay {
33 33 background-color: #fafafa;
34 34 }
35 35 .CodeRay pre { margin: 0px }
36 36
37 37 span.CodeRay { white-space: pre; border: 0px; padding: 2px }
38 38
39 39 .CodeRay .no { padding: 0px 4px }
40 40 .CodeRay .code { width: 100% }
41 41
42 42 ol.CodeRay { font-size: 10pt }
43 43 ol.CodeRay li { white-space: pre }
44 44
45 45 .CodeRay .code pre { overflow: auto }
46 46
47 47 .CodeRay .debug { color:white ! important; background:blue ! important; }
48 48
49 49 .CodeRay .af { color:#00C }
50 50 .CodeRay .an { color:#007 }
51 51 .CodeRay .av { color:#700 }
52 52 .CodeRay .aw { color:#C00 }
53 53 .CodeRay .bi { color:#509; font-weight:bold }
54 54 .CodeRay .c { color:#666; }
55 55
56 56 .CodeRay .ch { color:#04D }
57 57 .CodeRay .ch .k { color:#04D }
58 58 .CodeRay .ch .dl { color:#039 }
59 59
60 60 .CodeRay .cl { color:#B06; font-weight:bold }
61 61 .CodeRay .co { color:#036; font-weight:bold }
62 62 .CodeRay .cr { color:#0A0 }
63 63 .CodeRay .cv { color:#369 }
64 64 .CodeRay .df { color:#099; font-weight:bold }
65 65 .CodeRay .di { color:#088; font-weight:bold }
66 66 .CodeRay .dl { color:black }
67 67 .CodeRay .do { color:#970 }
68 68 .CodeRay .ds { color:#D42; font-weight:bold }
69 69 .CodeRay .e { color:#666; font-weight:bold }
70 70 .CodeRay .en { color:#800; font-weight:bold }
71 71 .CodeRay .er { color:#F00; background-color:#FAA }
72 72 .CodeRay .ex { color:#F00; font-weight:bold }
73 73 .CodeRay .fl { color:#60E; font-weight:bold }
74 74 .CodeRay .fu { color:#06B; font-weight:bold }
75 75 .CodeRay .gv { color:#d70; font-weight:bold }
76 76 .CodeRay .hx { color:#058; font-weight:bold }
77 77 .CodeRay .i { color:#00D; font-weight:bold }
78 78 .CodeRay .ic { color:#B44; font-weight:bold }
79 79
80 80 .CodeRay .il { background: #eee }
81 81 .CodeRay .il .il { background: #ddd }
82 82 .CodeRay .il .il .il { background: #ccc }
83 83 .CodeRay .il .idl { font-weight: bold; color: #888 }
84 84
85 85 .CodeRay .in { color:#B2B; font-weight:bold }
86 86 .CodeRay .iv { color:#33B }
87 87 .CodeRay .la { color:#970; font-weight:bold }
88 88 .CodeRay .lv { color:#963 }
89 89 .CodeRay .oc { color:#40E; font-weight:bold }
90 90 .CodeRay .of { color:#000; font-weight:bold }
91 91 .CodeRay .op { }
92 92 .CodeRay .pc { color:#038; font-weight:bold }
93 93 .CodeRay .pd { color:#369; font-weight:bold }
94 94 .CodeRay .pp { color:#579 }
95 95 .CodeRay .pt { color:#339; font-weight:bold }
96 96 .CodeRay .r { color:#080; font-weight:bold }
97 97
98 98 .CodeRay .rx { background-color:#fff0ff }
99 99 .CodeRay .rx .k { color:#808 }
100 100 .CodeRay .rx .dl { color:#404 }
101 101 .CodeRay .rx .mod { color:#C2C }
102 102 .CodeRay .rx .fu { color:#404; font-weight: bold }
103 103
104 104 .CodeRay .s { background-color:#fff0f0 }
105 105 .CodeRay .s .s { background-color:#ffe0e0 }
106 106 .CodeRay .s .s .s { background-color:#ffd0d0 }
107 107 .CodeRay .s .k { color:#D20 }
108 108 .CodeRay .s .dl { color:#710 }
109 109
110 110 .CodeRay .sh { background-color:#f0fff0 }
111 111 .CodeRay .sh .k { color:#2B2 }
112 112 .CodeRay .sh .dl { color:#161 }
113 113
114 114 .CodeRay .sy { color:#A60 }
115 115 .CodeRay .sy .k { color:#A60 }
116 116 .CodeRay .sy .dl { color:#630 }
117 117
118 118 .CodeRay .ta { color:#070 }
119 119 .CodeRay .tf { color:#070; font-weight:bold }
120 120 .CodeRay .ts { color:#D70; font-weight:bold }
121 121 .CodeRay .ty { color:#339; font-weight:bold }
122 122 .CodeRay .v { color:#036 }
123 123 .CodeRay .xt { color:#444 }
@@ -1,262 +1,268
1 1 require "set"
2 2
3 3 module CodeRay
4 4 module Encoders
5 5
6 6 # = HTML Encoder
7 7 #
8 8 # This is CodeRay's most important highlighter:
9 9 # It provides save, fast XHTML generation and CSS support.
10 10 #
11 11 # == Usage
12 12 #
13 13 # require 'coderay'
14 14 # puts CodeRay.scan('Some /code/', :ruby).html #-> a HTML page
15 15 # puts CodeRay.scan('Some /code/', :ruby).html(:wrap => :span)
16 16 # #-> <span class="CodeRay"><span class="co">Some</span> /code/</span>
17 17 # puts CodeRay.scan('Some /code/', :ruby).span #-> the same
18 18 #
19 19 # puts CodeRay.scan('Some code', :ruby).html(
20 20 # :wrap => nil,
21 21 # :line_numbers => :inline,
22 22 # :css => :style
23 23 # )
24 24 # #-> <span class="no">1</span> <span style="color:#036; font-weight:bold;">Some</span> code
25 25 #
26 26 # == Options
27 27 #
28 # === :escape
29 # Escape html entities
30 # Default: true
31 #
28 32 # === :tab_width
29 33 # Convert \t characters to +n+ spaces (a number.)
30 34 # Default: 8
31 35 #
32 36 # === :css
33 37 # How to include the styles; can be :class or :style.
34 38 #
35 39 # Default: :class
36 40 #
37 41 # === :wrap
38 42 # Wrap in :page, :div, :span or nil.
39 43 #
40 44 # You can also use Encoders::Div and Encoders::Span.
41 45 #
42 46 # Default: nil
43 47 #
44 48 # === :line_numbers
45 49 # Include line numbers in :table, :inline, :list or nil (no line numbers)
46 50 #
47 51 # Default: nil
48 52 #
49 53 # === :line_number_start
50 54 # Where to start with line number counting.
51 55 #
52 56 # Default: 1
53 57 #
54 58 # === :bold_every
55 59 # Make every +n+-th number appear bold.
56 60 #
57 61 # Default: 10
58 62 #
59 63 # === :hint
60 64 # Include some information into the output using the title attribute.
61 65 # Can be :info (show token type on mouse-over), :info_long (with full path)
62 66 # or :debug (via inspect).
63 67 #
64 68 # Default: false
65 69 class HTML < Encoder
66 70
67 71 include Streamable
68 72 register_for :html
69 73
70 74 FILE_EXTENSION = 'html'
71 75
72 76 DEFAULT_OPTIONS = {
77 :escape => true,
73 78 :tab_width => 8,
74 79
75 80 :level => :xhtml,
76 81 :css => :class,
77 82
78 83 :style => :cycnus,
79 84
80 85 :wrap => nil,
81 86
82 87 :line_numbers => nil,
83 88 :line_number_start => 1,
84 89 :bold_every => 10,
85 90
86 91 :hint => false,
87 92 }
88 93
89 94 helper :output, :css
90 95
91 96 attr_reader :css
92 97
93 98 protected
94 99
95 100 HTML_ESCAPE = { #:nodoc:
96 101 '&' => '&amp;',
97 102 '"' => '&quot;',
98 103 '>' => '&gt;',
99 104 '<' => '&lt;',
100 105 }
101 106
102 107 # This was to prevent illegal HTML.
103 108 # Strange chars should still be avoided in codes.
104 109 evil_chars = Array(0x00...0x20) - [?\n, ?\t, ?\s]
105 110 evil_chars.each { |i| HTML_ESCAPE[i.chr] = ' ' }
106 111 #ansi_chars = Array(0x7f..0xff)
107 112 #ansi_chars.each { |i| HTML_ESCAPE[i.chr] = '&#%d;' % i }
108 113 # \x9 (\t) and \xA (\n) not included
109 114 #HTML_ESCAPE_PATTERN = /[\t&"><\0-\x8\xB-\x1f\x7f-\xff]/
110 115 HTML_ESCAPE_PATTERN = /[\t"&><\0-\x8\xB-\x1f]/
111 116
112 117 TOKEN_KIND_TO_INFO = Hash.new { |h, kind|
113 118 h[kind] =
114 119 case kind
115 120 when :pre_constant
116 121 'Predefined constant'
117 122 else
118 123 kind.to_s.gsub(/_/, ' ').gsub(/\b\w/) { $&.capitalize }
119 124 end
120 125 }
121 126
122 127 TRANSPARENT_TOKEN_KINDS = [
123 128 :delimiter, :modifier, :content, :escape, :inline_delimiter,
124 129 ].to_set
125 130
126 131 # Generate a hint about the given +classes+ in a +hint+ style.
127 132 #
128 133 # +hint+ may be :info, :info_long or :debug.
129 134 def self.token_path_to_hint hint, classes
130 135 title =
131 136 case hint
132 137 when :info
133 138 TOKEN_KIND_TO_INFO[classes.first]
134 139 when :info_long
135 140 classes.reverse.map { |kind| TOKEN_KIND_TO_INFO[kind] }.join('/')
136 141 when :debug
137 142 classes.inspect
138 143 end
139 144 " title=\"#{title}\""
140 145 end
141 146
142 147 def setup options
143 148 super
144 149
145 150 @HTML_ESCAPE = HTML_ESCAPE.dup
146 151 @HTML_ESCAPE["\t"] = ' ' * options[:tab_width]
147 152
153 @escape = options[:escape]
148 154 @opened = [nil]
149 155 @css = CSS.new options[:style]
150 156
151 157 hint = options[:hint]
152 158 if hint and not [:debug, :info, :info_long].include? hint
153 159 raise ArgumentError, "Unknown value %p for :hint; \
154 160 expected :info, :debug, false, or nil." % hint
155 161 end
156 162
157 163 case options[:css]
158 164
159 165 when :class
160 166 @css_style = Hash.new do |h, k|
161 167 c = Tokens::ClassOfKind[k.first]
162 168 if c == :NO_HIGHLIGHT and not hint
163 169 h[k.dup] = false
164 170 else
165 171 title = if hint
166 172 HTML.token_path_to_hint(hint, k[1..-1] << k.first)
167 173 else
168 174 ''
169 175 end
170 176 if c == :NO_HIGHLIGHT
171 177 h[k.dup] = '<span%s>' % [title]
172 178 else
173 179 h[k.dup] = '<span%s class="%s">' % [title, c]
174 180 end
175 181 end
176 182 end
177 183
178 184 when :style
179 185 @css_style = Hash.new do |h, k|
180 186 if k.is_a? ::Array
181 187 styles = k.dup
182 188 else
183 189 styles = [k]
184 190 end
185 191 type = styles.first
186 192 classes = styles.map { |c| Tokens::ClassOfKind[c] }
187 193 if classes.first == :NO_HIGHLIGHT and not hint
188 194 h[k] = false
189 195 else
190 196 styles.shift if TRANSPARENT_TOKEN_KINDS.include? styles.first
191 197 title = HTML.token_path_to_hint hint, styles
192 198 style = @css[*classes]
193 199 h[k] =
194 200 if style
195 201 '<span%s style="%s">' % [title, style]
196 202 else
197 203 false
198 204 end
199 205 end
200 206 end
201 207
202 208 else
203 209 raise ArgumentError, "Unknown value %p for :css." % options[:css]
204 210
205 211 end
206 212 end
207 213
208 214 def finish options
209 215 not_needed = @opened.shift
210 216 @out << '</span>' * @opened.size
211 217 unless @opened.empty?
212 218 warn '%d tokens still open: %p' % [@opened.size, @opened]
213 219 end
214 220
215 221 @out.extend Output
216 222 @out.css = @css
217 223 @out.numerize! options[:line_numbers], options
218 224 @out.wrap! options[:wrap]
219 225
220 226 super
221 227 end
222 228
223 229 def token text, type
224 230 if text.is_a? ::String
225 if text =~ /#{HTML_ESCAPE_PATTERN}/o
231 if @escape && (text =~ /#{HTML_ESCAPE_PATTERN}/o)
226 232 text = text.gsub(/#{HTML_ESCAPE_PATTERN}/o) { |m| @HTML_ESCAPE[m] }
227 233 end
228 234 @opened[0] = type
229 235 if style = @css_style[@opened]
230 236 @out << style << text << '</span>'
231 237 else
232 238 @out << text
233 239 end
234 240 else
235 241 case text
236 242 when :open
237 243 @opened[0] = type
238 244 @out << (@css_style[@opened] || '<span>')
239 245 @opened << type
240 246 when :close
241 247 if @opened.empty?
242 248 # nothing to close
243 249 else
244 250 if $DEBUG and (@opened.size == 1 or @opened.last != type)
245 251 raise 'Malformed token stream: Trying to close a token (%p) \
246 252 that is not open. Open are: %p.' % [type, @opened[1..-1]]
247 253 end
248 254 @out << '</span>'
249 255 @opened.pop
250 256 end
251 257 when nil
252 258 raise 'Token with nil as text was given: %p' % [[text, type]]
253 259 else
254 260 raise 'unknown token kind: %p' % text
255 261 end
256 262 end
257 263 end
258 264
259 265 end
260 266
261 267 end
262 268 end
General Comments 0
You need to be logged in to leave comments. Login now