diff --git a/.gitignore b/.gitignore index fa67b4db01..ab3f0caee6 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,6 @@ node_modules/ *.vsix .DS_Store .luacheckcache + +# Swift Test Project +/resources/playground/swift/cursorless-test-project/.build diff --git a/packages/app-web-docs/src/docs/user/languages/swift.mdx b/packages/app-web-docs/src/docs/user/languages/swift.mdx new file mode 100644 index 0000000000..3735a04806 --- /dev/null +++ b/packages/app-web-docs/src/docs/user/languages/swift.mdx @@ -0,0 +1,5 @@ +import { Language } from "./components/Language"; + +# Swift + + diff --git a/packages/lib-common/src/scopeSupportFacets/languageScopeSupport.ts b/packages/lib-common/src/scopeSupportFacets/languageScopeSupport.ts index 39d5d106f7..ac6beb2ca9 100644 --- a/packages/lib-common/src/scopeSupportFacets/languageScopeSupport.ts +++ b/packages/lib-common/src/scopeSupportFacets/languageScopeSupport.ts @@ -27,6 +27,7 @@ import { scalaScopeSupport } from "./scala"; import { scmScopeSupport } from "./scm"; import type { LanguageScopeSupportFacetMap } from "./scopeSupportFacets.types"; import { scssScopeSupport } from "./scss"; +import { swiftScopeSupport } from "./swift"; import { talonScopeSupport } from "./talon"; import { talonListScopeSupport } from "./talonList"; import { typescriptScopeSupport } from "./typescript"; @@ -64,6 +65,7 @@ export const languageScopeSupport: StringRecord = scala: scalaScopeSupport, scm: scmScopeSupport, scss: scssScopeSupport, + swift: swiftScopeSupport, "talon-list": talonListScopeSupport, talon: talonScopeSupport, typescript: typescriptScopeSupport, diff --git a/packages/lib-common/src/scopeSupportFacets/swift.ts b/packages/lib-common/src/scopeSupportFacets/swift.ts new file mode 100644 index 0000000000..cdb3fb9623 --- /dev/null +++ b/packages/lib-common/src/scopeSupportFacets/swift.ts @@ -0,0 +1,386 @@ +import type { LanguageScopeSupportFacetMap } from "./scopeSupportFacets.types"; +import { ScopeSupportFacetLevel } from "./scopeSupportFacets.types"; + +const { supported, unsupported, notApplicable } = ScopeSupportFacetLevel; + +export const swiftScopeSupport: LanguageScopeSupportFacetMap = { + /* SUPPORTED OR UNSUPPORTED (PLANNED) */ + + // if/else/elif + ifStatement: supported, + "branch.if": supported, + "branch.if.else": supported, + "branch.if.elif.else": supported, + "branch.if.iteration": supported, + "condition.if": supported, + "interior.if": supported, + + // ternary operator + "branch.ternary": unsupported, + "branch.ternary.iteration": unsupported, + "condition.ternary": unsupported, + + // for loop + "statement.foreach": supported, + "name.foreach": supported, + "value.foreach": supported, + "type.foreach": supported, + + // while loop + "statement.while": unsupported, + "condition.while": unsupported, + "interior.while": unsupported, + + // repeat-while loop (equivalent to do-while loops in other languages) + "statement.doWhile": unsupported, + "condition.doWhile": unsupported, + "interior.doWhile": unsupported, + + // do-catch (equivalent to try-catch in other languages) + // we're probably going to want a new scope facet for swift's try statements (`statement.tryErrorable`?) + "statement.try": unsupported, + "branch.try": unsupported, + "branch.try.iteration": unsupported, + "interior.try": unsupported, + + // switch + "statement.switch": unsupported, + "branch.switchCase": unsupported, + "branch.switchCase.iteration": unsupported, + "condition.switchCase": unsupported, + "condition.switchCase.iteration": unsupported, + "value.switch": unsupported, + "interior.switch": unsupported, + "interior.switchCase": unsupported, + + // misc control transfer (returns, throw/break/continue statements, etc) + "statement.return": unsupported, + "value.return": unsupported, + "value.return.lambda": unsupported, + "statement.throw": unsupported, + "value.throw": unsupported, + "statement.break": unsupported, + "statement.continue": unsupported, + + // enum + "statement.enum": supported, + "name.enum": supported, + "name.iteration.enum": supported, + "type.enum": supported, + "interior.enum": supported, + "value.iteration.enum": supported, + + // class + class: supported, + "statement.class": supported, + "name.class": supported, + + // protocol (equivalent to interfaces in other languages) + "statement.interface": supported, + "name.interface": supported, + + // "standard" functions & methods + namedFunction: unsupported, + "namedFunction.method": unsupported, + "statement.function": unsupported, + "statement.method": unsupported, + "name.function": unsupported, + "name.method": unsupported, + + // constructors + "namedFunction.constructor": unsupported, + "statement.constructor": unsupported, + "name.constructor": unsupported, + + // protocol method declarations + "statement.method.interface": unsupported, + "name.method.interface": unsupported, + + // closures/lambda functions + anonymousFunction: unsupported, + "interior.lambda": unsupported, + + // function calls + functionCall: unsupported, + "functionCall.constructor": unsupported, + "functionCall.method": unsupported, + "functionCall.chain": unsupported, + "functionCall.generic": unsupported, + "functionCall.enum": unsupported, + + // function callee + functionCallee: unsupported, + "functionCallee.constructor": unsupported, + "functionCallee.method": unsupported, + "functionCallee.chain": unsupported, + "functionCallee.generic": unsupported, + "functionCallee.enum": unsupported, + + // argument (actual) + "argument.actual.singleLine": unsupported, + "argument.actual.multiLine": unsupported, + "argument.actual.iteration": unsupported, + "argument.actual.method.singleLine": unsupported, + "argument.actual.method.multiLine": unsupported, + "argument.actual.method.iteration": unsupported, + "argument.actual.constructor.singleLine": unsupported, + "argument.actual.constructor.multiLine": unsupported, + "argument.actual.constructor.iteration": unsupported, + "argument.actual.enum.singleLine": unsupported, + "argument.actual.enum.multiLine": unsupported, + "argument.actual.enum.iteration": unsupported, + + // argument (formal) + "argument.formal.singleLine": unsupported, + "argument.formal.multiLine": unsupported, + "argument.formal.iteration": unsupported, + "argument.formal.method.singleLine": unsupported, + "argument.formal.method.multiLine": unsupported, + "argument.formal.method.iteration": unsupported, + "argument.formal.constructor.singleLine": unsupported, + "argument.formal.constructor.multiLine": unsupported, + "argument.formal.constructor.iteration": unsupported, + "argument.formal.lambda.singleLine": unsupported, + "argument.formal.lambda.multiLine": unsupported, + "argument.formal.lambda.iteration": unsupported, + "argument.formal.catch": unsupported, + + // argument list (actual) + "argumentList.actual.empty": unsupported, + "argumentList.actual.singleLine": unsupported, + "argumentList.actual.multiLine": unsupported, + "argumentList.actual.method.empty": unsupported, + "argumentList.actual.method.singleLine": unsupported, + "argumentList.actual.method.multiLine": unsupported, + "argumentList.actual.constructor.empty": unsupported, + "argumentList.actual.constructor.singleLine": unsupported, + "argumentList.actual.constructor.multiLine": unsupported, + "argumentList.actual.enum.empty": unsupported, + "argumentList.actual.enum.singleLine": unsupported, + "argumentList.actual.enum.multiLine": unsupported, + + // argument list (formal) + "argumentList.formal.empty": unsupported, + "argumentList.formal.singleLine": unsupported, + "argumentList.formal.multiLine": unsupported, + "argumentList.formal.lambda.empty": unsupported, + "argumentList.formal.lambda.singleLine": unsupported, + "argumentList.formal.lambda.multiLine": unsupported, + "argumentList.formal.method.empty": unsupported, + "argumentList.formal.method.singleLine": unsupported, + "argumentList.formal.method.multiLine": unsupported, + "argumentList.formal.constructor.empty": unsupported, + "argumentList.formal.constructor.singleLine": unsupported, + "argumentList.formal.constructor.multiLine": unsupported, + + // variables and constants (var, let) + fieldAccess: unsupported, + + "statement.field.class": unsupported, + "statement.field.interface": unsupported, + "statement.variable.uninitialized": unsupported, + "statement.variable.initialized": unsupported, + "statement.variable.destructuring": unsupported, + "statement.constant": unsupported, + + "name.field.class": notApplicable, + "name.field.interface": notApplicable, + "name.field.enum": notApplicable, + "name.variable.uninitialized": unsupported, + "name.variable.initialized": unsupported, + "name.variable.destructuring": unsupported, + "name.constant": unsupported, + + "value.constant": unsupported, + "value.field.class": unsupported, + "value.field.interface": unsupported, + "value.field.enum": unsupported, + + // assignments + "statement.assignment": unsupported, + "statement.assignment.destructuring": unsupported, + "statement.assignment.compound": unsupported, + "name.assignment": unsupported, + "name.assignment.destructuring": unsupported, + "name.assignment.compound": unsupported, + + // comments + "comment.line": supported, + "textFragment.comment.line": supported, + "textFragment.comment.block": supported, + "comment.block": supported, + + // strings + "string.singleLine": supported, + "string.multiLine": supported, + "textFragment.string.multiLine": supported, + "textFragment.string.singleLine": supported, + + // document-wide iteration + "statement.iteration.document": supported, + "class.iteration.document": supported, + "namedFunction.iteration.document": supported, + "name.iteration.document": supported, + "value.iteration.document": supported, + "type.iteration.document": supported, + + // per-class iteration + "statement.iteration.class": supported, + "class.iteration.class": supported, + "namedFunction.iteration.class": supported, + "name.iteration.class": supported, + "value.iteration.class": supported, + "type.iteration.class": supported, + + // per-protocol iteration + "statement.iteration.interface": supported, + "name.iteration.interface": supported, + "type.iteration.interface": supported, + + // per-block iteration -- todo: do classlikes, functions, protocols, branches, etc. count as blocks? + "statement.iteration.block": unsupported, + "name.iteration.block": unsupported, + "value.iteration.block": unsupported, + "type.iteration.block": unsupported, + + // unenclosed collection item + "collectionItem.unenclosed.singleLine": unsupported, + "collectionItem.unenclosed.multiLine": unsupported, + "collectionItem.unenclosed.iteration": unsupported, + + // enclosed collections + map: unsupported, + list: unsupported, + "key.mapPair": unsupported, + "key.mapPair.iteration": unsupported, + + // misc + regularExpression: unsupported, + disqualifyDelimiter: unsupported, + pairDelimiter: unsupported, + "name.typeAlias": unsupported, + "statement.typeAlias": unsupported, + "statement.misc": unsupported, + // todo: do static variables/constants fulfill the scope facet "statement.static", or is that only for static blocks? + // "statement.static": unsupported, + + /* NOT APPLICABLE */ + + // c-style for loop + "statement.for": notApplicable, + "condition.for": notApplicable, + "interior.for": notApplicable, + + // loop branches + "branch.loop": notApplicable, + "branch.loop.iteration": notApplicable, + + // XML/CSS/LaTeX/Markdown specific + section: notApplicable, + "section.iteration.document": notApplicable, + "section.iteration.parent": notApplicable, + element: notApplicable, + tags: notApplicable, + startTag: notApplicable, + endTag: notApplicable, + "interior.element": notApplicable, + "textFragment.element": notApplicable, + attribute: notApplicable, + "key.attribute": notApplicable, + "value.attribute": notApplicable, + environment: notApplicable, + notebookCell: notApplicable, + selector: notApplicable, + unit: notApplicable, + "interior.cell": notApplicable, + + // Command + command: notApplicable, + "statement.command": notApplicable, + "name.command": notApplicable, + "value.command": notApplicable, + "interior.command": notApplicable, + + // Resource + "statement.resource": notApplicable, + "name.resource": notApplicable, + "value.resource": notApplicable, + "type.resource": notApplicable, + "interior.resource": notApplicable, + + // Explicit namespace declarations + "statement.namespace": notApplicable, + "name.namespace": notApplicable, + "interior.namespace": notApplicable, + + // Misc + "statement.update": notApplicable, + "statement.package": notApplicable, + "statement.yield": notApplicable, + "value.yield": notApplicable, + "interior.static": notApplicable, + + // TODO: reorganize these!!! + "name.argument.actual": notApplicable, + "name.argument.actual.iteration": notApplicable, + "name.argument.formal": notApplicable, + "name.argument.formal.iteration": notApplicable, + "name.argument.formal.method": notApplicable, + "name.argument.formal.method.iteration": notApplicable, + "name.argument.formal.lambda": notApplicable, + "name.argument.formal.lambda.iteration": notApplicable, + "name.argument.formal.constructor": notApplicable, + "name.argument.formal.constructor.iteration": notApplicable, + "name.argument.catch": notApplicable, + + "value.variable": notApplicable, + "value.variable.destructuring": notApplicable, + "value.assignment": notApplicable, + "value.assignment.destructuring": notApplicable, + "value.assignment.compound": notApplicable, + "value.mapPair": notApplicable, + "value.mapPair.iteration": notApplicable, + "value.typeAlias": notApplicable, + "value.argument.actual": notApplicable, + "value.argument.actual.iteration": notApplicable, + "value.argument.formal": notApplicable, + "value.argument.formal.iteration": notApplicable, + "value.argument.formal.method": notApplicable, + "value.argument.formal.method.iteration": notApplicable, + "value.argument.formal.constructor": notApplicable, + "value.argument.formal.constructor.iteration": notApplicable, + "value.argument.formal.lambda": notApplicable, + "value.argument.formal.lambda.iteration": notApplicable, + + "type.variable.uninitialized": notApplicable, + "type.variable.initialized": notApplicable, + "type.constant": notApplicable, + "type.return": notApplicable, + "type.return.method": notApplicable, + "type.return.lambda": notApplicable, + "type.field.class": notApplicable, + "type.field.interface": notApplicable, + "type.alias": notApplicable, + "type.cast": notApplicable, + "type.class": notApplicable, + "type.interface": notApplicable, + "type.typeArgument": notApplicable, + "type.typeArgument.iteration": notApplicable, + "type.argument.formal": notApplicable, + "type.argument.formal.iteration": notApplicable, + "type.argument.formal.method": notApplicable, + "type.argument.formal.method.iteration": notApplicable, + "type.argument.formal.lambda": notApplicable, + "type.argument.formal.lambda.iteration": notApplicable, + "type.argument.formal.constructor": notApplicable, + "type.argument.formal.constructor.iteration": notApplicable, + "type.argument.catch": notApplicable, + + "interior.class": notApplicable, + "interior.interface": notApplicable, + "interior.function": notApplicable, + "interior.constructor": notApplicable, + "interior.method": notApplicable, + "interior.foreach": notApplicable, + +}; diff --git a/packages/lib-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts b/packages/lib-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts index 796fcfce00..d65ac56d2a 100644 --- a/packages/lib-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts +++ b/packages/lib-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts @@ -92,6 +92,43 @@ class NotParentType extends QueryPredicateOperator { } } +/** + * A predicate operator that returns true if no node from which this node + * descends is of the given type. For example, + * + * ```scm + * (#not-any-ancestor-type? @foo string) + * ``` + * + * will reject the match if the `@foo` capture has any ancestor which is a `string` node, + * taking into account the capture's parent, said parent's parent, and so + * on until a root node is reached. + * + * Like with `#not-parent-type?`, you may pass multiple types to this predicate, in which case + * the math will be rejected has any of said types as any of its ancestor nodes. + */ +class NotAnyAncestorType extends QueryPredicateOperator { + name = "not-any-ancestor-type?" as const; + schema = z.tuple([q.node, q.string]).rest(q.string); + run(capture: MutableQueryCapture, ...types: string[]) { + var target = getNode(capture).parent; + const ancestorTypes: string[] = [] + if (target != null) { + while (target != null) { + ancestorTypes.push(target.type); + target = target.parent; + } + for (const type of types) { + if (ancestorTypes.includes(type)) { + return false; + } + } + } + // Fall through if node has no parent or if ancestorTypes and types have no matches + return true; + } +} + /** * A predicate operator that returns true if the node is the nth child of its * parent. For example, `(#is-nth-child? @foo 0)` will reject the match if the @@ -462,6 +499,7 @@ export const queryPredicateOperators = [ new TrimEnd(), new DocumentRange(), new NotParentType(), + new NotAnyAncestorType(), new IsNthChild(), new ChildRange(), new CharacterRange(), diff --git a/resources/fixtures/scopes/swift/branch.if.elif.else.scope b/resources/fixtures/scopes/swift/branch.if.elif.else.scope new file mode 100644 index 0000000000..c8aa5e8b4e --- /dev/null +++ b/resources/fixtures/scopes/swift/branch.if.elif.else.scope @@ -0,0 +1,95 @@ +if foo {} +else if bar {} +else {} +--- + +[#1 Content] = +[#1 Domain] = 0:0-0:9 + >---------< +0| if foo {} + +[#1 Removal] = 0:0-1:0 + >--------- +0| if foo {} +1| else if bar {} + < + +[#1 Insertion delimiter] = "\n" + + +[#2 Content] = +[#2 Domain] = 1:0-1:14 + >--------------< +1| else if bar {} + +[#2 Removal] = 1:0-2:0 + >-------------- +1| else if bar {} +2| else {} + < + +[#2 Insertion delimiter] = "\n" + + +[#3 Content] = +[#3 Domain] = 1:0-2:7 + >-------------- +1| else if bar {} +2| else {} + -------< + +[#3 Removal] = 0:9-2:7 + > +0| if foo {} +1| else if bar {} +2| else {} + -------< + +[#3 Insertion delimiter] = "\n" + + +[#4 Content] = +[#4 Domain] = 1:5-1:14 + >---------< +1| else if bar {} + +[#4 Removal] = 1:5-2:0 + >--------- +1| else if bar {} +2| else {} + < + +[#4 Leading delimiter] = 1:4-1:5 + >-< +1| else if bar {} + +[#4 Insertion delimiter] = "\n" + + +[#5 Content] = +[#5 Removal] = +[#5 Domain] = 1:5-2:7 + >--------- +1| else if bar {} +2| else {} + -------< + +[#5 Leading delimiter] = 1:4-1:5 + >-< +1| else if bar {} + +[#5 Insertion delimiter] = "\n" + + +[#6 Content] = +[#6 Domain] = 2:0-2:7 + >-------< +2| else {} + +[#6 Removal] = 1:14-2:7 + > +1| else if bar {} +2| else {} + -------< + +[#6 Insertion delimiter] = "\n" diff --git a/resources/fixtures/scopes/swift/branch.if.else.scope b/resources/fixtures/scopes/swift/branch.if.else.scope new file mode 100644 index 0000000000..fb73666f03 --- /dev/null +++ b/resources/fixtures/scopes/swift/branch.if.else.scope @@ -0,0 +1,41 @@ +if true {} +else {} +--- + +[#1 Content] = +[#1 Domain] = 0:0-0:10 + >----------< +0| if true {} + +[#1 Removal] = 0:0-1:0 + >---------- +0| if true {} +1| else {} + < + +[#1 Insertion delimiter] = "\n" + + +[#2 Content] = +[#2 Removal] = +[#2 Domain] = 0:0-1:7 + >---------- +0| if true {} +1| else {} + -------< + +[#2 Insertion delimiter] = "\n" + + +[#3 Content] = +[#3 Domain] = 1:0-1:7 + >-------< +1| else {} + +[#3 Removal] = 0:10-1:7 + > +0| if true {} +1| else {} + -------< + +[#3 Insertion delimiter] = "\n" diff --git a/resources/fixtures/scopes/swift/branch.if.iteration.scope b/resources/fixtures/scopes/swift/branch.if.iteration.scope new file mode 100644 index 0000000000..4284edc393 --- /dev/null +++ b/resources/fixtures/scopes/swift/branch.if.iteration.scope @@ -0,0 +1,12 @@ +if foo {} +else if bar {} +else {} +--- + +[Content] = +[Domain] = 0:0-2:7 + >--------- +0| if foo {} +1| else if bar {} +2| else {} + -------< diff --git a/resources/fixtures/scopes/swift/branch.if.scope b/resources/fixtures/scopes/swift/branch.if.scope new file mode 100644 index 0000000000..95d75aa3fb --- /dev/null +++ b/resources/fixtures/scopes/swift/branch.if.scope @@ -0,0 +1,10 @@ +if true {} +--- + +[Content] = +[Removal] = +[Domain] = 0:0-0:10 + >----------< +0| if true {} + +[Insertion delimiter] = "\n" diff --git a/resources/fixtures/scopes/swift/class.scope b/resources/fixtures/scopes/swift/class.scope new file mode 100644 index 0000000000..80a9ab592c --- /dev/null +++ b/resources/fixtures/scopes/swift/class.scope @@ -0,0 +1,10 @@ +struct Foo {} +--- + +[Content] = +[Removal] = +[Domain] = 0:0-0:13 + >-------------< +0| struct Foo {} + +[Insertion delimiter] = "\n\n" diff --git a/resources/fixtures/scopes/swift/comment.block.scope b/resources/fixtures/scopes/swift/comment.block.scope new file mode 100644 index 0000000000..477e57d0ee --- /dev/null +++ b/resources/fixtures/scopes/swift/comment.block.scope @@ -0,0 +1,15 @@ +/* +aaa +*/ +--- + +[Content] = +[Removal] = +[Domain] = 0:0-2:2 + >-- +0| /* +1| aaa +2| */ + --< + +[Insertion delimiter] = "\n" diff --git a/resources/fixtures/scopes/swift/comment.line.scope b/resources/fixtures/scopes/swift/comment.line.scope new file mode 100644 index 0000000000..a469659c87 --- /dev/null +++ b/resources/fixtures/scopes/swift/comment.line.scope @@ -0,0 +1,10 @@ +// aaa +--- + +[Content] = +[Removal] = +[Domain] = 0:0-0:6 + >------< +0| // aaa + +[Insertion delimiter] = "\n" diff --git a/resources/fixtures/scopes/swift/condition.if.scope b/resources/fixtures/scopes/swift/condition.if.scope new file mode 100644 index 0000000000..39fee6c27b --- /dev/null +++ b/resources/fixtures/scopes/swift/condition.if.scope @@ -0,0 +1,24 @@ +if foo {} +--- + +[Content] = 0:3-0:6 + >---< +0| if foo {} + +[Removal] = 0:3-0:7 + >----< +0| if foo {} + +[Leading delimiter] = 0:2-0:3 + >-< +0| if foo {} + +[Trailing delimiter] = 0:6-0:7 + >-< +0| if foo {} + +[Domain] = 0:0-0:9 + >---------< +0| if foo {} + +[Insertion delimiter] = " " diff --git a/resources/fixtures/scopes/swift/ifStatement.scope b/resources/fixtures/scopes/swift/ifStatement.scope new file mode 100644 index 0000000000..95d75aa3fb --- /dev/null +++ b/resources/fixtures/scopes/swift/ifStatement.scope @@ -0,0 +1,10 @@ +if true {} +--- + +[Content] = +[Removal] = +[Domain] = 0:0-0:10 + >----------< +0| if true {} + +[Insertion delimiter] = "\n" diff --git a/resources/fixtures/scopes/swift/interior.if.scope b/resources/fixtures/scopes/swift/interior.if.scope new file mode 100644 index 0000000000..9a35dae364 --- /dev/null +++ b/resources/fixtures/scopes/swift/interior.if.scope @@ -0,0 +1,10 @@ +if foo { } +--- + +[Content] = +[Removal] = +[Domain] = 0:8-0:10 + >--< +0| if foo { } + +[Insertion delimiter] = " " diff --git a/resources/fixtures/scopes/swift/name.class.scope b/resources/fixtures/scopes/swift/name.class.scope new file mode 100644 index 0000000000..c4ecaeeb34 --- /dev/null +++ b/resources/fixtures/scopes/swift/name.class.scope @@ -0,0 +1,21 @@ +class Foo {} +--- + +[Content] = +[Domain] = 0:6-0:9 + >---< +0| class Foo {} + +[Removal] = 0:6-0:10 + >----< +0| class Foo {} + +[Leading delimiter] = 0:5-0:6 + >-< +0| class Foo {} + +[Trailing delimiter] = 0:9-0:10 + >-< +0| class Foo {} + +[Insertion delimiter] = " " diff --git a/resources/fixtures/scopes/swift/name.enum.scope b/resources/fixtures/scopes/swift/name.enum.scope new file mode 100644 index 0000000000..2162ad8604 --- /dev/null +++ b/resources/fixtures/scopes/swift/name.enum.scope @@ -0,0 +1,21 @@ +enum Foo {} +--- + +[Content] = +[Domain] = 0:5-0:8 + >---< +0| enum Foo {} + +[Removal] = 0:5-0:9 + >----< +0| enum Foo {} + +[Leading delimiter] = 0:4-0:5 + >-< +0| enum Foo {} + +[Trailing delimiter] = 0:8-0:9 + >-< +0| enum Foo {} + +[Insertion delimiter] = " " diff --git a/resources/fixtures/scopes/swift/name.foreach.scope b/resources/fixtures/scopes/swift/name.foreach.scope new file mode 100644 index 0000000000..c8e077322e --- /dev/null +++ b/resources/fixtures/scopes/swift/name.foreach.scope @@ -0,0 +1,24 @@ +for foo in bar {} +--- + +[Content] = 0:4-0:7 + >---< +0| for foo in bar {} + +[Removal] = 0:4-0:8 + >----< +0| for foo in bar {} + +[Leading delimiter] = 0:3-0:4 + >-< +0| for foo in bar {} + +[Trailing delimiter] = 0:7-0:8 + >-< +0| for foo in bar {} + +[Domain] = 0:0-0:17 + >-----------------< +0| for foo in bar {} + +[Insertion delimiter] = " " diff --git a/resources/fixtures/scopes/swift/name.interface.scope b/resources/fixtures/scopes/swift/name.interface.scope new file mode 100644 index 0000000000..7268b1a3e0 --- /dev/null +++ b/resources/fixtures/scopes/swift/name.interface.scope @@ -0,0 +1,21 @@ +protocol Foo {} +--- + +[Content] = +[Domain] = 0:9-0:12 + >---< +0| protocol Foo {} + +[Removal] = 0:9-0:13 + >----< +0| protocol Foo {} + +[Leading delimiter] = 0:8-0:9 + >-< +0| protocol Foo {} + +[Trailing delimiter] = 0:12-0:13 + >-< +0| protocol Foo {} + +[Insertion delimiter] = " " diff --git a/resources/fixtures/scopes/swift/statement.class.scope b/resources/fixtures/scopes/swift/statement.class.scope new file mode 100644 index 0000000000..a6008d311f --- /dev/null +++ b/resources/fixtures/scopes/swift/statement.class.scope @@ -0,0 +1,10 @@ +class Foo {} +--- + +[Content] = +[Removal] = +[Domain] = 0:0-0:12 + >------------< +0| class Foo {} + +[Insertion delimiter] = "\n" diff --git a/resources/fixtures/scopes/swift/statement.enum.scope b/resources/fixtures/scopes/swift/statement.enum.scope new file mode 100644 index 0000000000..88dd9cd028 --- /dev/null +++ b/resources/fixtures/scopes/swift/statement.enum.scope @@ -0,0 +1,10 @@ +enum Foo {} +--- + +[Content] = +[Removal] = +[Domain] = 0:0-0:11 + >-----------< +0| enum Foo {} + +[Insertion delimiter] = "\n" diff --git a/resources/fixtures/scopes/swift/statement.foreach.scope b/resources/fixtures/scopes/swift/statement.foreach.scope new file mode 100644 index 0000000000..005a6562ec --- /dev/null +++ b/resources/fixtures/scopes/swift/statement.foreach.scope @@ -0,0 +1,10 @@ +for foo in bar {} +--- + +[Content] = +[Removal] = +[Domain] = 0:0-0:17 + >-----------------< +0| for foo in bar {} + +[Insertion delimiter] = "\n" diff --git a/resources/fixtures/scopes/swift/statement.interface.scope b/resources/fixtures/scopes/swift/statement.interface.scope new file mode 100644 index 0000000000..ea6dad63a6 --- /dev/null +++ b/resources/fixtures/scopes/swift/statement.interface.scope @@ -0,0 +1,10 @@ +protocol Foo {} +--- + +[Content] = +[Removal] = +[Domain] = 0:0-0:15 + >---------------< +0| protocol Foo {} + +[Insertion delimiter] = "\n" diff --git a/resources/fixtures/scopes/swift/string.multiLine.scope b/resources/fixtures/scopes/swift/string.multiLine.scope new file mode 100644 index 0000000000..e17fb9fb0f --- /dev/null +++ b/resources/fixtures/scopes/swift/string.multiLine.scope @@ -0,0 +1,15 @@ +""" +aaa +""" +--- + +[Content] = +[Removal] = +[Domain] = 0:0-2:3 + >--- +0| """ +1| aaa +2| """ + ---< + +[Insertion delimiter] = " " diff --git a/resources/fixtures/scopes/swift/string.singleLine.scope b/resources/fixtures/scopes/swift/string.singleLine.scope new file mode 100644 index 0000000000..f0d1750ce4 --- /dev/null +++ b/resources/fixtures/scopes/swift/string.singleLine.scope @@ -0,0 +1,10 @@ +"aaa" +--- + +[Content] = +[Removal] = +[Domain] = 0:0-0:5 + >-----< +0| "aaa" + +[Insertion delimiter] = " " diff --git a/resources/fixtures/scopes/swift/textFragment.comment.block.scope b/resources/fixtures/scopes/swift/textFragment.comment.block.scope new file mode 100644 index 0000000000..2bc3b6e027 --- /dev/null +++ b/resources/fixtures/scopes/swift/textFragment.comment.block.scope @@ -0,0 +1,15 @@ +/* +aaa +*/ +--- + +[Content] = +[Removal] = +[Domain] = 0:0-2:2 + >-- +0| /* +1| aaa +2| */ + --< + +[Insertion delimiter] = " " diff --git a/resources/fixtures/scopes/swift/textFragment.comment.line.scope b/resources/fixtures/scopes/swift/textFragment.comment.line.scope new file mode 100644 index 0000000000..e47b42fed9 --- /dev/null +++ b/resources/fixtures/scopes/swift/textFragment.comment.line.scope @@ -0,0 +1,10 @@ +// aaa +--- + +[Content] = +[Removal] = +[Domain] = 0:0-0:6 + >------< +0| // aaa + +[Insertion delimiter] = " " diff --git a/resources/fixtures/scopes/swift/textFragment.string.multiLine.scope b/resources/fixtures/scopes/swift/textFragment.string.multiLine.scope new file mode 100644 index 0000000000..43868aa5c8 --- /dev/null +++ b/resources/fixtures/scopes/swift/textFragment.string.multiLine.scope @@ -0,0 +1,15 @@ +""" +aaa +""" +--- + +[Content] = +[Removal] = +[Domain] = 0:3-2:0 + > +0| """ +1| aaa +2| """ + < + +[Insertion delimiter] = " " diff --git a/resources/fixtures/scopes/swift/textFragment.string.singleLine.scope b/resources/fixtures/scopes/swift/textFragment.string.singleLine.scope new file mode 100644 index 0000000000..0ab585ba66 --- /dev/null +++ b/resources/fixtures/scopes/swift/textFragment.string.singleLine.scope @@ -0,0 +1,10 @@ +"aaa" +--- + +[Content] = +[Removal] = +[Domain] = 0:1-0:4 + >---< +0| "aaa" + +[Insertion delimiter] = " " diff --git a/resources/fixtures/scopes/swift/type.foreach.scope b/resources/fixtures/scopes/swift/type.foreach.scope new file mode 100644 index 0000000000..8e867374c0 --- /dev/null +++ b/resources/fixtures/scopes/swift/type.foreach.scope @@ -0,0 +1,20 @@ +for foo: Type in bar {} +--- + +[Content] = 0:9-0:13 + >----< +0| for foo: Type in bar {} + +[Removal] = 0:7-0:13 + >------< +0| for foo: Type in bar {} + +[Leading delimiter] = 0:7-0:9 + >--< +0| for foo: Type in bar {} + +[Domain] = 0:0-0:23 + >-----------------------< +0| for foo: Type in bar {} + +[Insertion delimiter] = " " diff --git a/resources/fixtures/scopes/swift/value.foreach.scope b/resources/fixtures/scopes/swift/value.foreach.scope new file mode 100644 index 0000000000..d1ab264c5d --- /dev/null +++ b/resources/fixtures/scopes/swift/value.foreach.scope @@ -0,0 +1,24 @@ +for foo in bar {} +--- + +[Content] = 0:11-0:14 + >---< +0| for foo in bar {} + +[Removal] = 0:11-0:15 + >----< +0| for foo in bar {} + +[Leading delimiter] = 0:10-0:11 + >-< +0| for foo in bar {} + +[Trailing delimiter] = 0:14-0:15 + >-< +0| for foo in bar {} + +[Domain] = 0:0-0:17 + >-----------------< +0| for foo in bar {} + +[Insertion delimiter] = " " diff --git a/resources/playground/Java.java b/resources/playground/Java.java index 2001b365a2..0b65e8e8d2 100644 --- a/resources/playground/Java.java +++ b/resources/playground/Java.java @@ -2,5 +2,10 @@ public class Java { private Java(String name) { String value = "hello"; System.out.println(value); + if (true) { + //1 + } else if (false) { + //2 + } } } diff --git a/resources/playground/rust.rs b/resources/playground/rust.rs new file mode 100644 index 0000000000..1cfa874e89 --- /dev/null +++ b/resources/playground/rust.rs @@ -0,0 +1,8 @@ +fn hello() { + let apple: Bool = false + if true { + //1 + } else if false { + //2 + } +} \ No newline at end of file diff --git a/resources/playground/swift/cursorless-test-project/Package.swift b/resources/playground/swift/cursorless-test-project/Package.swift new file mode 100644 index 0000000000..09374339a2 --- /dev/null +++ b/resources/playground/swift/cursorless-test-project/Package.swift @@ -0,0 +1,8 @@ +// swift-tools-version: 6.2 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "cursorless-test-project" +) diff --git a/resources/playground/swift/cursorless-test-project/test.swift b/resources/playground/swift/cursorless-test-project/test.swift new file mode 100644 index 0000000000..d425f8e351 --- /dev/null +++ b/resources/playground/swift/cursorless-test-project/test.swift @@ -0,0 +1,92 @@ +struct ExamplesStruct { + static let constant = "hello world" + public var publicVariable: UInt128 = 0 + var uninitializedVariable: String? + internal static func exampleFunction(exampleLabel: String) -> String { + if exampleLabel.isEmpty { + return "goodbye world" + } else if (publicVariable % 2 == 0) { + return constant + ", " + exampleLabel + "!" + } else if (publicVariable % 3 == 0){ + return constant + ", " + exampleLabel + "?!" + } else { + return constant + ", " + exampleLabel + "..." + } + } + public mutating func secondExampleFunction(exampleLabel: String, times: any UnsignedInteger) { + let cast: UInt128 = numericCast(times) + publicVariable += cast + let fizz = publicVariable % 3 == 0 + let buzz: Bool = publicVariable % 5 == 0 + if fizz || buzz { + print((fizz ? "fizz" : "") + (buzz ? "buzz" : "")) + } + for _ in 0 ..< cast { + print(ExamplesStruct.exampleFunction(exampleLabel: exampleLabel)) + } + for c: Character in "test" { + + } + } +} + +let basicMultilineString = """ +I am +a multiline string +!! +""" + +let extendedDelimiter = #"The radio said "No, John. You are the zombie." And then John was the zombie."# + +let multilineExtendedDelimiter = #""" +""""""""" waow +"""# + +enum ExampleEnum { + case exampleCaseOne + case exampleCaseTwo +} +/* + These Examples are Pissing me off... + I'm the original + Multiline walker +*/ + +enum ExampleCompactEnum { + case compactCaseOne, compactCaseTwo +} + +protocol ExampleProtocol { + var setGetMember: String? { set get } + var getOnlyNumber: Double! {get} // using an explicit unwrap type like this is legal, but poor practice in real code +} + +actor ExampleActor { + +} + +func nestedClassInFunc() { + let ternaryDecider = true + let ternaryOp = ternaryDecider ? "foo" : "bar" + let switchExample: UInt8 = 22 + var foobart = 0 + switch switchExample { + case 0: foobart = 5 + case 22: foobart = 42 + default: foobart = 69 + } + class Nested { + func veryNested() { + var i = 0 + while i < 10 { + i += 1 + } + repeat { + i -= 1 + } while i > 0 + struct HyperNested { + + } + } + } +} \ No newline at end of file diff --git a/resources/queries/swift.scm b/resources/queries/swift.scm new file mode 100644 index 0000000000..d2e8e8b8c9 --- /dev/null +++ b/resources/queries/swift.scm @@ -0,0 +1,146 @@ +;; https://github.com/alex-pinkus/tree-sitter-swift/blob/with-generated-files/src/grammar.json + +;; document-wide +(source_file) @class.iteration @statement.iteration @name.iteration @value.iteration @type.iteration +(#document-range! @class.iteration @statement.iteration @name.iteration @value.iteration @type.iteration) + +;; single line comment +(comment) @comment @textFragment + +;; multiline comment +(multiline_comment) @comment @textFragment + +;; single line string +(line_string_literal + text: (_) @interior @textFragment +) @string + +;; multiline string +(multi_line_string_literal + text: (_) @interior @textFragment +) @string + +;; extended delimiter/"raw" strings (both multiline and single line) -- waiting on better tree-sitter support for these +;;(raw_string_literal +;; text: (_) @interior @textFragment +;;) @string + +;; Protocol decl. +(protocol_declaration + name: (_) @name + body: (protocol_body + "{" @interior.start.endOf + "}" @interior.end.startOf + ) +) @statement + +;; if statement +( + (if_statement) @ifStatement @statement @branch.iteration + (#not-parent-type? @ifStatement if_statement) +) + +;; if statement w/ condition & child branches +( + (if_statement + "if" @branch.start @branch.removal.start + condition: (_) @condition + "}" @branch.end @branch.removal.end + (else)? @branch.removal.end.startOf + ) @condition.domain + (#not-parent-type? @condition.domain else) +) + +;; else if +( + (else) @branch.start @condition.domain.start + (if_statement + condition: (_) @condition @condition.domain.end + "}" @branch.end + ) +) + +;; else +( + (else) @branch.start + "}" @branch.end +) + +;; generic property delc +(property_declaration + name: (_) @name + ;;(type_annotation: + ;; ":" + ;;) +) @statement + +;; Generic interior w/ top-level iterations +(_ + "{" @interior.start.endOf @statement.iteration.start.endOf @name.iteration.start.endOf + "}" @interior.end.startOf @statement.iteration.end.startOf @name.iteration.end.startOf +) + +(_ + "{" @value.iteration.start.endOf @type.iteration.start.endOf @namedFunction.iteration.start.endOf + "}" @value.iteration.end.startOf @type.iteration.end.startOf @namedFunction.iteration.end.startOf +) + +;; Generic interior -- class iteration +;; Classlikes (class/struct/actor) can be nested within other classlikes, and within both top-level functions and member functions. +;; They, however, cannot be nested within branches or loops of any kind. +( + ( + (_ + "{" @class.iteration.start.endOf + "}" @class.iteration.end.startOf + ) @_dummy + ) (#type? @_dummy class_declaration function_declaration) + (#not-any-ancestor-type? @_dummy if_statement switch_statement for_statement while_statement) + (#not-any-ancestor-type? @_dummy do_statement repeat_while_statement protocol_declaration) +) + +;; Generic interior -- branch and condition iteration +;; Branches and their conditions cannot be top-level but otherwise have no restrictions +( + ( + (_ + "{" @branch.iteration.start.endOf @condition.iteration.start.endOf + "}" @condition.iteration.end.startOf @branch.iteration.end.startOf + ) @_dummy + ) (#not-parent-type? @_dummy source_file) +) + +;; Non-enum class (struct/class/actor) decl. +(class_declaration + name: (_) @name + body: (class_body + "{" @interior.start.endOf + "}" @interior.end.startOf + . + ) +) @statement @class + +;; Enum "class" decl. +(class_declaration + name: (_) @name @type + body: (enum_class_body + "{" @interior.start.endOf + "}" @interior.end.startOf + . + ) +) @statement + +;; For loop +( + (for_statement + "for" + item: (_) @name + (type_annotation + ":" @type.leading + . + _ @type @name.trailing + )? + "in" + collection: (_) @value + ) @statement @_.domain +)