drnic-haml 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (190) hide show
  1. data/.yardopts +5 -0
  2. data/CONTRIBUTING +4 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +347 -0
  5. data/REVISION +1 -0
  6. data/Rakefile +371 -0
  7. data/VERSION +1 -0
  8. data/VERSION_NAME +1 -0
  9. data/bin/css2sass +7 -0
  10. data/bin/haml +9 -0
  11. data/bin/html2haml +7 -0
  12. data/bin/sass +8 -0
  13. data/extra/haml-mode.el +663 -0
  14. data/extra/sass-mode.el +205 -0
  15. data/extra/update_watch.rb +13 -0
  16. data/init.rb +8 -0
  17. data/lib/haml.rb +40 -0
  18. data/lib/haml/buffer.rb +307 -0
  19. data/lib/haml/engine.rb +301 -0
  20. data/lib/haml/error.rb +22 -0
  21. data/lib/haml/exec.rb +470 -0
  22. data/lib/haml/filters.rb +341 -0
  23. data/lib/haml/helpers.rb +560 -0
  24. data/lib/haml/helpers/action_view_extensions.rb +40 -0
  25. data/lib/haml/helpers/action_view_mods.rb +176 -0
  26. data/lib/haml/herb.rb +96 -0
  27. data/lib/haml/html.rb +308 -0
  28. data/lib/haml/precompiler.rb +997 -0
  29. data/lib/haml/shared.rb +78 -0
  30. data/lib/haml/template.rb +51 -0
  31. data/lib/haml/template/patch.rb +58 -0
  32. data/lib/haml/template/plugin.rb +71 -0
  33. data/lib/haml/util.rb +244 -0
  34. data/lib/haml/version.rb +64 -0
  35. data/lib/sass.rb +24 -0
  36. data/lib/sass/css.rb +423 -0
  37. data/lib/sass/engine.rb +491 -0
  38. data/lib/sass/environment.rb +79 -0
  39. data/lib/sass/error.rb +162 -0
  40. data/lib/sass/files.rb +133 -0
  41. data/lib/sass/plugin.rb +170 -0
  42. data/lib/sass/plugin/merb.rb +57 -0
  43. data/lib/sass/plugin/rails.rb +23 -0
  44. data/lib/sass/repl.rb +58 -0
  45. data/lib/sass/script.rb +55 -0
  46. data/lib/sass/script/bool.rb +17 -0
  47. data/lib/sass/script/color.rb +183 -0
  48. data/lib/sass/script/funcall.rb +50 -0
  49. data/lib/sass/script/functions.rb +199 -0
  50. data/lib/sass/script/lexer.rb +191 -0
  51. data/lib/sass/script/literal.rb +177 -0
  52. data/lib/sass/script/node.rb +14 -0
  53. data/lib/sass/script/number.rb +381 -0
  54. data/lib/sass/script/operation.rb +45 -0
  55. data/lib/sass/script/parser.rb +222 -0
  56. data/lib/sass/script/string.rb +12 -0
  57. data/lib/sass/script/unary_operation.rb +34 -0
  58. data/lib/sass/script/variable.rb +31 -0
  59. data/lib/sass/tree/comment_node.rb +84 -0
  60. data/lib/sass/tree/debug_node.rb +30 -0
  61. data/lib/sass/tree/directive_node.rb +70 -0
  62. data/lib/sass/tree/for_node.rb +48 -0
  63. data/lib/sass/tree/if_node.rb +54 -0
  64. data/lib/sass/tree/import_node.rb +69 -0
  65. data/lib/sass/tree/mixin_def_node.rb +29 -0
  66. data/lib/sass/tree/mixin_node.rb +48 -0
  67. data/lib/sass/tree/node.rb +252 -0
  68. data/lib/sass/tree/prop_node.rb +106 -0
  69. data/lib/sass/tree/root_node.rb +56 -0
  70. data/lib/sass/tree/rule_node.rb +220 -0
  71. data/lib/sass/tree/variable_node.rb +34 -0
  72. data/lib/sass/tree/while_node.rb +31 -0
  73. data/rails/init.rb +1 -0
  74. data/test/benchmark.rb +99 -0
  75. data/test/haml/engine_test.rb +1129 -0
  76. data/test/haml/helper_test.rb +282 -0
  77. data/test/haml/html2haml_test.rb +258 -0
  78. data/test/haml/markaby/standard.mab +52 -0
  79. data/test/haml/mocks/article.rb +6 -0
  80. data/test/haml/results/content_for_layout.xhtml +12 -0
  81. data/test/haml/results/eval_suppressed.xhtml +9 -0
  82. data/test/haml/results/filters.xhtml +62 -0
  83. data/test/haml/results/helpers.xhtml +93 -0
  84. data/test/haml/results/helpful.xhtml +10 -0
  85. data/test/haml/results/just_stuff.xhtml +68 -0
  86. data/test/haml/results/list.xhtml +12 -0
  87. data/test/haml/results/nuke_inner_whitespace.xhtml +40 -0
  88. data/test/haml/results/nuke_outer_whitespace.xhtml +148 -0
  89. data/test/haml/results/original_engine.xhtml +20 -0
  90. data/test/haml/results/partial_layout.xhtml +5 -0
  91. data/test/haml/results/partials.xhtml +21 -0
  92. data/test/haml/results/render_layout.xhtml +3 -0
  93. data/test/haml/results/silent_script.xhtml +74 -0
  94. data/test/haml/results/standard.xhtml +162 -0
  95. data/test/haml/results/tag_parsing.xhtml +23 -0
  96. data/test/haml/results/very_basic.xhtml +5 -0
  97. data/test/haml/results/whitespace_handling.xhtml +89 -0
  98. data/test/haml/rhtml/_av_partial_1.rhtml +12 -0
  99. data/test/haml/rhtml/_av_partial_2.rhtml +8 -0
  100. data/test/haml/rhtml/action_view.rhtml +62 -0
  101. data/test/haml/rhtml/standard.rhtml +54 -0
  102. data/test/haml/spec_test.rb +44 -0
  103. data/test/haml/template_test.rb +217 -0
  104. data/test/haml/templates/_av_partial_1.haml +9 -0
  105. data/test/haml/templates/_av_partial_1_ugly.haml +9 -0
  106. data/test/haml/templates/_av_partial_2.haml +5 -0
  107. data/test/haml/templates/_av_partial_2_ugly.haml +5 -0
  108. data/test/haml/templates/_layout.erb +3 -0
  109. data/test/haml/templates/_layout_for_partial.haml +3 -0
  110. data/test/haml/templates/_partial.haml +8 -0
  111. data/test/haml/templates/_text_area.haml +3 -0
  112. data/test/haml/templates/action_view.haml +47 -0
  113. data/test/haml/templates/action_view_ugly.haml +47 -0
  114. data/test/haml/templates/breakage.haml +8 -0
  115. data/test/haml/templates/content_for_layout.haml +8 -0
  116. data/test/haml/templates/eval_suppressed.haml +11 -0
  117. data/test/haml/templates/filters.haml +66 -0
  118. data/test/haml/templates/helpers.haml +95 -0
  119. data/test/haml/templates/helpful.haml +11 -0
  120. data/test/haml/templates/just_stuff.haml +83 -0
  121. data/test/haml/templates/list.haml +12 -0
  122. data/test/haml/templates/nuke_inner_whitespace.haml +32 -0
  123. data/test/haml/templates/nuke_outer_whitespace.haml +144 -0
  124. data/test/haml/templates/original_engine.haml +17 -0
  125. data/test/haml/templates/partial_layout.haml +3 -0
  126. data/test/haml/templates/partialize.haml +1 -0
  127. data/test/haml/templates/partials.haml +12 -0
  128. data/test/haml/templates/render_layout.haml +2 -0
  129. data/test/haml/templates/silent_script.haml +40 -0
  130. data/test/haml/templates/standard.haml +42 -0
  131. data/test/haml/templates/standard_ugly.haml +42 -0
  132. data/test/haml/templates/tag_parsing.haml +21 -0
  133. data/test/haml/templates/very_basic.haml +4 -0
  134. data/test/haml/templates/whitespace_handling.haml +87 -0
  135. data/test/haml/util_test.rb +92 -0
  136. data/test/linked_rails.rb +12 -0
  137. data/test/sass/css2sass_test.rb +294 -0
  138. data/test/sass/engine_test.rb +956 -0
  139. data/test/sass/functions_test.rb +126 -0
  140. data/test/sass/more_results/more1.css +9 -0
  141. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  142. data/test/sass/more_results/more_import.css +29 -0
  143. data/test/sass/more_templates/_more_partial.sass +2 -0
  144. data/test/sass/more_templates/more1.sass +23 -0
  145. data/test/sass/more_templates/more_import.sass +11 -0
  146. data/test/sass/plugin_test.rb +229 -0
  147. data/test/sass/results/alt.css +4 -0
  148. data/test/sass/results/basic.css +9 -0
  149. data/test/sass/results/compact.css +5 -0
  150. data/test/sass/results/complex.css +87 -0
  151. data/test/sass/results/compressed.css +1 -0
  152. data/test/sass/results/expanded.css +19 -0
  153. data/test/sass/results/import.css +29 -0
  154. data/test/sass/results/line_numbers.css +49 -0
  155. data/test/sass/results/mixins.css +95 -0
  156. data/test/sass/results/multiline.css +24 -0
  157. data/test/sass/results/nested.css +22 -0
  158. data/test/sass/results/parent_ref.css +13 -0
  159. data/test/sass/results/script.css +16 -0
  160. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  161. data/test/sass/results/subdir/subdir.css +3 -0
  162. data/test/sass/results/units.css +11 -0
  163. data/test/sass/script_test.rb +261 -0
  164. data/test/sass/templates/_partial.sass +2 -0
  165. data/test/sass/templates/alt.sass +16 -0
  166. data/test/sass/templates/basic.sass +23 -0
  167. data/test/sass/templates/bork1.sass +2 -0
  168. data/test/sass/templates/bork2.sass +2 -0
  169. data/test/sass/templates/bork3.sass +2 -0
  170. data/test/sass/templates/compact.sass +17 -0
  171. data/test/sass/templates/complex.sass +307 -0
  172. data/test/sass/templates/compressed.sass +15 -0
  173. data/test/sass/templates/expanded.sass +17 -0
  174. data/test/sass/templates/import.sass +11 -0
  175. data/test/sass/templates/importee.sass +19 -0
  176. data/test/sass/templates/line_numbers.sass +13 -0
  177. data/test/sass/templates/mixins.sass +76 -0
  178. data/test/sass/templates/multiline.sass +20 -0
  179. data/test/sass/templates/nested.sass +25 -0
  180. data/test/sass/templates/nested_bork1.sass +2 -0
  181. data/test/sass/templates/nested_bork2.sass +2 -0
  182. data/test/sass/templates/nested_bork3.sass +2 -0
  183. data/test/sass/templates/parent_ref.sass +25 -0
  184. data/test/sass/templates/script.sass +101 -0
  185. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  186. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  187. data/test/sass/templates/subdir/subdir.sass +6 -0
  188. data/test/sass/templates/units.sass +11 -0
  189. data/test/test_helper.rb +44 -0
  190. metadata +298 -0
@@ -0,0 +1,205 @@
1
+ ;;; sass-mode.el --- Major mode for editing Sass files
2
+
3
+ ;; Copyright (c) 2007, 2008 Nathan Weizenbaum
4
+
5
+ ;; Author: Nathan Weizenbaum
6
+ ;; URL: http://github.com/nex3/haml/tree/master
7
+ ;; Version: 2.2.6
8
+ ;; Created: 2007-03-15
9
+ ;; By: Nathan Weizenbaum
10
+ ;; Keywords: markup, language, css
11
+
12
+ ;;; Commentary:
13
+
14
+ ;; Because Sass's indentation schema is similar
15
+ ;; to that of YAML and Python, many indentation-related
16
+ ;; functions are similar to those in yaml-mode and python-mode.
17
+
18
+ ;; To install, save this on your load path and add the following to
19
+ ;; your .emacs file:
20
+ ;;
21
+ ;; (require 'sass-mode)
22
+
23
+ ;;; Code:
24
+
25
+ (require 'haml-mode)
26
+
27
+ ;; User definable variables
28
+
29
+ (defgroup sass nil
30
+ "Support for the Sass template language."
31
+ :group 'languages
32
+ :prefix "sass-")
33
+
34
+ (defcustom sass-mode-hook nil
35
+ "Hook run when entering Sass mode."
36
+ :type 'hook
37
+ :group 'sass)
38
+
39
+ (defcustom sass-indent-offset 2
40
+ "Amount of offset per level of indentation."
41
+ :type 'integer
42
+ :group 'sass)
43
+
44
+ (defvar sass-non-block-openers
45
+ '("^ *:[^ \t]+[ \t]+[^ \t]"
46
+ "^ *[^ \t:]+[ \t]*[=:][ \t]*[^ \t]")
47
+ "A list of regexps that match lines of Sass that don't open blocks.
48
+ That is, a Sass line that can't have text nested beneath it
49
+ should be matched by a regexp in this list.")
50
+
51
+ ;; Font lock
52
+
53
+ (defconst sass-selector-font-lock-keywords
54
+ '(;; Attribute selectors (e.g. p[foo=bar])
55
+ ("\\[\\([^]=]+\\)" (1 font-lock-variable-name-face)
56
+ ("[~|$^*]?=\\([^]=]+\\)" nil nil (1 font-lock-string-face)))
57
+ ("&" 0 font-lock-constant-face)
58
+ ("\\.\\w+" 0 font-lock-type-face)
59
+ ("#\\w+" 0 font-lock-keyword-face)
60
+ ;; Pseudo-selectors, optionally with arguments (e.g. :first, :nth-child(12))
61
+ ("\\(::?\\w+\\)" (1 font-lock-function-name-face)
62
+ ("(\\([^)]+\\))" nil nil (1 font-lock-string-face)))))
63
+
64
+ (defconst sass-script-font-lock-keywords
65
+ `(("\"\\([^\"\\\\]\\|\\\\.\\)*\"" 0 font-lock-string-face)
66
+ ("!\\(\\w\\|_\\)+" 0 font-lock-variable-name-face)
67
+ ("#[0-9a-fA-F]\\{0,6\\}" 0 font-lock-preprocessor-face)
68
+ (,(regexp-opt
69
+ '("true" "false" "black" "silver" "gray" "white" "maroon" "red"
70
+ "purple" "fuchsia" "green" "lime" "olive" "yellow" "navy"
71
+ "blue" "teal" "aqua"))
72
+ 0 font-lock-constant-face)
73
+ (,(regexp-opt '("and" "or" "not")) 0 font-lock-keyword-face)))
74
+
75
+ (defconst sass-syntax-table
76
+ (let ((st (make-syntax-table)))
77
+ (modify-syntax-entry ?- "w" st)
78
+ (modify-syntax-entry ?_ "w" st)
79
+ st))
80
+
81
+ (defconst sass-script-syntax-table
82
+ (let ((st (make-syntax-table sass-syntax-table)))
83
+ (modify-syntax-entry ?- "." st)
84
+ st))
85
+
86
+ (defconst sass-font-lock-keywords
87
+ '((sass-highlight-line 1 nil nil t)))
88
+
89
+ (defconst sass-line-keywords
90
+ '(("@\\(\\w+\\)" 0 font-lock-keyword-face sass-highlight-directive)
91
+ ("/[/*].*" 0 font-lock-comment-face)
92
+ ("[=+]\\w+" 0 font-lock-function-name-face sass-highlight-script-after-match)
93
+ ("!\\w+" 0 font-lock-variable-name-face sass-highlight-script-after-match)
94
+ (":\\w+" 0 font-lock-variable-name-face)
95
+ ("\\w+\s*:" 0 font-lock-variable-name-face)
96
+ ("\\(\\w+\\)\s*=" 1 font-lock-variable-name-face sass-highlight-script-after-match)
97
+ ("\\(:\\w+\\)\s*=" 1 font-lock-variable-name-face sass-highlight-script-after-match)
98
+ (".*" sass-highlight-selector))
99
+ "A list of full-line Sass syntax to highlight, used by `sass-highlight-line'.
100
+
101
+ Each item is either of the form (REGEXP SUBEXP FACE), (REGEXP FN),
102
+ or (REGEXP SUBEXP FACE FN). Each REGEXP is run successively on the
103
+ beginning of non-whitespace on the current line until one matches.
104
+ If it has SUBEXP and FACE, then SUBEXP is highlighted using FACE.
105
+ If it has FN, FN is run.")
106
+
107
+ (defun sass-highlight-line (limit)
108
+ "Highlight a single line using some Sass single-line syntax.
109
+ This syntax is taken from `sass-line-keywords'.
110
+ LIMIT is the limit of the search."
111
+ (save-match-data
112
+ (when (re-search-forward "^ *\\(.+\\)$" limit t)
113
+ (goto-char (match-beginning 1))
114
+ (dolist (keyword sass-line-keywords)
115
+ (destructuring-bind (keyword subexp-or-fn &optional face fn) keyword
116
+ (when (looking-at keyword)
117
+ (if (integerp subexp-or-fn)
118
+ (put-text-property (match-beginning subexp-or-fn)
119
+ (match-end subexp-or-fn)
120
+ 'face face)
121
+ (setq fn subexp-or-fn))
122
+ (when fn (funcall fn))
123
+ (end-of-line)
124
+ (return t)))))))
125
+
126
+ (defun sass-highlight-selector ()
127
+ "Highlight a CSS selector starting at `point' and ending at `end-of-line'."
128
+ (let ((font-lock-keywords sass-selector-font-lock-keywords)
129
+ font-lock-multiline)
130
+ (font-lock-fontify-region
131
+ (point) (progn (end-of-line) (point))))
132
+ t)
133
+
134
+ (defun sass-highlight-script (beg end)
135
+ "Highlight a section of SassScript between BEG and END."
136
+ (save-match-data
137
+ (with-syntax-table sass-script-syntax-table
138
+ (let ((font-lock-keywords sass-script-font-lock-keywords)
139
+ font-lock-syntax-table
140
+ font-lock-extend-region-functions)
141
+ (font-lock-fontify-region beg end)))))
142
+
143
+ (defun sass-highlight-script-after-match ()
144
+ "Highlight a section of SassScript after the last match."
145
+ (end-of-line)
146
+ (sass-highlight-script (match-end 0) (point)))
147
+
148
+ (defun sass-highlight-directive ()
149
+ "Highlight a Sass directive."
150
+ (goto-char (match-end 0))
151
+ (block nil
152
+ (case (intern (match-string 1))
153
+ (for
154
+ (unless (looking-at " +!\\w+") (return))
155
+ (put-text-property (match-beginning 0) (match-end 0)
156
+ 'face font-lock-variable-name-face)
157
+ (goto-char (match-end 0))
158
+ (unless (looking-at " +from") (return))
159
+ (put-text-property (match-beginning 0) (match-end 0)
160
+ 'face font-lock-keyword-face)
161
+ (goto-char (match-end 0))
162
+ (when (looking-at " +\\(.+?\\) +\\(to\\|through\\)")
163
+ (sass-highlight-script (match-beginning 1) (match-end 1))
164
+ (put-text-property (match-beginning 2) (match-end 2)
165
+ 'face font-lock-keyword-face))
166
+ (sass-highlight-script-after-match))
167
+
168
+ (else
169
+ (unless (looking-at " +if") (return))
170
+ (put-text-property (match-beginning 0) (match-end 0)
171
+ 'face font-lock-keyword-face)
172
+ (sass-highlight-script-after-match))
173
+
174
+ ((if while debug) (sass-highlight-script-after-match)))))
175
+
176
+ ;; Constants
177
+
178
+ ;; Mode setup
179
+
180
+ ;;;###autoload
181
+ (define-derived-mode sass-mode haml-mode "Sass"
182
+ "Major mode for editing Sass files."
183
+ (set-syntax-table sass-syntax-table)
184
+ (setq font-lock-extend-region-functions
185
+ '(font-lock-extend-region-wholelines font-lock-extend-region-multiline))
186
+ (setq font-lock-multiline nil)
187
+ (setq comment-start "/*")
188
+ (set (make-local-variable 'haml-indent-function) 'sass-indent-p)
189
+ (set (make-local-variable 'haml-indent-offset) sass-indent-offset)
190
+ (setq font-lock-defaults '(sass-font-lock-keywords t t)))
191
+
192
+ ;; Indentation
193
+
194
+ (defun sass-indent-p ()
195
+ "Return non-nil if the current line can have lines nested beneath it."
196
+ (loop for opener in sass-non-block-openers
197
+ unless (looking-at opener) return t
198
+ return nil))
199
+
200
+ ;;;###autoload
201
+ (add-to-list 'auto-mode-alist '("\\.sass$" . sass-mode))
202
+
203
+ ;; Setup/Activation
204
+ (provide 'sass-mode)
205
+ ;;; sass-mode.el ends here
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'sinatra'
3
+ require 'json'
4
+ set :port, 3123
5
+ set :environment, :production
6
+ enable :lock
7
+ Dir.chdir(File.dirname(__FILE__) + "/..")
8
+
9
+ post "/" do
10
+ puts "Recieved payload!"
11
+ puts "Rev: #{`git name-rev HEAD`.strip}"
12
+ system %{rake handle_update --trace REF=#{JSON.parse(params["payload"])["ref"].inspect}}
13
+ end
data/init.rb ADDED
@@ -0,0 +1,8 @@
1
+ begin
2
+ require File.join(File.dirname(__FILE__), 'lib', 'haml') # From here
3
+ rescue LoadError
4
+ require 'haml' # From gem
5
+ end
6
+
7
+ # Load Haml and Sass
8
+ Haml.init_rails(binding)
@@ -0,0 +1,40 @@
1
+ dir = File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift dir unless $LOAD_PATH.include?(dir)
3
+
4
+ require 'haml/version'
5
+
6
+ # The module that contains everything Haml-related:
7
+ #
8
+ # * {Haml::Engine} is the class used to render Haml within Ruby code.
9
+ # * {Haml::Helpers} contains Ruby helpers available within Haml templates.
10
+ # * {Haml::Template} interfaces with web frameworks (Rails in particular).
11
+ # * {Haml::Error} is raised when Haml encounters an error.
12
+ # * {Haml::HTML} handles conversion of HTML to Haml.
13
+ #
14
+ # Also see the {file:HAML_REFERENCE.md full Haml reference}.
15
+ module Haml
16
+ extend Haml::Version
17
+
18
+ # A string representing the version of Haml.
19
+ # A more fine-grained representation is available from Haml.version.
20
+ VERSION = version[:string] unless defined?(Haml::VERSION)
21
+
22
+ # Initializes Haml for Rails.
23
+ #
24
+ # This method is called by `init.rb`,
25
+ # which is run by Rails on startup.
26
+ # We use it rather than putting stuff straight into `init.rb`
27
+ # so we can change the initialization behavior
28
+ # without modifying the file itself.
29
+ #
30
+ # @param binding [Binding] The context of the `init.rb` file.
31
+ # This isn't actually used;
32
+ # it's just passed in in case it needs to be used in the future
33
+ def self.init_rails(binding)
34
+ # No &method here for Rails 2.1 compatibility
35
+ %w[haml/template sass sass/plugin].each {|f| require f}
36
+ end
37
+ end
38
+
39
+ require 'haml/util'
40
+ require 'haml/engine'
@@ -0,0 +1,307 @@
1
+ module Haml
2
+ # This class is used only internally. It holds the buffer of HTML that
3
+ # is eventually output as the resulting document.
4
+ # It's called from within the precompiled code,
5
+ # and helps reduce the amount of processing done within `instance_eval`ed code.
6
+ class Buffer
7
+ include Haml::Helpers
8
+ include Haml::Util
9
+
10
+ # The string that holds the compiled HTML. This is aliased as
11
+ # `_erbout` for compatibility with ERB-specific code.
12
+ #
13
+ # @return [String]
14
+ attr_accessor :buffer
15
+
16
+ # The options hash passed in from {Haml::Engine}.
17
+ #
18
+ # @return [Hash<String, Object>]
19
+ # @see Haml::Engine#options_for_buffer
20
+ attr_accessor :options
21
+
22
+ # The {Buffer} for the enclosing Haml document.
23
+ # This is set for partials and similar sorts of nested templates.
24
+ # It's `nil` at the top level (see \{#toplevel?}).
25
+ #
26
+ # @return [Buffer]
27
+ attr_accessor :upper
28
+
29
+ # nil if there's no capture_haml block running,
30
+ # and the position at which it's beginning the capture if there is one.
31
+ #
32
+ # @return [Fixnum, nil]
33
+ attr_accessor :capture_position
34
+
35
+ # @return [Boolean]
36
+ # @see #active?
37
+ attr_writer :active
38
+
39
+ # @return [Boolean] Whether or not the format is XHTML
40
+ def xhtml?
41
+ not html?
42
+ end
43
+
44
+ # @return [Boolean] Whether or not the format is any flavor of HTML
45
+ def html?
46
+ html4? or html5?
47
+ end
48
+
49
+ # @return [Boolean] Whether or not the format is HTML4
50
+ def html4?
51
+ @options[:format] == :html4
52
+ end
53
+
54
+ # @return [Boolean] Whether or not the format is HTML5.
55
+ def html5?
56
+ @options[:format] == :html5
57
+ end
58
+
59
+ # @return [Boolean] Whether or not this buffer is a top-level template,
60
+ # as opposed to a nested partial
61
+ def toplevel?
62
+ upper.nil?
63
+ end
64
+
65
+ # Whether or not this buffer is currently being used to render a Haml template.
66
+ # Returns `false` if a subtemplate is being rendered,
67
+ # even if it's a subtemplate of this buffer's template.
68
+ #
69
+ # @return [Boolean]
70
+ def active?
71
+ @active
72
+ end
73
+
74
+ # @return [Fixnum] The current indentation level of the document
75
+ def tabulation
76
+ @real_tabs + @tabulation
77
+ end
78
+
79
+ # Sets the current tabulation of the document.
80
+ #
81
+ # @param val [Fixnum] The new tabulation
82
+ def tabulation=(val)
83
+ val = val - @real_tabs
84
+ @tabulation = val > -1 ? val : 0
85
+ end
86
+
87
+ # @param upper [Buffer] The parent buffer
88
+ # @param options [Hash<Symbol, Object>] An options hash.
89
+ # See {Haml::Engine#options\_for\_buffer}
90
+ def initialize(upper = nil, options = {})
91
+ @active = true
92
+ @upper = upper
93
+ @options = options
94
+ @buffer = ruby1_8? ? "" : "".encode(Encoding.find(options[:encoding]))
95
+ @tabulation = 0
96
+
97
+ # The number of tabs that Engine thinks we should have
98
+ # @real_tabs + @tabulation is the number of tabs actually output
99
+ @real_tabs = 0
100
+ end
101
+
102
+ # Appends text to the buffer, properly tabulated.
103
+ # Also modifies the document's indentation.
104
+ #
105
+ # @param text [String] The text to append
106
+ # @param tab_change [Fixnum] The number of tabs by which to increase
107
+ # or decrease the document's indentation
108
+ # @param dont_tab_up [Boolean] If true, don't indent the first line of `text`
109
+ def push_text(text, tab_change, dont_tab_up)
110
+ if @tabulation > 0
111
+ # Have to push every line in by the extra user set tabulation.
112
+ # Don't push lines with just whitespace, though,
113
+ # because that screws up precompiled indentation.
114
+ text.gsub!(/^(?!\s+$)/m, tabs)
115
+ text.sub!(tabs, '') if dont_tab_up
116
+ end
117
+
118
+ @buffer << text
119
+ @real_tabs += tab_change
120
+ end
121
+
122
+ # Modifies the indentation of the document.
123
+ #
124
+ # @param tab_change [Fixnum] The number of tabs by which to increase
125
+ # or decrease the document's indentation
126
+ def adjust_tabs(tab_change)
127
+ @real_tabs += tab_change
128
+ end
129
+
130
+ Haml::Util.def_static_method(self, :format_script, [:result],
131
+ :preserve_script, :in_tag, :preserve_tag, :escape_html,
132
+ :nuke_inner_whitespace, :interpolated, :ugly, <<RUBY)
133
+ <% unless ugly %>
134
+ # If we're interpolated,
135
+ # then the custom tabulation is handled in #push_text.
136
+ # The easiest way to avoid it here is to reset @tabulation.
137
+ <% if interpolated %>
138
+ old_tabulation = @tabulation
139
+ @tabulation = 0
140
+ <% end %>
141
+
142
+ tabulation = @real_tabs
143
+ result = result.to_s.<% if nuke_inner_whitespace %>strip<% else %>rstrip<% end %>
144
+ <% else %>
145
+ result = result.to_s<% if nuke_inner_whitespace %>.strip<% end %>
146
+ <% end %>
147
+
148
+ <% if escape_html %> result = html_escape(result) <% end %>
149
+
150
+ <% if preserve_tag %>
151
+ result = Haml::Helpers.preserve(result)
152
+ <% elsif preserve_script %>
153
+ result = Haml::Helpers.find_and_preserve(result, options[:preserve])
154
+ <% end %>
155
+
156
+ <% if ugly %>
157
+ return result
158
+ <% else %>
159
+
160
+ has_newline = result.include?("\\n")
161
+ <% if in_tag && !nuke_inner_whitespace %>
162
+ <% unless preserve_tag %> if !has_newline <% end %>
163
+ @real_tabs -= 1
164
+ <% if interpolated %> @tabulation = old_tabulation <% end %>
165
+ return result
166
+ <% unless preserve_tag %> end <% end %>
167
+ <% end %>
168
+
169
+ # Precompiled tabulation may be wrong
170
+ <% if !interpolated && !in_tag %>
171
+ result = tabs + result if @tabulation > 0
172
+ <% end %>
173
+
174
+ if has_newline
175
+ result = result.gsub "\\n", "\\n" + tabs(tabulation)
176
+
177
+ # Add tabulation if it wasn't precompiled
178
+ <% if in_tag && !nuke_inner_whitespace %> result = tabs(tabulation) + result <% end %>
179
+ end
180
+
181
+ <% if in_tag && !nuke_inner_whitespace %>
182
+ result = "\\n\#{result}\\n\#{tabs(tabulation-1)}"
183
+ @real_tabs -= 1
184
+ <% end %>
185
+ <% if interpolated %> @tabulation = old_tabulation <% end %>
186
+ result
187
+ <% end %>
188
+ RUBY
189
+
190
+ # Takes the various information about the opening tag for an element,
191
+ # formats it, and appends it to the buffer.
192
+ def open_tag(name, self_closing, try_one_line, preserve_tag, escape_html, class_id,
193
+ nuke_outer_whitespace, nuke_inner_whitespace, obj_ref, content, *attributes_hashes)
194
+ tabulation = @real_tabs
195
+
196
+ attributes = class_id
197
+ attributes_hashes.each do |old|
198
+ self.class.merge_attrs(attributes, to_hash(old.map {|k, v| [k.to_s, v]}))
199
+ end
200
+ self.class.merge_attrs(attributes, parse_object_ref(obj_ref)) if obj_ref
201
+
202
+ if self_closing && xhtml?
203
+ str = " />" + (nuke_outer_whitespace ? "" : "\n")
204
+ else
205
+ str = ">" + ((if self_closing && html?
206
+ nuke_outer_whitespace
207
+ else
208
+ try_one_line || preserve_tag || nuke_inner_whitespace
209
+ end) ? "" : "\n")
210
+ end
211
+
212
+ attributes = Precompiler.build_attributes(html?, @options[:attr_wrapper], attributes)
213
+ @buffer << "#{nuke_outer_whitespace || @options[:ugly] ? '' : tabs(tabulation)}<#{name}#{attributes}#{str}"
214
+
215
+ if content
216
+ @buffer << "#{content}</#{name}>" << (nuke_outer_whitespace ? "" : "\n")
217
+ return
218
+ end
219
+
220
+ @real_tabs += 1 unless self_closing || nuke_inner_whitespace
221
+ end
222
+
223
+ # Remove the whitespace from the right side of the buffer string.
224
+ # Doesn't do anything if we're at the beginning of a capture_haml block.
225
+ def rstrip!
226
+ if capture_position.nil?
227
+ buffer.rstrip!
228
+ return
229
+ end
230
+
231
+ buffer << buffer.slice!(capture_position..-1).rstrip
232
+ end
233
+
234
+ # Merges two attribute hashes.
235
+ # This is the same as `to.merge!(from)`,
236
+ # except that it merges id and class attributes.
237
+ #
238
+ # ids are concatenated with `"_"`,
239
+ # and classes are concatenated with `" "`.
240
+ #
241
+ # Destructively modifies both `to` and `from`.
242
+ #
243
+ # @param to [Hash<String, String>] The attribute hash to merge into
244
+ # @param from [Hash<String, String>] The attribute hash to merge from
245
+ # @return [Hash<String, String>] `to`, after being merged
246
+ def self.merge_attrs(to, from)
247
+ if to['id'] && from['id']
248
+ to['id'] << '_' << from.delete('id').to_s
249
+ elsif to['id'] || from['id']
250
+ from['id'] ||= to['id']
251
+ end
252
+
253
+ if to['class'] && from['class']
254
+ # Make sure we don't duplicate class names
255
+ from['class'] = (from['class'].split(' ') | to['class'].split(' ')).sort.join(' ')
256
+ elsif to['class'] || from['class']
257
+ from['class'] ||= to['class']
258
+ end
259
+
260
+ to.merge!(from)
261
+ end
262
+
263
+ private
264
+
265
+ @@tab_cache = {}
266
+ # Gets `count` tabs. Mostly for internal use.
267
+ def tabs(count = 0)
268
+ tabs = [count + @tabulation, 0].max
269
+ @@tab_cache[tabs] ||= ' ' * tabs
270
+ end
271
+
272
+ # Takes an array of objects and uses the class and id of the first
273
+ # one to create an attributes hash.
274
+ # The second object, if present, is used as a prefix,
275
+ # just like you can do with `dom_id()` and `dom_class()` in Rails
276
+ def parse_object_ref(ref)
277
+ prefix = ref[1]
278
+ ref = ref[0]
279
+ # Let's make sure the value isn't nil. If it is, return the default Hash.
280
+ return {} if ref.nil?
281
+ class_name =
282
+ if ref.respond_to?(:haml_object_ref)
283
+ ref.haml_object_ref
284
+ else
285
+ underscore(ref.class)
286
+ end
287
+ id = "#{class_name}_#{ref.id || 'new'}"
288
+ if prefix
289
+ class_name = "#{ prefix }_#{ class_name}"
290
+ id = "#{ prefix }_#{ id }"
291
+ end
292
+
293
+ {'id' => id, 'class' => class_name}
294
+ end
295
+
296
+ # Changes a word from camel case to underscores.
297
+ # Based on the method of the same name in Rails' Inflector,
298
+ # but copied here so it'll run properly without Rails.
299
+ def underscore(camel_cased_word)
300
+ camel_cased_word.to_s.gsub(/::/, '_').
301
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
302
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
303
+ tr("-", "_").
304
+ downcase
305
+ end
306
+ end
307
+ end