drnic-haml 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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