Skip to content

Commit

Permalink
Add onlyIfDescendantOfNodeWithData and `onlyIfNotDescendantOfNodeWi…
Browse files Browse the repository at this point in the history
…thData`

This hadn't occurred to me until I tried to mark illegal optional-chaining operators in certain contexts, much like how it's done in pulsar-edit#79. But it's a powerful way to match things regardless of their exact ancestry.
  • Loading branch information
savetheclocktower committed Mar 28, 2023
1 parent 07d1283 commit ece5101
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 13 deletions.
39 changes: 34 additions & 5 deletions packages/language-javascript/grammars/ts/highlights.scm
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,28 @@
((object_pattern
(shorthand_property_identifier_pattern) @variable.other.assignment.destructuring.js))

; A variable object destructuring with default value:
; The "foo" in `let { foo = true } = something`
(object_assignment_pattern
(shorthand_property_identifier_pattern) @variable.other.assignment.destructuring.js)

; A variable object alias destructuring:
; The "bar" and "foo" in `let { bar: foo } = something`
(object_pattern
(pair_pattern
; TODO: This arguably isn't an object key.
key: (_) @entity.other.attribute-name.js
value: (identifier) @variable.other.assignment.destructuring.js))

; A variable object alias destructuring with default value:
; The "bar" and "foo" in `let { bar: foo = true } = something`
(object_pattern
(pair_pattern
; TODO: This arguably isn't an object key.
key: (_) @entity.other.attribute-name.js
value: (assignment_pattern
left: (identifier) @variable.other.assignment.destructuring.js)))

; A variable array destructuring:
; The "foo" and "bar" in `let [foo, bar] = something`
(variable_declarator
Expand Down Expand Up @@ -438,11 +457,6 @@
(#match? @constant.other.js "^[A-Z_][A-Z0-9_]*$")
(#set! shy true))

(object_pattern
(pair_pattern
key: (_) @entity.other.attribute-name.js
value: (identifier) @variable.other.assignment.js))

; TODO: What do we do with computed object keys?
;
; { [foo]: "bar" }
Expand Down Expand Up @@ -567,6 +581,21 @@
; use "?." to target it.
(optional_chain) @keyword.operator.accessor.optional-chaining.js

; Optional chaining is illegal…:

; …on the left-hand side of an assignment.
(assignment_expression
left: (_) @_IGNORE_
(#set! prohibitsOptionalChaining true))

; …within a `new` expression.
(new_expression
constructor: (_) @_IGNORE_
(#set! prohibitsOptionalChaining true))

((optional_chain) @invalid.illegal.optional-chain.js
(#set! onlyIfDescendantOfNodeWithData prohibitsOptionalChaining))


; PUNCTUATION
; ===========
Expand Down
13 changes: 8 additions & 5 deletions packages/language-javascript/grammars/ts/indents.scm
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

; 'case' and 'default' need to be indented one level more than their containing
; `switch`.
(["case" "default"] @match
(#set! matchIndentOf parent.parent.startPosition)
(#set! offsetIndent 1))
(["case" "default"] @match
(#set! matchIndentOf parent.parent.startPosition)
(#set! offsetIndent 1))


; An `if` statement without an opening brace should indent the next line…
Expand All @@ -21,6 +21,9 @@
condition: (_) @indent
consequence: (expression_statement) @dedent.next)

(template_substitution "}" @_IGNORE_
(#set! final true))

[
"{"
"("
Expand All @@ -39,5 +42,5 @@
; JSX
; ===

(jsx_opening_element ">" @indent)
(jsx_closing_element ">" @dedent)
(jsx_opening_element ">") @indent
(jsx_closing_element ">") @dedent
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
] @meta.group.capturing.regexp

[
(identity_escape)
(control_escape)
(character_class_escape)
] @constant.character.escape.backslash.regexp
Expand Down
30 changes: 27 additions & 3 deletions src/scope-resolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ class ScopeResolver {
for (let key in props) {
if (!(key in ScopeResolver.TESTS)) { continue; }
let test = ScopeResolver.TESTS[key];
if (!test(existingData, props, node, this.languageLayer)) {
if (!test(existingData, props, node, this)) {
return false;
}
}
Expand Down Expand Up @@ -327,8 +327,8 @@ ScopeResolver.TESTS = {
return node.hasError();
},

onlyIfInjection(existingData, props, node, layer) {
return layer.depth > 0;
onlyIfInjection(existingData, props, node, instance) {
return instance.languageLayer.depth > 0;
},

// Passes only if the given node is the first among its siblings.
Expand Down Expand Up @@ -439,6 +439,30 @@ ScopeResolver.TESTS = {
let { onlyIfNotAncestorOfType: type } = props;
let descendants = node.descendantsOfType(type);
return descendants.length === 0;
},

onlyIfDescendantOfNodeWithData(existingData, props, node, instance) {
let { onlyIfDescendantOfNodeWithData: key } = props;
let current = node;
while (current.parent) {
current = current.parent;
let data = instance.getDataForRange(current);
if (data === undefined) { continue; }
if (key in data) return true;
}
return false;
},

onlyIfNotDescendantOfNodeWithData(existingData, props, node, instance) {
let { onlyIfNotDescendantOfNodeWithData: key } = props;
let current = node;
while (current.parent) {
current = current.parent;
let data = instance.getDataForRange(current);
if (data === undefined) { continue; }
if (key in data) return false;
}
return true;
}
};

Expand Down

0 comments on commit ece5101

Please sign in to comment.