Skip to content

Commit

Permalink
Merge pull request #13 from edward/support-quoted-strings
Browse files Browse the repository at this point in the history
Adds support for native quoted strings
  • Loading branch information
bolandrm committed Jul 13, 2015
2 parents 4264d24 + f84d140 commit 9bde318
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 116 deletions.
13 changes: 7 additions & 6 deletions lib/sassc/functions_handler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ def setup(native_options)
when :sass_null
# no-op
when :sass_string
native_string = Native.string_get_value(native_value)
argument = Script::String.new(Script::String.unquote(native_string), Script::String.type(native_string))
value = Native.string_get_value(native_value)
type = Native.string_get_type(native_value)
argument = Script::String.new(value, type)

custom_function_arguments << argument
when :sass_color
Expand All @@ -46,11 +47,11 @@ def setup(native_options)
next error_tag if error_tag

begin
value = functions.send(custom_function, *custom_function_arguments)
script_value = functions.send(custom_function, *custom_function_arguments)

if value
value = Script::String.new(Script::String.unquote(value.to_s), value.type)
value.to_native
if script_value
script_value.options = @options
script_value.to_native
else
Script::String.new("").to_native
end
Expand Down
11 changes: 11 additions & 0 deletions lib/sassc/native/native_functions_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@ module Native

# ADDAPI union Sass_Value* ADDCALL sass_make_number (double val, const char* unit);
attach_function :sass_make_number, [:double, :string], :sass_value_ptr

# ADDAPI union Sass_Value* ADDCALL sass_make_string (const char* val);
attach_function :sass_make_string, [:string], :sass_value_ptr

# ADDAPI union Sass_Value* ADDCALL sass_make_qstring (const char* val);
attach_function :sass_make_qstring, [:string], :sass_value_ptr

# ADDAPI enum Sass_Tag ADDCALL sass_value_get_tag (const union Sass_Value* v);
attach_function :sass_value_get_tag, [:sass_value_ptr], SassTag
Expand All @@ -24,6 +28,13 @@ module Native
# ADDAPI const char* ADDCALL sass_string_get_value (const union Sass_Value* v);
attach_function :sass_string_get_value, [:sass_value_ptr], :string

# ADDAPI bool ADDCALL sass_string_is_quoted(const union Sass_Value* v);
attach_function :sass_string_is_quoted, [:sass_value_ptr], :bool

def self.string_get_type(native_value)
string_is_quoted(native_value) ? :string : :identifier
end

# ADDAPI double ADDCALL sass_color_get_r (const union Sass_Value* v);
# ADDAPI void ADDCALL sass_color_set_r (union Sass_Value* v, double r);
# ADDAPI double ADDCALL sass_color_get_g (const union Sass_Value* v);
Expand Down
2 changes: 1 addition & 1 deletion lib/sassc/script.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ def self.formatted_function_name(function_name)
end

require_relative "script/functions"
require_relative "script/string"

module Sass
module Script
Expand All @@ -26,4 +25,5 @@ module Script

require 'sass/util'
require 'sass/script/value/base'
require_relative "script/string"
require_relative "script/color"
102 changes: 7 additions & 95 deletions lib/sassc/script/string.rb
Original file line number Diff line number Diff line change
@@ -1,102 +1,14 @@
require 'sass/script/value/string'

module SassC
module Script
class String
# The Ruby value of the string.
#
# @return [String]
attr_reader :value

# Whether this is a CSS string or a CSS identifier.
# The difference is that strings are written with double-quotes,
# while identifiers aren't.
#
# @return [Symbol] `:string` or `:identifier`
attr_reader :type

def self.value(contents)
contents.gsub("\\\n", "").gsub(/\\(?:([0-9a-fA-F]{1,6})\s?|(.))/) do
next $2 if $2
# Handle unicode escapes as per CSS Syntax Level 3 section 4.3.8.
code_point = $1.to_i(16)
if code_point == 0 || code_point > 0x10FFFF ||
(code_point >= 0xD800 && code_point <= 0xDFFF)
'�'
else
[code_point].pack("U")
end
end
end

def self.quote(contents, quote = nil)
# Short-circuit if there are no characters that need quoting.
unless contents =~ /[\n\\"']/
quote ||= '"'
return "#{quote}#{contents}#{quote}"
end

if quote.nil?
if contents.include?('"')
if contents.include?("'")
quote = '"'
else
quote = "'"
end
else
quote = '"'
end
end

# Replace single backslashes with multiples.
contents = contents.gsub("\\", "\\\\\\\\")

if quote == '"'
contents = contents.gsub('"', "\\\"")
class String < ::Sass::Script::Value::String
def to_native(opts = {})
if opts[:quote] == :none || type == :identifier
Native::make_string(to_s)
else
contents = contents.gsub("'", "\\'")
Native::make_qstring(to_s)
end

contents = contents.gsub(/\n(?![a-fA-F0-9\s])/, "\\a").gsub("\n", "\\a ")
"#{quote}#{contents}#{quote}"
end

def self.type(contents)
unquote(contents) == contents ? :identifier : :string
end

def self.unquote(contents)
s = contents.dup

case contents[0, 1]
when "'", '"', '`'
s[0] = ''
end

case contents[-1, 1]
when "'", '"', '`'
s[-1] = ''
end

return s
end

# Creates a new string.
#
# @param value [String] See \{#value}
# @param type [Symbol] See \{#type}
def initialize(value, type = :identifier)
value.freeze unless value.nil? || value == true || value == false
@value = value
@type = type
end

def to_native
Native::make_string(to_s)
end

# @see Value#to_s
def to_s(opts = {})
return @value.gsub(/\n\s*/, ' ') if opts[:quote] == :none || @type == :identifier
SassC::Script::String.quote(value, opts[:quote])
end
end
end
Expand Down
30 changes: 16 additions & 14 deletions test/functions_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@ def teardown
end

def test_functions_may_return_sass_string_type
engine = Engine.new("div {url: url(sass_return_path('foo.svg'));}")
engine = Engine.new(<<-SCSS)
div {
url: url(sass_return_path("foo.svg")); }
SCSS

assert_equal <<-EOS, engine.render
assert_equal <<-EXPECTED_SCSS, engine.render
div {
url: url("foo.svg"); }
EOS
EXPECTED_SCSS
end

def test_functions_work_with_varying_quotes_and_string_types
Expand Down Expand Up @@ -54,12 +57,17 @@ def test_function_with_no_return_value
end

def test_function_with_optional_arguments
engine = Engine.new("div {url: optional_arguments('first'); url: optional_arguments('second', 'qux')}")
assert_equal <<-EOS, engine.render
engine = Engine.new(<<-SCSS)
div {
url: optional_arguments('first');
url: optional_arguments('second', 'qux'); }
SCSS

assert_equal <<-EXPECTED_SCSS, engine.render
div {
url: "first/bar";
url: "second/qux"; }
EOS
EXPECTED_SCSS
end

def test_functions_may_accept_sass_color_type
Expand Down Expand Up @@ -103,12 +111,6 @@ def test_function_with_error

private

SassString = Struct.new(:value, :type) do
def to_s
value
end
end

module Script::Functions
def javascript_path(path)
Script::String.new("/js/#{path.value}", :string)
Expand All @@ -119,11 +121,11 @@ def no_return_path(path)
end

def sass_return_path(path)
return SassString.new("'#{path.value}'", :string)
Script::String.new("#{path.value}", :string)
end

def optional_arguments(path, optional = "bar")
return SassString.new("#{path}/#{optional}", :string)
Script::String.new("#{path.value}/#{optional}", :string)
end

def function_that_raises_errors()
Expand Down

0 comments on commit 9bde318

Please sign in to comment.