Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ let package = Package(
.package(url: "https://github.com/krzyzanowskim/STTextView", branch: "main"),
.package(url: "https://github.com/ChimeHQ/SwiftTreeSitter", from: "0.6.0"),
.package(url: "https://github.com/lukepistrol/tree-sitter-c.git", branch: "feature/spm"),
.package(url: "https://github.com/lukepistrol/tree-sitter-cpp.git", branch: "feature/spm"),
.package(url: "https://github.com/lukepistrol/tree-sitter-c-sharp.git", branch: "feature/spm"),
.package(url: "https://github.com/lukepistrol/tree-sitter-css.git", branch: "feature/spm"),
.package(url: "https://github.com/mattmassicotte/tree-sitter-go.git", branch: "feature/swift"),
.package(url: "https://github.com/camdencheek/tree-sitter-go-mod.git", branch: "main"),
Expand All @@ -37,6 +39,8 @@ let package = Package(
"STTextView",
"SwiftTreeSitter",
.product(name: "TreeSitterC", package: "tree-sitter-c"),
.product(name: "TreeSitterCPP", package: "tree-sitter-cpp"),
.product(name: "TreeSitterCSharp", package: "tree-sitter-c-sharp"),
.product(name: "TreeSitterCSS", package: "tree-sitter-css"),
.product(name: "TreeSitterGo", package: "tree-sitter-go"),
.product(name: "TreeSitterGoMod", package: "tree-sitter-go-mod"),
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ struct ContentView: View {
- [ ] Agda
- [ ] Bash
- [x] C
- [ ] C++
- [ ] C#
- [x] C++
- [x] C#
- [ ] CodeQL
- [x] CSS
- [ ] Embedded template (ERB, EJS)
Expand Down
70 changes: 45 additions & 25 deletions Sources/CodeEditTextView/CodeLanguage/CodeLanguage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import tree_sitter
import SwiftTreeSitter

import TreeSitterC
import TreeSitterCPP
import TreeSitterCSharp
import TreeSitterCSS
import TreeSitterGo
import TreeSitterGoMod
Expand All @@ -26,22 +28,25 @@ import TreeSitterYAML

/// A structure holding metadata for code languages
public struct CodeLanguage {
/// The ID of the ``CodeLanguage``
/// The ID of the language
public let id: TreeSitterLanguage

/// The display name of the ``CodeLanguage``
/// The display name of the language
public let displayName: String

/// A set of file extensions for the ``CodeLanguage``
/// A set of file extensions for the language
public let extensions: Set<String>

/// The query URL for the ``CodeLanguage`` if available
/// The query URL for the language if available
public var queryURL: URL? {
Bundle.main.resourceURL?
.appendingPathComponent(bundle)
.appendingPathComponent(highlights)
}

/// The query URL of a language this language inherits from. (e.g.: C for C++)
public var parentQueryURL: URL?

private var bundle: String {
"TreeSitter\(displayName)_TreeSitter\(displayName).bundle"
}
Expand All @@ -50,7 +55,7 @@ public struct CodeLanguage {
"Contents/Resources/queries/highlights.scm"
}

/// The tree-sitter language for the ``CodeLanguage`` if available
/// The tree-sitter language for the language if available
public var language: Language? {
guard let ts_language = ts_language else { return nil }
return Language(language: ts_language)
Expand All @@ -60,6 +65,10 @@ public struct CodeLanguage {
switch id {
case .c:
return tree_sitter_c()
case .cpp:
return tree_sitter_cpp()
case .cSharp:
return tree_sitter_c_sharp()
case .css:
return tree_sitter_css()
case .go:
Expand Down Expand Up @@ -91,11 +100,11 @@ public struct CodeLanguage {
}
}

/// Gets the corresponding ``CodeLanguage`` for the given file URL
/// Gets the corresponding language for the given file URL
///
/// Uses the `pathExtension` URL component to detect the ``CodeLanguage``
/// - Parameter url: The URL to get the ``CodeLanguage`` for.
/// - Returns: A ``CodeLanguage`` structure
/// Uses the `pathExtension` URL component to detect the language
/// - Parameter url: The URL to get the language for.
/// - Returns: A language structure
public static func detectLanguageFrom(url: URL) -> CodeLanguage {
let fileExtension = url.pathExtension.lowercased()
let fileName = url.pathComponents.last?.lowercased()
Expand All @@ -108,7 +117,7 @@ public struct CodeLanguage {
}
}

/// The default ``CodeLanguage`` (plain text)
/// The default language (plain text)
public static let `default` = CodeLanguage(
id: .plainText,
displayName: "Plain Text",
Expand All @@ -118,9 +127,11 @@ public struct CodeLanguage {

public extension CodeLanguage {

/// A collection of available ``CodeLanguage`` structures.
/// A collection of available language structures.
static let knownLanguages: [CodeLanguage] = [
.c,
.cpp,
.cSharp,
.css,
.go,
.goMod,
Expand All @@ -136,45 +147,54 @@ public extension CodeLanguage {
.yaml
]

/// A ``CodeLanguage`` structure for `C`
/// A language structure for `C`
static let c: CodeLanguage = .init(id: .c, displayName: "C", extensions: ["c", "h", "o"])

/// A ``CodeLanguage`` structure for `CSS`
/// A language structure for `C++`
static let cpp: CodeLanguage = .init(id: .cpp,
displayName: "CPP",
extensions: ["cpp", "h", "cc"],
parentQueryURL: CodeLanguage.c.queryURL)

/// A language structure for `C#`
static let cSharp: CodeLanguage = .init(id: .cSharp, displayName: "CSharp", extensions: ["cs"])

/// A language structure for `CSS`
static let css: CodeLanguage = .init(id: .css, displayName: "CSS", extensions: ["css"])

/// A ``CodeLanguage`` structure for `Go`
/// A language structure for `Go`
static let go: CodeLanguage = .init(id: .go, displayName: "Go", extensions: ["go"])

/// A ``CodeLanguage`` structure for `GoMod`
/// A language structure for `GoMod`
static let goMod: CodeLanguage = .init(id: .goMod, displayName: "GoMod", extensions: ["mod"])

/// A ``CodeLanguage`` structure for `HTML`
/// A language structure for `HTML`
static let html: CodeLanguage = .init(id: .html, displayName: "HTML", extensions: ["html", "htm"])

/// A ``CodeLanguage`` structure for `Java`
/// A language structure for `Java`
static let java: CodeLanguage = .init(id: .java, displayName: "Java", extensions: ["java"])

/// A ``CodeLanguage`` structure for `JavaScript`
/// A language structure for `JavaScript`
static let javascript: CodeLanguage = .init(id: .javascript, displayName: "JS", extensions: ["js"])

/// A ``CodeLanguage`` structure for `JSON`
/// A language structure for `JSON`
static let json: CodeLanguage = .init(id: .json, displayName: "JSON", extensions: ["json"])

/// A ``CodeLanguage`` structure for `PHP`
/// A language structure for `PHP`
static let php: CodeLanguage = .init(id: .php, displayName: "PHP", extensions: ["php"])

/// A ``CodeLanguage`` structure for `Python`
/// A language structure for `Python`
static let python: CodeLanguage = .init(id: .python, displayName: "Python", extensions: ["py"])

/// A ``CodeLanguage`` structure for `Ruby`
/// A language structure for `Ruby`
static let ruby: CodeLanguage = .init(id: .ruby, displayName: "Ruby", extensions: ["rb"])

/// A ``CodeLanguage`` structure for `Rust`
/// A language structure for `Rust`
static let rust: CodeLanguage = .init(id: .rust, displayName: "Rust", extensions: ["rs"])

/// A ``CodeLanguage`` structure for `Swift`
/// A language structure for `Swift`
static let swift: CodeLanguage = .init(id: .swift, displayName: "Swift", extensions: ["swift"])

/// A ``CodeLanguage`` structure for `YAML`
/// A language structure for `YAML`
static let yaml: CodeLanguage = .init(id: .yaml, displayName: "YAML", extensions: ["yml", "yaml"])
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import Foundation
/// A collection of languages that are supported by `tree-sitter`
public enum TreeSitterLanguage: String {
case c
case cpp
case cSharp
case css
case go
case goMod
Expand Down
30 changes: 29 additions & 1 deletion Sources/CodeEditTextView/CodeLanguage/TreeSitterModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ public class TreeSitterModel {
switch language {
case .c:
return cQuery
case .cpp:
return cppQuery
case .cSharp:
return cSharpQuery
case .css:
return cssQuery
case .go:
Expand Down Expand Up @@ -58,6 +62,16 @@ public class TreeSitterModel {
return queryFor(.c)
}()

/// Query for `C++` files.
public lazy var cppQuery: Query? = {
return queryFor(.cpp)
}()

/// Query for `C#` files.
public lazy var cSharpQuery: Query? = {
return queryFor(.cSharp)
}()

/// Query for `CSS` files.
public lazy var cssQuery: Query? = {
return queryFor(.css)
Expand Down Expand Up @@ -126,7 +140,21 @@ public class TreeSitterModel {
private func queryFor(_ codeLanguage: CodeLanguage) -> Query? {
guard let language = codeLanguage.language,
let url = codeLanguage.queryURL else { return nil }
return try? language.query(contentsOf: url)
if let parentURL = codeLanguage.parentQueryURL,
let data = combinedQueryData(for: [url, parentURL]) {
return try? Query(language: language, data: data)
} else {
return try? language.query(contentsOf: url)
}
}

private func combinedQueryData(for fileURLs: [URL]) -> Data? {
let rawQuery = fileURLs.compactMap { try? String(contentsOf: $0) }.joined(separator: "\n")
if !rawQuery.isEmpty {
return rawQuery.data(using: .utf8)
} else {
return nil
}
}

private init() {}
Expand Down