Commit edbadbe0a21803102dae4ee08ae2f37d3208c5dc

Maxime Thirouin 2013-02-02T07:29:07

Add Sass (scss) support

diff --git a/code.js b/code.js
index b88c9a2..984ce96 100644
--- a/code.js
+++ b/code.js
@@ -47,6 +47,10 @@ var components = {
 		'coffeescript': {
 			title: 'CoffeeScript',
 			require: 'javascript'
+		},
+		'scss': {
+			title: 'Sass (Scss)',
+			require: 'css'
 		}
 	},
 	plugins: {
diff --git a/components/prism-scss.js b/components/prism-scss.js
new file mode 100644
index 0000000..c51c55c
--- /dev/null
+++ b/components/prism-scss.js
@@ -0,0 +1,35 @@
+Prism.languages.scss = Prism.languages.extend('css', {
+	'comment': {
+		pattern: /(^|[^\\])(\/\*[\w\W]*?\*\/|\/\/.*?(\r?\n|$))/g,
+		lookbehind: true
+	},
+	// aturle is just the @***, not the entire rule (to highlight var & stuffs)
+	// + add ability to highlight number & unit for media queries
+	'atrule': /@[\w-]+(?=\s+(\(|\{|;))/gi,
+	// url, compassified
+	'url': /([-a-z]+-)*url(?=\()/gi,
+	// CSS selector regex is not appropriate for Sass
+	// since there can be lot more things (var, @ directive, nesting..)
+	// a selector must start at the end of a property or after a brace (end of other rules or nesting)
+	// it can contain some caracters that aren't used for defining rules or end of selector, & (parent selector), or interpolated variable
+	// the end of a selector is found when there is no rules in it ( {} or {\s}) or if there is a property (because an interpolated var
+	// can "pass" as a selector- e.g: proper#{$erty})
+	// this one was ard to do, so please be careful if you edit this one :)
+	'selector': /([^@;:\{\}\(\)]?([^@;:\{\}\(\)]|&|\#\{\$[-_\w]+\})+)(?=\s*\{(\}|\s|[^\}]+(:|\{)[^\}]+))/gm
+});
+
+Prism.languages.insertBefore('scss', 'atrule', {
+	'keyword': /@(if|else if|else|for|each|while|import|extend|debug|warn|mixin|include|function|return)|(?=@for\s+\$[-_\w]+\s)+from/i
+});
+
+Prism.languages.insertBefore('scss', 'property', {
+	'variable': /\$[-_\w]+/i
+});
+
+Prism.languages.insertBefore('scss', 'ignore', {
+	'placeholder': /%[-_\w]+/i,
+	'statement': /\B!(default|optional)\b/gi,
+	'boolean': /\b(true|false)\b/g,
+	'null': /\b(null)\b/g,
+	'operator': /\s+([-+]{1,2}|={1,2}|!=|\|?\||\?|\*|\/|\%)\s+/g
+});
diff --git a/components/prism-scss.min.js b/components/prism-scss.min.js
new file mode 100644
index 0000000..058dbc8
--- /dev/null
+++ b/components/prism-scss.min.js
@@ -0,0 +1 @@
+Prism.languages.java=Prism.languages.extend("clike",{keyword:/\b(abstract|continue|for|new|switch|assert|default|goto|package|synchronized|boolean|do|if|private|this|break|double|implements|protected|throw|byte|else|import|public|throws|case|enum|instanceof|return|transient|catch|extends|int|short|try|char|final|interface|static|void|class|finally|long|strictfp|volatile|const|float|native|super|while)\b/g,number:/\b0b[01]+\b|\b0x[\da-f]*\.?[\da-fp\-]+\b|\b\d*\.?\d+[e]?[\d]*[df]\b|\W\d*\.?\d+\b/gi,operator:{pattern:/([^\.]|^)([-+]{1,2}|!|=?<|=?>|={1,2}|(&){1,2}|\|?\||\?|\*|\/|%|\^|(<){2}|($gt;){2,3}|:|~)/g,lookbehind:!0}});Prism.languages.scss=Prism.languages.extend("css",{comment:{pattern:/(^|[^\\])(\/\*[\w\W]*?\*\/|\/\/.*?(\r?\n|$))/g,lookbehind:!0},atrule:/@[\w-]+(?=\s+(\(|\{|;))/gi,url:/([-a-z]+-)*url(?=\()/gi,selector:/([^@;:\{\}\(\)]?([^@;:\{\}\(\)]|&|\#\{\$[-_\w]+\})+)(?=\s*\{(\}|\s|[^\}]+(:|\{)[^\}]+))/gm});Prism.languages.insertBefore("scss","atrule",{keyword:/@(if|else if|else|for|each|while|import|extend|debug|warn|mixin|include|function|return)|(?=@for\s+\$[-_\w]+\s)+from/i});Prism.languages.insertBefore("scss","property",{variable:/\$[-_\w]+/i});Prism.languages.insertBefore("scss","ignore",{placeholder:/%[-_\w]+/i,statement:/\B!(default|optional)\b/gi,"boolean":/\b(true|false)\b/g,"null":/\b(null)\b/g,operator:/\s+([-+]{1,2}|={1,2}|!=|\|?\||\?|\*|\/|\%)\s+/g});
\ No newline at end of file
diff --git a/examples.html b/examples.html
index 4797e90..07f6695 100644
--- a/examples.html
+++ b/examples.html
@@ -303,8 +303,6 @@ public class PrismJS {
 	<p>As you can notice String keyword is not highlighted because it's not a Java language keyword (cf. <a href="http://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html">Java Language Keywords</a>). The main reason is that String is not a primitive type such as <em>int</em> but a class type like <em>Integer</em>.</p>
 </section>
 
-
-
 <section class="language-coffeescript">
 	<h1>Coffeescript</h1>
 
@@ -370,6 +368,140 @@ class CoffeeScript extends Prism.Javascript
 	</code></pre>
 </section>
 
+<section class="language-scss">
+	<h1>Sass (Scss)</h2>
+
+	<h2>Comments</h2>
+	<pre><code>// This is a simple one line comment</code></pre>
+
+	<h2>Variables</h2>
+	<pre><code>$var: value;
+$bool: true;
+$nullValue: null;
+$interpolation: -vendor-#{$var};</code></pre>
+
+	<h2>Nesting</h2>
+	<pre><code>sel {
+	ector {
+		#chi,
+		ld .ren { } inline {} * {}
+	}
+	
+	&.parentSelector {
+		nested & test #{$inter}pol {}
+	}
+}</code></pre>
+
+	<h2>Directives</h2>
+	<pre><code>@import "compass";
+
+.class {
+	@extend %placeholder;
+}
+
+@debug $var;
+
+@warn "This is a Warning in the console";</code></pre>
+
+	<h2>Keywords</h2>
+	<pre><code>@mixin super-mixin() {
+	@include great-mixin();
+}
+
+@function fn() {
+	@return value;
+}
+	</code></pre>
+
+	<h2>URLs Functions (Compass Style)</h2>
+	<pre><code>selector {
+	background: image-url('stuff.png');
+}
+
+@font-face {
+	src: font-url('font.woff');
+}</code></pre>
+
+	<h2>Statements</h2>
+	<pre><code>$var: value !default;
+
+selector {
+	@extend %stuff !optional;
+}</code></pre>
+
+	<h2>Controls</h2>
+	<pre><code>@if(true) {
+	// ...
+}
+@else if {
+	// ...
+}
+@else {
+	// ...
+}
+
+@for $i from 1 through 3 {
+	// ...
+}
+
+@each $animal in puma, sea-slug, egret, salamander {
+	// ...
+}
+
+$i: 2;
+@while $i > 0 {
+  //...
+  $i: $i - 1; // note the operator
+  $i: 3 * 2 + $i % 5;
+}</code></pre>
+
+	<h2>Scss full example</h2>
+	<pre><code>@import "compass";
+	
+@mixin mixin($param) {
+	@if($param==true) {
+		selector-#{$test}-stuff,
+		sel2 {
+			property: -moz-#{$param};
+			aze: rty;
+			
+			child {
+				test: $param * 3;
+				a: bcd;
+				color: #f45f55;
+			}
+		}
+	}
+	@else {
+		property: $param + #aaa;
+		test: stupid;
+	}
+}
+
+$i: 2;
+@while $i > 0 {
+	sel#{$i}: $i * 2
+}
+
+selector {
+	@media (min-width: 800px) {
+		stuff: $thing;
+	}
+}</code></pre>
+
+	<p>
+		This Scss syntax can be used just for CSS too since regular CSS is valid Scss too.
+		Highlight renders better with an appropriate theme (to customize specific tokens).
+	</p>
+	<h2>Known issues</h2>
+	<ul>
+		<li>&amp; keyword and interpolated variables are not highlighted in selectors.</li>
+		<li>Some control directives parameters are parsed as selectors.</li>
+		<li>Does not work very well with indented Sass syntax. That's why it's just called "scss".</li>
+	</ul>
+	<p>If you are good with regex, maybe you can help to improve this.</p>
+</section>
+
 <section id="failures" class="language-javascript">
 	<h1>Known failures (JavaScript)</h1>
 	<p>There are certain edge cases where Prism will fail.
diff --git a/prism.js b/prism.js
index 239c615..3daaa40 100644
--- a/prism.js
+++ b/prism.js
@@ -501,3 +501,44 @@ Prism.languages.insertBefore('coffeescript', 'keyword', {
 
   'attr-name': /[_?a-z-|A-Z-]+(\s*:)| @[_?$?a-z-|A-Z-]+(\s*)| /g
 });
+
+/* **********************************************
+     Begin prism-scss.js
+********************************************** */
+
+Prism.languages.scss = Prism.languages.extend('css', {
+	'comment': {
+		pattern: /(^|[^\\])(\/\*[\w\W]*?\*\/|\/\/.*?(\r?\n|$))/g,
+		lookbehind: true
+	},
+	// aturle is just the @***, not the entire rule (to highlight var & stuffs)
+	// + add ability to highlight number & unit for media queries
+	'atrule': /@[\w-]+(?=\s+(\(|\{|;))/gi,
+	// url, compassified
+	'url': /([-a-z]+-)*url(?=\()/gi,
+	// CSS selector regex is not appropriate for Sass
+	// since there can be lot more things (var, @ directive, nesting..)
+	// a selector must start at the end of a property or after a brace (end of other rules or nesting)
+	// it can contain some caracters that aren't used for defining rules or end of selector, & (parent selector), or interpolated variable
+	// the end of a selector is found when there is no rules in it ( {} or {\s}) or if there is a property (because an interpolated var
+	// can "pass" as a selector- e.g: proper#{$erty})
+	// this one was ard to do, so please be careful if you edit this one :)
+	'selector': /([^@;:\{\}\(\)]?([^@;:\{\}\(\)]|&amp;|\#\{\$[-_\w]+\})+)(?=\s*\{(\}|\s|[^\}]+(:|\{)[^\}]+))/gm
+});
+
+Prism.languages.insertBefore('scss', 'atrule', {
+	'keyword': /@(if|else if|else|for|each|while|import|extend|debug|warn|mixin|include|function|return)|(?=@for\s+\$[-_\w]+\s)+from/i
+});
+
+Prism.languages.insertBefore('scss', 'property', {
+	'variable': /\$[-_\w]+/i
+});
+
+Prism.languages.insertBefore('scss', 'ignore', {
+	'placeholder': /%[-_\w]+/i,
+	'statement': /\B!(default|optional)\b/gi,
+	'boolean': /\b(true|false)\b/g,
+	'null': /\b(null)\b/g,
+	'operator': /\s+([-+]{1,2}|={1,2}|!=|\|?\||\?|\*|\/|\%)\s+/g
+});
+