Page MenuHomePhabricator

D8992.id21355.diff
No OneTemporary

D8992.id21355.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -87,6 +87,8 @@
'ArcanistJSONLintLinter' => 'lint/linter/ArcanistJSONLintLinter.php',
'ArcanistJSONLintLinterTestCase' => 'lint/linter/__tests__/ArcanistJSONLintLinterTestCase.php',
'ArcanistLandWorkflow' => 'workflow/ArcanistLandWorkflow.php',
+ 'ArcanistLesscLinter' => 'lint/linter/ArcanistLesscLinter.php',
+ 'ArcanistLesscLinterTestCase' => 'lint/linter/__tests__/ArcanistLesscLinterTestCase.php',
'ArcanistLiberateWorkflow' => 'workflow/ArcanistLiberateWorkflow.php',
'ArcanistLintConsoleRenderer' => 'lint/renderer/ArcanistLintConsoleRenderer.php',
'ArcanistLintEngine' => 'lint/engine/ArcanistLintEngine.php',
@@ -254,6 +256,8 @@
'ArcanistJSONLintLinter' => 'ArcanistExternalLinter',
'ArcanistJSONLintLinterTestCase' => 'ArcanistArcanistLinterTestCase',
'ArcanistLandWorkflow' => 'ArcanistBaseWorkflow',
+ 'ArcanistLesscLinter' => 'ArcanistExternalLinter',
+ 'ArcanistLesscLinterTestCase' => 'ArcanistArcanistLinterTestCase',
'ArcanistLiberateWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistLintConsoleRenderer' => 'ArcanistLintRenderer',
'ArcanistLintJSONRenderer' => 'ArcanistLintRenderer',
diff --git a/src/lint/linter/ArcanistLesscLinter.php b/src/lint/linter/ArcanistLesscLinter.php
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/ArcanistLesscLinter.php
@@ -0,0 +1,151 @@
+<?php
+
+/**
+ * A linter for LESSCSS files.
+ *
+ * This linter uses [[https://github.com/less/less.js | lessc]] to detect
+ * errors and potential problems in [[http://lesscss.org/ | LESS]] code.
+ */
+final class ArcanistLesscLinter extends ArcanistExternalLinter {
+
+ const LINT_RUNTIME_ERROR = 1;
+ const LINT_ARGUMENT_ERROR = 2;
+ const LINT_FILE_ERROR = 3;
+ const LINT_NAME_ERROR = 4;
+ const LINT_OPERATION_ERROR = 5;
+ const LINT_PARSE_ERROR = 6;
+ const LINT_SYNTAX_ERROR = 7;
+
+ public function getLinterName() {
+ return 'LESSC';
+ }
+
+ public function getLinterConfigurationName() {
+ return 'lessc';
+ }
+
+ public function getLintNameMap() {
+ return array(
+ self::LINT_RUNTIME_ERROR => pht('Runtime Error'),
+ self::LINT_ARGUMENT_ERROR => pht('Argument Error'),
+ self::LINT_FILE_ERROR => pht('File Error'),
+ self::LINT_NAME_ERROR => pht('Name Error'),
+ self::LINT_OPERATION_ERROR => pht('Operation Error'),
+ self::LINT_PARSE_ERROR => pht('Parse Error'),
+ self::LINT_SYNTAX_ERROR => pht('Syntax Error'),
+ );
+ }
+
+ public function getDefaultBinary() {
+ return 'lessc';
+ }
+
+ public function getCacheVersion() {
+ list($stdout) = execx('%C --version', $this->getExecutableCommand());
+
+ $matches = array();
+ if (preg_match('/^lessc (?P<version>\d+\.\d+\.\d+)$/', $stdout, $matches)) {
+ $version = $matches['version'];
+ } else {
+ return false;
+ }
+ }
+
+ public function getInstallInstructions() {
+ return pht('Install lessc using `npm install -g lessc`.');
+ }
+
+ public function shouldExpectCommandErrors() {
+ return true;
+ }
+
+ public function supportsReadDataFromStdin() {
+ // Technically `lessc` can read data from standard input however, when doing
+ // so, relative imports cannot be resolved. Therefore, this functionality is
+ // disabled.
+ return false;
+ }
+
+ public function getReadDataFromStdinFilename() {
+ return '-';
+ }
+
+ protected function getMandatoryFlags() {
+ return array(
+ '--lint',
+ '--no-color',
+ '--strict-math=' . ($this->getConfig('strict-math') ? 'on' : 'off'),
+ '--strict-units=' . ($this->getConfig('strict-units') ? 'on' : 'off'));
+ }
+
+ protected function parseLinterOutput($path, $err, $stdout, $stderr) {
+ $lines = phutil_split_lines($stderr, false);
+
+ $messages = array();
+ foreach ($lines as $line) {
+ $matches = null;
+ $match = preg_match(
+ '/^(?P<name>\w+): (?P<description>.+) ' .
+ 'in (?P<path>.+|-) ' .
+ 'on line (?P<line>\d+), column (?P<column>\d+):$/',
+ $line,
+ $matches);
+
+ if ($match) {
+ switch ($matches['name']) {
+ case 'RuntimeError':
+ $code = self::LINT_RUNTIME_ERROR;
+ break;
+
+ case 'ArgumentError':
+ $code = self::LINT_ARGUMENT_ERROR;
+ break;
+
+ case 'FileError':
+ $code = self::LINT_FILE_ERROR;
+ break;
+
+ case 'NameError':
+ $code = self::LINT_NAME_ERROR;
+ break;
+
+ case 'OperationError':
+ $code = self::LINT_OPERATION_ERROR;
+ break;
+
+ case 'ParseError':
+ $code = self::LINT_PARSE_ERROR;
+ break;
+
+ case 'SyntaxError':
+ $code = self::LINT_SYNTAX_ERROR;
+ break;
+
+ default:
+ throw new RuntimeException(pht(
+ 'Unrecognized lint message code "%s".',
+ $code));
+ }
+
+ $code = $this->getLintCodeFromLinterConfigurationKey($matches['name']);
+
+ $message = new ArcanistLintMessage();
+ $message->setPath($path);
+ $message->setLine($matches['line']);
+ $message->setChar($matches['column']);
+ $message->setCode($this->getLintMessageFullCode($code));
+ $message->setSeverity($this->getLintMessageSeverity($code));
+ $message->setName($this->getLintMessageName($code));
+ $message->setDescription(ucfirst($matches['description']));
+
+ $messages[] = $message;
+ }
+ }
+
+ if ($err && !$messages) {
+ return false;
+ }
+
+ return $messages;
+ }
+}
diff --git a/src/lint/linter/__tests__/ArcanistLesscLinterTestCase.php b/src/lint/linter/__tests__/ArcanistLesscLinterTestCase.php
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/ArcanistLesscLinterTestCase.php
@@ -0,0 +1,12 @@
+<?php
+
+final class ArcanistLesscLinterTestCase
+ extends ArcanistArcanistLinterTestCase {
+
+ public function testLesscLinter() {
+ $this->executeTestsInDirectory(
+ dirname(__FILE__).'/lessc/',
+ new ArcanistLesscLinter());
+ }
+
+}
diff --git a/src/lint/linter/__tests__/lessc/colors.lint-test b/src/lint/linter/__tests__/lessc/colors.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/colors.lint-test
@@ -0,0 +1,99 @@
+#yelow {
+ #short {
+ color: #fea;
+ }
+ #long {
+ color: #ffeeaa;
+ }
+ #rgba {
+ color: rgba(255, 238, 170, 0.1);
+ }
+ #argb {
+ color: argb(rgba(255, 238, 170, 0.1));
+ }
+}
+
+#blue {
+ #short {
+ color: #00f;
+ }
+ #long {
+ color: #0000ff;
+ }
+ #rgba {
+ color: rgba(0, 0, 255, 0.1);
+ }
+ #argb {
+ color: argb(rgba(0, 0, 255, 0.1));
+ }
+}
+
+#alpha #hsla {
+ color: hsla(11, 20%, 20%, 0.6);
+}
+
+#overflow {
+ .a { color: (#111111 - #444444); } // #000000
+ .b { color: (#eee + #fff); } // #ffffff
+ .c { color: (#aaa * 3); } // #ffffff
+ .d { color: (#00ee00 + #009900); } // #00ff00
+ .e { color: rgba(-99.9, 31.4159, 321, 0.42); }
+}
+
+#grey {
+ color: rgb(200, 200, 200);
+}
+
+#333333 {
+ color: rgb(20%, 20%, 20%);
+}
+
+#808080 {
+ color: hsl(50, 0%, 50%);
+}
+
+#00ff00 {
+ color: hsl(120, 100%, 50%);
+}
+
+.lightenblue {
+ color: lighten(blue, 10%);
+}
+
+.darkenblue {
+ color: darken(blue, 10%);
+}
+
+.unknowncolors {
+ color: blue2;
+ border: 2px solid superred;
+}
+
+.transparent {
+ color: transparent;
+ background-color: rgba(0, 0, 0, 0);
+}
+#alpha {
+ @colorvar: rgba(150, 200, 150, 0.7);
+ #fromvar {
+ opacity: alpha(@colorvar);
+ }
+ #short {
+ opacity: alpha(#aaa);
+ }
+ #long {
+ opacity: alpha(#bababa);
+ }
+ #rgba {
+ opacity: alpha(rgba(50, 120, 95, 0.2));
+ }
+ #hsl {
+ opacity: alpha(hsl(120, 100%, 50%));
+ }
+}
+
+#percentage {
+ color: red(rgb(100%, 0, 0));
+ border-color: rgba(100%, 0, 0, 50%);
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/comments.lint-test b/src/lint/linter/__tests__/lessc/comments.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/comments.lint-test
@@ -0,0 +1,84 @@
+/******************\
+* *
+* Comment Header *
+* *
+\******************/
+
+/*
+
+ Comment
+
+*/
+
+/*
+ * Comment Test
+ *
+ * - cloudhead (http://cloudhead.net)
+ *
+ */
+
+////////////////
+@var: "content";
+////////////////
+
+/* Colors
+ * ------
+ * #EDF8FC (background blue)
+ * #166C89 (darkest blue)
+ *
+ * Text:
+ * #333 (standard text) // A comment within a comment!
+ * #1F9EC9 (standard link)
+ *
+ */
+
+/* @group Variables
+------------------- */
+#comments /* boo *//* boo again*/,
+//.commented_out1
+//.commented_out2
+//.commented_out3
+.comments //end of comments1
+//end of comments2
+{
+ /**/ // An empty comment
+ color: red; /* A C-style comment */ /* A C-style comment */
+ background-color: orange; // A little comment
+ font-size: 12px;
+
+ /* lost comment */ content: @var;
+
+ border: 1px solid black;
+
+ // padding & margin //
+ padding: 0; // }{ '"
+ margin: 2em;
+} //
+
+/* commented out
+ #more-comments {
+ color: grey;
+ }
+*/
+
+.selector /* .with */, .lots, /* of */ .comments {
+ color: grey, /* blue */ orange;
+ -webkit-border-radius: 2px /* webkit only */;
+ -moz-border-radius: (2px * 4) /* moz only with operation */;
+}
+
+.mixin_def_with_colors(@a: white, // in
+ @b: 1px //put in @b - causes problems! --->
+ ) // the
+ when (@a = white) {
+ .test {
+ color: @b;
+ }
+}
+.mixin_def_with_colors();
+
+#last { color: blue }
+//
+
+/* *//* { *//* *//* *//* */#div { color:#A33; }/* } */
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/css-3.lint-test b/src/lint/linter/__tests__/lessc/css-3.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/css-3.lint-test
@@ -0,0 +1,130 @@
+.comma-delimited {
+ text-shadow: -1px -1px 1px red, 6px 5px 5px yellow;
+ -moz-box-shadow: 0pt 0pt 2px rgba(255, 255, 255, 0.4) inset,
+ 0pt 4px 6px rgba(255, 255, 255, 0.4) inset;
+ -webkit-transform: rotate(-0.0000000001deg);
+}
+@font-face {
+ font-family: Headline;
+ unicode-range: U+??????, U+0???, U+0-7F, U+A5;
+}
+.other {
+ -moz-transform: translate(0, 11em) rotate(-90deg);
+ transform: rotateX(45deg);
+}
+.item[data-cra_zy-attr1b-ut3=bold] {
+ font-weight: bold;
+}
+p:not([class*="lead"]) {
+ color: black;
+}
+
+input[type="text"].class#id[attr=32]:not(1) {
+ color: white;
+}
+
+div#id.class[a=1][b=2].class:not(1) {
+ color: white;
+}
+
+ul.comma > li:not(:only-child)::after {
+ color: white;
+}
+
+ol.comma > li:nth-last-child(2)::after {
+ color: white;
+}
+
+li:nth-child(4n+1),
+li:nth-child(-5n),
+li:nth-child(-n+2) {
+ color: white;
+}
+
+a[href^="http://"] {
+ color: black;
+}
+
+a[href$="http://"] {
+ color: black;
+}
+
+form[data-disabled] {
+ color: black;
+}
+
+p::before {
+ color: black;
+}
+
+#issue322 {
+ -webkit-animation: anim2 7s infinite ease-in-out;
+}
+
+@-webkit-keyframes frames {
+ 0% { border: 1px }
+ 5.5% { border: 2px }
+ 100% { border: 3px }
+}
+
+@keyframes fontbulger1 {
+ to {
+ font-size: 15px;
+ }
+ from,to {
+ font-size: 12px;
+ }
+ 0%,100% {
+ font-size: 12px;
+ }
+}
+
+.units {
+ font: 1.2rem/2rem;
+ font: 8vw/9vw;
+ font: 10vh/12vh;
+ font: 12vm/15vm;
+ font: 12vmin/15vmin;
+ font: 1.2ch/1.5ch;
+}
+
+@supports ( box-shadow: 2px 2px 2px black ) or
+ ( -moz-box-shadow: 2px 2px 2px black ) {
+ .outline {
+ box-shadow: 2px 2px 2px black;
+ -moz-box-shadow: 2px 2px 2px black;
+ }
+}
+
+@-x-document url-prefix(""github.com"") {
+ h1 {
+ color: red;
+ }
+}
+
+@viewport {
+ font-size: 10px;
+}
+@namespace foo url(http://www.example.com);
+
+foo|h1 { color: blue; }
+foo|* { color: yellow; }
+|h1 { color: red; }
+*|h1 { color: green; }
+h1 { color: green; }
+.upper-test {
+ UpperCaseProperties: allowed;
+}
+@host {
+ div {
+ display: block;
+ }
+}
+::distributed(input::placeholder) {
+ color: #b3b3b3;
+}
+.shadow ^ .dom,
+body ^^ .shadow {
+ display: done;
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/css-escapes.lint-test b/src/lint/linter/__tests__/lessc/css-escapes.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/css-escapes.lint-test
@@ -0,0 +1,34 @@
+@ugly: fuchsia;
+
+.escape\|random\|char {
+ color: red;
+}
+
+.mixin\!tUp {
+ font-weight: bold;
+}
+
+// class="404"
+.\34 04 {
+ background: red;
+
+ strong {
+ color: @ugly;
+ .mixin\!tUp;
+ }
+}
+
+.trailingTest\+ {
+ color: red;
+}
+
+/* This hideous test of hideousness checks for the selector "blockquote" with various permutations of hex escapes */
+\62\6c\6f \63 \6B \0071 \000075o\74 e {
+ color: silver;
+}
+
+[ng\:cloak],
+ng\:form {
+ display: none;
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/css-guards.lint-test b/src/lint/linter/__tests__/lessc/css-guards.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/css-guards.lint-test
@@ -0,0 +1,65 @@
+
+.light when (lightness(@a) > 50%) {
+ color: green;
+}
+.dark when (lightness(@a) < 50%) {
+ color: orange;
+}
+@a: #ddd;
+
+.see-the {
+ @a: #444; // this mirrors what mixins do - they evaluate guards at the point of execution
+ .light();
+ .dark();
+}
+
+.hide-the {
+ .light();
+ .dark();
+}
+
+.multiple-conditions-1 when (@b = 1), (@c = 2), (@d = 3) {
+ color: red;
+}
+
+.multiple-conditions-2 when (@b = 1), (@c = 2), (@d = 2) {
+ color: blue;
+}
+
+@b: 2;
+@c: 3;
+@d: 3;
+
+.inheritance when (@b = 2) {
+ .test {
+ color: black;
+ }
+ &:hover {
+ color: pink;
+ }
+ .hideme when (@b = 1) {
+ color: green;
+ }
+ & when (@b = 1) {
+ hideme: green;
+ }
+}
+
+.hideme when (@b = 1) {
+ .test {
+ color: black;
+ }
+ &:hover {
+ color: pink;
+ }
+ .hideme when (@b = 1) {
+ color: green;
+ }
+}
+
+& when (@b = 1) {
+ .hideme {
+ color: red;
+ }
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/css.lint-test b/src/lint/linter/__tests__/lessc/css.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/css.lint-test
@@ -0,0 +1,109 @@
+@charset "utf-8";
+div { color: black; }
+div { width: 99%; }
+
+* {
+ min-width: 45em;
+}
+
+h1, h2 > a > p, h3 {
+ color: none;
+}
+
+div.class {
+ color: blue;
+}
+
+div#id {
+ color: green;
+}
+
+.class#id {
+ color: purple;
+}
+
+.one.two.three {
+ color: grey;
+}
+
+@media print {
+ * {
+ font-size: 3em;
+ }
+}
+
+@media screen {
+ * {
+ font-size: 10px;
+ }
+}
+
+@font-face {
+ font-family: 'Garamond Pro';
+}
+
+a:hover, a:link {
+ color: #999;
+}
+
+p, p:first-child {
+ text-transform: none;
+}
+
+q:lang(no) {
+ quotes: none;
+}
+
+p + h1 {
+ font-size: +2.2em;
+}
+
+#shorthands {
+ border: 1px solid #000;
+ font: 12px/16px Arial;
+ font: 100%/16px Arial;
+ margin: 1px 0;
+ padding: 0 auto;
+}
+
+#more-shorthands {
+ margin: 0;
+ padding: 1px 0 2px 0;
+ font: normal small/20px 'Trebuchet MS', Verdana, sans-serif;
+ font: 0/0 a;
+ border-radius: 5px / 10px;
+}
+
+.misc {
+ -moz-border-radius: 2px;
+ display: -moz-inline-stack;
+ width: .1em;
+ background-color: #009998;
+ background: -webkit-gradient(linear, left top, left bottom, from(red), to(blue));
+ margin: ;
+ .nested-multiple {
+ multiple-semi-colons: yes;;;;;;
+ };
+ filter: alpha(opacity=100);
+ width: auto\9;
+}
+
+#important {
+ color: red !important;
+ width: 100%!important;
+ height: 20px ! important;
+}
+
+.def-font(@name) {
+ @font-face {
+ font-family: @name
+ }
+}
+
+.def-font(font-a);
+.def-font(font-b);
+
+.æøå {
+ margin: 0;
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/detached-rulesets.lint-test b/src/lint/linter/__tests__/lessc/detached-rulesets.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/detached-rulesets.lint-test
@@ -0,0 +1,104 @@
+@ruleset: {
+ color: black;
+ background: white;
+ };
+
+@a: 1px;
+.wrap-mixin(@ruleset) {
+ @a: hidden and if you see this in the output its a bug;
+ @b: visible;
+ @d: magic-frame; // same behaviour as mixin calls - falls back to this frame
+ .wrap-selector {
+ @c: visible;
+ @ruleset();
+ visible-one: @b;
+ visible-two: @c;
+ }
+};
+
+.wrap-mixin({
+ color: black;
+ one: @a;
+ @b: hidden and if you see this in the output its a bug;
+ @c: hidden and if you see this in the output its a bug;
+ four: @d;
+});
+
+.wrap-mixin(@ruleset: {
+ color: red;
+});
+
+.wrap-mixin(@ruleset);
+
+.desktop-and-old-ie(@rules) {
+ @media screen and (min-width: 1200) { @rules(); }
+ html.lt-ie9 & { @rules(); }
+}
+
+header {
+ background: blue;
+
+ .desktop-and-old-ie({
+ background: red;
+ });
+}
+
+.wrap-mixin-calls-wrap(@ruleset) {
+ .wrap-mixin(@ruleset);
+};
+
+.wrap-mixin({
+ test: extra-wrap;
+ .wrap-mixin-calls-wrap({
+ test: wrapped-twice;
+ });
+});
+
+.wrap-mixin({
+ test-func: unit(90px);
+ test-arithmetic: unit((9+9), px);
+});
+// without mixins
+@ruleset-2: {
+ b: 1;
+};
+.without-mixins {
+ @ruleset-2();
+}
+@my-ruleset: {
+ .my-selector {
+ @media tv {
+ background-color: black;
+ }
+ }
+ };
+@media (orientation:portrait) {
+ @my-ruleset();
+ .wrap-media-mixin({
+ @media tv {
+ .triple-wrapped-mq {
+ triple: true;
+ }
+ }
+ });
+}
+.wrap-media-mixin(@ruleset) {
+ @media widescreen {
+ @media print {
+ @ruleset();
+ }
+ @ruleset();
+ }
+ @ruleset();
+}
+// unlocking mixins
+@my-mixins: {
+ .mixin() {
+ test: test;
+ }
+};
+@my-mixins();
+.a {
+ .mixin();
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/empty.lint-test b/src/lint/linter/__tests__/lessc/empty.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/empty.lint-test
@@ -0,0 +1 @@
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/errors/add-mixed-units.lint-test b/src/lint/linter/__tests__/lessc/errors/add-mixed-units.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/add-mixed-units.lint-test
@@ -0,0 +1,8 @@
+.a {
+ error: (1px + 3em);
+}
+~~~~~~~~~~
+error:2:3
+~~~~~~~~~~
+~~~~~~~~~~
+{"config":{"strict-units":true}}
diff --git a/src/lint/linter/__tests__/lessc/errors/add-mixed-units2.lint-test b/src/lint/linter/__tests__/lessc/errors/add-mixed-units2.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/add-mixed-units2.lint-test
@@ -0,0 +1,8 @@
+.a {
+ error: ((1px * 2px) + (3em * 3px));
+}
+~~~~~~~~~~
+error:2:3
+~~~~~~~~~~
+~~~~~~~~~~
+{"config":{"strict-units":true}}
diff --git a/src/lint/linter/__tests__/lessc/errors/at-rules-undefined-var.lint-test b/src/lint/linter/__tests__/lessc/errors/at-rules-undefined-var.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/at-rules-undefined-var.lint-test
@@ -0,0 +1,6 @@
+
+@keyframes @name {
+ 50% {width: 20px;}
+}
+~~~~~~~~~~
+error:2:12
diff --git a/src/lint/linter/__tests__/lessc/errors/bad-variable-declaration1.lint-test b/src/lint/linter/__tests__/lessc/errors/bad-variable-declaration1.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/bad-variable-declaration1.lint-test
@@ -0,0 +1,3 @@
+@@demo: "hi";
+~~~~~~~~~~
+error:1:1
diff --git a/src/lint/linter/__tests__/lessc/errors/color-func-invalid-color.lint-test b/src/lint/linter/__tests__/lessc/errors/color-func-invalid-color.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/color-func-invalid-color.lint-test
@@ -0,0 +1,5 @@
+.test {
+ color: color("NOT A COLOR");
+}
+~~~~~~~~~~
+error:2:10
diff --git a/src/lint/linter/__tests__/lessc/errors/comment-in-selector.lint-test b/src/lint/linter/__tests__/lessc/errors/comment-in-selector.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/comment-in-selector.lint-test
@@ -0,0 +1,3 @@
+#gaga /* Comment */ span { color: red }
+~~~~~~~~~~
+error:1:21
diff --git a/src/lint/linter/__tests__/lessc/errors/css-guard-default-func.lint-test b/src/lint/linter/__tests__/lessc/errors/css-guard-default-func.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/css-guard-default-func.lint-test
@@ -0,0 +1,6 @@
+
+selector when (default()) {
+ color: red;
+}
+~~~~~~~~~~
+error:2:16
diff --git a/src/lint/linter/__tests__/lessc/errors/detached-ruleset-1.lint-test b/src/lint/linter/__tests__/lessc/errors/detached-ruleset-1.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/detached-ruleset-1.lint-test
@@ -0,0 +1,8 @@
+@a: {
+ b: 1;
+};
+.a {
+ a: @a;
+}
+~~~~~~~~~~
+error:5:3
diff --git a/src/lint/linter/__tests__/lessc/errors/detached-ruleset-2.lint-test b/src/lint/linter/__tests__/lessc/errors/detached-ruleset-2.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/detached-ruleset-2.lint-test
@@ -0,0 +1,8 @@
+@a: {
+ b: 1;
+};
+.a {
+ a: @a();
+}
+~~~~~~~~~~
+error:5:3
diff --git a/src/lint/linter/__tests__/lessc/errors/detached-ruleset-3.lint-test b/src/lint/linter/__tests__/lessc/errors/detached-ruleset-3.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/detached-ruleset-3.lint-test
@@ -0,0 +1,6 @@
+@a: {
+ b: 1;
+};
+@a();
+~~~~~~~~~~
+error:2:3
diff --git a/src/lint/linter/__tests__/lessc/errors/detached-ruleset-4.lint-test b/src/lint/linter/__tests__/lessc/errors/detached-ruleset-4.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/detached-ruleset-4.lint-test
@@ -0,0 +1,7 @@
+.mixin-definition(@a: {
+ b: 1;
+}) {
+ @a();
+}
+~~~~~~~~~~
+error:1:18
diff --git a/src/lint/linter/__tests__/lessc/errors/detached-ruleset-5.lint-test b/src/lint/linter/__tests__/lessc/errors/detached-ruleset-5.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/detached-ruleset-5.lint-test
@@ -0,0 +1,6 @@
+.mixin-definition(@b) {
+ @a();
+}
+.mixin-definition({color: red;});
+~~~~~~~~~~
+error:4:1
diff --git a/src/lint/linter/__tests__/lessc/errors/detached-ruleset-6.lint-test b/src/lint/linter/__tests__/lessc/errors/detached-ruleset-6.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/detached-ruleset-6.lint-test
@@ -0,0 +1,7 @@
+.a {
+ b: {
+ color: red;
+ };
+}
+~~~~~~~~~~
+error:2:3
diff --git a/src/lint/linter/__tests__/lessc/errors/divide-mixed-units.lint-test b/src/lint/linter/__tests__/lessc/errors/divide-mixed-units.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/divide-mixed-units.lint-test
@@ -0,0 +1,8 @@
+.a {
+ error: (1px / 3em);
+}
+~~~~~~~~~~
+error:2:3
+~~~~~~~~~~
+~~~~~~~~~~
+{"config":{"strict-units":true}}
diff --git a/src/lint/linter/__tests__/lessc/errors/extend-no-selector.lint-test b/src/lint/linter/__tests__/lessc/errors/extend-no-selector.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/extend-no-selector.lint-test
@@ -0,0 +1,5 @@
+:extend(.a all) {
+ property: red;
+}
+~~~~~~~~~~
+error:1:17
diff --git a/src/lint/linter/__tests__/lessc/errors/extend-not-at-end.lint-test b/src/lint/linter/__tests__/lessc/errors/extend-not-at-end.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/extend-not-at-end.lint-test
@@ -0,0 +1,5 @@
+.a:extend(.b all).c {
+ property: red;
+}
+~~~~~~~~~~
+error:1:21
diff --git a/src/lint/linter/__tests__/lessc/errors/import-missing.lint-test b/src/lint/linter/__tests__/lessc/errors/import-missing.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/import-missing.lint-test
@@ -0,0 +1,8 @@
+.a {
+ color: green;
+ // tests line number for import reference is correct
+}
+
+@import "file-does-not-exist.less";
+~~~~~~~~~~
+error:6:1
diff --git a/src/lint/linter/__tests__/lessc/errors/import-no-semi.lint-test b/src/lint/linter/__tests__/lessc/errors/import-no-semi.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/import-no-semi.lint-test
@@ -0,0 +1,3 @@
+@import "this-statement-is-invalid.less"
+~~~~~~~~~~
+error:1:1
diff --git a/src/lint/linter/__tests__/lessc/errors/javascript-error.lint-test b/src/lint/linter/__tests__/lessc/errors/javascript-error.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/javascript-error.lint-test
@@ -0,0 +1,5 @@
+.scope {
+ var: `this.foo.toJS()`;
+}
+~~~~~~~~~~
+error:2:27
diff --git a/src/lint/linter/__tests__/lessc/errors/javascript-undefined-var.lint-test b/src/lint/linter/__tests__/lessc/errors/javascript-undefined-var.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/javascript-undefined-var.lint-test
@@ -0,0 +1,5 @@
+.scope {
+ @a: `@{b}`;
+}
+~~~~~~~~~~
+error:2:15
diff --git a/src/lint/linter/__tests__/lessc/errors/mixed-mixin-definition-args-1.lint-test b/src/lint/linter/__tests__/lessc/errors/mixed-mixin-definition-args-1.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/mixed-mixin-definition-args-1.lint-test
@@ -0,0 +1,8 @@
+.mixin(@a : 4, @b : 3, @c: 2) {
+ will: fail;
+}
+.mixin-test {
+ .mixin(@a: 5; @b: 6, @c: 7);
+}
+~~~~~~~~~~
+error:5:30
diff --git a/src/lint/linter/__tests__/lessc/errors/mixed-mixin-definition-args-2.lint-test b/src/lint/linter/__tests__/lessc/errors/mixed-mixin-definition-args-2.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/mixed-mixin-definition-args-2.lint-test
@@ -0,0 +1,8 @@
+.mixin(@a : 4, @b : 3, @c: 2) {
+ will: fail;
+}
+.mixin-test {
+ .mixin(@a: 5, @b: 6; @c: 7);
+}
+~~~~~~~~~~
+error:5:26
diff --git a/src/lint/linter/__tests__/lessc/errors/mixin-not-defined.lint-test b/src/lint/linter/__tests__/lessc/errors/mixin-not-defined.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/mixin-not-defined.lint-test
@@ -0,0 +1,13 @@
+
+.error-is-further-on() {
+}
+
+.pad-here-to-reproduce-error-in() {
+}
+
+.the-import-subfolder-test() {
+}
+
+.mixin-not-defined();
+~~~~~~~~~~
+error:11:1
diff --git a/src/lint/linter/__tests__/lessc/errors/mixin-not-matched.lint-test b/src/lint/linter/__tests__/lessc/errors/mixin-not-matched.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/mixin-not-matched.lint-test
@@ -0,0 +1,8 @@
+@saxofon:trumpete;
+
+.mixin(saxofon) {
+}
+
+.mixin(@saxofon);
+~~~~~~~~~~
+error:6:1
diff --git a/src/lint/linter/__tests__/lessc/errors/mixin-not-matched2.lint-test b/src/lint/linter/__tests__/lessc/errors/mixin-not-matched2.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/mixin-not-matched2.lint-test
@@ -0,0 +1,8 @@
+@saxofon:trumpete;
+
+.mixin(@a, @b) {
+}
+
+.mixin(@a: @saxofon);
+~~~~~~~~~~
+error:6:1
diff --git a/src/lint/linter/__tests__/lessc/errors/mixin-not-visible-in-scope-1.lint-test b/src/lint/linter/__tests__/lessc/errors/mixin-not-visible-in-scope-1.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/mixin-not-visible-in-scope-1.lint-test
@@ -0,0 +1,11 @@
+.something {
+ & {
+ .a {value: a}
+ }
+
+ & {
+ .b {.a} // was Err. before 1.6.2
+ }
+}
+~~~~~~~~~~
+error:7:13
diff --git a/src/lint/linter/__tests__/lessc/errors/mixins-guards-default-func-1.lint-test b/src/lint/linter/__tests__/lessc/errors/mixins-guards-default-func-1.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/mixins-guards-default-func-1.lint-test
@@ -0,0 +1,11 @@
+
+guard-default-func-conflict {
+ .m(@x, 1) {}
+ .m(@x, 2) when (default()) {}
+ .m(@x, 2) when (default()) {}
+
+ .m(1, 1);
+ .m(1, 2);
+}
+~~~~~~~~~~
+error:8:5
diff --git a/src/lint/linter/__tests__/lessc/errors/mixins-guards-default-func-2.lint-test b/src/lint/linter/__tests__/lessc/errors/mixins-guards-default-func-2.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/mixins-guards-default-func-2.lint-test
@@ -0,0 +1,11 @@
+
+guard-default-func-conflict {
+ .m(1) {}
+ .m(@x) when not(default()) {}
+ .m(@x) when (@x = 3) and (default()) {}
+
+ .m(2);
+ .m(3);
+}
+~~~~~~~~~~
+error:8:5
diff --git a/src/lint/linter/__tests__/lessc/errors/mixins-guards-default-func-3.lint-test b/src/lint/linter/__tests__/lessc/errors/mixins-guards-default-func-3.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/mixins-guards-default-func-3.lint-test
@@ -0,0 +1,11 @@
+
+guard-default-func-conflict {
+ .m(1) {}
+ .m(@x) when not(default()) {}
+ .m(@x) when not(default()) {}
+
+ .m(1);
+ .m(2);
+}
+~~~~~~~~~~
+error:8:5
diff --git a/src/lint/linter/__tests__/lessc/errors/multiple-guards-on-css-selectors.lint-test b/src/lint/linter/__tests__/lessc/errors/multiple-guards-on-css-selectors.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/multiple-guards-on-css-selectors.lint-test
@@ -0,0 +1,6 @@
+@ie8: true;
+.a when (@ie8 = true),
+.b {
+}
+~~~~~~~~~~
+error:3:1
diff --git a/src/lint/linter/__tests__/lessc/errors/multiple-guards-on-css-selectors2.lint-test b/src/lint/linter/__tests__/lessc/errors/multiple-guards-on-css-selectors2.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/multiple-guards-on-css-selectors2.lint-test
@@ -0,0 +1,6 @@
+@ie8: true;
+.a,
+.b when (@ie8 = true) {
+}
+~~~~~~~~~~
+error:3:23
diff --git a/src/lint/linter/__tests__/lessc/errors/multiply-mixed-units.lint-test b/src/lint/linter/__tests__/lessc/errors/multiply-mixed-units.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/multiply-mixed-units.lint-test
@@ -0,0 +1,12 @@
+/* Test */
+#blah {
+ // blah
+}
+.a {
+ error: (1px * 1em);
+}
+~~~~~~~~~~
+error:6:3
+~~~~~~~~~~
+~~~~~~~~~~
+{"config":{"strict-units":true}}
diff --git a/src/lint/linter/__tests__/lessc/errors/parens-error-1.lint-test b/src/lint/linter/__tests__/lessc/errors/parens-error-1.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/parens-error-1.lint-test
@@ -0,0 +1,5 @@
+.a {
+ something: (12 (13 + 5 -23) + 5);
+}
+~~~~~~~~~~
+error:2:18
diff --git a/src/lint/linter/__tests__/lessc/errors/parens-error-2.lint-test b/src/lint/linter/__tests__/lessc/errors/parens-error-2.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/parens-error-2.lint-test
@@ -0,0 +1,5 @@
+.a {
+ something: (12 * (13 + 5 -23));
+}
+~~~~~~~~~~
+error:2:28
diff --git a/src/lint/linter/__tests__/lessc/errors/parens-error-3.lint-test b/src/lint/linter/__tests__/lessc/errors/parens-error-3.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/parens-error-3.lint-test
@@ -0,0 +1,5 @@
+.a {
+ something: (12 + (13 + 10 -23));
+}
+~~~~~~~~~~
+error:2:29
diff --git a/src/lint/linter/__tests__/lessc/errors/parse-error-curly-bracket.lint-test b/src/lint/linter/__tests__/lessc/errors/parse-error-curly-bracket.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/parse-error-curly-bracket.lint-test
@@ -0,0 +1,6 @@
+body {
+ background-color: #fff;
+ }
+}
+~~~~~~~~~~
+error:4:1
diff --git a/src/lint/linter/__tests__/lessc/errors/parse-error-extra-parens.lint-test b/src/lint/linter/__tests__/lessc/errors/parse-error-extra-parens.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/parse-error-extra-parens.lint-test
@@ -0,0 +1,7 @@
+@media (extra: bracket)) {
+ body {
+ background-color: #fff;
+ }
+}
+~~~~~~~~~~
+error:1:24
diff --git a/src/lint/linter/__tests__/lessc/errors/parse-error-missing-bracket.lint-test b/src/lint/linter/__tests__/lessc/errors/parse-error-missing-bracket.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/parse-error-missing-bracket.lint-test
@@ -0,0 +1,4 @@
+body {
+ background-color: #fff;
+~~~~~~~~~~
+error:1:6
diff --git a/src/lint/linter/__tests__/lessc/errors/parse-error-missing-parens.lint-test b/src/lint/linter/__tests__/lessc/errors/parse-error-missing-parens.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/parse-error-missing-parens.lint-test
@@ -0,0 +1,7 @@
+@media (missing: bracket {
+ body {
+ background-color: #fff;
+ }
+}
+~~~~~~~~~~
+error:1:8
diff --git a/src/lint/linter/__tests__/lessc/errors/percentage-missing-space.lint-test b/src/lint/linter/__tests__/lessc/errors/percentage-missing-space.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/percentage-missing-space.lint-test
@@ -0,0 +1,5 @@
+.a {
+ error: calc(1 %);
+}
+~~~~~~~~~~
+error:2:3
diff --git a/src/lint/linter/__tests__/lessc/errors/property-asterisk-only-name.lint-test b/src/lint/linter/__tests__/lessc/errors/property-asterisk-only-name.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/property-asterisk-only-name.lint-test
@@ -0,0 +1,5 @@
+a {
+ * : 1;
+}
+~~~~~~~~~~
+error:2:5
diff --git a/src/lint/linter/__tests__/lessc/errors/property-ie5-hack.lint-test b/src/lint/linter/__tests__/lessc/errors/property-ie5-hack.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/property-ie5-hack.lint-test
@@ -0,0 +1,5 @@
+.test {
+ display/*/: block; /*sorry for IE5*/
+}
+~~~~~~~~~~
+error:2:3
diff --git a/src/lint/linter/__tests__/lessc/errors/property-in-root.lint-test b/src/lint/linter/__tests__/lessc/errors/property-in-root.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/property-in-root.lint-test
@@ -0,0 +1,6 @@
+.a() {
+ prop:1;
+}
+.a();
+~~~~~~~~~~
+error:2:3
diff --git a/src/lint/linter/__tests__/lessc/errors/property-in-root3.lint-test b/src/lint/linter/__tests__/lessc/errors/property-in-root3.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/property-in-root3.lint-test
@@ -0,0 +1,6 @@
+prop:1;
+.a {
+ prop:1;
+}
+~~~~~~~~~~
+error:1:1
diff --git a/src/lint/linter/__tests__/lessc/errors/property-interp-not-defined.lint-test b/src/lint/linter/__tests__/lessc/errors/property-interp-not-defined.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/property-interp-not-defined.lint-test
@@ -0,0 +1,3 @@
+a {outline-@{color}: green}
+~~~~~~~~~~
+error:1:12
diff --git a/src/lint/linter/__tests__/lessc/errors/recursive-variable.lint-test b/src/lint/linter/__tests__/lessc/errors/recursive-variable.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/recursive-variable.lint-test
@@ -0,0 +1,3 @@
+@bodyColor: darken(@bodyColor, 30%);
+~~~~~~~~~~
+error:1:20
diff --git a/src/lint/linter/__tests__/lessc/errors/svg-gradient1.lint-test b/src/lint/linter/__tests__/lessc/errors/svg-gradient1.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/svg-gradient1.lint-test
@@ -0,0 +1,5 @@
+.a {
+ a: svg-gradient(horizontal, black, white);
+}
+~~~~~~~~~~
+error:2:6
diff --git a/src/lint/linter/__tests__/lessc/errors/svg-gradient2.lint-test b/src/lint/linter/__tests__/lessc/errors/svg-gradient2.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/svg-gradient2.lint-test
@@ -0,0 +1,5 @@
+.a {
+ a: svg-gradient(to bottom, black, orange, 45%, white);
+}
+~~~~~~~~~~
+error:2:6
diff --git a/src/lint/linter/__tests__/lessc/errors/svg-gradient3.lint-test b/src/lint/linter/__tests__/lessc/errors/svg-gradient3.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/svg-gradient3.lint-test
@@ -0,0 +1,5 @@
+.a {
+ a: svg-gradient(black, orange);
+}
+~~~~~~~~~~
+error:2:6
diff --git a/src/lint/linter/__tests__/lessc/errors/unit-function.lint-test b/src/lint/linter/__tests__/lessc/errors/unit-function.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/errors/unit-function.lint-test
@@ -0,0 +1,8 @@
+.a {
+ font-size: unit(80/16,rem);
+}
+~~~~~~~~~~
+error:2:14
+~~~~~~~~~~
+~~~~~~~~~~
+{"config":{"strict-math":true}}
diff --git a/src/lint/linter/__tests__/lessc/extend-chaining.lint-test b/src/lint/linter/__tests__/lessc/extend-chaining.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/extend-chaining.lint-test
@@ -0,0 +1,92 @@
+//very simple chaining
+.a {
+ color: black;
+}
+.b:extend(.a) {}
+.c:extend(.b) {}
+
+//very simple chaining, ordering not important
+
+.d:extend(.e) {}
+.e:extend(.f) {}
+.f {
+ color: black;
+}
+
+//extend with all
+
+.g.h {
+ color: black;
+}
+.i.j:extend(.g all) {
+ color: white;
+}
+.k:extend(.i all) {}
+
+//extend multi-chaining
+
+.l {
+ color: black;
+}
+.m:extend(.l){}
+.n:extend(.m){}
+.o:extend(.n){}
+.p:extend(.o){}
+.q:extend(.p){}
+.r:extend(.q){}
+.s:extend(.r){}
+.t:extend(.s){}
+
+// self referencing is ignored
+
+.u {color: black;}
+.v.u.v:extend(.u all){}
+
+// circular reference because the new extend product will match the existing extend
+
+.w:extend(.w) {color: black;}
+.v.w.v:extend(.w all){}
+
+// classic circular references
+
+.x:extend(.z) {
+ color: x;
+}
+.y:extend(.x) {
+ color: y;
+}
+.z:extend(.y) {
+ color: z;
+}
+
+//very simple chaining, but with the extend inside the ruleset
+.va {
+ color: black;
+}
+.vb {
+ &:extend(.va);
+ color: white;
+}
+.vc {
+ &:extend(.vb);
+}
+
+// media queries - dont extend outside, do extend inside
+
+@media tv {
+ .ma:extend(.a,.b,.c,.d,.e,.f,.g,.h,.i,.j,.k,.l,.m,.n,.o,.p,.q,.r,.s,.t,.u,.v,.w,.x,.y,.z,.md) {
+ color: black;
+ }
+ .md {
+ color: white;
+ }
+ @media plasma {
+ .me, .mf {
+ &:extend(.mb,.md);
+ background: red;
+ }
+ }
+}
+.mb:extend(.ma) {};
+.mc:extend(.mb) {};
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/extend-clearfix.lint-test b/src/lint/linter/__tests__/lessc/extend-clearfix.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/extend-clearfix.lint-test
@@ -0,0 +1,20 @@
+.clearfix {
+ *zoom: 1;
+ &:after {
+ content: '';
+ display: block;
+ clear: both;
+ height: 0;
+ }
+}
+
+.foo {
+ &:extend(.clearfix all);
+ color: red;
+}
+
+.bar {
+ &:extend(.clearfix all);
+ color: blue;
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/extend-exact.lint-test b/src/lint/linter/__tests__/lessc/extend-exact.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/extend-exact.lint-test
@@ -0,0 +1,47 @@
+.replace.replace,
+.c.replace + .replace {
+ .replace,
+ .c {
+ prop: copy-paste-replace;
+ }
+}
+.rep_ace:extend(.replace.replace .replace) {}
+
+.a .b .c {
+ prop: not_effected;
+}
+
+.a {
+ prop: is_effected;
+ .b {
+ prop: not_effected;
+ }
+ .b.c {
+ prop: not_effected;
+ }
+}
+
+.c, .a {
+ .b, .a {
+ .a, .c {
+ prop: not_effected;
+ }
+ }
+}
+
+.effected {
+ &:extend(.a);
+ &:extend(.b);
+ &:extend(.c);
+}
+
+.e {
+ && {
+ prop: extend-double;
+ &:hover {
+ hover: not-extended;
+ }
+ }
+}
+.dbl:extend(.e.e) {}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/extend-media.lint-test b/src/lint/linter/__tests__/lessc/extend-media.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/extend-media.lint-test
@@ -0,0 +1,25 @@
+.ext1 .ext2 {
+ background: black;
+}
+
+@media tv {
+ .ext1 .ext3 {
+ color: white;
+ }
+ .tv-lowres :extend(.ext1 all) {
+ background: blue;
+ }
+ @media hires {
+ .ext1 .ext4 {
+ color: green;
+ }
+ .tv-hires :extend(.ext1 all) {
+ background: red;
+ }
+ }
+}
+
+.all:extend(.ext1 all) {
+
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/extend-nest.lint-test b/src/lint/linter/__tests__/lessc/extend-nest.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/extend-nest.lint-test
@@ -0,0 +1,66 @@
+.sidebar {
+ width: 300px;
+ background: red;
+
+ .box {
+ background: #FFF;
+ border: 1px solid #000;
+ margin: 10px 0;
+ }
+}
+
+.sidebar2 {
+ &:extend(.sidebar all);
+ background: blue;
+}
+
+.type1 {
+ .sidebar3 {
+ &:extend(.sidebar all);
+ background: green;
+ }
+}
+
+.type2 {
+ &.sidebar4 {
+ &:extend(.sidebar all);
+ background: red;
+ }
+}
+
+.button {
+ color: black;
+ &:hover {
+ color: white;
+ }
+}
+.submit {
+ &:extend(.button);
+ &:hover:extend(.button:hover) {}
+}
+
+.nomatch {
+ &:hover:extend(.button :hover) {}
+}
+
+.button2 {
+ :hover {
+ nested: white;
+ }
+}
+.button2 :hover {
+ notnested: black;
+}
+
+.nomatch :extend(.button2:hover) {}
+
+.amp-test-a,
+.amp-test-b {
+ .amp-test-c &.amp-test-d&.amp-test-e {
+ .amp-test-f&+&.amp-test-g:extend(.amp-test-h) {}
+ }
+}
+.amp-test-h {
+ test: extended by masses of selectors;
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/extend-selector.lint-test b/src/lint/linter/__tests__/lessc/extend-selector.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/extend-selector.lint-test
@@ -0,0 +1,100 @@
+.error {
+ border: 1px #f00;
+ background: #fdd;
+}
+.error.intrusion {
+ font-size: 1.3em;
+ font-weight: bold;
+}
+.intrusion .error {
+ display: none;
+}
+.badError:extend(.error all) {
+ border-width: 3px;
+}
+
+.foo .bar, .foo .baz {
+ display: none;
+}
+
+.ext1 .ext2
+ :extend(.foo all) {
+}
+
+.ext3:extend(.foo all),
+.ext4:extend(.foo all) {
+}
+
+div.ext5,
+.ext6 > .ext5 {
+ width: 100px;
+}
+
+.should-not-exist-in-output,
+.ext7:extend(.ext5 all) {
+}
+
+.ext {
+ test: 1;
+}
+// same as
+// .a .c:extend(.ext all)
+// .b .c:extend(.ext all)
+// .a .c .d
+// .b .c .d
+.a, .b {
+ test: 2;
+ .c:extend(.ext all) {
+ test: 3;
+ .d {
+ test: 4;
+ }
+ }
+}
+
+.replace.replace,
+.c.replace + .replace {
+ .replace,
+ .c {
+ prop: copy-paste-replace;
+ }
+}
+.rep_ace:extend(.replace all) {}
+
+.attributes {
+ [data="test"] {
+ extend: attributes;
+ }
+ .attribute-test {
+ &:extend([data="test"] all);
+ }
+ [data] {
+ extend: attributes2;
+ }
+ .attribute-test2 {
+ &:extend([data] all); //you could argue it should match [data="test"]... not for now though...
+ }
+ @attr-data: "test3";
+ [data=@{attr-data}] {
+ extend: attributes2;
+ }
+ .attribute-test {
+ &:extend([data="test3"] all);
+ }
+}
+
+.header {
+ .header-nav {
+ background: red;
+ &:before {
+ background: blue;
+ }
+ }
+}
+
+.footer {
+ .footer-nav {
+ &:extend( .header .header-nav all );
+ }
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/extend.lint-test b/src/lint/linter/__tests__/lessc/extend.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/extend.lint-test
@@ -0,0 +1,82 @@
+.error {
+ border: 1px #f00;
+ background: #fdd;
+}
+.error.intrusion {
+ font-size: 1.3em;
+ font-weight: bold;
+}
+.intrusion .error {
+ display: none;
+}
+.badError {
+ &:extend(.error all);
+ border-width: 3px;
+}
+
+.foo .bar, .foo .baz {
+ display: none;
+}
+
+.ext1 .ext2 {
+ &:extend(.foo all);
+}
+
+.ext3,
+.ext4 {
+ &:extend(.foo all);
+ &:extend(.bar all);
+}
+
+div.ext5,
+.ext6 > .ext5 {
+ width: 100px;
+}
+
+.ext7 {
+ &:extend(.ext5 all);
+}
+
+.ext8.ext9 {
+ result: add-foo;
+}
+.ext8 .ext9,
+.ext8 + .ext9,
+.ext8 > .ext9 {
+ result: bar-matched;
+}
+.ext8.nomatch {
+ result: none;
+}
+.ext8 {
+ .ext9 {
+ result: match-nested-bar;
+ }
+}
+.ext8 {
+ &.ext9 {
+ result: match-nested-foo;
+ }
+}
+
+.fuu:extend(.ext8.ext9 all) {}
+.buu:extend(.ext8 .ext9 all) {}
+.zap:extend(.ext8 + .ext9 all) {}
+.zoo:extend(.ext8 > .ext9 all) {}
+
+.aa {
+ color: black;
+ .dd {
+ background: red;
+ }
+}
+.bb {
+ background: red;
+ .bb {
+ color: black;
+ }
+}
+.cc:extend(.aa,.bb) {}
+.ee:extend(.dd all,.bb) {}
+.ff:extend(.dd,.bb all) {}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/extract-and-length.lint-test b/src/lint/linter/__tests__/lessc/extract-and-length.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/extract-and-length.lint-test
@@ -0,0 +1,134 @@
+
+// simple array/list:
+
+.multiunit {
+ @v: abc "abc" 1 1px 1% #123;
+ length: length(@v);
+ extract: extract(@v, 1) extract(@v, 2) extract(@v, 3) extract(@v, 4) extract(@v, 5) extract(@v, 6);
+}
+
+.incorrect-index {
+ @v1: a b c;
+ @v2: a, b, c;
+ v1: extract(@v1, 5);
+ v2: extract(@v2, -2);
+}
+
+.scalar {
+ @var: variable;
+ var-value: extract(@var, 1);
+ var-length: length(@var);
+ ill-index: extract(@var, 2);
+
+ name-value: extract(name, 1);
+ string-value: extract("string", 1);
+ number-value: extract(12345678, 1);
+ color-value: extract(blue, 1);
+ rgba-value: extract(rgba(80, 160, 240, 0.67), 1);
+ empty-value: extract(~'', 1);
+
+ name-length: length(name);
+ string-length: length("string");
+ number-length: length(12345678);
+ color-length: length(blue);
+ rgba-length: length(rgba(80, 160, 240, 0.67));
+ empty-length: length(~'');
+}
+
+.mixin-arguments {
+ .mixin-args(a b c d);
+ .mixin-args(a, b, c, d);
+ .mixin-args(1; 2; 3; 4);
+}
+
+.mixin-args(@value) {
+ &-1 {
+ length: length(@value);
+ extract: extract(@value, 3) ~"|" extract(@value, 2) ~"|" extract(@value, 1);
+ }
+}
+
+.mixin-args(...) {
+ &-2 {
+ length: length(@arguments);
+ extract: extract(@arguments, 3) ~"|" extract(@arguments, 2) ~"|" extract(@arguments, 1);
+ }
+}
+
+.mixin-args(@values...) {
+ &-3 {
+ length: length(@values);
+ extract: extract(@values, 3) ~"|" extract(@values, 2) ~"|" extract(@values, 1);
+ }
+}
+
+.mixin-args(@head, @tail...) {
+ &-4 {
+ length: length(@tail);
+ extract: extract(@tail, 2) ~"|" extract(@tail, 1);
+ }
+}
+
+// "multidimensional" array/list
+
+.md-space-comma {
+ @v: a b c, 1 2 3, "x" "y" "z";
+ length-1: length(@v);
+ extract-1: extract(@v, 2);
+ length-2: length(extract(@v, 2));
+ extract-2: extract(extract(@v, 2), 2);
+
+ &-as-args {.mixin-args(a b c, 1 2 3, "x" "y" "z")}
+}
+
+.md-cat-space-comma {
+ @a: a b c;
+ @b: 1 2 3;
+ @c: "x" "y" "z";
+ @v: @a, @b, @c;
+ length-1: length(@v);
+ extract-1: extract(@v, 2);
+ length-2: length(extract(@v, 2));
+ extract-2: extract(extract(@v, 2), 2);
+
+ &-as-args {.mixin-args(@a, @b, @c)}
+}
+
+.md-cat-comma-space {
+ @a: a, b, c;
+ @b: 1, 2, 3;
+ @c: "x", "y", "z";
+ @v: @a @b @c;
+ length-1: length(@v);
+ extract-1: extract(@v, 2);
+ length-2: length(extract(@v, 2));
+ extract-2: extract(extract(@v, 2), 2);
+
+ &-as-args {.mixin-args(@a @b @c)}
+}
+
+.md-3D {
+ @a: a b c d, 1 2 3 4;
+ @b: 5 6 7 8, e f g h;
+ .3D(@a, @b);
+
+ .3D(...) {
+
+ @v1: @arguments;
+ length-1: length(@v1);
+ extract-1: extract(@v1, 1);
+
+ @v2: extract(@v1, 2);
+ length-2: length(@v2);
+ extract-2: extract(@v2, 1);
+
+ @v3: extract(@v2, 1);
+ length-3: length(@v3);
+ extract-3: extract(@v3, 3);
+
+ @v4: extract(@v3, 4);
+ length-4: length(@v4);
+ extract-4: extract(@v4, 1);
+ }
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/functions.lint-test b/src/lint/linter/__tests__/lessc/functions.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/functions.lint-test
@@ -0,0 +1,169 @@
+#functions {
+ @var: 10;
+ @colors: #000, #fff;
+ color: _color("evil red"); // #660000
+ width: increment(15);
+ height: undefined("self");
+ border-width: add(2, 3);
+ variable: increment(@var);
+ background: linear-gradient(@colors);
+}
+
+#built-in {
+ @r: 32;
+ escaped: e("-Some::weird(#thing, y)");
+ lighten: lighten(#ff0000, 40%);
+ darken: darken(#ff0000, 40%);
+ saturate: saturate(#29332f, 20%);
+ desaturate: desaturate(#203c31, 20%);
+ greyscale: greyscale(#203c31);
+ hsl-clamp: hsl(380, 150%, 150%);
+ spin-p: spin(hsl(340, 50%, 50%), 40);
+ spin-n: spin(hsl(30, 50%, 50%), -40);
+ luma-white: luma(#fff);
+ luma-black: luma(#000);
+ luma-black-alpha: luma(rgba(0,0,0,0.5));
+ luma-red: luma(#ff0000);
+ luma-green: luma(#00ff00);
+ luma-blue: luma(#0000ff);
+ luma-yellow: luma(#ffff00);
+ luma-cyan: luma(#00ffff);
+ luma-white-alpha: luma(rgba(255,255,255,0.5));
+ contrast-filter: contrast(30%);
+ saturate-filter: saturate(5%);
+ contrast-white: contrast(#fff);
+ contrast-black: contrast(#000);
+ contrast-red: contrast(#ff0000);
+ contrast-green: contrast(#00ff00);
+ contrast-blue: contrast(#0000ff);
+ contrast-yellow: contrast(#ffff00);
+ contrast-cyan: contrast(#00ffff);
+ contrast-light: contrast(#fff, #111111, #eeeeee);
+ contrast-dark: contrast(#000, #111111, #eeeeee);
+ contrast-wrongorder: contrast(#fff, #eeeeee, #111111, 0.5);
+ contrast-light-thresh: contrast(#fff, #111111, #eeeeee, 0.5);
+ contrast-dark-thresh: contrast(#000, #111111, #eeeeee, 0.5);
+ contrast-high-thresh: contrast(#555, #111111, #eeeeee, 0.6);
+ contrast-low-thresh: contrast(#555, #111111, #eeeeee, 0.1);
+ contrast-light-thresh-per: contrast(#fff, #111111, #eeeeee, 50%);
+ contrast-dark-thresh-per: contrast(#000, #111111, #eeeeee, 50%);
+ contrast-high-thresh-per: contrast(#555, #111111, #eeeeee, 60%);
+ contrast-low-thresh-per: contrast(#555, #111111, #eeeeee, 10%);
+ replace: replace("Hello, Mars.", "Mars\.", "World!");
+ replace-captured: replace("This is a string.", "(string)\.$", "new $1.");
+ replace-with-flags: replace("One + one = 4", "one", "2", "gi");
+ replace-single-quoted: replace('foo-1', "1", "2");
+ replace-escaped-string: replace(~"bar-1", "1", "2");
+ replace-keyword: replace(baz-1, "1", "2");
+ format: %("rgb(%d, %d, %d)", @r, 128, 64);
+ format-string: %("hello %s", "world");
+ format-multiple: %("hello %s %d", "earth", 2);
+ format-url-encode: %("red is %A", #ff0000);
+ format-single-quoted: %('hello %s', "single world");
+ format-escaped-string: %(~"hello %s", "escaped world");
+ eformat: e(%("rgb(%d, %d, %d)", @r, 128, 64));
+
+ unitless: unit(12px);
+ unit: unit((13px + 1px), em);
+ unitpercentage: unit(100, %);
+
+ get-unit: get-unit(10px);
+ get-unit-empty: get-unit(10);
+
+ hue: hue(hsl(98, 12%, 95%));
+ saturation: saturation(hsl(98, 12%, 95%));
+ lightness: lightness(hsl(98, 12%, 95%));
+ hsvhue: hsvhue(hsv(98, 12%, 95%));
+ hsvsaturation: hsvsaturation(hsv(98, 12%, 95%));
+ hsvvalue: hsvvalue(hsv(98, 12%, 95%));
+ red: red(#f00);
+ green: green(#0f0);
+ blue: blue(#00f);
+ rounded: round((@r/3));
+ rounded-two: round((@r/3), 2);
+ roundedpx: round((10px / 3));
+ roundedpx-three: round((10px / 3), 3);
+ rounded-percentage: round(10.2%);
+ ceil: ceil(10.1px);
+ floor: floor(12.9px);
+ sqrt: sqrt(25px);
+ pi: pi();
+ mod: mod(13m, 11cm); // could take into account units, doesn't at the moment
+ abs: abs(-4%);
+ tan: tan(42deg);
+ sin: sin(10deg);
+ cos: cos(12);
+ atan: atan(tan(0.1rad));
+ atan: convert(acos(cos(34deg)), deg);
+ atan: convert(acos(cos(50grad)), deg);
+ pow: pow(8px, 2);
+ pow: pow(4, 3);
+ pow: pow(3, 3em);
+ min: min(0);
+ min: min(6, 5);
+ min: min(1pt, 3pt);
+ min: min(1cm, 3mm);
+ max: max(1, 3);
+ max: max(3em, 1em, 2em, 5em);
+ percentage: percentage((10px / 50));
+ color: color("#ff0011");
+ tint: tint(#777777, 13);
+ tint-full: tint(#777777, 100);
+ tint-percent: tint(#777777, 13%);
+ tint-negative: tint(#777777, -13%);
+ shade: shade(#777777, 13);
+ shade-full: shade(#777777, 100);
+ shade-percent: shade(#777777, 13%);
+ shade-negative: shade(#777777, -13%);
+
+ fade-out: fadeOut(red, 5%); // support fadeOut and fadeout
+ fade-in: fadein(fadeout(red, 10%), 5%);
+
+ hsv: hsv(5, 50%, 30%);
+ hsva: hsva(3, 50%, 30%, 0.2);
+
+ mix: mix(#ff0000, #ffff00, 80);
+ mix-0: mix(#ff0000, #ffff00, 0);
+ mix-100: mix(#ff0000, #ffff00, 100);
+ mix-weightless: mix(#ff0000, #ffff00);
+ mixt: mix(#ff0000, transparent);
+
+ .is-a {
+ color: iscolor(#ddd);
+ color1: iscolor(red);
+ color2: iscolor(rgb(0, 0, 0));
+ color3: iscolor(transparent);
+ keyword: iskeyword(hello);
+ number: isnumber(32);
+ string: isstring("hello");
+ pixel: ispixel(32px);
+ percent: ispercentage(32%);
+ em: isem(32em);
+ cat: isunit(32cat, cat);
+ }
+}
+
+#alpha {
+ alpha: darken(hsla(25, 50%, 50%, 0.6), 10%);
+ alpha2: alpha(rgba(3, 4, 5, 0.5));
+ alpha3: alpha(transparent);
+}
+
+#blendmodes {
+ multiply: multiply(#f60000, #f60000);
+ screen: screen(#f60000, #0000f6);
+ overlay: overlay(#f60000, #0000f6);
+ softlight: softlight(#f60000, #ffffff);
+ hardlight: hardlight(#f60000, #0000f6);
+ difference: difference(#f60000, #0000f6);
+ exclusion: exclusion(#f60000, #0000f6);
+ average: average(#f60000, #0000f6);
+ negation: negation(#f60000, #313131);
+}
+
+#extract-and-length {
+ @anon: A B C 1 2 3;
+ extract: extract(@anon, 6) extract(@anon, 5) extract(@anon, 4) extract(@anon, 3) extract(@anon, 2) extract(@anon, 1);
+ length: length(@anon);
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/ie-filters.lint-test b/src/lint/linter/__tests__/lessc/ie-filters.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/ie-filters.lint-test
@@ -0,0 +1,16 @@
+@fat: 0;
+@cloudhead: "#000000";
+
+.nav {
+ filter: progid:DXImageTransform.Microsoft.Alpha(opacity = 20);
+ filter: progid:DXImageTransform.Microsoft.Alpha(opacity=@fat);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#333333", endColorstr=@cloudhead, GradientType=@fat);
+}
+.evalTest(@arg) {
+ filter: progid:DXImageTransform.Microsoft.Alpha(opacity=@arg);
+}
+.evalTest1 {
+ .evalTest(30);
+ .evalTest(5);
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/javascript.lint-test b/src/lint/linter/__tests__/lessc/javascript.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/javascript.lint-test
@@ -0,0 +1,30 @@
+.eval {
+ js: `42`;
+ js: `1 + 1`;
+ js: `"hello world"`;
+ js: `[1, 2, 3]`;
+ title: `typeof process.title`;
+ ternary: `(1 + 1 == 2 ? true : false)`;
+ multiline: `(function(){var x = 1 + 1;
+ return x})()`;
+}
+.scope {
+ @foo: 42;
+ var: `parseInt(this.foo.toJS())`;
+ escaped: ~`2 + 5 + 'px'`;
+}
+.vars {
+ @var: `4 + 4`;
+ width: @var;
+}
+.escape-interpol {
+ @world: "world";
+ width: ~`"hello" + " " + @{world}`;
+}
+.arrays {
+ @ary: 1, 2, 3;
+ @ary2: 1 2 3;
+ ary: `@{ary}.join(', ')`;
+ ary1: `@{ary2}.join(', ')`;
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/lazy-eval.lint-test b/src/lint/linter/__tests__/lessc/lazy-eval.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/lazy-eval.lint-test
@@ -0,0 +1,7 @@
+@var: @a;
+@a: 100%;
+
+.lazy-eval {
+ width: @var;
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/media.lint-test b/src/lint/linter/__tests__/lessc/media.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/media.lint-test
@@ -0,0 +1,235 @@
+
+// For now, variables can't be declared inside @media blocks.
+
+@var: 42;
+
+@media print {
+ .class {
+ color: blue;
+ .sub {
+ width: @var;
+ }
+ }
+ .top, header > h1 {
+ color: (#222 * 2);
+ }
+}
+
+@media screen {
+ @base: 8;
+ body { max-width: (@base * 60); }
+}
+
+@ratio_large: 16;
+@ratio_small: 9;
+
+@media all and (device-aspect-ratio: @ratio_large / @ratio_small) {
+ body { max-width: 800px; }
+}
+
+@media all and (orientation:portrait) {
+ aside { float: none; }
+}
+
+@media handheld and (min-width: @var), screen and (min-width: 20em) {
+ body {
+ max-width: 480px;
+ }
+}
+
+body {
+ @media print {
+ padding: 20px;
+
+ header {
+ background-color: red;
+ }
+
+ @media (orientation:landscape) {
+ margin-left: 20px;
+ }
+ }
+}
+
+@media screen {
+ .sidebar {
+ width: 300px;
+ @media (orientation: landscape) {
+ width: 500px;
+ }
+ }
+}
+
+@media a {
+ .first {
+ @media b {
+ .second {
+ .third {
+ width: 300px;
+ @media c {
+ width: 500px;
+ }
+ }
+ .fourth {
+ width: 3;
+ }
+ }
+ }
+ }
+}
+
+body {
+ @media a, b and c {
+ width: 95%;
+
+ @media x, y {
+ width: 100%;
+ }
+ }
+}
+
+.mediaMixin(@fallback: 200px) {
+ background: black;
+
+ @media handheld {
+ background: white;
+
+ @media (max-width: @fallback) {
+ background: red;
+ }
+ }
+}
+
+.a {
+ .mediaMixin(100px);
+}
+
+.b {
+ .mediaMixin();
+}
+@smartphone: ~"only screen and (max-width: 200px)";
+@media @smartphone {
+ body {
+ width: 480px;
+ }
+}
+
+@media print {
+ @page :left {
+ margin: 0.5cm;
+ }
+ @page :right {
+ margin: 0.5cm;
+ }
+ @page Test:first {
+ margin: 1cm;
+ }
+ @page :first {
+ size: 8.5in 11in;
+ @top-left {
+ margin: 1cm;
+ }
+ @top-left-corner {
+ margin: 1cm;
+ }
+ @top-center {
+ margin: 1cm;
+ }
+ @top-right {
+ margin: 1cm;
+ }
+ @top-right-corner {
+ margin: 1cm;
+ }
+ @bottom-left {
+ margin: 1cm;
+ }
+ @bottom-left-corner {
+ margin: 1cm;
+ }
+ @bottom-center {
+ margin: 1cm;
+ }
+ @bottom-right {
+ margin: 1cm;
+ }
+ @bottom-right-corner {
+ margin: 1cm;
+ }
+ @left-top {
+ margin: 1cm;
+ }
+ @left-middle {
+ margin: 1cm;
+ }
+ @left-bottom {
+ margin: 1cm;
+ }
+ @right-top {
+ margin: 1cm;
+ }
+ @right-middle {
+ content: "Page " counter(page);
+ }
+ @right-bottom {
+ margin: 1cm;
+ }
+ }
+}
+
+@media (-webkit-min-device-pixel-ratio: 2), (min--moz-device-pixel-ratio: 2), (-o-min-device-pixel-ratio: 2/1), (min-resolution: 2dppx), (min-resolution: 128dpcm) {
+ .b {
+ background: red;
+ }
+}
+
+.bg() {
+ background: red;
+
+ @media (max-width: 500px) {
+ background: green;
+ }
+}
+
+body {
+ .bg();
+}
+
+@bpMedium: 1000px;
+@media (max-width: @bpMedium) {
+ body {
+ .bg();
+ background: blue;
+ }
+}
+
+@media (max-width: 1200px) {
+ /* a comment */
+
+ @media (max-width: 900px) {
+ body { font-size: 11px; }
+ }
+}
+
+.nav-justified {
+ @media (min-width: 480px) {
+ > li {
+ display: table-cell;
+ }
+ }
+}
+
+.menu
+{
+ @media (min-width: 768px) {
+ .nav-justified();
+ }
+}
+@all: ~"all";
+@tv: ~"tv";
+@media @all and @tv {
+ .all-and-tv-variables {
+ var: all-and-tv;
+ }
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/merge.lint-test b/src/lint/linter/__tests__/lessc/merge.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/merge.lint-test
@@ -0,0 +1,60 @@
+.first-transform() {
+ transform+: rotate(90deg), skew(30deg);
+}
+.second-transform() {
+ transform+: scale(2,4);
+}
+.third-transform() {
+ transform: scaleX(45deg);
+}
+.fourth-transform() {
+ transform+: scaleX(45deg);
+}
+.fifth-transform() {
+ transform+: scale(2,4) !important;
+}
+.first-background() {
+ background+: url(data://img1.png);
+}
+.second-background() {
+ background+: url(data://img2.png);
+}
+
+.test1 {
+ // Can merge values
+ .first-transform();
+ .second-transform();
+}
+.test2 {
+ // Wont merge values without +: merge directive, for backwards compatibility with css
+ .first-transform();
+ .third-transform();
+}
+.test3 {
+ // Wont merge values from two sources with different properties
+ .fourth-transform();
+ .first-background();
+}
+.test4 {
+ // Wont merge values from sources that merked as !important, for backwards compatibility with css
+ .first-transform();
+ .fifth-transform();
+}
+.test5 {
+ // Wont merge values from mixins that merked as !important, for backwards compatibility with css
+ .first-transform();
+ .second-transform() !important;
+}
+.test6 {
+ // Ignores !merge if no peers found
+ .second-transform();
+}
+
+.test-interleaved {
+ transform+: t1;
+ background+: b1;
+ transform+: t2;
+ background+: b2, b3;
+ transform+: t3;
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/mixins-args.lint-test b/src/lint/linter/__tests__/lessc/mixins-args.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/mixins-args.lint-test
@@ -0,0 +1,216 @@
+.mixin (@a: 1px, @b: 50%) {
+ width: (@a * 5);
+ height: (@b - 1%);
+}
+
+.mixina (@style, @width, @color: black) {
+ border: @width @style @color;
+}
+
+.mixiny
+(@a: 0, @b: 0) {
+ margin: @a;
+ padding: @b;
+}
+
+.hidden() {
+ color: transparent; // asd
+}
+
+#hidden {
+ .hidden;
+}
+
+#hidden1 {
+ .hidden();
+}
+
+.two-args {
+ color: blue;
+ .mixin(2px, 100%);
+ .mixina(dotted, 2px);
+}
+
+.one-arg {
+ .mixin(3px);
+}
+
+.no-parens {
+ .mixin;
+}
+
+.no-args {
+ .mixin();
+}
+
+.var-args {
+ @var: 9;
+ .mixin(@var, (@var * 2));
+}
+
+.multi-mix {
+ .mixin(2px, 30%);
+ .mixiny(4, 5);
+}
+
+.maxa(@arg1: 10, @arg2: #f00) {
+ padding: (@arg1 * 2px);
+ color: @arg2;
+}
+
+body {
+ .maxa(15);
+}
+
+@glob: 5;
+.global-mixin(@a:2) {
+ width: (@glob + @a);
+}
+
+.scope-mix {
+ .global-mixin(3);
+}
+
+.nested-ruleset (@width: 200px) {
+ width: @width;
+ .column { margin: @width; }
+}
+.content {
+ .nested-ruleset(600px);
+}
+
+//
+
+.same-var-name2(@radius) {
+ radius: @radius;
+}
+.same-var-name(@radius) {
+ .same-var-name2(@radius);
+}
+#same-var-name {
+ .same-var-name(5px);
+}
+
+//
+
+.var-inside () {
+ @var: 10px;
+ width: @var;
+}
+#var-inside { .var-inside; }
+
+.mixin-arguments (@width: 0px, ...) {
+ border: @arguments;
+ width: @width;
+}
+
+.arguments {
+ .mixin-arguments(1px, solid, black);
+}
+.arguments2 {
+ .mixin-arguments();
+}
+.arguments3 {
+ .mixin-arguments;
+}
+
+.mixin-arguments2 (@width, @rest...) {
+ border: @arguments;
+ rest: @rest;
+ width: @width;
+}
+.arguments4 {
+ .mixin-arguments2(0, 1, 2, 3, 4);
+}
+
+// Edge cases
+
+.edge-case {
+ .mixin-arguments("{");
+}
+
+// Division vs. Literal Slash
+.border-radius(@r: 2px/5px) {
+ border-radius: @r;
+}
+.slash-vs-math {
+ .border-radius();
+ .border-radius(5px/10px);
+ .border-radius((3px * 2));
+}
+// semi-colon vs comma for delimiting
+
+.mixin-takes-one(@a) {
+ one: @a;
+}
+
+.mixin-takes-two(@a; @b) {
+ one: @a;
+ two: @b;
+}
+
+.comma-vs-semi-colon {
+ .mixin-takes-two(@a : a; @b : b, c);
+ .mixin-takes-two(@a : d, e; @b : f);
+ .mixin-takes-one(@a: g);
+ .mixin-takes-one(@a : h;);
+ .mixin-takes-one(i);
+ .mixin-takes-one(j;);
+ .mixin-takes-two(k, l);
+ .mixin-takes-one(m, n;);
+ .mixin-takes-two(o, p; q);
+ .mixin-takes-two(r, s; t;);
+}
+
+.mixin-conflict(@a:defA, @b:defB, @c:defC) {
+ three: @a, @b, @c;
+}
+
+.mixin-conflict(@a:defA, @b:defB, @c:defC, @d:defD) {
+ four: @a, @b, @c, @d;
+}
+
+#named-conflict {
+ .mixin-conflict(11, 12, 13, @a:a);
+ .mixin-conflict(@a:a, 21, 22, 23);
+}
+@a: 3px;
+.mixin-default-arg(@a: 1px, @b: @a, @c: @b) {
+ defaults: 1px 1px 1px;
+ defaults: 2px 2px 2px;
+}
+
+.test-mixin-default-arg {
+ .mixin-default-arg();
+ .mixin-default-arg(2px);
+}
+
+.mixin-comma-default1(@color; @padding; @margin: 2, 2, 2, 2) {
+ margin: @margin;
+}
+.selector {
+ .mixin-comma-default1(#33acfe; 4);
+}
+.mixin-comma-default2(@margin: 2, 2, 2, 2;) {
+ margin: @margin;
+}
+.selector2 {
+ .mixin-comma-default2();
+}
+.mixin-comma-default3(@margin: 2, 2, 2, 2) {
+ margin: @margin;
+}
+.selector3 {
+ .mixin-comma-default3(4,2,2,2);
+}
+
+.test-calling-one-arg-mixin(@a) {
+}
+
+.test-calling-one-arg-mixin(@a, @b, @rest...) {
+}
+
+div {
+ .test-calling-one-arg-mixin(1);
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/mixins-closure.lint-test b/src/lint/linter/__tests__/lessc/mixins-closure.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/mixins-closure.lint-test
@@ -0,0 +1,27 @@
+.scope {
+ @var: 99px;
+ .mixin () {
+ width: @var;
+ }
+}
+
+.class {
+ .scope > .mixin;
+}
+
+.overwrite {
+ @var: 0px;
+ .scope > .mixin;
+}
+
+.nested {
+ @var: 5px;
+ .mixin () {
+ width: @var;
+ }
+ .class {
+ @var: 10px;
+ .mixin;
+ }
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/mixins-guards-default-func.lint-test b/src/lint/linter/__tests__/lessc/mixins-guards-default-func.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/mixins-guards-default-func.lint-test
@@ -0,0 +1,196 @@
+
+// basics:
+
+guard-default-basic-1 {
+ .m(1) {case: 1}
+ .m(@x) when (default()) {default: @x}
+
+ &-1 {.m(1)}
+ &-2 {.m(2)}
+}
+
+guard-default-basic-2 {
+ .m(1) {case: 1}
+ .m(2) {case: 2}
+ .m(3) {case: 3}
+ .m(@x) when (default()) {default: @x}
+
+ &-0 {.m(0)}
+ &-2 {.m(2)}
+}
+
+guard-default-basic-3 {
+ .m(@x) when (@x = 1) {case: 1}
+ .m(2) {case: 2}
+ .m(@x) when (@x = 3) {case: 3}
+ .m(@x) when (default()) {default: @x}
+
+ &-0 {.m(0)}
+ &-2 {.m(2)}
+ &-3 {.m(3)}
+}
+
+guard-default-definition-order {
+ .m(@x) when (default()) {default: @x}
+ .m(@x) when (@x = 1) {case: 1}
+ .m(2) {case: 2}
+ .m(@x) when (@x = 3) {case: 3}
+
+ &-0 {.m(0)}
+ &-2 {.m(2)}
+ &-2 {.m(3)}
+}
+
+// out of guard:
+
+guard-default-out-of-guard {
+ .m(1) {case-1: 1}
+ .m(@x: default()) when (default()) {default: @x}
+
+ &-0 {
+ case-0: default();
+ .m(1);
+ .m(2);
+ case-2: default();
+ }
+ &-1 {.m(default())}
+ &-2 {.m()}
+}
+
+// expressions:
+
+guard-default-expr-not {
+ .m(1) {case: 1}
+ .m(@x) when not(default()) {default: @x}
+
+ &-1 {.m(1)}
+ &-2 {.m(2)}
+}
+
+guard-default-expr-eq {
+ .m(@x) when (@x = true) {case: @x}
+ .m(@x) when (@x = false) {case: @x}
+ .m(@x) when (@x = default()) {default: @x}
+
+ &-true {.m(true)}
+ &-false {.m(false)}
+}
+
+guard-default-expr-or {
+ .m(1) {case: 1}
+ .m(2) {case: 2}
+ .m(@x) when (default()), (@x = 2) {default: @x}
+
+ &-1 {.m(1)}
+ &-2 {.m(2)}
+ &-3 {.m(3)}
+}
+
+guard-default-expr-and {
+ .m(1) {case: 1}
+ .m(2) {case: 2}
+ .m(@x) when (default()) and (@x = 3) {default: @x}
+
+ &-1 {.m(1)}
+ &-2 {.m(2)}
+ &-3 {.m(3)}
+ &-4 {.m(4)}
+}
+
+guard-default-expr-always {
+ .m(1) {case: 1}
+ .m(@x) when (default()), not(default()) {default: @x} // always match
+
+ &-1 {.m(1)}
+ &-2 {.m(2)}
+}
+
+guard-default-expr-never {
+ .m(1) {case: 1}
+ .m(@x) when (default()) and not(default()) {default: @x} // never match
+
+ &-1 {.m(1)}
+ &-2 {.m(2)}
+}
+
+
+// not conflicting multiple default() uses:
+
+guard-default-multi-1 {
+ .m(0) {case: 0}
+ .m(@x) when (default()) {default-1: @x}
+ .m(2) when (default()) {default-2: @x}
+
+ &-0 {.m(0)}
+ &-1 {.m(1)}
+}
+
+guard-default-multi-2 {
+ .m(1, @x) when (default()) {default-1: @x}
+ .m(2, @x) when (default()) {default-2: @x}
+ .m(@x, yes) when (default()) {default-3: @x}
+
+ &-1 {.m(1, no)}
+ &-2 {.m(2, no)}
+ &-3 {.m(3, yes)}
+}
+
+guard-default-multi-3 {
+ .m(red) {case-1: darkred}
+ .m(blue) {case-2: darkblue}
+ .m(@x) when (iscolor(@x)) and (default()) {default-color: @x}
+ .m('foo') {case-1: I am 'foo'}
+ .m('bar') {case-2: I am 'bar'}
+ .m(@x) when (isstring(@x)) and (default()) {default-string: I am @x}
+
+ &-blue {.m(blue)}
+ &-green {.m(green)}
+ &-foo {.m('foo')}
+ &-baz {.m('baz')}
+}
+
+guard-default-multi-4 {
+ .m(@x) when (default()), not(default()) {always: @x}
+ .m(@x) when (default()) and not(default()) {never: @x}
+ .m(2) {case: 2}
+
+ .m(1);
+ .m(2);
+}
+
+guard-default-not-ambiguos-2 {
+ .m(@x) {case: 1}
+ .m(@x) when (default()) {default: @x}
+ .m(@x) when not(default()) {not-default: @x}
+
+ .m(2);
+}
+
+guard-default-not-ambiguos-3 {
+ .m(@x) {case: 1}
+ .m(@x) when not(default()) {not-default-1: @x}
+ .m(@x) when not(default()) {not-default-2: @x}
+
+ .m(2);
+}
+
+// default & scope
+
+guard-default-scopes {
+ .s1() {.m(@v) {1: no condition}}
+ .s2() {.m(@v) when (@v) {2: when true}}
+ .s3() {.m(@v) when (default()) {3: when default}}
+
+ &-3 {
+ .s2();
+ .s3();
+ .m(false);
+ }
+
+ &-1 {
+ .s1();
+ .s3();
+ .m(false);
+ }
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/mixins-guards.lint-test b/src/lint/linter/__tests__/lessc/mixins-guards.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/mixins-guards.lint-test
@@ -0,0 +1,154 @@
+
+// Stacking, functions..
+
+.light (@a) when (lightness(@a) > 50%) {
+ color: white;
+}
+.light (@a) when (lightness(@a) < 50%) {
+ color: black;
+}
+.light (@a) {
+ margin: 1px;
+}
+
+.light1 { .light(#ddd) }
+.light2 { .light(#444) }
+
+// Arguments against each other
+
+.max (@a, @b) when (@a > @b) {
+ width: @a;
+}
+.max (@a, @b) when (@a < @b) {
+ width: @b;
+}
+
+.max1 { .max(3, 6) }
+.max2 { .max(8, 1) }
+
+// Globals inside guards
+
+@g: auto;
+
+.glob (@a) when (@a = @g) {
+ margin: @a @g;
+}
+.glob1 { .glob(auto) }
+
+// Other operators
+
+.ops (@a) when (@a >= 0) {
+ height: gt-or-eq;
+}
+.ops (@a) when (@a =< 0) {
+ height: lt-or-eq;
+}
+.ops (@a) when (@a <= 0) {
+ height: lt-or-eq-alias;
+}
+.ops (@a) when not(@a = 0) {
+ height: not-eq;
+}
+.ops1 { .ops(0) }
+.ops2 { .ops(1) }
+.ops3 { .ops(-1) }
+
+// Scope and default values
+
+@a: auto;
+
+.default (@a: inherit) when (@a = inherit) {
+ content: default;
+}
+.default1 { .default }
+
+// true & false keywords
+.test (@a) when (@a) {
+ content: "true.";
+}
+.test (@a) when not (@a) {
+ content: "false.";
+}
+
+.test1 { .test(true) }
+.test2 { .test(false) }
+.test3 { .test(1) }
+.test4 { .test(boo) }
+.test5 { .test("true") }
+
+// Boolean expressions
+
+.bool () when (true) and (false) { content: true and false } // FALSE
+.bool () when (true) and (true) { content: true and true } // TRUE
+.bool () when (true) { content: true } // TRUE
+.bool () when (false) and (false) { content: true } // FALSE
+.bool () when (false), (true) { content: false, true } // TRUE
+.bool () when (false) and (true) and (true), (true) { content: false and true and true, true } // TRUE
+.bool () when (true) and (true) and (false), (false) { content: true and true and false, false } // FALSE
+.bool () when (false), (true) and (true) { content: false, true and true } // TRUE
+.bool () when (false), (false), (true) { content: false, false, true } // TRUE
+.bool () when (false), (false) and (true), (false) { content: false, false and true, false } // FALSE
+.bool () when (false), (true) and (true) and (true), (false) { content: false, true and true and true, false } // TRUE
+.bool () when not (false) { content: not false }
+.bool () when not (true) and not (false) { content: not true and not false }
+.bool () when not (true) and not (true) { content: not true and not true }
+.bool () when not (false) and (false), not (false) { content: not false and false, not false }
+
+.bool1 { .bool }
+
+.equality-unit-test(@num) when (@num = 1%) {
+ test: fail;
+}
+.equality-unit-test(@num) when (@num = 2) {
+ test: pass;
+}
+.equality-units {
+ .equality-unit-test(1px);
+ .equality-unit-test(2px);
+}
+
+.colorguard(@col) when (@col = red) { content: is @col; }
+.colorguard(@col) when not (blue = @col) { content: is not blue its @col; }
+.colorguard(@col) {}
+.colorguardtest {
+ .colorguard(red);
+ .colorguard(blue);
+ .colorguard(purple);
+}
+
+.stringguard(@str) when (@str = "theme1") { content: is theme1; }
+.stringguard(@str) when not ("theme2" = @str) { content: is not theme2; }
+.stringguard(@str) when (~"theme1" = @str) { content: is theme1 no quotes; }
+.stringguard(@str) {}
+.stringguardtest {
+ .stringguard("theme1");
+ .stringguard("theme2");
+ .stringguard(theme1);
+}
+
+.mixin(...) {
+ catch:all;
+}
+.mixin(@var) when (@var=4) {
+ declare: 4;
+}
+.mixin(@var) when (@var=4px) {
+ declare: 4px;
+}
+#tryNumberPx {
+ .mixin(4px);
+}
+
+.lock-mixin(@a) {
+ .inner-locked-mixin(@x: @a) when (@a = 1) {
+ a: @a;
+ x: @x;
+ }
+}
+.call-lock-mixin {
+ .lock-mixin(1);
+ .call-inner-lock-mixin {
+ .inner-locked-mixin();
+ }
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/mixins-important.lint-test b/src/lint/linter/__tests__/lessc/mixins-important.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/mixins-important.lint-test
@@ -0,0 +1,26 @@
+.submixin(@a) {
+ border-width: @a;
+}
+.mixin (9) {
+ border: 9 !important;
+}
+.mixin (@a: 0) {
+ border: @a;
+ boxer: @a;
+ .inner {
+ test: @a;
+ }
+ // comment
+ .submixin(@a);
+}
+
+.class {
+ .mixin(1);
+ .mixin(2) !important;
+ .mixin(3);
+ .mixin(4) !important;
+ .mixin(5);
+ .mixin !important;
+ .mixin(9);
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/mixins-interpolated.lint-test b/src/lint/linter/__tests__/lessc/mixins-interpolated.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/mixins-interpolated.lint-test
@@ -0,0 +1,70 @@
+
+@a1: foo;
+@a2: ~".foo";
+@a4: ~"#foo";
+
+.@{a1} {
+ a: 1;
+}
+
+@{a2} {
+ a: 2;
+}
+
+#@{a1} {
+ a: 3;
+}
+
+@{a4} {
+ a: 4;
+}
+
+mi-test-a {
+ .foo;
+ #foo;
+}
+
+.b .bb {
+ &.@{a1}-xxx .yyy-@{a1}@{a4} {
+ & @{a2}.bbb {
+ b: 1;
+ }
+ }
+}
+
+mi-test-b {
+ .b.bb.foo-xxx.yyy-foo#foo.foo.bbb;
+}
+
+@c1: @a1;
+@c2: bar;
+@c3: baz;
+
+#@{c1}-foo {
+ > .@{c2} {
+ .@{c3} {
+ c: c;
+ }
+ }
+}
+
+mi-test-c {
+ &-1 {#foo-foo;}
+ &-2 {#foo-foo > .bar;}
+ &-3 {#foo-foo > .bar.baz;}
+}
+
+.Person(@name, @gender_) {
+ .@{name} {
+ @gender: @gender_;
+ .sayGender() {
+ gender: @gender;
+ }
+ }
+}
+
+mi-test-d {
+ .Person(person, "Male");
+ .person.sayGender();
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/mixins-named-args.lint-test b/src/lint/linter/__tests__/lessc/mixins-named-args.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/mixins-named-args.lint-test
@@ -0,0 +1,37 @@
+.mixin (@a: 1px, @b: 50%) {
+ width: (@a * 5);
+ height: (@b - 1%);
+ args: @arguments;
+}
+.mixin (@a: 1px, @b: 50%) when (@b > 75%){
+ text-align: center;
+}
+
+.named-arg {
+ color: blue;
+ .mixin(@b: 100%);
+}
+
+.class {
+ @var: 20%;
+ .mixin(@b: @var);
+}
+
+.all-args-wrong-args {
+ .mixin(@b: 10%, @a: 2px);
+}
+
+.mixin2 (@a: 1px, @b: 50%, @c: 50) {
+ width: (@a * 5);
+ height: (@b - 1%);
+ color: (#000000 + @c);
+}
+
+.named-args2 {
+ .mixin2(3px, @c: 100);
+}
+
+.named-args3 {
+ .mixin2(@b: 30%, @c: #123456);
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/mixins-nested.lint-test b/src/lint/linter/__tests__/lessc/mixins-nested.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/mixins-nested.lint-test
@@ -0,0 +1,23 @@
+.mix-inner (@var) {
+ border-width: @var;
+}
+
+.mix (@a: 10) {
+ .inner {
+ height: (@a * 10);
+
+ .innest {
+ width: @a;
+ .mix-inner((@a * 2));
+ }
+ }
+}
+
+.class {
+ .mix(30);
+}
+
+.class2 {
+ .mix(60);
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/mixins-pattern.lint-test b/src/lint/linter/__tests__/lessc/mixins-pattern.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/mixins-pattern.lint-test
@@ -0,0 +1,103 @@
+.mixin (...) {
+ variadic: true;
+}
+.mixin (@a...) {
+ named-variadic: true;
+}
+.mixin () {
+ zero: 0;
+}
+.mixin (@a: 1px) {
+ one: 1;
+}
+.mixin (@a) {
+ one-req: 1;
+}
+.mixin (@a: 1px, @b: 2px) {
+ two: 2;
+}
+
+.mixin (@a, @b, @c) {
+ three-req: 3;
+}
+
+.mixin (@a: 1px, @b: 2px, @c: 3px) {
+ three: 3;
+}
+
+.zero {
+ .mixin();
+}
+
+.one {
+ .mixin(1);
+}
+
+.two {
+ .mixin(1, 2);
+}
+
+.three {
+ .mixin(1, 2, 3);
+}
+
+//
+
+.mixout ('left') {
+ left: 1;
+}
+
+.mixout ('right') {
+ right: 1;
+}
+
+.left {
+ .mixout('left');
+}
+.right {
+ .mixout('right');
+}
+
+//
+
+.border (@side, @width) {
+ color: black;
+ .border-side(@side, @width);
+}
+.border-side (left, @w) {
+ border-left: @w;
+}
+.border-side (right, @w) {
+ border-right: @w;
+}
+
+.border-right {
+ .border(right, 4px);
+}
+.border-left {
+ .border(left, 4px);
+}
+
+//
+
+
+.border-radius (@r) {
+ both: (@r * 10);
+}
+.border-radius (@r, left) {
+ left: @r;
+}
+.border-radius (@r, right) {
+ right: @r;
+}
+
+.only-right {
+ .border-radius(33, right);
+}
+.only-left {
+ .border-radius(33, left);
+}
+.left-right {
+ .border-radius(33);
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/mixins.lint-test b/src/lint/linter/__tests__/lessc/mixins.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/mixins.lint-test
@@ -0,0 +1,142 @@
+.mixin { border: 1px solid black; }
+.mixout { border-color: orange; }
+.borders { border-style: dashed; }
+
+#namespace {
+ .borders {
+ border-style: dotted;
+ }
+ .biohazard {
+ content: "death";
+ .man {
+ color: transparent;
+ }
+ }
+}
+#theme {
+ > .mixin {
+ background-color: grey;
+ }
+}
+#container {
+ color: black;
+ .mixin;
+ .mixout;
+ #theme > .mixin;
+}
+
+#header {
+ .milk {
+ color: white;
+ .mixin;
+ #theme > .mixin;
+ }
+ #cookie {
+ .chips {
+ #namespace .borders;
+ .calories {
+ #container;
+ }
+ }
+ .borders;
+ }
+}
+.secure-zone { #namespace .biohazard .man; }
+.direct {
+ #namespace > .borders;
+}
+
+.bo, .bar {
+ width: 100%;
+}
+.bo {
+ border: 1px;
+}
+.ar.bo.ca {
+ color: black;
+}
+.jo.ki {
+ background: none;
+}
+.amp {
+ &.support {
+ color: orange;
+ .higher {
+ top: 0px;
+ }
+ &.deeper {
+ height: auto;
+ }
+ }
+}
+.extended {
+ .bo;
+ .jo.ki;
+ .amp.support;
+ .amp.support.higher;
+ .amp.support.deeper;
+}
+.do .re .mi .fa {
+ .sol .la {
+ .si {
+ color: cyan;
+ }
+ }
+}
+.mutli-selector-parents {
+ .do.re.mi.fa.sol.la.si;
+}
+.foo .bar {
+ .bar;
+}
+.has_parents() {
+ & .underParents {
+ color: red;
+ }
+}
+.has_parents();
+.parent {
+ .has_parents();
+}
+.margin_between(@above, @below) {
+ * + & { margin-top: @above; }
+ legend + & { margin-top: 0; }
+ & + * { margin-top: @below; }
+}
+h1 { .margin_between(25px, 10px); }
+h2 { .margin_between(20px, 8px); }
+h3 { .margin_between(15px, 5px); }
+
+.mixin_def(@url, @position){
+ background-image: @url;
+ background-position: @position;
+}
+.error{
+ @s: "/";
+ .mixin_def( "@{s}a.png", center center);
+}
+.recursion() {
+ color: black;
+}
+.test-rec {
+ .recursion {
+ .recursion();
+ }
+}
+.paddingFloat(@padding) { padding-left: @padding; }
+
+.button {
+ .paddingFloat(((10px + 12) * 2));
+
+ &.large { .paddingFloat(((10em * 2) * 2)); }
+}
+.clearfix() {
+ // ...
+}
+.clearfix {
+ .clearfix();
+}
+.foo {
+ .clearfix();
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/no-output.lint-test b/src/lint/linter/__tests__/lessc/no-output.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/no-output.lint-test
@@ -0,0 +1,3 @@
+.mixin() {
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/operations.lint-test b/src/lint/linter/__tests__/lessc/operations.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/operations.lint-test
@@ -0,0 +1,63 @@
+#operations {
+ color: (#110000 + #000011 + #001100); // #111111
+ height: (10px / 2px + 6px - 1px * 2); // 9px
+ width: (2 * 4 - 5em); // 3em
+ .spacing {
+ height: (10px / 2px+6px-1px*2);
+ width: (2 * 4-5em);
+ }
+ substraction: (20 - 10 - 5 - 5); // 0
+ division: (20 / 5 / 4); // 1
+}
+
+@x: 4;
+@y: 12em;
+
+.with-variables {
+ height: (@x + @y); // 16em
+ width: (12 + @y); // 24em
+ size: (5cm - @x); // 1cm
+}
+
+.with-functions {
+ color: (rgb(200, 200, 200) / 2);
+ color: (2 * hsl(0, 50%, 50%));
+ color: (rgb(10, 10, 10) + hsl(0, 50%, 50%));
+}
+
+@z: -2;
+
+.negative {
+ height: (2px + @z); // 0px
+ width: (2px - @z); // 4px
+}
+
+.shorthands {
+ padding: -1px 2px 0 -4px; //
+}
+
+.rem-dimensions {
+ font-size: (20rem / 5 + 1.5rem); // 5.5rem
+}
+
+.colors {
+ color: #123; // #112233
+ border-color: (#234 + #111111); // #334455
+ background-color: (#222222 - #fff); // #000000
+ .other {
+ color: (2 * #111); // #222222
+ border-color: (#333333 / 3 + #111); // #222222
+ }
+}
+
+.negations {
+ @var: 4px;
+ variable: (-@var); // 4
+ variable1: (-@var + @var); // 0
+ variable2: (@var + -@var); // 0
+ variable3: (@var - -@var); // 8
+ variable4: (-@var - -@var); // 0
+ paren: (-(@var)); // -4px
+ paren2: (-(2 + 2) * -@var); // 16
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/parens.lint-test b/src/lint/linter/__tests__/lessc/parens.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/parens.lint-test
@@ -0,0 +1,46 @@
+.parens {
+ @var: 1px;
+ border: (@var * 2) solid black;
+ margin: (@var * 1) (@var + 2) (4 * 4) 3;
+ width: (6 * 6);
+ padding: 2px (6 * 6px);
+}
+
+.more-parens {
+ @var: (2 * 2);
+ padding: (2 * @var) 4 4 (@var * 1px);
+ width-all: ((@var * @var) * 6);
+ width-first: ((@var * @var)) * 6;
+ width-keep: (@var * @var) * 6;
+ height-keep: (7 * 7) + (8 * 8);
+ height-all: ((7 * 7) + (8 * 8));
+ height-parts: ((7 * 7)) + ((8 * 8));
+ margin-keep: (4 * (5 + 5) / 2) - (@var * 2);
+ margin-parts: ((4 * (5 + 5) / 2)) - ((@var * 2));
+ margin-all: ((4 * (5 + 5) / 2) + (-(@var * 2)));
+ border-radius-keep: 4px * (1 + 1) / @var + 3px;
+ border-radius-parts: ((4px * (1 + 1))) / ((@var + 3px));
+ border-radius-all: (4px * (1 + 1) / @var + 3px);
+ //margin: (6 * 6)px;
+}
+
+.negative {
+ @var: 1;
+ neg-var: -@var; // -1 ?
+ neg-var-paren: -(@var); // -(1) ?
+}
+
+.nested-parens {
+ width: 2 * (4 * (2 + (1 + 6))) - 1;
+ height: ((2 + 3) * (2 + 3) / (9 - 4)) + 1;
+}
+
+.mixed-units {
+ margin: 2px 4em 1 5pc;
+ padding: (2px + 4px) 1em 2px 2;
+}
+
+.test-false-negatives {
+ a: ~"(";
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/property-name-interp.lint-test b/src/lint/linter/__tests__/lessc/property-name-interp.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/property-name-interp.lint-test
@@ -0,0 +1,42 @@
+
+test {
+ @prefix: ufo-;
+ @a: border;
+ @bb: top;
+ @c_c: left;
+ @d-d4: radius;
+ @-: -;
+
+ @{a}: 0;
+ @{prefix}width: 50%;
+ *-z-@{a} :1px dashed blue;
+ -www-@{a}-@{bb}: 2px;
+ @{d-d4}-is-not-a-@{a}:true;
+ @{a}-@{bb}-@{c_c}-@{d-d4} : 2em;
+ @{a}@{-}@{bb}@{-}red@{-}@{d-d4}-: 3pt;
+
+ .mixin(mixer);
+ .merge(ish, base);
+}
+
+@global: global;
+
+.mixin(@arg) {
+ @local: local;
+ @{global}-@{local}-@{arg}-property: strong;
+}
+
+.merge(@p, @v) {
+ &-merge {
+ @prefix: pre;
+ @suffix: ish;
+ @{prefix}-property-ish+ :high;
+ pre-property-@{suffix} +: middle;
+ @{prefix}-property-@{suffix}+: low;
+ @{prefix}-property-@{p} + : @v;
+
+ @subterfuge: ~'+';
+ pre-property-ish@{subterfuge}: nice try dude;
+ }
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/rulesets.lint-test b/src/lint/linter/__tests__/lessc/rulesets.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/rulesets.lint-test
@@ -0,0 +1,31 @@
+#first > .one {
+ > #second .two > #deux {
+ width: 50%;
+ #third {
+ &:focus {
+ color: black;
+ #fifth {
+ > #sixth {
+ .seventh #eighth {
+ + #ninth {
+ color: purple;
+ }
+ }
+ }
+ }
+ }
+ height: 100%;
+ }
+ #fourth, #five, #six {
+ color: #110000;
+ .seven, .eight > #nine {
+ border: 1px solid black;
+ }
+ #ten {
+ color: red;
+ }
+ }
+ }
+ font-size: 2em;
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/scope.lint-test b/src/lint/linter/__tests__/lessc/scope.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/scope.lint-test
@@ -0,0 +1,105 @@
+@x: red;
+@x: blue;
+@z: transparent;
+@mix: none;
+
+.mixin {
+ @mix: #989;
+}
+@mix: blue;
+.tiny-scope {
+ color: @mix; // #989
+ .mixin;
+}
+
+.scope1 {
+ @y: orange;
+ @z: black;
+ color: @x; // blue
+ border-color: @z; // black
+ .hidden {
+ @x: #131313;
+ }
+ .scope2 {
+ @y: red;
+ color: @x; // blue
+ .scope3 {
+ @local: white;
+ color: @y; // red
+ border-color: @z; // black
+ background-color: @local; // white
+ }
+ }
+}
+
+#namespace {
+ .scoped_mixin() {
+ @local-will-be-made-global: green;
+ .scope {
+ scoped-val: @local-will-be-made-global;
+ }
+ }
+}
+
+#namespace > .scoped_mixin();
+
+.setHeight(@h) { @height: 1024px; }
+.useHeightInMixinCall(@h) { .useHeightInMixinCall { mixin-height: @h; } }
+@mainHeight: 50%;
+.setHeight(@mainHeight);
+.heightIsSet { height: @height; }
+.useHeightInMixinCall(@height);
+
+.importRuleset() {
+ .imported {
+ exists: true;
+ }
+}
+.importRuleset();
+.testImported {
+ .imported;
+}
+
+@parameterDefault: 'top level';
+@anotherVariable: 'top level';
+//mixin uses top-level variables
+.mixinNoParam(@parameter: @parameterDefault) when (@parameter = 'top level') {
+ default: @parameter;
+ scope: @anotherVariable;
+ sub-scope-only: @subScopeOnly;
+}
+
+#allAreUsedHere {
+ //redefine top-level variables in different scope
+ @parameterDefault: 'inside';
+ @anotherVariable: 'inside';
+ @subScopeOnly: 'inside';
+ //use the mixin
+ .mixinNoParam();
+}
+#parentSelectorScope {
+ @col: white;
+ & {
+ @col: black;
+ }
+ prop: @col;
+ & {
+ @col: black;
+ }
+}
+.test-empty-mixin() {
+}
+#parentSelectorScopeMixins {
+ & {
+ .test-empty-mixin() {
+ should: never seee 1;
+ }
+ }
+ .test-empty-mixin();
+ & {
+ .test-empty-mixin() {
+ should: never seee 2;
+ }
+ }
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/selectors.lint-test b/src/lint/linter/__tests__/lessc/selectors.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/selectors.lint-test
@@ -0,0 +1,157 @@
+h1, h2, h3 {
+ a, p {
+ &:hover {
+ color: red;
+ }
+ }
+}
+
+#all { color: blue; }
+#the { color: blue; }
+#same { color: blue; }
+
+ul, li, div, q, blockquote, textarea {
+ margin: 0;
+}
+
+td {
+ margin: 0;
+ padding: 0;
+}
+
+td, input {
+ line-height: 1em;
+}
+
+a {
+ color: red;
+
+ &:hover { color: blue; }
+
+ div & { color: green; }
+
+ p & span { color: yellow; }
+}
+
+.foo {
+ .bar, .baz {
+ & .qux {
+ display: block;
+ }
+ .qux & {
+ display: inline;
+ }
+ .qux& {
+ display: inline-block;
+ }
+ .qux & .biz {
+ display: none;
+ }
+ }
+}
+
+.b {
+ &.c {
+ .a& {
+ color: red;
+ }
+ }
+}
+
+.b {
+ .c & {
+ &.a {
+ color: red;
+ }
+ }
+}
+
+.p {
+ .foo &.bar {
+ color: red;
+ }
+}
+
+.p {
+ .foo&.bar {
+ color: red;
+ }
+}
+
+.foo {
+ .foo + & {
+ background: amber;
+ }
+ & + & {
+ background: amber;
+ }
+}
+
+.foo, .bar {
+ & + & {
+ background: amber;
+ }
+}
+
+.foo, .bar {
+ a, b {
+ & > & {
+ background: amber;
+ }
+ }
+}
+
+.other ::fnord { color: red }
+.other::fnord { color: red }
+.other {
+ ::bnord {color: red }
+ &::bnord {color: red }
+}
+// selector interpolation
+@theme: blood;
+@selector: ~".@{theme}";
+@{selector} {
+ color:red;
+}
+@{selector}red {
+ color: green;
+}
+.red {
+ #@{theme}.@{theme}&.black {
+ color:black;
+ }
+}
+@num: 3;
+:nth-child(@{num}) {
+ selector: interpolated;
+}
+.test {
+ &:nth-child(odd):not(:nth-child(3)) {
+ color: #ff0000;
+ }
+}
+[prop],
+[prop=10%],
+[prop="value@{num}"],
+[prop*="val@{num}"],
+[|prop~="val@{num}"],
+[*|prop$="val@{num}"],
+[ns|prop^="val@{num}"],
+[@{num}^="val@{num}"],
+[@{num}=@{num}],
+[@{num}] {
+ attributes: yes;
+}
+
+/*
+Large comment means chunk will be emitted after } which means chunk will begin with whitespace...
+blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank
+blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank
+blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank
+blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank
+blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank
+*/
+@{selector} {
+ color: red;
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/strings.lint-test b/src/lint/linter/__tests__/lessc/strings.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/strings.lint-test
@@ -0,0 +1,58 @@
+#strings {
+ background-image: url("http://son-of-a-banana.com");
+ quotes: "~" "~";
+ content: "#*%:&^,)!.(~*})";
+ empty: "";
+ brackets: "{" "}";
+ escapes: "\"hello\" \\world";
+ escapes2: "\"llo";
+}
+#comments {
+ content: "/* hello */ // not-so-secret";
+}
+#single-quote {
+ quotes: "'" "'";
+ content: '""#!&""';
+ empty: '';
+ semi-colon: ';';
+}
+#escaped {
+ filter: ~"DX.Transform.MS.BS.filter(opacity=50)";
+}
+#one-line { image: url(http://tooks.com) }
+#crazy { image: url(http://), "}", url("http://}") }
+#interpolation {
+ @var: '/dev';
+ url: "http://lesscss.org@{var}/image.jpg";
+
+ @var2: 256;
+ url2: "http://lesscss.org/image-@{var2}.jpg";
+
+ @var3: #456;
+ url3: "http://lesscss.org@{var3}";
+
+ @var4: hello;
+ url4: "http://lesscss.org/@{var4}";
+
+ @var5: 54.4px;
+ url5: "http://lesscss.org/@{var5}";
+}
+
+// multiple calls with string interpolation
+
+.mix-mul (@a: green) {
+ color: ~"@{a}";
+}
+.mix-mul-class {
+ .mix-mul(blue);
+ .mix-mul(red);
+ .mix-mul(black);
+ .mix-mul(orange);
+}
+
+@test: Arial, Verdana, San-Serif;
+.watermark {
+ @family: ~"Univers, @{test}";
+ family: @family;
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/variables-in-at-rules.lint-test b/src/lint/linter/__tests__/lessc/variables-in-at-rules.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/variables-in-at-rules.lint-test
@@ -0,0 +1,21 @@
+
+@Eight: 8;
+@charset "UTF-@{Eight}";
+
+@ns: less;
+@namespace @ns "http://lesscss.org";
+
+@name: enlarger;
+@keyframes @name {
+ from {font-size: 12px;}
+ to {font-size: 15px;}
+}
+
+.m(reducer);
+.m(@name) {
+ @-webkit-keyframes @name {
+ from {font-size: 13px;}
+ to {font-size: 10px;}
+ }
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/variables.lint-test b/src/lint/linter/__tests__/lessc/variables.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/variables.lint-test
@@ -0,0 +1,84 @@
+@a: 2;
+@x: (@a * @a);
+@y: (@x + 1);
+@z: (@x * 2 + @y);
+@var: -1;
+
+.variables {
+ width: (@z + 1cm); // 14cm
+}
+
+@b: @a * 10;
+@c: #888;
+
+@fonts: "Trebuchet MS", Verdana, sans-serif;
+@f: @fonts;
+
+@quotes: "~" "~";
+@q: @quotes;
+@onePixel: 1px;
+
+.variables {
+ height: (@b + @x + 0px); // 24px
+ color: @c;
+ font-family: @f;
+ quotes: @q;
+}
+
+.redef {
+ @var: 0;
+ .inition {
+ @var: 4;
+ @var: 2;
+ three: @var;
+ @var: 3;
+ }
+ zero: @var;
+}
+
+.values {
+ minus-one: @var;
+ @a: 'Trebuchet';
+ @multi: 'A', B, C;
+ font-family: @a, @a, @a;
+ color: @c !important;
+ multi: something @multi, @a;
+}
+
+.variable-names {
+ @var: 'hello';
+ @name: 'var';
+ name: @@name;
+}
+
+.alpha {
+ @var: 42;
+ filter: alpha(opacity=@var);
+}
+
+.polluteMixin() {
+ @a: 'pollution';
+}
+.testPollution {
+ @a: 'no-pollution';
+ a: @a;
+ .polluteMixin();
+ a: @a;
+}
+
+.units {
+ width: @onePixel;
+ same-unit-as-previously: (@onePixel / @onePixel);
+ square-pixel-divided: (@onePixel * @onePixel / @onePixel);
+ odd-unit: unit((@onePixel * 4em / 2cm));
+ percentage: (10 * 50%);
+ pixels: (50px * 10);
+ conversion-metric-a: (20mm + 1cm);
+ conversion-metric-b: (1cm + 20mm);
+ conversion-imperial: (1in + 72pt + 6pc);
+ custom-unit: (42octocats * 10);
+ custom-unit-cancelling: (8cats * 9dogs / 4cats);
+ mix-units: (1px + 1em);
+ invalid-units: (1px * 1px);
+}
+~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/lessc/whitespace.lint-test b/src/lint/linter/__tests__/lessc/whitespace.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/lessc/whitespace.lint-test
@@ -0,0 +1,43 @@
+.whitespace
+ { color: white; }
+
+.whitespace
+{
+ color: white;
+}
+ .whitespace
+{ color: white; }
+
+.whitespace{color:white;}
+.whitespace { color : white ; }
+
+.white,
+.space,
+.mania
+{ color: white; }
+
+.no-semi-column { color: white }
+.no-semi-column {
+ color: white;
+ white-space: pre
+}
+.no-semi-column {border: 2px solid white}
+.newlines {
+ background: the,
+ great,
+ wall;
+ border: 2px
+ solid
+ black;
+}
+.empty {
+
+}
+.sel
+.newline_ws .tab_ws {
+color:
+white;
+background-position: 45
+-23;
+}
+~~~~~~~~~~

File Metadata

Mime Type
text/plain
Expires
Fri, Oct 18, 4:01 PM (3 w, 4 d ago)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/5m/ch/zuu62swnpgr6o655
Default Alt Text
D8992.id21355.diff (93 KB)

Event Timeline