Skip to content

Commit 0b71252

Browse files
Yan123-techyan
andauthored
CI: format and syntax checks instead of build (#10)
* build: add a swift-format configuration 4-space indentation and a 120-column limit, matching what the sources already use. Without a config swift-format defaults to 2 spaces and reports 3301 Indentation warnings across the tree, which would bury the warnings that matter. Also renames the LSP SymbolKind.null_ case to nullValue: the trailing underscore is a keyword workaround and trips AlwaysUseLowerCamelCase. * style: apply swift-format across Sources and Tests Mechanical reformat only; no behaviour change. Build and the 35 tests pass unchanged. Fixes two spots the formatter flagged as more than whitespace: - FileExplorerView's context menu set two properties on one line separated by semicolons, now one statement per line - import blocks are now lexicographically ordered swift-format lint --strict reports zero warnings afterwards. * ci: check formatting and syntax instead of building Build and test cannot run on a hosted runner: the package declares swift-tools-version 6.4, CodeEditSourceEditor 0.15 needs SwiftPM 6.4 to generate its resource bundles, and runners top out at 6.3.3. Lowering the manifest to 6.3 clears SwiftPM but then fails inside a dependency -- CodeEditSymbols 0.2.3 declares no resources, so SwiftPM 6.3 does not emit Bundle.module. The toolchain is the constraint, not this package. Replace the non-blocking build job with two checks that do work anywhere: - Format: swift-format lint --strict, then format --in-place and fail if git sees a change. swift-format ships inside the Xcode toolchain, so it is present regardless of the Swift version. - Syntax: swiftc -parse on every file. Parsing does not resolve the dependency graph, so it needs no SwiftPM. This catches malformed source but not type errors. Neither substitutes for a build; build and test stay a local and release-time gate until a runner ships 6.4. The comment at the bottom of the file says so, and names what to add when that happens. --------- Co-authored-by: yan <yan@localhost>
1 parent 5868aa1 commit 0b71252

20 files changed

Lines changed: 158 additions & 91 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,28 +11,62 @@ concurrency:
1111
cancel-in-progress: true
1212

1313
jobs:
14-
build-and-test:
15-
name: Build and test
14+
format:
15+
name: Format
1616
runs-on: macos-latest
17-
# This job cannot pass yet. Package.swift declares swift-tools-version 6.4 and
18-
# CodeEditSourceEditor 0.15 needs SwiftPM 6.4 to generate its resource bundles;
19-
# hosted runners top out at 6.3.x, and no runner image or Xcode version reaches 6.4.
20-
# Lowering the manifest to 6.3 gets past SwiftPM but then fails in a dependency:
21-
# CodeEditSymbols 0.2.3 → 'Bundle' has no member 'module'
22-
# so the toolchain is the constraint, not our source. Running non-blocking so the
23-
# steps still report the real state. Remove this line when a runner ships 6.4.
24-
continue-on-error: true
2517

2618
steps:
2719
- uses: actions/checkout@v4
2820

2921
- name: Show toolchain
22+
run: swift --version
23+
24+
# swift-format ships inside the Xcode toolchain, so this runs on every runner
25+
# regardless of which Swift version it carries. `lint` exits non-zero on warnings;
26+
# `--strict` promotes the softer diagnostics too.
27+
- name: Lint
28+
run: |
29+
xcrun swift-format lint --recursive --strict --configuration .swift-format Sources Tests
30+
31+
# Reformat in place, then fail if git sees a change: that means the committed
32+
# formatting is not what the formatter produces.
33+
- name: Check formatting is applied
3034
run: |
31-
xcode-select -p
32-
swift --version
35+
xcrun swift-format format --in-place --recursive --configuration .swift-format Sources Tests
36+
if ! git diff --quiet; then
37+
echo "::error::Formatting differs. Run:"
38+
echo "::error::swift-format format --in-place --recursive --configuration .swift-format Sources Tests"
39+
git --no-pager diff --stat
40+
exit 1
41+
fi
3342
34-
- name: Build
35-
run: swift build
43+
syntax:
44+
name: Syntax
45+
runs-on: macos-latest
46+
47+
steps:
48+
- uses: actions/checkout@v4
49+
50+
- name: Show toolchain
51+
run: swift --version
52+
53+
# Type-checking needs the full dependency graph, which needs SwiftPM 6.4 for
54+
# CodeEditSourceEditor 0.15's resource bundles. Parsing does not, so this is
55+
# the strongest check available on a hosted runner today. See README.
56+
- name: Parse every source file
57+
run: |
58+
rc=0
59+
while read -r file; do
60+
if ! output=$(swiftc -parse "$file" 2>&1); then
61+
echo "::error file=${file}::$(echo "$output" | head -1)"
62+
rc=1
63+
fi
64+
done < <(find Sources Tests -name '*.swift')
65+
exit $rc
3666
37-
- name: Test
38-
run: swift test
67+
# Build and test run locally and in release, not here: the package declares
68+
# swift-tools-version 6.4 and CodeEditSourceEditor 0.15 needs SwiftPM 6.4 to
69+
# generate its resource bundles, while hosted runners top out at 6.3.3. Lowering the
70+
# manifest to 6.3 fails inside a dependency (CodeEditSymbols 0.2.3 has no resources
71+
# declared, so SwiftPM 6.3 does not emit Bundle.module). Add a `build` job here when
72+
# a runner ships 6.4.

‎.swift-format‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"version": 1,
3+
"indentation": {
4+
"spaces": 4
5+
},
6+
"lineLength": 120
7+
}

‎Sources/CodeEditorApp/Sources/CodeEditorApp.swift‎

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import SwiftUI
21
import CodeEditorCore
32
import CodeEditorThemes
43
import CodeEditorUI
4+
import SwiftUI
55

66
@main
77
struct CodeEditorApp: App {
@@ -78,10 +78,12 @@ struct CodeEditorApp: App {
7878
}
7979
.keyboardShortcut("b", modifiers: .command)
8080

81-
Toggle("Status Bar", isOn: Binding(
82-
get: { appState.isStatusBarVisible },
83-
set: { appState.isStatusBarVisible = $0 }
84-
))
81+
Toggle(
82+
"Status Bar",
83+
isOn: Binding(
84+
get: { appState.isStatusBarVisible },
85+
set: { appState.isStatusBarVisible = $0 }
86+
))
8587
}
8688

8789
CommandGroup(after: .toolbar) {
@@ -93,10 +95,12 @@ struct CodeEditorApp: App {
9395

9496
// MARK: Format
9597
CommandGroup(replacing: .textFormatting) {
96-
Toggle("Word Wrap", isOn: Binding(
97-
get: { appState.editorState.wrapLines },
98-
set: { appState.editorState.setWrapLines($0) }
99-
))
98+
Toggle(
99+
"Word Wrap",
100+
isOn: Binding(
101+
get: { appState.editorState.wrapLines },
102+
set: { appState.editorState.setWrapLines($0) }
103+
))
100104

101105
Menu("Tab Width") {
102106
ForEach([2, 4, 8], id: \.self) { width in
@@ -143,4 +147,4 @@ struct CodeEditorApp: App {
143147
object: panel.urls
144148
)
145149
}
146-
}
150+
}

‎Sources/CodeEditorApp/Sources/MainWindowView.swift‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import SwiftUI
21
import CodeEditorCore
32
import CodeEditorUI
3+
import SwiftUI
44

55
/// The app window: sidebar, tab bar, editor, terminal, status bar.
66
struct MainWindowView: View {
@@ -217,4 +217,4 @@ private struct WelcomeView: View {
217217
#Preview {
218218
MainWindowView(appState: AppState())
219219
.frame(width: 1200, height: 800)
220-
}
220+
}

‎Sources/CodeEditorCore/Sources/Document/Document.swift‎

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,24 @@ public struct Language: Sendable, Equatable, Hashable {
2323
}
2424

2525
/// Standard document languages.
26-
public extension Language {
27-
static let swift = Language(identifier: "swift", displayName: "Swift", fileExtensions: ["swift"])
28-
static let javascript = Language(identifier: "javascript", displayName: "JavaScript", fileExtensions: ["js", "jsx", "mjs", "cjs"])
29-
static let typescript = Language(identifier: "typescript", displayName: "TypeScript", fileExtensions: ["ts", "tsx"])
30-
static let python = Language(identifier: "python", displayName: "Python", fileExtensions: ["py"])
31-
static let json = Language(identifier: "json", displayName: "JSON", fileExtensions: ["json"])
32-
static let markdown = Language(identifier: "markdown", displayName: "Markdown", fileExtensions: ["md", "markdown"])
33-
static let html = Language(identifier: "html", displayName: "HTML", fileExtensions: ["html", "htm"])
34-
static let css = Language(identifier: "css", displayName: "CSS", fileExtensions: ["css", "scss", "sass"])
35-
static let shell = Language(identifier: "shell", displayName: "Shell", fileExtensions: ["sh", "bash", "zsh"])
36-
static let yaml = Language(identifier: "yaml", displayName: "YAML", fileExtensions: ["yml", "yaml"])
37-
static let toml = Language(identifier: "toml", displayName: "TOML", fileExtensions: ["toml"])
38-
static let text = Language(identifier: "plaintext", displayName: "Plain Text", fileExtensions: ["txt"])
39-
static let unknown = Language(identifier: "plaintext", displayName: "Plain Text")
26+
extension Language {
27+
public static let swift = Language(identifier: "swift", displayName: "Swift", fileExtensions: ["swift"])
28+
public static let javascript = Language(
29+
identifier: "javascript", displayName: "JavaScript", fileExtensions: ["js", "jsx", "mjs", "cjs"])
30+
public static let typescript = Language(
31+
identifier: "typescript", displayName: "TypeScript", fileExtensions: ["ts", "tsx"])
32+
public static let python = Language(identifier: "python", displayName: "Python", fileExtensions: ["py"])
33+
public static let json = Language(identifier: "json", displayName: "JSON", fileExtensions: ["json"])
34+
public static let markdown = Language(
35+
identifier: "markdown", displayName: "Markdown", fileExtensions: ["md", "markdown"])
36+
public static let html = Language(identifier: "html", displayName: "HTML", fileExtensions: ["html", "htm"])
37+
public static let css = Language(identifier: "css", displayName: "CSS", fileExtensions: ["css", "scss", "sass"])
38+
public static let shell = Language(
39+
identifier: "shell", displayName: "Shell", fileExtensions: ["sh", "bash", "zsh"])
40+
public static let yaml = Language(identifier: "yaml", displayName: "YAML", fileExtensions: ["yml", "yaml"])
41+
public static let toml = Language(identifier: "toml", displayName: "TOML", fileExtensions: ["toml"])
42+
public static let text = Language(identifier: "plaintext", displayName: "Plain Text", fileExtensions: ["txt"])
43+
public static let unknown = Language(identifier: "plaintext", displayName: "Plain Text")
4044
}
4145

4246
// MARK: - LineEnding
@@ -208,7 +212,7 @@ public final class Document: Identifiable {
208212
let ext = url.pathExtension.lowercased()
209213
let all: [Language] = [
210214
.swift, .javascript, .typescript, .python, .json, .markdown,
211-
.html, .css, .shell, .yaml, .toml, .text
215+
.html, .css, .shell, .yaml, .toml, .text,
212216
]
213217
return all.first { $0.fileExtensions.contains(ext) } ?? .unknown
214218
}
@@ -358,7 +362,8 @@ public final class Document: Identifiable {
358362
/// Select the line containing `offset`.
359363
public func selectLine(containing offset: Int) {
360364
guard let position = lineAndColumn(for: offset),
361-
let range = range(ofLines: position.line, to: position.line) else { return }
365+
let range = range(ofLines: position.line, to: position.line)
366+
else { return }
362367
setSelection(from: range.location, to: range.location + range.length)
363368
}
364369

@@ -537,4 +542,4 @@ extension DocumentManager: DocumentDelegate {
537542
public func documentDidChangeLanguage(_ document: Document, to language: Language) {}
538543

539544
public func documentDidChangeLineEnding(_ document: Document, to lineEnding: LineEnding) {}
540-
}
545+
}

‎Sources/CodeEditorCore/Sources/FileSystem/FileSystemManager.swift‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,8 @@ public final class FileSystemManager {
244244

245245
do {
246246
let contents = try provider.contentsOfDirectory(at: url)
247-
let items = contents
247+
let items =
248+
contents
248249
.filter { !$0.lastPathComponent.hasPrefix(".") || $0.lastPathComponent == ".gitignore" }
249250
.map { FileSystemItem(url: $0, fileSystemProvider: provider) }
250251
.sorted(by: Self.sorted)
@@ -332,4 +333,4 @@ public final class FileSystemManager {
332333
loadingPaths.remove(Self.pathKey(for: url))
333334
await load(url)
334335
}
335-
}
336+
}

‎Sources/CodeEditorCore/Sources/TextSelection/TextSelectionManager.swift‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ public final class TextSelectionManager {
5656
/// The text covered by all selections, in document order.
5757
public func selectedText(in content: String) -> String {
5858
let string = content as NSString
59-
return selections
59+
return
60+
selections
6061
.sorted { $0.start < $1.start }
6162
.compactMap { selection -> String? in
6263
let range = selection.nsRange
@@ -69,7 +70,8 @@ public final class TextSelectionManager {
6970
/// Line numbers touched by the primary selection, used to highlight the gutter.
7071
public func selectedLines(in document: Document) -> Set<Int> {
7172
guard let start = document.lineAndColumn(for: primarySelection.start),
72-
let end = document.lineAndColumn(for: primarySelection.end) else { return [] }
73+
let end = document.lineAndColumn(for: primarySelection.end)
74+
else { return [] }
7375
return Set(start.line...end.line)
7476
}
75-
}
77+
}

‎Sources/CodeEditorLSP/JSONRPCTransport.swift‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,12 @@ public actor JSONRPCTransport {
136136
let headerData = buffer[buffer.startIndex..<headerEnd.lowerBound]
137137
let header = String(decoding: headerData, as: UTF8.self)
138138

139-
guard let length = header
140-
.split(separator: "\r\n")
141-
.first(where: { $0.lowercased().hasPrefix("content-length:") })
142-
.flatMap({ Int($0.split(separator: ":", maxSplits: 1)[1].trimmingCharacters(in: .whitespaces)) })
139+
guard
140+
let length =
141+
header
142+
.split(separator: "\r\n")
143+
.first(where: { $0.lowercased().hasPrefix("content-length:") })
144+
.flatMap({ Int($0.split(separator: ":", maxSplits: 1)[1].trimmingCharacters(in: .whitespaces)) })
143145
else {
144146
// Malformed header; drop it and resynchronize on the next one.
145147
buffer.removeSubrange(buffer.startIndex...headerEnd.upperBound)
@@ -239,4 +241,4 @@ public actor JSONRPCTransport {
239241
pending.removeAll()
240242
}
241243
}
242-
}
244+
}

‎Sources/CodeEditorLSP/LSPClient.swift‎

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public enum LanguageServerRegistry {
2323
public static var sourceKitLSPPath: String? {
2424
let candidates = [
2525
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/sourcekit-lsp",
26-
"/Library/Developer/CommandLineTools/usr/bin/sourcekit-lsp"
26+
"/Library/Developer/CommandLineTools/usr/bin/sourcekit-lsp",
2727
]
2828
return candidates.first { FileManager.default.isExecutableFile(atPath: $0) }
2929
}
@@ -105,7 +105,9 @@ public final class LSPClient {
105105
params: InitializeParams(
106106
rootURI: rootURL?.absoluteString,
107107
capabilities: .default,
108-
workspaceFolders: rootURL.map { [WorkspaceFolder(uri: $0.absoluteString, name: $0.lastPathComponent)] }
108+
workspaceFolders: rootURL.map {
109+
[WorkspaceFolder(uri: $0.absoluteString, name: $0.lastPathComponent)]
110+
}
109111
),
110112
as: InitializeResult.self
111113
)
@@ -229,10 +231,12 @@ public final class LSPClient {
229231
private func handleNotification(method: String, params: JSONValue) {
230232
switch method {
231233
case "textDocument/publishDiagnostics":
232-
guard let payload = try? JSONDecoder().decode(
233-
PublishDiagnosticsPayload.self,
234-
from: JSONEncoder().encode(params)
235-
) else { return }
234+
guard
235+
let payload = try? JSONDecoder().decode(
236+
PublishDiagnosticsPayload.self,
237+
from: JSONEncoder().encode(params)
238+
)
239+
else { return }
236240
diagnosticsByURI[payload.uri] = payload.diagnostics
237241
onDiagnosticsChanged?(payload.uri, payload.diagnostics)
238242

@@ -306,4 +310,4 @@ struct LocationResponse: Decodable {
306310
location = nil
307311
}
308312
}
309-
}
313+
}

‎Sources/CodeEditorLSP/Types/LSPTypes.swift‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ public enum SymbolKind: Int, Codable, Sendable {
269269
case array = 16
270270
case object = 17
271271
case key = 18
272-
case null_ = 19
272+
case nullValue = 19
273273
case boolean = 20
274274
case arrayElement = 21
275275
}
@@ -583,4 +583,4 @@ public enum JSONValue: Codable, Sendable {
583583
public func decode<T: Decodable>(as type: T.Type, using decoder: JSONDecoder) throws -> T {
584584
try decoder.decode(T.self, from: JSONEncoder().encode(self))
585585
}
586-
}
586+
}

0 commit comments

Comments
 (0)