diff --git a/data/playground/swift.swift b/data/playground/swift.swift new file mode 100644 index 0000000000..d794974dcc --- /dev/null +++ b/data/playground/swift.swift @@ -0,0 +1,175 @@ +// Source: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/guidedtour/ + +print("Hello, world!") +// Prints "Hello, world!" + +var myVariable = 42 +myVariable = 50 +let myConstant = 42 + +let apples = 3 +let oranges = 5 + +let quotation = """ + Even though there's whitespace to the left, + the actual lines aren't indented. + Except for this line. + Double quotes (") can appear without being escaped. + + I still have \(apples + oranges) pieces of fruit. + """ + +var fruits = ["strawberries", "limes", "tangerines"] +fruits[1] = "grapes" + +var occupations = [ + "Malcolm": "Captain", + "Kaylee": "Mechanic", +] +occupations["Jayne"] = "Public Relations" + +let individualScores = [75, 43, 103, 87, 12] +var teamScore = 0 +for score in individualScores { + if score > 50 { + teamScore += 3 + } + else { + teamScore += 1 + } +} + +let vegetable = "red pepper" +switch vegetable { +case "celery": + print("Add some raisins and make ants on a log.") +case "cucumber", "watercress": + print("That would make a good tea sandwich.") +case let x where x.hasSuffix("pepper"): + print("Is it a spicy \(x)?") +default: + print("Everything tastes good in soup.") +} +// Prints "Is it a spicy red pepper?" + +@discardableResult +func greet(person: String, day: String) -> String { + "Hello \(person), today is \(day)." +} +greet(person: "Bob", day: "Tuesday") + +func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) { + var min = scores[0] + var max = scores[0] + var sum = 0 + + for score in scores { + if score > max { + max = score + } + else if score < min { + min = score + } + sum += score + } + + return (min, max, sum) +} +let statistics = calculateStatistics(scores: [5, 3, 100, 3, 9]) +print(statistics.sum) + +class NamedShape { + var numberOfSides: Int = 0 + var name: String + + init(name: String) { + self.name = name + } + + func simpleDescription() -> String { + "A shape with \(numberOfSides) sides." + } +} + +class Square: NamedShape { + var sideLength: Double + + init(sideLength: Double, name: String) { + self.sideLength = sideLength + super.init(name: name) + numberOfSides = 4 + } + + func area() -> Double { + sideLength * sideLength + } + + override func simpleDescription() -> String { + "A square with sides of length \(sideLength)." + } +} + +let test = Square(sideLength: 5.2, name: "my test square") +_ = test.area() +_ = test.simpleDescription() + +enum Rank: Int { + case ace = 1 + case two, three, four, five, six, seven, eight, nine, ten + case jack, queen, king + + func simpleDescription() -> String { + switch self { + case .ace: + return "ace" + case .jack: + return "jack" + case .queen: + return "queen" + case .king: + return "king" + default: + return String(self.rawValue) + } + } +} +let ace = Rank.ace + +func fetchUserID(from server: String) async -> Int { + if server == "primary" { + return 97 + } + return 501 +} + +Task { + await fetchUserID(from: "primary") +} + +protocol ExampleProtocol { + var simpleDescription: String { get } + mutating func adjust() +} + +var fridgeIsOpen = false +let fridgeContent = ["milk", "eggs", "leftovers"] + +func fridgeContains(_ food: String) -> Bool { + fridgeIsOpen = true + defer { + fridgeIsOpen = false + } + + let result = fridgeContent.contains(food) + return result +} + +if fridgeContains("banana") { + print("Found a banana") +} +print(fridgeIsOpen) +// Prints "false" + +let contentHeight = 40 +let hasHeader = true +let rowHeight = contentHeight + (hasHeader ? 50 : 20) diff --git a/packages/common/src/scopeSupportFacets/languageScopeSupport.ts b/packages/common/src/scopeSupportFacets/languageScopeSupport.ts index 8a2c0124a6..bbeee8e274 100644 --- a/packages/common/src/scopeSupportFacets/languageScopeSupport.ts +++ b/packages/common/src/scopeSupportFacets/languageScopeSupport.ts @@ -24,6 +24,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 { typescriptScopeSupport } from "./typescript"; import { typescriptreactScopeSupport } from "./typescriptreact"; @@ -56,6 +57,7 @@ export const languageScopeSupport: StringRecord = scala: scalaScopeSupport, scm: scmScopeSupport, scss: scssScopeSupport, + swift: swiftScopeSupport, talon: talonScopeSupport, typescript: typescriptScopeSupport, typescriptreact: typescriptreactScopeSupport, diff --git a/packages/common/src/scopeSupportFacets/swift.ts b/packages/common/src/scopeSupportFacets/swift.ts new file mode 100644 index 0000000000..68f0fab5ae --- /dev/null +++ b/packages/common/src/scopeSupportFacets/swift.ts @@ -0,0 +1,188 @@ +import type { LanguageScopeSupportFacetMap } from "./scopeSupportFacets.types"; +import { ScopeSupportFacetLevel } from "./scopeSupportFacets.types"; + +const { supported, unsupported, notApplicable } = ScopeSupportFacetLevel; + +export const swiftScopeSupport: LanguageScopeSupportFacetMap = { + // Command related + command: notApplicable, + + // XML/HTML related + element: notApplicable, + startTag: notApplicable, + endTag: notApplicable, + tags: notApplicable, + attribute: supported, // Swift has attributes like @available, @objc + environment: notApplicable, + + // Document structure + section: notApplicable, + + // Data structures and control flow + list: supported, + map: supported, + ifStatement: supported, + regularExpression: supported, + switchStatementSubject: supported, + fieldAccess: supported, + + // Statements and classes + statement: supported, + "statement.class": supported, + "statement.iteration.document": supported, + "statement.iteration.block": supported, + + class: supported, + "class.iteration.document": supported, + "class.iteration.block": supported, + className: supported, + "className.iteration.document": supported, + "className.iteration.block": supported, + + // Functions + namedFunction: supported, + "namedFunction.method": supported, + "namedFunction.method.iteration.class": supported, + "namedFunction.constructor": supported, // Swift has initializers + "namedFunction.iteration": supported, + "namedFunction.iteration.document": supported, + anonymousFunction: supported, // Swift has closures + functionName: supported, + "functionName.method": supported, + "functionName.method.iteration.class": supported, + "functionName.constructor": supported, + "functionName.iteration": supported, + "functionName.iteration.document": supported, + + // Function calls + functionCall: supported, + "functionCall.constructor": supported, + functionCallee: supported, + "functionCallee.constructor": supported, + + // Arguments + "argument.actual": supported, + "argument.actual.iteration": supported, + "argument.actual.method": supported, + "argument.actual.method.iteration": supported, + "argument.actual.constructor": supported, + "argument.actual.constructor.iteration": supported, + "argument.formal": supported, + "argument.formal.iteration": supported, + "argument.formal.method": supported, + "argument.formal.method.iteration": supported, + "argument.formal.constructor": supported, + "argument.formal.constructor.iteration": supported, + + // Comments + "comment.line": supported, + "comment.block": supported, + + // Strings + "string.singleLine": supported, + "string.multiLine": supported, + + // Text fragments + "textFragment.comment.line": supported, + "textFragment.comment.block": supported, + "textFragment.string.singleLine": supported, + "textFragment.string.multiLine": supported, + + // Delimiters + disqualifyDelimiter: supported, + pairDelimiter: supported, + + // Branches + "branch.if": supported, + "branch.loop": supported, + "branch.if.iteration": supported, + "branch.try": supported, + "branch.try.iteration": supported, + "branch.switchCase": supported, + "branch.switchCase.iteration": supported, + "branch.ternary": supported, + + // Collection items + "collectionItem.unenclosed": supported, + "collectionItem.unenclosed.iteration": supported, + + // Conditions + "condition.if": supported, + "condition.while": supported, + "condition.doWhile": unsupported, // Swift doesn't have do-while loops + "condition.for": supported, + "condition.ternary": supported, + "condition.switchCase": supported, + "condition.switchCase.iteration": supported, + + // Names + "name.assignment": supported, + "name.assignment.pattern": supported, + "name.variable": supported, + "name.variable.pattern": supported, + "name.foreach": supported, + "name.function": supported, + "name.method": supported, + "name.constructor": supported, + "name.class": supported, + "name.field": supported, + "name.resource": unsupported, // Swift doesn't have explicit resource management blocks + "name.resource.iteration": unsupported, + "name.argument.formal": supported, + "name.argument.formal.iteration": supported, + "name.argument.formal.method": supported, + "name.argument.formal.method.iteration": supported, + "name.argument.formal.constructor": supported, + "name.argument.formal.constructor.iteration": supported, + "name.iteration.block": supported, + "name.iteration.document": supported, + + // Keys + "key.attribute": supported, + "key.mapPair": supported, + "key.mapPair.iteration": supported, + + // Values + "value.assignment": supported, + "value.variable": supported, + "value.variable.pattern": supported, + "value.mapPair": supported, + "value.mapPair.iteration": supported, + "value.foreach": supported, + "value.attribute": supported, + "value.return": supported, + "value.return.lambda": supported, + "value.field": supported, + "value.yield": unsupported, // Swift doesn't have yield statements + "value.resource": unsupported, + "value.resource.iteration": unsupported, + "value.argument.formal": supported, + "value.argument.formal.iteration": supported, + "value.argument.formal.method": supported, + "value.argument.formal.method.iteration": supported, + "value.argument.formal.constructor": supported, + "value.argument.formal.constructor.iteration": supported, + "value.typeAlias": supported, + + // Types + "type.variable": supported, + "type.argument.formal": supported, + "type.argument.formal.iteration": supported, + "type.argument.formal.method": supported, + "type.argument.formal.method.iteration": supported, + "type.argument.formal.constructor": supported, + "type.argument.formal.constructor.iteration": supported, + "type.return": supported, + "type.field": supported, + "type.field.iteration": supported, + "type.foreach": supported, + "type.interface": supported, // Swift has protocols which are similar to interfaces + "type.class": supported, + "type.alias": supported, + "type.cast": supported, + "type.typeArgument": supported, + "type.typeArgument.iteration": supported, + + // Notebook + notebookCell: notApplicable, +}; diff --git a/packages/cursorless-org-docs/src/docs/user/languages/swift.mdx b/packages/cursorless-org-docs/src/docs/user/languages/swift.mdx new file mode 100644 index 0000000000..75330bbd25 --- /dev/null +++ b/packages/cursorless-org-docs/src/docs/user/languages/swift.mdx @@ -0,0 +1,5 @@ +import Language from "./Language"; + +# Swift + + diff --git a/queries/swift.scm b/queries/swift.scm new file mode 100644 index 0000000000..69a201dde7 --- /dev/null +++ b/queries/swift.scm @@ -0,0 +1,110 @@ +[ + (import_declaration) + (property_declaration) + (typealias_declaration) + (switch_entry) +] @statement + +(function_declaration + name: (_) @functionName +) @namedFunction @functionName.domain + +(class_declaration + name: (_) @className +) @class @className.domain + +(protocol_declaration + name: (_) @className +) @class @className.domain + +[ + (class_declaration) + (protocol_declaration) +] @namedFunction.iteration @functionName.iteration + +(array_literal) @list + +(dictionary_literal) @map + +(regex_literal) @regularExpression + +[ + (class_declaration) + (protocol_declaration) + (function_declaration) +] @statement.iteration + + +( + (value_arguments + (_)? @_.leading.endOf + . + (_) @argumentOrParameter + . + (_)? @_.trailing.startOf + ) @_dummy + (#not-type? @argumentOrParameter "comment") + (#single-or-multi-line-delimiter! @argumentOrParameter @_dummy ", " ",\n") +) + +( + (type_arguments + (_)? @_.leading.endOf + . + (_) @argumentOrParameter + . + (_)? @_.trailing.startOf + ) @_dummy + (#not-type? @argumentOrParameter "comment") + (#single-or-multi-line-delimiter! @argumentOrParameter @_dummy ", " ",\n") +) + +(value_arguments + "(" @argumentOrParameter.iteration.start.endOf + ")" @argumentOrParameter.iteration.end.startOf +) @argumentOrParameter.iteration.domain + +(type_arguments + "<" @argumentOrParameter.iteration.start.endOf + ">" @argumentOrParameter.iteration.end.startOf +) @argumentOrParameter.iteration.domain + +[ + (comment) + (multiline_comment) +] @comment @textFragment + +[ + (line_string_literal) + (multi_line_string_literal) + (raw_string_literal) +] @string @textFragment + +;; (line_string_literal +;; "\"" @textFragment.start.endOf +;; "\"" @textFragment.end.startOf +;; ) @string + +[ + (if_statement) + (guard_statement) +] @ifStatement + +(switch_statement + expr: (_) @private.switchStatementSubject + (#child-range! @private.switchStatementSubject 0 -1 true true) +) @_.domain + +(switch_pattern) @condition + +;;!! true ? 0 : 1; +;;! ^^^^ +;;! ^ ^ +;;! -------------- +(ternary_expression + condition: (_) @condition + if_true: (_) @branch +) @condition.domain +(ternary_expression + if_false: (_) @branch +)