diff --git a/data/fixtures/scopes/css/argument.actual.iteration.scope b/data/fixtures/scopes/css/argument.actual.iteration.scope new file mode 100644 index 0000000000..c3f245e5bc --- /dev/null +++ b/data/fixtures/scopes/css/argument.actual.iteration.scope @@ -0,0 +1,12 @@ +* { + width: calc(100% - 50px); +} +--- + +[Range] = 1:14-1:25 + >-----------< +1| width: calc(100% - 50px); + +[Domain] = 1:13-1:26 + >-------------< +1| width: calc(100% - 50px); diff --git a/data/fixtures/scopes/css/argument.actual.iteration2.scope b/data/fixtures/scopes/css/argument.actual.iteration2.scope new file mode 100644 index 0000000000..0ba3798918 --- /dev/null +++ b/data/fixtures/scopes/css/argument.actual.iteration2.scope @@ -0,0 +1,12 @@ +* { + background: repeating-linear-gradient(red, orange 50px); +} +--- + +[Range] = 1:40-1:56 + >----------------< +1| background: repeating-linear-gradient(red, orange 50px); + +[Domain] = 1:39-1:57 + >------------------< +1| background: repeating-linear-gradient(red, orange 50px); diff --git a/data/fixtures/scopes/css/argument.actual.scope b/data/fixtures/scopes/css/argument.actual.scope new file mode 100644 index 0000000000..ae18b1f067 --- /dev/null +++ b/data/fixtures/scopes/css/argument.actual.scope @@ -0,0 +1,12 @@ +* { + width: calc(100% - 50px); +} +--- + +[Content] = +[Removal] = +[Domain] = 1:14-1:25 + >-----------< +1| width: calc(100% - 50px); + +[Insertion delimiter] = ", " diff --git a/data/fixtures/scopes/css/argument.actual2.scope b/data/fixtures/scopes/css/argument.actual2.scope new file mode 100644 index 0000000000..8d7aa001cd --- /dev/null +++ b/data/fixtures/scopes/css/argument.actual2.scope @@ -0,0 +1,35 @@ +* { + background: repeating-linear-gradient(red, orange 50px); +} +--- + +[#1 Content] = +[#1 Domain] = 1:40-1:43 + >---< +1| background: repeating-linear-gradient(red, orange 50px); + +[#1 Removal] = 1:40-1:45 + >-----< +1| background: repeating-linear-gradient(red, orange 50px); + +[#1 Trailing delimiter] = 1:43-1:45 + >--< +1| background: repeating-linear-gradient(red, orange 50px); + +[#1 Insertion delimiter] = ", " + + +[#2 Content] = +[#2 Domain] = 1:45-1:56 + >-----------< +1| background: repeating-linear-gradient(red, orange 50px); + +[#2 Removal] = 1:43-1:56 + >-------------< +1| background: repeating-linear-gradient(red, orange 50px); + +[#2 Leading delimiter] = 1:43-1:45 + >--< +1| background: repeating-linear-gradient(red, orange 50px); + +[#2 Insertion delimiter] = ", " diff --git a/data/fixtures/scopes/css/argument.actual3.scope b/data/fixtures/scopes/css/argument.actual3.scope new file mode 100644 index 0000000000..5884622652 --- /dev/null +++ b/data/fixtures/scopes/css/argument.actual3.scope @@ -0,0 +1,35 @@ +* { + clip-path: ellipse(115px 55px at 50% 40%); +} +--- + +[#1 Content] = +[#1 Domain] = 1:21-1:31 + >----------< +1| clip-path: ellipse(115px 55px at 50% 40%); + +[#1 Removal] = 1:21-1:35 + >--------------< +1| clip-path: ellipse(115px 55px at 50% 40%); + +[#1 Trailing delimiter] = 1:31-1:35 + >----< +1| clip-path: ellipse(115px 55px at 50% 40%); + +[#1 Insertion delimiter] = ", " + + +[#2 Content] = +[#2 Domain] = 1:35-1:42 + >-------< +1| clip-path: ellipse(115px 55px at 50% 40%); + +[#2 Removal] = 1:31-1:42 + >-----------< +1| clip-path: ellipse(115px 55px at 50% 40%); + +[#2 Leading delimiter] = 1:31-1:35 + >----< +1| clip-path: ellipse(115px 55px at 50% 40%); + +[#2 Insertion delimiter] = ", " diff --git a/data/fixtures/scopes/css/argument.actual4.scope b/data/fixtures/scopes/css/argument.actual4.scope new file mode 100644 index 0000000000..9ad6d81348 --- /dev/null +++ b/data/fixtures/scopes/css/argument.actual4.scope @@ -0,0 +1,55 @@ +* { + clip-path: polygon(50% 0%, 60% 40%, 100% 50%); +} +--- + +[#1 Content] = +[#1 Domain] = 1:21-1:27 + >------< +1| clip-path: polygon(50% 0%, 60% 40%, 100% 50%); + +[#1 Removal] = 1:21-1:29 + >--------< +1| clip-path: polygon(50% 0%, 60% 40%, 100% 50%); + +[#1 Trailing delimiter] = 1:27-1:29 + >--< +1| clip-path: polygon(50% 0%, 60% 40%, 100% 50%); + +[#1 Insertion delimiter] = ", " + + +[#2 Content] = +[#2 Domain] = 1:29-1:36 + >-------< +1| clip-path: polygon(50% 0%, 60% 40%, 100% 50%); + +[#2 Removal] = 1:29-1:38 + >---------< +1| clip-path: polygon(50% 0%, 60% 40%, 100% 50%); + +[#2 Leading delimiter] = 1:27-1:29 + >--< +1| clip-path: polygon(50% 0%, 60% 40%, 100% 50%); + +[#2 Trailing delimiter] = 1:36-1:38 + >--< +1| clip-path: polygon(50% 0%, 60% 40%, 100% 50%); + +[#2 Insertion delimiter] = ", " + + +[#3 Content] = +[#3 Domain] = 1:38-1:46 + >--------< +1| clip-path: polygon(50% 0%, 60% 40%, 100% 50%); + +[#3 Removal] = 1:36-1:46 + >----------< +1| clip-path: polygon(50% 0%, 60% 40%, 100% 50%); + +[#3 Leading delimiter] = 1:36-1:38 + >--< +1| clip-path: polygon(50% 0%, 60% 40%, 100% 50%); + +[#3 Insertion delimiter] = ", " diff --git a/data/fixtures/scopes/css/collectionItem.unenclosed.iteration.scope b/data/fixtures/scopes/css/collectionItem.unenclosed.iteration.scope new file mode 100644 index 0000000000..91a7fc74f5 --- /dev/null +++ b/data/fixtures/scopes/css/collectionItem.unenclosed.iteration.scope @@ -0,0 +1,19 @@ +* { + color: red; + padding: 1rem; +} +--- + +[Range] = 1:4-2:18 + >----------- +1| color: red; +2| padding: 1rem; + ------------------< + +[Domain] = 0:3-3:0 + > +0| * { +1| color: red; +2| padding: 1rem; +3| } + < diff --git a/data/fixtures/scopes/css/collectionItem.unenclosed.scope b/data/fixtures/scopes/css/collectionItem.unenclosed.scope new file mode 100644 index 0000000000..58fe1f0ccf --- /dev/null +++ b/data/fixtures/scopes/css/collectionItem.unenclosed.scope @@ -0,0 +1,61 @@ +* { + color: red; + padding: 1rem; +} +--- + +[#1 Content] = +[#1 Domain] = 1:4-1:15 + >-----------< +1| color: red; + +[#1 Removal] = 1:0-2:0 + >--------------- +1| color: red; +2| padding: 1rem; + < + +[#1 Leading delimiter] = 1:0-1:4 + >----< +1| color: red; + +[#1 Insertion delimiter] = "\n" + + +[#2 Content] = +[#2 Domain] = 1:4-2:18 + >----------- +1| color: red; +2| padding: 1rem; + ------------------< + +[#2 Removal] = 1:0-3:0 + >--------------- +1| color: red; +2| padding: 1rem; +3| } + < + +[#2 Leading delimiter] = 1:0-1:4 + >----< +1| color: red; + +[#2 Insertion delimiter] = ",\n" + + +[#3 Content] = +[#3 Domain] = 2:4-2:18 + >--------------< +2| padding: 1rem; + +[#3 Removal] = 2:0-3:0 + >------------------ +2| padding: 1rem; +3| } + < + +[#3 Leading delimiter] = 2:0-2:4 + >----< +2| padding: 1rem; + +[#3 Insertion delimiter] = "\n" diff --git a/data/fixtures/scopes/css/comment.block.scope b/data/fixtures/scopes/css/comment.block.scope index 0bc848174c..32e87e4c47 100644 --- a/data/fixtures/scopes/css/comment.block.scope +++ b/data/fixtures/scopes/css/comment.block.scope @@ -1,17 +1,15 @@ /* - foo - bar + Hello world */ --- [Content] = [Removal] = -[Domain] = 0:0-3:2 +[Domain] = 0:0-2:2 >-- 0| /* -1| foo -2| bar -3| */ +1| Hello world +2| */ --< [Insertion delimiter] = "\n" diff --git a/data/fixtures/scopes/css/functionCall.scope b/data/fixtures/scopes/css/functionCall.scope new file mode 100644 index 0000000000..30a5dfd32e --- /dev/null +++ b/data/fixtures/scopes/css/functionCall.scope @@ -0,0 +1,19 @@ +* { + transform: translate(-50%, -50%); +} +--- + +[Content] = +[Domain] = 1:15-1:36 + >---------------------< +1| transform: translate(-50%, -50%); + +[Removal] = 1:14-1:36 + >----------------------< +1| transform: translate(-50%, -50%); + +[Leading delimiter] = 1:14-1:15 + >-< +1| transform: translate(-50%, -50%); + +[Insertion delimiter] = " " diff --git a/data/fixtures/scopes/css/functionCallee.scope b/data/fixtures/scopes/css/functionCallee.scope new file mode 100644 index 0000000000..14b4a7e25e --- /dev/null +++ b/data/fixtures/scopes/css/functionCallee.scope @@ -0,0 +1,19 @@ +* { + transform: translate(-50%, -50%); +} +--- + +[Content] = +[Removal] = 1:15-1:24 + >---------< +1| transform: translate(-50%, -50%); + +[Leading delimiter] = 1:14-1:15 + >-< +1| transform: translate(-50%, -50%); + +[Domain] = 1:15-1:36 + >---------------------< +1| transform: translate(-50%, -50%); + +[Insertion delimiter] = " " diff --git a/data/fixtures/scopes/css/key.mapPair.iteration.scope b/data/fixtures/scopes/css/key.mapPair.iteration.scope new file mode 100644 index 0000000000..d005076e82 --- /dev/null +++ b/data/fixtures/scopes/css/key.mapPair.iteration.scope @@ -0,0 +1,24 @@ +* { + color: red; + padding: 1rem; +} +--- + +[#1 Range] = +[#1 Domain] = 0:0-3:1 + >--- +0| * { +1| color: red; +2| padding: 1rem; +3| } + -< + + +[#2 Range] = +[#2 Domain] = 0:3-3:0 + > +0| * { +1| color: red; +2| padding: 1rem; +3| } + < diff --git a/data/fixtures/scopes/css/key.mapPair.scope b/data/fixtures/scopes/css/key.mapPair.scope new file mode 100644 index 0000000000..f6ad65301c --- /dev/null +++ b/data/fixtures/scopes/css/key.mapPair.scope @@ -0,0 +1,42 @@ +* { + color: red; + padding: 1rem; +} +--- + +[#1 Content] = 1:4-1:9 + >-----< +1| color: red; + +[#1 Removal] = 1:0-1:9 + >---------< +1| color: red; + +[#1 Leading delimiter] = 1:0-1:4 + >----< +1| color: red; + +[#1 Domain] = 1:4-1:15 + >-----------< +1| color: red; + +[#1 Insertion delimiter] = " " + + +[#2 Content] = 2:4-2:11 + >-------< +2| padding: 1rem; + +[#2 Removal] = 2:0-2:11 + >-----------< +2| padding: 1rem; + +[#2 Leading delimiter] = 2:0-2:4 + >----< +2| padding: 1rem; + +[#2 Domain] = 2:4-2:18 + >--------------< +2| padding: 1rem; + +[#2 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/css/map.scope b/data/fixtures/scopes/css/map.scope new file mode 100644 index 0000000000..ba97241bb3 --- /dev/null +++ b/data/fixtures/scopes/css/map.scope @@ -0,0 +1,28 @@ +* { + color: red; + padding: 1rem; +} +--- + +[Content] = +[Domain] = 0:2-3:1 + >- +0| * { +1| color: red; +2| padding: 1rem; +3| } + -< + +[Removal] = 0:1-3:1 + >-- +0| * { +1| color: red; +2| padding: 1rem; +3| } + -< + +[Leading delimiter] = 0:1-0:2 + >-< +0| * { + +[Insertion delimiter] = " " diff --git a/data/fixtures/scopes/css/name.iteration.block.scope b/data/fixtures/scopes/css/name.iteration.block.scope index 5b469d872a..61324ccd17 100644 --- a/data/fixtures/scopes/css/name.iteration.block.scope +++ b/data/fixtures/scopes/css/name.iteration.block.scope @@ -1,13 +1,24 @@ -a { color: red; } +a { + color: red; + padding: 1rem; +} --- [#1 Range] = -[#1 Domain] = 0:0-0:17 - >-----------------< -0| a { color: red; } +[#1 Domain] = 0:0-3:1 + >--- +0| a { +1| color: red; +2| padding: 1rem; +3| } + -< [#2 Range] = -[#2 Domain] = 0:3-0:16 - >-------------< -0| a { color: red; } +[#2 Domain] = 0:3-3:0 + > +0| a { +1| color: red; +2| padding: 1rem; +3| } + < diff --git a/data/fixtures/scopes/css/name.iteration.document.scope b/data/fixtures/scopes/css/name.iteration.document.scope index 2d3bdd8815..d005076e82 100644 --- a/data/fixtures/scopes/css/name.iteration.document.scope +++ b/data/fixtures/scopes/css/name.iteration.document.scope @@ -1,7 +1,24 @@ -/* hello */ +* { + color: red; + padding: 1rem; +} --- -[Range] = -[Domain] = 0:0-0:11 - >-----------< -0| /* hello */ +[#1 Range] = +[#1 Domain] = 0:0-3:1 + >--- +0| * { +1| color: red; +2| padding: 1rem; +3| } + -< + + +[#2 Range] = +[#2 Domain] = 0:3-3:0 + > +0| * { +1| color: red; +2| padding: 1rem; +3| } + < diff --git a/data/fixtures/scopes/css/statement.iteration.document.scope b/data/fixtures/scopes/css/statement.iteration.document.scope new file mode 100644 index 0000000000..73f55a4ba0 --- /dev/null +++ b/data/fixtures/scopes/css/statement.iteration.document.scope @@ -0,0 +1,14 @@ +* { + color: red; +} + +--- + +[Range] = +[Domain] = 0:0-3:0 + >--- +0| * { +1| color: red; +2| } +3| + < diff --git a/data/fixtures/scopes/css/statement.scope b/data/fixtures/scopes/css/statement.scope new file mode 100644 index 0000000000..1ba073ba6d --- /dev/null +++ b/data/fixtures/scopes/css/statement.scope @@ -0,0 +1,15 @@ +* { + color: red; +} +--- + +[Content] = +[Removal] = +[Domain] = 0:0-2:1 + >--- +0| * { +1| color: red; +2| } + -< + +[Insertion delimiter] = "\n" diff --git a/data/fixtures/scopes/css/string.singleLine.scope b/data/fixtures/scopes/css/string.singleLine.scope index 4c61e1ddf8..55f5128468 100644 --- a/data/fixtures/scopes/css/string.singleLine.scope +++ b/data/fixtures/scopes/css/string.singleLine.scope @@ -1,19 +1,19 @@ * { - color: "foo" + color: "red" } --- [Content] = [Domain] = 1:11-1:16 >-----< -1| color: "foo" +1| color: "red" [Removal] = 1:10-1:16 >------< -1| color: "foo" +1| color: "red" [Leading delimiter] = 1:10-1:11 >-< -1| color: "foo" +1| color: "red" [Insertion delimiter] = " " diff --git a/data/fixtures/scopes/css/textFragment.comment.block.scope b/data/fixtures/scopes/css/textFragment.comment.block.scope new file mode 100644 index 0000000000..9573c83d49 --- /dev/null +++ b/data/fixtures/scopes/css/textFragment.comment.block.scope @@ -0,0 +1,15 @@ +/* + Hello world +*/ +--- + +[Content] = +[Removal] = +[Domain] = 0:0-2:2 + >-- +0| /* +1| Hello world +2| */ + --< + +[Insertion delimiter] = " " diff --git a/data/fixtures/scopes/css/textFragment.string.singleLine.scope b/data/fixtures/scopes/css/textFragment.string.singleLine.scope new file mode 100644 index 0000000000..8b5a5df793 --- /dev/null +++ b/data/fixtures/scopes/css/textFragment.string.singleLine.scope @@ -0,0 +1,12 @@ +* { + color: "red" +} +--- + +[Content] = +[Removal] = +[Domain] = 1:12-1:15 + >---< +1| color: "red" + +[Insertion delimiter] = " " diff --git a/data/fixtures/scopes/css/value.mapPair.iteration.scope b/data/fixtures/scopes/css/value.mapPair.iteration.scope new file mode 100644 index 0000000000..d005076e82 --- /dev/null +++ b/data/fixtures/scopes/css/value.mapPair.iteration.scope @@ -0,0 +1,24 @@ +* { + color: red; + padding: 1rem; +} +--- + +[#1 Range] = +[#1 Domain] = 0:0-3:1 + >--- +0| * { +1| color: red; +2| padding: 1rem; +3| } + -< + + +[#2 Range] = +[#2 Domain] = 0:3-3:0 + > +0| * { +1| color: red; +2| padding: 1rem; +3| } + < diff --git a/data/fixtures/scopes/css/value.mapPair.scope b/data/fixtures/scopes/css/value.mapPair.scope new file mode 100644 index 0000000000..cf9d10c731 --- /dev/null +++ b/data/fixtures/scopes/css/value.mapPair.scope @@ -0,0 +1,42 @@ +* { + color: red; + padding: 1rem; +} +--- + +[#1 Content] = 1:11-1:14 + >---< +1| color: red; + +[#1 Removal] = 1:10-1:14 + >----< +1| color: red; + +[#1 Leading delimiter] = 1:10-1:11 + >-< +1| color: red; + +[#1 Domain] = 1:4-1:15 + >-----------< +1| color: red; + +[#1 Insertion delimiter] = " " + + +[#2 Content] = 2:13-2:17 + >----< +2| padding: 1rem; + +[#2 Removal] = 2:12-2:17 + >-----< +2| padding: 1rem; + +[#2 Leading delimiter] = 2:12-2:13 + >-< +2| padding: 1rem; + +[#2 Domain] = 2:4-2:18 + >--------------< +2| padding: 1rem; + +[#2 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/css/value.mapPair2.scope b/data/fixtures/scopes/css/value.mapPair2.scope new file mode 100644 index 0000000000..c4ee412dd7 --- /dev/null +++ b/data/fixtures/scopes/css/value.mapPair2.scope @@ -0,0 +1,22 @@ +* { + margin: 1px 2px 3px 4px; +} +--- + +[Content] = 1:12-1:27 + >---------------< +1| margin: 1px 2px 3px 4px; + +[Removal] = 1:11-1:27 + >----------------< +1| margin: 1px 2px 3px 4px; + +[Leading delimiter] = 1:11-1:12 + >-< +1| margin: 1px 2px 3px 4px; + +[Domain] = 1:4-1:28 + >------------------------< +1| margin: 1px 2px 3px 4px; + +[Insertion delimiter] = " " diff --git a/data/fixtures/scopes/scss/argument.actual.iteration.scope b/data/fixtures/scopes/scss/argument.actual.iteration.scope new file mode 100644 index 0000000000..bb88c5dafc --- /dev/null +++ b/data/fixtures/scopes/scss/argument.actual.iteration.scope @@ -0,0 +1,10 @@ +@mixin replace-text($image, $color: red) {} +--- + +[Range] = 0:20-0:39 + >-------------------< +0| @mixin replace-text($image, $color: red) {} + +[Domain] = 0:0-0:43 + >-------------------------------------------< +0| @mixin replace-text($image, $color: red) {} diff --git a/data/fixtures/scopes/scss/argument.actual.scope b/data/fixtures/scopes/scss/argument.actual.scope new file mode 100644 index 0000000000..13d5375618 --- /dev/null +++ b/data/fixtures/scopes/scss/argument.actual.scope @@ -0,0 +1,33 @@ +@mixin replace-text($image, $color: red) {} +--- + +[#1 Content] = +[#1 Domain] = 0:20-0:26 + >------< +0| @mixin replace-text($image, $color: red) {} + +[#1 Removal] = 0:20-0:28 + >--------< +0| @mixin replace-text($image, $color: red) {} + +[#1 Trailing delimiter] = 0:26-0:28 + >--< +0| @mixin replace-text($image, $color: red) {} + +[#1 Insertion delimiter] = ", " + + +[#2 Content] = +[#2 Domain] = 0:28-0:39 + >-----------< +0| @mixin replace-text($image, $color: red) {} + +[#2 Removal] = 0:26-0:39 + >-------------< +0| @mixin replace-text($image, $color: red) {} + +[#2 Leading delimiter] = 0:26-0:28 + >--< +0| @mixin replace-text($image, $color: red) {} + +[#2 Insertion delimiter] = ", " diff --git a/data/fixtures/scopes/scss/comment.line.scope b/data/fixtures/scopes/scss/comment.line.scope index a469659c87..0723a12261 100644 --- a/data/fixtures/scopes/scss/comment.line.scope +++ b/data/fixtures/scopes/scss/comment.line.scope @@ -1,10 +1,10 @@ -// aaa +// Hello wold --- [Content] = [Removal] = -[Domain] = 0:0-0:6 - >------< -0| // aaa +[Domain] = 0:0-0:13 + >-------------< +0| // Hello wold [Insertion delimiter] = "\n" diff --git a/data/fixtures/scopes/scss/condition.if.scope b/data/fixtures/scopes/scss/condition.if.scope new file mode 100644 index 0000000000..03e516a73a --- /dev/null +++ b/data/fixtures/scopes/scss/condition.if.scope @@ -0,0 +1,29 @@ +* { + @if $container-width < $base-width { } + @else { } +} +--- + +[Content] = 1:6-1:36 + >------------------------------< +1| @if $container-width < $base-width { } + +[Removal] = 1:6-1:37 + >-------------------------------< +1| @if $container-width < $base-width { } + +[Leading delimiter] = 1:5-1:6 + >-< +1| @if $container-width < $base-width { } + +[Trailing delimiter] = 1:36-1:37 + >-< +1| @if $container-width < $base-width { } + +[Domain] = 1:2-2:11 + >-------------------------------------- +1| @if $container-width < $base-width { } +2| @else { } + -----------< + +[Insertion delimiter] = " " diff --git a/data/fixtures/scopes/scss/functionName.iteration.block.scope b/data/fixtures/scopes/scss/functionName.iteration.block.scope index 5b469d872a..4637c1ce18 100644 --- a/data/fixtures/scopes/scss/functionName.iteration.block.scope +++ b/data/fixtures/scopes/scss/functionName.iteration.block.scope @@ -1,13 +1,13 @@ -a { color: red; } +* { color: red; } --- [#1 Range] = [#1 Domain] = 0:0-0:17 >-----------------< -0| a { color: red; } +0| * { color: red; } [#2 Range] = [#2 Domain] = 0:3-0:16 >-------------< -0| a { color: red; } +0| * { color: red; } diff --git a/data/fixtures/scopes/scss/functionName.iteration.document.scope b/data/fixtures/scopes/scss/functionName.iteration.document.scope index 2d3bdd8815..9549701ed3 100644 --- a/data/fixtures/scopes/scss/functionName.iteration.document.scope +++ b/data/fixtures/scopes/scss/functionName.iteration.document.scope @@ -1,7 +1,16 @@ -/* hello */ +* { color: red; } + --- -[Range] = -[Domain] = 0:0-0:11 - >-----------< -0| /* hello */ +[#1 Range] = +[#1 Domain] = 0:0-1:0 + >----------------- +0| * { color: red; } +1| + < + + +[#2 Range] = +[#2 Domain] = 0:3-0:16 + >-------------< +0| * { color: red; } diff --git a/data/fixtures/scopes/scss/functionName.scope b/data/fixtures/scopes/scss/functionName.scope new file mode 100644 index 0000000000..c587a9a695 --- /dev/null +++ b/data/fixtures/scopes/scss/functionName.scope @@ -0,0 +1,17 @@ +@function calculate-margin($size) {} +--- + +[Content] = +[Removal] = 0:10-0:26 + >----------------< +0| @function calculate-margin($size) {} + +[Leading delimiter] = 0:9-0:10 + >-< +0| @function calculate-margin($size) {} + +[Domain] = 0:0-0:36 + >------------------------------------< +0| @function calculate-margin($size) {} + +[Insertion delimiter] = " " diff --git a/data/fixtures/scopes/scss/functionName2.scope b/data/fixtures/scopes/scss/functionName2.scope new file mode 100644 index 0000000000..b11d5d5dab --- /dev/null +++ b/data/fixtures/scopes/scss/functionName2.scope @@ -0,0 +1,17 @@ +@mixin replace-text($image, $color: red) {} +--- + +[Content] = +[Removal] = 0:7-0:19 + >------------< +0| @mixin replace-text($image, $color: red) {} + +[Leading delimiter] = 0:6-0:7 + >-< +0| @mixin replace-text($image, $color: red) {} + +[Domain] = 0:0-0:43 + >-------------------------------------------< +0| @mixin replace-text($image, $color: red) {} + +[Insertion delimiter] = " " diff --git a/data/fixtures/scopes/scss/ifStatement.scope b/data/fixtures/scopes/scss/ifStatement.scope new file mode 100644 index 0000000000..3af7418e4e --- /dev/null +++ b/data/fixtures/scopes/scss/ifStatement.scope @@ -0,0 +1,25 @@ +* { + @if $container-width < $base-width { } + @else { } +} +--- + +[Content] = +[Domain] = 1:2-2:11 + >-------------------------------------- +1| @if $container-width < $base-width { } +2| @else { } + -----------< + +[Removal] = 1:0-3:0 + >---------------------------------------- +1| @if $container-width < $base-width { } +2| @else { } +3| } + < + +[Leading delimiter] = 1:0-1:2 + >--< +1| @if $container-width < $base-width { } + +[Insertion delimiter] = "\n" diff --git a/data/fixtures/scopes/scss/name.argument.formal.iteration.scope b/data/fixtures/scopes/scss/name.argument.formal.iteration.scope new file mode 100644 index 0000000000..26a2864393 --- /dev/null +++ b/data/fixtures/scopes/scss/name.argument.formal.iteration.scope @@ -0,0 +1,22 @@ +@mixin replace-text($image, $color: red) {} +--- + +[#1 Range] = +[#1 Domain] = 0:0-0:43 + >-------------------------------------------< +0| @mixin replace-text($image, $color: red) {} + + +[#2 Range] = 0:20-0:39 + >-------------------< +0| @mixin replace-text($image, $color: red) {} + +[#2 Domain] = 0:19-0:40 + >---------------------< +0| @mixin replace-text($image, $color: red) {} + + +[#3 Range] = +[#3 Domain] = 0:42-0:42 + >< +0| @mixin replace-text($image, $color: red) {} diff --git a/data/fixtures/scopes/scss/name.argument.formal.scope b/data/fixtures/scopes/scss/name.argument.formal.scope new file mode 100644 index 0000000000..1d45bb8b31 --- /dev/null +++ b/data/fixtures/scopes/scss/name.argument.formal.scope @@ -0,0 +1,45 @@ +@mixin replace-text($image, $color: red) {} +--- + +[#1 Content] = +[#1 Removal] = 0:7-0:19 + >------------< +0| @mixin replace-text($image, $color: red) {} + +[#1 Leading delimiter] = 0:6-0:7 + >-< +0| @mixin replace-text($image, $color: red) {} + +[#1 Domain] = 0:0-0:43 + >-------------------------------------------< +0| @mixin replace-text($image, $color: red) {} + +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = +[#2 Domain] = 0:20-0:26 + >------< +0| @mixin replace-text($image, $color: red) {} + +[#2 Insertion delimiter] = " " + + +[#3 Content] = 0:28-0:34 + >------< +0| @mixin replace-text($image, $color: red) {} + +[#3 Removal] = 0:27-0:34 + >-------< +0| @mixin replace-text($image, $color: red) {} + +[#3 Leading delimiter] = 0:27-0:28 + >-< +0| @mixin replace-text($image, $color: red) {} + +[#3 Domain] = 0:28-0:39 + >-----------< +0| @mixin replace-text($image, $color: red) {} + +[#3 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/scss/name.function.scope b/data/fixtures/scopes/scss/name.function.scope new file mode 100644 index 0000000000..29e2b977e5 --- /dev/null +++ b/data/fixtures/scopes/scss/name.function.scope @@ -0,0 +1,17 @@ +@function calculate-margin() {} +--- + +[Content] = +[Removal] = 0:10-0:26 + >----------------< +0| @function calculate-margin() {} + +[Leading delimiter] = 0:9-0:10 + >-< +0| @function calculate-margin() {} + +[Domain] = 0:0-0:31 + >-------------------------------< +0| @function calculate-margin() {} + +[Insertion delimiter] = " " diff --git a/data/fixtures/scopes/scss/name.function2.scope b/data/fixtures/scopes/scss/name.function2.scope new file mode 100644 index 0000000000..498ff99bde --- /dev/null +++ b/data/fixtures/scopes/scss/name.function2.scope @@ -0,0 +1,17 @@ +@mixin replace-text() {} +--- + +[Content] = +[Removal] = 0:7-0:19 + >------------< +0| @mixin replace-text() {} + +[Leading delimiter] = 0:6-0:7 + >-< +0| @mixin replace-text() {} + +[Domain] = 0:0-0:24 + >------------------------< +0| @mixin replace-text() {} + +[Insertion delimiter] = " " diff --git a/data/fixtures/scopes/scss/namedFunction.iteration.block.scope b/data/fixtures/scopes/scss/namedFunction.iteration.block.scope index 5b469d872a..4637c1ce18 100644 --- a/data/fixtures/scopes/scss/namedFunction.iteration.block.scope +++ b/data/fixtures/scopes/scss/namedFunction.iteration.block.scope @@ -1,13 +1,13 @@ -a { color: red; } +* { color: red; } --- [#1 Range] = [#1 Domain] = 0:0-0:17 >-----------------< -0| a { color: red; } +0| * { color: red; } [#2 Range] = [#2 Domain] = 0:3-0:16 >-------------< -0| a { color: red; } +0| * { color: red; } diff --git a/data/fixtures/scopes/scss/namedFunction.iteration.document.scope b/data/fixtures/scopes/scss/namedFunction.iteration.document.scope index 2d3bdd8815..9549701ed3 100644 --- a/data/fixtures/scopes/scss/namedFunction.iteration.document.scope +++ b/data/fixtures/scopes/scss/namedFunction.iteration.document.scope @@ -1,7 +1,16 @@ -/* hello */ +* { color: red; } + --- -[Range] = -[Domain] = 0:0-0:11 - >-----------< -0| /* hello */ +[#1 Range] = +[#1 Domain] = 0:0-1:0 + >----------------- +0| * { color: red; } +1| + < + + +[#2 Range] = +[#2 Domain] = 0:3-0:16 + >-------------< +0| * { color: red; } diff --git a/data/fixtures/scopes/scss/namedFunction.scope b/data/fixtures/scopes/scss/namedFunction.scope new file mode 100644 index 0000000000..27e20ca551 --- /dev/null +++ b/data/fixtures/scopes/scss/namedFunction.scope @@ -0,0 +1,10 @@ +@function calculate-margin($size) {} +--- + +[Content] = +[Removal] = +[Domain] = 0:0-0:36 + >------------------------------------< +0| @function calculate-margin($size) {} + +[Insertion delimiter] = "\n\n" diff --git a/data/fixtures/scopes/scss/namedFunction2.scope b/data/fixtures/scopes/scss/namedFunction2.scope new file mode 100644 index 0000000000..ac00d85480 --- /dev/null +++ b/data/fixtures/scopes/scss/namedFunction2.scope @@ -0,0 +1,10 @@ +@mixin replace-text($image, $color: red) {} +--- + +[Content] = +[Removal] = +[Domain] = 0:0-0:43 + >-------------------------------------------< +0| @mixin replace-text($image, $color: red) {} + +[Insertion delimiter] = "\n\n" diff --git a/data/fixtures/scopes/scss/textFragment.comment.line.scope b/data/fixtures/scopes/scss/textFragment.comment.line.scope new file mode 100644 index 0000000000..40bc14eca6 --- /dev/null +++ b/data/fixtures/scopes/scss/textFragment.comment.line.scope @@ -0,0 +1,10 @@ +// Hello wold +--- + +[Content] = +[Removal] = +[Domain] = 0:0-0:13 + >-------------< +0| // Hello wold + +[Insertion delimiter] = " " diff --git a/data/fixtures/scopes/scss/value.argument.formal.iteration.scope b/data/fixtures/scopes/scss/value.argument.formal.iteration.scope new file mode 100644 index 0000000000..26a2864393 --- /dev/null +++ b/data/fixtures/scopes/scss/value.argument.formal.iteration.scope @@ -0,0 +1,22 @@ +@mixin replace-text($image, $color: red) {} +--- + +[#1 Range] = +[#1 Domain] = 0:0-0:43 + >-------------------------------------------< +0| @mixin replace-text($image, $color: red) {} + + +[#2 Range] = 0:20-0:39 + >-------------------< +0| @mixin replace-text($image, $color: red) {} + +[#2 Domain] = 0:19-0:40 + >---------------------< +0| @mixin replace-text($image, $color: red) {} + + +[#3 Range] = +[#3 Domain] = 0:42-0:42 + >< +0| @mixin replace-text($image, $color: red) {} diff --git a/data/fixtures/scopes/scss/value.argument.formal.scope b/data/fixtures/scopes/scss/value.argument.formal.scope new file mode 100644 index 0000000000..ac78d9e69a --- /dev/null +++ b/data/fixtures/scopes/scss/value.argument.formal.scope @@ -0,0 +1,20 @@ +@mixin replace-text($image, $color: red) {} +--- + +[Content] = 0:36-0:39 + >---< +0| @mixin replace-text($image, $color: red) {} + +[Removal] = 0:35-0:39 + >----< +0| @mixin replace-text($image, $color: red) {} + +[Leading delimiter] = 0:35-0:36 + >-< +0| @mixin replace-text($image, $color: red) {} + +[Domain] = 0:28-0:39 + >-----------< +0| @mixin replace-text($image, $color: red) {} + +[Insertion delimiter] = " " diff --git a/data/fixtures/scopes/scss/value.return.scope b/data/fixtures/scopes/scss/value.return.scope new file mode 100644 index 0000000000..147dfef9aa --- /dev/null +++ b/data/fixtures/scopes/scss/value.return.scope @@ -0,0 +1,22 @@ +@function double($num) { + @return $num * 2; +} +--- + +[Content] = 1:10-1:18 + >--------< +1| @return $num * 2; + +[Removal] = 1:9-1:18 + >---------< +1| @return $num * 2; + +[Leading delimiter] = 1:9-1:10 + >-< +1| @return $num * 2; + +[Domain] = 1:2-1:19 + >-----------------< +1| @return $num * 2; + +[Insertion delimiter] = " " diff --git a/packages/common/src/scopeSupportFacets/css.ts b/packages/common/src/scopeSupportFacets/css.ts index 4e29dc1536..e79d96aeba 100644 --- a/packages/common/src/scopeSupportFacets/css.ts +++ b/packages/common/src/scopeSupportFacets/css.ts @@ -1,15 +1,179 @@ import type { LanguageScopeSupportFacetMap } from "./scopeSupportFacets.types"; import { ScopeSupportFacetLevel } from "./scopeSupportFacets.types"; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const { supported, unsupported, notApplicable } = ScopeSupportFacetLevel; +const { supported, notApplicable } = ScopeSupportFacetLevel; export const cssScopeSupport: LanguageScopeSupportFacetMap = { "comment.block": supported, "string.singleLine": supported, - "name.iteration.block": supported, - "name.iteration.document": supported, disqualifyDelimiter: supported, + map: supported, + + functionCall: supported, + functionCallee: supported, + + "argument.actual": supported, + "argument.actual.iteration": supported, + + "name.iteration.document": supported, + "name.iteration.block": supported, + + "textFragment.comment.block": supported, + "textFragment.string.singleLine": supported, + + "key.mapPair": supported, + "key.mapPair.iteration": supported, + + "value.mapPair": supported, + "value.mapPair.iteration": supported, + + "collectionItem.unenclosed": supported, + "collectionItem.unenclosed.iteration": supported, + + statement: supported, + "statement.iteration.document": supported, + + // Not applicable - "comment.line": unsupported, + anonymousFunction: notApplicable, + "argument.actual.constructor.iteration": notApplicable, + "argument.actual.constructor": notApplicable, + "argument.actual.method.iteration": notApplicable, + "argument.actual.method": notApplicable, + "argument.formal.constructor.iteration": notApplicable, + "argument.formal.constructor": notApplicable, + "argument.formal.iteration": notApplicable, + "argument.formal.method.iteration": notApplicable, + "argument.formal.method": notApplicable, + "argument.formal": notApplicable, + attribute: notApplicable, + "branch.if.iteration": notApplicable, + "branch.if": notApplicable, + "branch.loop": notApplicable, + "branch.switchCase.iteration": notApplicable, + "branch.switchCase": notApplicable, + "branch.ternary": notApplicable, + "branch.try.iteration": notApplicable, + "branch.try": notApplicable, + "class.iteration.block": notApplicable, + "class.iteration.document": notApplicable, + class: notApplicable, + "className.iteration.block": notApplicable, + "className.iteration.document": notApplicable, + className: notApplicable, + command: notApplicable, + "comment.line": notApplicable, + "condition.doWhile": notApplicable, + "condition.for": notApplicable, + "condition.if": notApplicable, + "condition.switchCase.iteration": notApplicable, + "condition.switchCase": notApplicable, + "condition.ternary": notApplicable, + "condition.while": notApplicable, + element: notApplicable, + endTag: notApplicable, + environment: notApplicable, + fieldAccess: notApplicable, + "functionCall.constructor": notApplicable, + "functionCallee.constructor": notApplicable, + "functionName.constructor": notApplicable, + "functionName.iteration.block": notApplicable, + "functionName.iteration.document": notApplicable, + "functionName.method.iteration.class": notApplicable, + "functionName.method": notApplicable, + functionName: notApplicable, + ifStatement: notApplicable, + "interior.cell": notApplicable, + "interior.class": notApplicable, + "interior.command": notApplicable, + "interior.element": notApplicable, + "interior.function": notApplicable, + "interior.if": notApplicable, + "interior.lambda": notApplicable, + "interior.loop": notApplicable, + "interior.resource": notApplicable, + "interior.switchCase": notApplicable, + "interior.ternary": notApplicable, + "interior.try": notApplicable, + "key.attribute": notApplicable, + list: notApplicable, + "name.argument.actual.iteration": notApplicable, + "name.argument.actual": notApplicable, + "name.argument.formal.constructor.iteration": notApplicable, + "name.argument.formal.constructor": notApplicable, + "name.argument.formal.iteration": notApplicable, + "name.argument.formal.method.iteration": notApplicable, + "name.argument.formal.method": notApplicable, + "name.argument.formal": notApplicable, + "name.assignment.pattern": notApplicable, + "name.assignment": notApplicable, + "name.class": notApplicable, + "name.constructor": notApplicable, + "name.field": notApplicable, + "name.foreach": notApplicable, + "name.function": notApplicable, + "name.method": notApplicable, + "name.resource.iteration": notApplicable, + "name.resource": notApplicable, + "name.variable.pattern": notApplicable, + "name.variable": notApplicable, + "namedFunction.constructor": notApplicable, + "namedFunction.iteration.block": notApplicable, + "namedFunction.iteration.document": notApplicable, + "namedFunction.method.iteration.class": notApplicable, + "namedFunction.method": notApplicable, + namedFunction: notApplicable, + notebookCell: notApplicable, + pairDelimiter: notApplicable, + regularExpression: notApplicable, + "section.iteration.document": notApplicable, + "section.iteration.parent": notApplicable, + section: notApplicable, + startTag: notApplicable, + "statement.iteration.block": notApplicable, + "statement.class": notApplicable, + "string.multiLine": notApplicable, + switchStatementSubject: notApplicable, + tags: notApplicable, + "textFragment.comment.line": notApplicable, + "textFragment.element": notApplicable, + "textFragment.string.multiLine": notApplicable, + "type.alias": notApplicable, + "type.argument.formal.constructor.iteration": notApplicable, + "type.argument.formal.constructor": notApplicable, + "type.argument.formal.iteration": notApplicable, + "type.argument.formal.method.iteration": notApplicable, + "type.argument.formal.method": notApplicable, + "type.argument.formal": notApplicable, + "type.cast": notApplicable, + "type.class": notApplicable, + "type.enum": notApplicable, + "type.field.iteration": notApplicable, + "type.field": notApplicable, + "type.foreach": notApplicable, + "type.interface": notApplicable, + "type.return": notApplicable, + "type.typeArgument.iteration": notApplicable, + "type.typeArgument": notApplicable, + "type.variable": notApplicable, + "value.argument.actual.iteration": notApplicable, + "value.argument.actual": notApplicable, + "value.argument.formal.constructor.iteration": notApplicable, + "value.argument.formal.constructor": notApplicable, + "value.argument.formal.iteration": notApplicable, + "value.argument.formal.method.iteration": notApplicable, + "value.argument.formal.method": notApplicable, + "value.argument.formal": notApplicable, + "value.assignment": notApplicable, + "value.attribute": notApplicable, + "value.field": notApplicable, + "value.foreach": notApplicable, + "value.resource.iteration": notApplicable, + "value.resource": notApplicable, + "value.return.lambda": notApplicable, + "value.return": notApplicable, + "value.typeAlias": notApplicable, + "value.variable.pattern": notApplicable, + "value.variable": notApplicable, + "value.yield": notApplicable, }; diff --git a/packages/common/src/scopeSupportFacets/scss.ts b/packages/common/src/scopeSupportFacets/scss.ts index cad5192215..d98fba0130 100644 --- a/packages/common/src/scopeSupportFacets/scss.ts +++ b/packages/common/src/scopeSupportFacets/scss.ts @@ -2,16 +2,40 @@ import { cssScopeSupport } from "./css"; import type { LanguageScopeSupportFacetMap } from "./scopeSupportFacets.types"; import { ScopeSupportFacetLevel } from "./scopeSupportFacets.types"; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const { supported, unsupported, notApplicable } = ScopeSupportFacetLevel; +const { supported, unsupported } = ScopeSupportFacetLevel; export const scssScopeSupport: LanguageScopeSupportFacetMap = { ...cssScopeSupport, + "comment.line": supported, + + "condition.if": supported, + + namedFunction: supported, "namedFunction.iteration.block": supported, "namedFunction.iteration.document": supported, + + functionName: supported, "functionName.iteration.block": supported, "functionName.iteration.document": supported, - "comment.line": supported, - disqualifyDelimiter: supported, + + "name.argument.formal": supported, + "name.argument.formal.iteration": supported, + "name.function": supported, + + "value.argument.formal": supported, + "value.argument.formal.iteration": supported, + "value.return": supported, + + ifStatement: supported, + + "textFragment.comment.line": supported, + + // Unsupported + + "branch.if": unsupported, + "branch.if.iteration": unsupported, + + "interior.function": unsupported, + "interior.if": unsupported, }; diff --git a/packages/cursorless-engine/src/languages/LegacyLanguageId.ts b/packages/cursorless-engine/src/languages/LegacyLanguageId.ts index 30f24eafb1..1b33483e8b 100644 --- a/packages/cursorless-engine/src/languages/LegacyLanguageId.ts +++ b/packages/cursorless-engine/src/languages/LegacyLanguageId.ts @@ -4,12 +4,10 @@ */ export const legacyLanguageIds = [ "clojure", - "css", "latex", "ruby", "rust", "scala", - "scss", ] as const; export type LegacyLanguageId = (typeof legacyLanguageIds)[number]; diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/QueryCapture.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/QueryCapture.ts index 9df81fa3df..5df0fcb802 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/QueryCapture.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/QueryCapture.ts @@ -9,6 +9,7 @@ import type { Point } from "web-tree-sitter"; interface SimpleSyntaxNode { readonly id: number; readonly type: string; + readonly isNamed: boolean; readonly parent: SimpleSyntaxNode | null; readonly children: Array; } diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts index 143edece3b..bf80bb9be2 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts @@ -1,3 +1,4 @@ +import type { Position } from "@cursorless/common"; import { Range, adjustPosition } from "@cursorless/common"; import { z } from "zod"; import { makeRangeFromPositions } from "../../util/nodeSelectors"; @@ -156,6 +157,59 @@ class ShrinkToMatch extends QueryPredicateOperator { } } +/** + * A predicate operator that modifies the range of the match to grow to named trailing siblings. + * + * An optional `notText` argument can be provided to break at siblings that match the given text. + * + * ``` + * (#grow-to-named-siblings! @foo "at") + * ``` + */ +class GrowToNamedSiblings extends QueryPredicateOperator { + name = "grow-to-named-siblings!" as const; + schema = z.union([z.tuple([q.node]), z.tuple([q.node, q.string])]); + + run(nodeInfo: MutableQueryCapture, notText?: string) { + const { node, range, document } = nodeInfo; + + if (node.parent == null) { + throw Error("Node has no parent"); + } + + const { children } = node.parent; + const nodeIndex = children.findIndex((n) => n.id === node.id); + let endPosition: Position | null = null; + + if (nodeIndex === -1) { + throw Error("Node not found in parent"); + } + + for (let i = nodeIndex + 1; i < children.length; ++i) { + const child = children[i]; + if (!child.isNamed) { + break; + } + const childRange = makeRangeFromPositions( + child.startPosition, + child.endPosition, + ); + + if (notText != null && notText === document.getText(childRange)) { + break; + } + + endPosition = childRange.end; + } + + if (endPosition != null) { + nodeInfo.range = new Range(range.start, endPosition); + } + + return true; + } +} + /** * A predicate operator that modifies the range of the match by trimming trailing whitespace, * similar to the javascript trimEnd function. @@ -273,6 +327,7 @@ export const queryPredicateOperators = [ new ChildRange(), new CharacterRange(), new ShrinkToMatch(), + new GrowToNamedSiblings(), new AllowMultiple(), new InsertionDelimiter(), new SingleOrMultilineDelimiter(), diff --git a/packages/cursorless-engine/src/languages/getNodeMatcher.ts b/packages/cursorless-engine/src/languages/getNodeMatcher.ts index fb68c5ed0a..a5cb458ccf 100644 --- a/packages/cursorless-engine/src/languages/getNodeMatcher.ts +++ b/packages/cursorless-engine/src/languages/getNodeMatcher.ts @@ -14,7 +14,6 @@ import latex from "./latex"; import { patternMatchers as ruby } from "./ruby"; import rust from "./rust"; import scala from "./scala"; -import { patternMatchers as scss } from "./scss"; export function getNodeMatcher( languageId: string, @@ -45,12 +44,10 @@ export const languageMatchers: Record< Partial> > = { clojure, - css: scss, latex, ruby, rust, scala, - scss, }; function matcherIncludeSiblings(matcher: NodeMatcher): NodeMatcher { diff --git a/packages/cursorless-engine/src/languages/scss.ts b/packages/cursorless-engine/src/languages/scss.ts deleted file mode 100644 index f4d3e718f9..0000000000 --- a/packages/cursorless-engine/src/languages/scss.ts +++ /dev/null @@ -1,151 +0,0 @@ -import type { SyntaxNode } from "web-tree-sitter"; -import type { SimpleScopeTypeType } from "@cursorless/common"; -import type { - NodeMatcherAlternative, - NodeMatcherValue, - SelectionWithEditor, -} from "../typings/Types"; -import { patternFinder } from "../util/nodeFinders"; -import { - cascadingMatcher, - conditionMatcher, - createPatternMatchers, - matcher, - patternMatcher, - trailingMatcher, -} from "../util/nodeMatchers"; -import { - childRangeSelector, - delimitedSelector, - simpleSelectionExtractor, -} from "../util/nodeSelectors"; - -// curl https://raw.githubusercontent.com/serenadeai/tree-sitter-scss/c478c6868648eff49eb04a4df90d703dc45b312a/src/node-types.json \ -// | jq '[.[] | select(.type =="stylesheet") | .children.types[] | select(.type !="declaration") | .type ]' - -const STATEMENT_TYPES = [ - "apply_statement", - "at_rule", - "charset_statement", - "debug_statement", - "each_statement", - "error_statement", - "for_statement", - "forward_statement", - "function_statement", - "if_statement", - "import_statement", - "include_statement", - "keyframes_statement", - "media_statement", - "mixin_statement", - "namespace_statement", - "placeholder", - "rule_set", - "supports_statement", - "use_statement", - "warn_statement", - "while_statement", -]; - -function isArgumentListDelimiter(node: SyntaxNode) { - return [",", "(", ")"].includes(node.type) || isAtDelimiter(node); -} - -/** - * Determines whether the given `node` is an `at` delimiter node, used in a css - * / scss argument list. For example, the `at` in the call - * `ellipse(115px 55px at 50% 40%)` - * - * @param node The node to check - * @returns `true` if the node is an `at` delimiter node - */ -function isAtDelimiter(node: SyntaxNode) { - return node.type === "plain_value" && node.text === "at"; -} - -/** - * Matches adjacent nodes returned from {@link siblingFunc} until it reaches a - * delimiter node. This is intended to handle the case of multiple values - * within two delimiters. e.g. `repeating-linear-gradient(red, orange 50px)` - * @param siblingFunc returns the previous or next sibling of the current node if present. - * @returns A non-delimiter node - */ -function findAdjacentArgValues( - siblingFunc: (node: SyntaxNode) => SyntaxNode | null, -) { - return (node: SyntaxNode) => { - // Handle the case where we are the cursor is placed before a delimiter, e.g. "|at" - // and we erroneously expand in both directions. - if (isAtDelimiter(node) || node.type === ",") { - node = node.previousSibling!; - } - - let nextPossibleRange = siblingFunc(node); - - while (nextPossibleRange && !isArgumentListDelimiter(nextPossibleRange)) { - node = nextPossibleRange; - nextPossibleRange = siblingFunc(nextPossibleRange); - } - return node; - }; -} - -function unitMatcher( - selection: SelectionWithEditor, - node: SyntaxNode, -): NodeMatcherValue[] | null { - if (node.type !== "declaration") { - return null; - } - - return node.descendantsOfType("unit").map((n) => ({ - node: n, - selection: simpleSelectionExtractor(selection.editor, n), - })); -} - -const nodeMatchers: Partial< - Record -> = { - condition: conditionMatcher("condition"), - statement: cascadingMatcher( - patternMatcher(...STATEMENT_TYPES), - matcher( - patternFinder("attribute_selector"), - childRangeSelector([], ["attribute_name", "string_value"]), - ), - ), - argumentOrParameter: cascadingMatcher( - matcher( - patternFinder("arguments.*!", "parameters.*!"), - delimitedSelector( - (node) => isArgumentListDelimiter(node), - ", ", - findAdjacentArgValues((node) => node.previousSibling), - findAdjacentArgValues((node) => node.nextSibling), - ), - ), - ), - collectionKey: trailingMatcher(["declaration.property_name!"], [":"]), - value: cascadingMatcher( - matcher( - patternFinder("declaration"), - childRangeSelector(["property_name", "variable_name"]), - ), - matcher( - patternFinder("include_statement", "namespace_statement"), - childRangeSelector(), - ), - patternMatcher( - "return_statement.*!", - "import_statement.*!", - "attribute_selector.plain_value!", - "attribute_selector.string_value!", - "parameter.default_value!", - ), - ), - unit: cascadingMatcher(patternMatcher("integer_value.unit!"), unitMatcher), -}; - -export const patternMatchers = createPatternMatchers(nodeMatchers); diff --git a/queries/css.scm b/queries/css.scm index 2a2543227b..4ffd0d047b 100644 --- a/queries/css.scm +++ b/queries/css.scm @@ -1,34 +1,140 @@ ;; https://github.com/tree-sitter/tree-sitter-css/blob/master/src/grammar.json -(string_value) @string @textFragment +[ + (at_rule) + (charset_statement) + (import_statement) + (keyframes_statement) + (media_statement) + (namespace_statement) + (rule_set) + (supports_statement) +] @statement -(comment) @comment @textFragment +;;!! "hello" +;;! ^^^^^ +( + (string_value) @string @textFragment + (#character-range! @textFragment 1 -1) +) -(declaration) @collectionItem +;;!! /* Hello */ +;;! ^^^^^^^^^^^ +(comment) @comment @textFragment +;;!! calc(100% - 50px); +;;! ^^^^^^^^^^^^^^^^^ (call_expression (function_name) @functionCallee ) @functionCall @functionCallee.domain +;;!! div {} +;;! ^^^ (rule_set (selectors) @selector ) @_.domain +;;!! width: 100px; (declaration - (property_name) @name -) @_.domain + (property_name) @name @collectionKey + . + (_) @value.start + (_)? @value.end + . +) @collectionItem @_.domain + +;;!! a[target="_blank"] +;;! ^^^^^^ (attribute_selector (attribute_name) @name + (string_value) @value ) @_.domain -(stylesheet) @name.iteration +;;!! @import "subs.css" +;;! ^^^^^^^^^^ +(import_statement + (_) @value +) @_.domain + +;;!! translate(-50%, -50%) +;;! ^^^^ ^^^^ +( + (arguments + (_)? @_.leading.endOf + . + [ + "(" + "," + ( + (plain_value) @_dummy + (#eq? @_dummy "at") + ) + ] + . + (_) @argumentOrParameter + ( + [ + "," + ( + (plain_value) @_dummy + (#eq? @_dummy "at") + ) + ] + . + (_) @_.trailing.startOf + )? + ) + (#grow-to-named-siblings! @argumentOrParameter "at") + (#insertion-delimiter! @argumentOrParameter ", ") +) + +(arguments + . + "(" @argumentOrParameter.iteration.start.endOf + ")" @argumentOrParameter.iteration.end.startOf + . +) @argumentOrParameter.iteration.domain + +;; Entire file +(stylesheet) @name.iteration @collectionKey.iteration @value.iteration +(stylesheet) @statement.iteration + +;; { } (block . - "{" @name.iteration.start.endOf - "}" @name.iteration.end.startOf + "{" @name.iteration.start.endOf @collectionKey.iteration.start.endOf @value.iteration.start.endOf + "}" @name.iteration.end.startOf @collectionKey.iteration.end.startOf @value.iteration.end.startOf . -) +) @map + +;;!! width: 100px; +;;! ^^ +(declaration + (integer_value + (unit) @unit + ) + (#allow-multiple! @unit) +) @_.domain + +(integer_value + (unit) @unit +) @_.domain + +;;!! @namespace prefix "XML-namespace-URL"; +;;! ^^^^^^^^^^^^^^^^^^^^^^^^^^ +(namespace_statement + (namespace_name) @value.start + (string_value) @value.end +) @_.domain + +;;!! @namespace url(http://www.w3.org/1999/xhtml); +;;! ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +(namespace_statement + (call_expression) @value +) @_.domain +;;!! div > a +;;! ^ (child_selector ">" @disqualifyDelimiter ) diff --git a/queries/scss.scm b/queries/scss.scm index 6cbdf48334..5370f3edd7 100644 --- a/queries/scss.scm +++ b/queries/scss.scm @@ -2,10 +2,44 @@ ;; https://github.com/serenadeai/tree-sitter-scss/blob/master/src/grammar.json +;; curl https://raw.githubusercontent.com/serenadeai/tree-sitter-scss/c478c6868648eff49eb04a4df90d703dc45b312a/src/node-types.json \ +;; | jq '[.[] | select(.type =="stylesheet") | .children.types[] | select(.type !="declaration") | .type ]' + +[ + (apply_statement) + (at_rule) + (charset_statement) + (debug_statement) + (each_statement) + (error_statement) + (for_statement) + (forward_statement) + (function_statement) + (if_statement) + (import_statement) + (include_statement) + (keyframes_statement) + (media_statement) + (mixin_statement) + (namespace_statement) + (placeholder) + (rule_set) + (supports_statement) + (use_statement) + (warn_statement) + (while_statement) +] @statement + (single_line_comment) @comment @textFragment (if_statement) @ifStatement +(if_statement + (if_clause + (condition) @condition + ) +) @_.domain + (mixin_statement (name) @functionName @name ) @namedFunction @functionName.domain @name.domain @@ -17,9 +51,45 @@ (declaration (variable_name) @name ) @_.domain -(parameter - (variable_name) @name -) @_.domain + +;;!! replace-text($image, $color: red) +;;! ^^^^^^ ^^^^^^^^^^^ +( + (parameters + (_)? @_.leading.endOf + . + (_) @argumentOrParameter + . + (_)? @_.trailing.startOf + ) + (#insertion-delimiter! @argumentOrParameter ", ") +) + +(_ + (parameters + . + "(" @argumentOrParameter.iteration.start.endOf + ")" @argumentOrParameter.iteration.end.startOf + . + ) +) @argumentOrParameter.iteration.domain + +(parameters + . + "(" @name.iteration.start.endOf @value.iteration.start.endOf + ")" @name.iteration.end.startOf @value.iteration.end.startOf + . +) @name.iteration.domain @value.iteration.domain + +;;!! foo($foo: 123) +;;! ^^^^ ^^^ +( + (parameter + (variable_name) @name + (default_value)? @value + ) @_.domain + (#not-eq? @_.domain "") +) (stylesheet) @namedFunction.iteration @functionName.iteration (block @@ -37,3 +107,16 @@ ">=" ] @disqualifyDelimiter ) + +;;!! @include rtl(float, left, right); +;;! ^^^^^^^^^^^^^^^^^^^^^^^ +(include_statement + (identifier) @value.start + (arguments) @value.end +) @_.domain + +;;!! @return 123 +;;! ^^^ +(return_statement + (_) @value +) @_.domain