Skip to content

Commit 66e2bfe

Browse files
committed
[CI] Add SwiftLint and Periphery for code style and dead code check
1 parent 73a5bd6 commit 66e2bfe

15 files changed

+151
-22
lines changed

.github/workflows/commit-ci.yml

+14
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ on:
33
push:
44
branches:
55
- '*'
6+
paths:
7+
- 'sources/**'
68
jobs:
79
build:
810
runs-on: macos-14
@@ -12,13 +14,25 @@ jobs:
1214
with:
1315
submodules: true
1416

17+
- name: Install SwiftLint
18+
run: brew install swiftlint
19+
20+
- name: Lint
21+
run: swiftlint
22+
1523
- name: Configure build environment
1624
run: |
1725
echo git_ref_name="$(git describe --always)" >> $GITHUB_ENV
1826
1927
- name: Build Squirrel
2028
run: ./action-build.sh package
2129

30+
- name: Install periphery
31+
run: brew install peripheryapp/periphery/periphery
32+
33+
- name: Check Unused Code
34+
run: periphery scan
35+
2236
- name: Upload Squirrel artifact
2337
uses: actions/upload-artifact@v4
2438
with:

.github/workflows/pull-request-ci.yml

+12
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,25 @@ jobs:
99
with:
1010
submodules: true
1111

12+
- name: Install SwiftLint
13+
run: brew install swiftlint
14+
15+
- name: Lint
16+
run: swiftlint
17+
1218
- name: Configure build environment
1319
run: |
1420
echo git_ref_name="$(git describe --always)" >> $GITHUB_ENV
1521
1622
- name: Build Squirrel
1723
run: ./action-build.sh package
1824

25+
- name: Install periphery
26+
run: brew install peripheryapp/periphery/periphery
27+
28+
- name: Check Unused Code
29+
run: periphery scan
30+
1931
- name: Upload Squirrel artifact
2032
uses: actions/upload-artifact@v4
2133
with:

.github/workflows/release-ci.yml

+14
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ on:
55
- '*'
66
branches:
77
- master
8+
paths:
9+
- 'sources/**'
810
workflow_dispatch:
911

1012
jobs:
@@ -19,9 +21,21 @@ jobs:
1921
fetch-depth: 0
2022
submodules: true
2123

24+
- name: Install SwiftLint
25+
run: brew install swiftlint
26+
27+
- name: Lint
28+
run: swiftlint
29+
2230
- name: Build Squirrel
2331
run: ./action-build.sh archive
2432

33+
- name: Install periphery
34+
run: brew install peripheryapp/periphery/periphery
35+
36+
- name: Check Unused Code
37+
run: periphery scan
38+
2539
- name: Build changelog
2640
id: release_log
2741
run: |

.periphery.yml

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
project: Squirrel.xcodeproj
2+
schemes:
3+
- Squirrel
4+
targets:
5+
- Squirrel

.swiftlint.yml

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# By default, SwiftLint uses a set of sensible default rules you can adjust:
2+
disabled_rules: # rule identifiers turned on by default to exclude from running
3+
- force_cast
4+
- force_try
5+
- todo
6+
opt_in_rules: # some rules are turned off by default, so you need to opt-in
7+
8+
# Alternatively, specify all rules explicitly by uncommenting this option:
9+
# only_rules: # delete `disabled_rules` & `opt_in_rules` if using this
10+
# - empty_parameters
11+
# - vertical_whitespace
12+
13+
analyzer_rules: # rules run by `swiftlint analyze`
14+
- explicit_self
15+
16+
included: # case-sensitive paths to include during linting. `--path` is ignored if present
17+
- sources
18+
excluded: # case-sensitive paths to ignore during linting. Takes precedence over `included`
19+
20+
# If true, SwiftLint will not fail if no lintable files are found.
21+
allow_zero_lintable_files: false
22+
23+
# If true, SwiftLint will treat all warnings as errors.
24+
strict: false
25+
26+
# rules that have both warning and error levels, can set just the warning level
27+
# implicitly
28+
line_length: 200
29+
function_body_length: 200
30+
# they can set both implicitly with an array
31+
type_body_length:
32+
- 300 # warning
33+
- 400 # error
34+
# or they can set both explicitly
35+
file_length:
36+
warning: 800
37+
error: 1200
38+
# naming rules can set warnings/errors for min_length and max_length
39+
# additionally they can set excluded names
40+
type_name:
41+
min_length: 4 # only warning
42+
max_length: # warning and error
43+
warning: 40
44+
error: 50
45+
excluded: # excluded via string
46+
allowed_symbols: ["_"] # these are allowed in type names
47+
identifier_name:
48+
min_length: # only min_length
49+
warning: 3
50+
error: 2
51+
excluded: [i, URL, of, by] # excluded via string array
52+
large_tuple:
53+
warning: 3
54+
error: 5
55+
reporter: "github-actions-logging" # reporter type (xcode, json, csv, checkstyle, codeclimate, junit, html, emoji, sonarqube, markdown, github-actions-logging, summary)
56+

sources/BridgingFunctions.swift

+5-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
import Foundation
99

1010
protocol DataSizeable {
11-
var data_size: Int32 { get set }
11+
// swiftlint:disable:next identifier_name
12+
var data_size: Int32 { get set }
1213
}
1314

1415
extension RimeContext_stdbool: DataSizeable {}
@@ -37,19 +38,21 @@ extension DataSizeable {
3738
let mutableCStr = strdup(cStr)
3839
// Free the existing string if there is one
3940
if let existing = self[keyPath: keypath] {
40-
free(UnsafeMutableRawPointer(mutating: existing))
41+
free(UnsafeMutableRawPointer(mutating: existing))
4142
}
4243
self[keyPath: keypath] = UnsafePointer(mutableCStr)
4344
}
4445
}
4546
}
4647

4748
infix operator ?= : AssignmentPrecedence
49+
// swiftlint:disable:next operator_whitespace
4850
func ?=<T>(left: inout T, right: T?) {
4951
if let right = right {
5052
left = right
5153
}
5254
}
55+
// swiftlint:disable:next operator_whitespace
5356
func ?=<T>(left: inout T?, right: T?) {
5457
if let right = right {
5558
left = right

sources/InputSource.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ final class SquirrelInstaller {
8080
}
8181
for (mode, inputSource) in getInputSource(modes: [modeToSelect]) {
8282
if let enabled = getBool(for: inputSource, key: kTISPropertyInputSourceIsEnabled),
83-
let selectable = getBool(for: inputSource, key: kTISPropertyInputSourceIsSelectCapable),
84-
let selected = getBool(for: inputSource, key: kTISPropertyInputSourceIsSelected),
83+
let selectable = getBool(for: inputSource, key: kTISPropertyInputSourceIsSelectCapable),
84+
let selected = getBool(for: inputSource, key: kTISPropertyInputSourceIsSelected),
8585
enabled && selectable && !selected {
8686
let error = TISSelectInputSource(inputSource)
8787
print("Selection \(error == noErr ? "succeeds" : "fails") for input source: \(mode.rawValue)")

sources/MacOSKeyCodes.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// MacOSKeyCOdes.swift
2+
// MacOSKeyCodes.swift
33
// Squirrel
44
//
55
// Created by Leo Liu on 5/9/24.

sources/Main.swift

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,16 @@ import InputMethodKit
1010

1111
@main
1212
struct SquirrelApp {
13-
static let userDir = if let pw = getpwuid(getuid()) {
14-
URL(fileURLWithFileSystemRepresentation: pw.pointee.pw_dir, isDirectory: true, relativeTo: nil).appending(components: "Library", "Rime")
13+
static let userDir = if let pwuid = getpwuid(getuid()) {
14+
URL(fileURLWithFileSystemRepresentation: pwuid.pointee.pw_dir, isDirectory: true, relativeTo: nil).appending(components: "Library", "Rime")
1515
} else {
1616
try! FileManager.default.url(for: .libraryDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent("Rime", isDirectory: true)
1717
}
1818
static let appDir = "/Library/Input Library/Squirrel.app".withCString { dir in
1919
URL(fileURLWithFileSystemRepresentation: dir, isDirectory: false, relativeTo: nil)
2020
}
2121

22+
// swiftlint:disable:next cyclomatic_complexity
2223
static func main() {
2324
let rimeAPI: RimeApi_stdbool = rime_get_api_stdbool().pointee
2425

sources/SquirrelApplicationDelegate.swift

+3
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ final class SquirrelApplicationDelegate: NSObject, NSApplicationDelegate, SPUSta
5858
}
5959

6060
func applicationWillTerminate(_ notification: Notification) {
61+
// swiftlint:disable:next notification_center_detachment
6162
NotificationCenter.default.removeObserver(self)
6263
DistributedNotificationCenter.default().removeObserver(self)
6364
panel?.hide()
@@ -132,8 +133,10 @@ final class SquirrelApplicationDelegate: NSObject, NSApplicationDelegate, SPUSta
132133
print("Error creating user data directory: \(userDataDir.path())")
133134
}
134135
}
136+
// swiftlint:disable identifier_name
135137
let notification_handler: @convention(c) (UnsafeMutableRawPointer?, RimeSessionId, UnsafePointer<CChar>?, UnsafePointer<CChar>?) -> Void = notificationHandler
136138
let context_object = Unmanaged.passUnretained(self).toOpaque()
139+
// swiftlint:enable identifier_name
137140
rimeAPI.set_notification_handler(notification_handler, context_object)
138141

139142
var squirrelTraits = RimeTraits.rimeStructInit()

sources/SquirrelConfig.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ final class SquirrelConfig {
130130

131131
// isVertical
132132
func updateTextOrientation(prefix: String) -> Bool {
133-
let textOrientation = getString("\(prefix)/text_orientation")
133+
let textOrientation = getString("\(prefix)/text_orientation")
134134
switch textOrientation {
135135
case "horizontal":
136136
return false

sources/SquirrelInputController.swift

+14-3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ final class SquirrelInputController: IMKInputController {
2828
private var chordDuration: TimeInterval = 0
2929
private var currentApp: String = ""
3030

31+
// swiftlint:disable:next cyclomatic_complexity
3132
override func handle(_ event: NSEvent!, client sender: Any!) -> Bool {
3233
let modifiers = event.modifierFlags
3334
let changes = lastModifiers.symmetricDifference(modifiers)
@@ -128,6 +129,7 @@ final class SquirrelInputController: IMKInputController {
128129
return success
129130
}
130131

132+
// swiftlint:disable:next identifier_name
131133
func page(up: Bool) -> Bool {
132134
var handled = false
133135
handled = rimeAPI.change_page(session, up)
@@ -282,7 +284,8 @@ private extension SquirrelInputController {
282284
if chordKeyCount > 0 && session != 0 {
283285
// simulate key-ups
284286
for i in 0..<chordKeyCount {
285-
if rimeAPI.process_key(session, Int32(chordKeyCodes[i]), Int32(chordModifiers[i] | kReleaseMask.rawValue)) {
287+
let handled = rimeAPI.process_key(session, Int32(chordKeyCodes[i]), Int32(chordModifiers[i] | kReleaseMask.rawValue))
288+
if handled {
286289
processedKeys = true
287290
}
288291
}
@@ -413,13 +416,15 @@ private extension SquirrelInputController {
413416
}
414417
}
415418

419+
// swiftlint:disable:next cyclomatic_complexity
416420
func rimeUpdate() {
417421
// print("[DEBUG] rimeUpdate")
418422
rimeConsumeCommittedText()
419423

420424
var status = RimeStatus_stdbool.rimeStructInit()
421425
if rimeAPI.get_status(session, &status) {
422426
// enable schema specific ui style
427+
// swiftlint:disable:next identifier_name
423428
if let schema_id = status.schema_id, schemaId == "" || schemaId != String(cString: schema_id) {
424429
schemaId = String(cString: schema_id)
425430
NSApp.squirrelAppDelegate.loadSettings(for: schemaId)
@@ -484,15 +489,20 @@ private extension SquirrelInputController {
484489
comments.append(candidate.comment.map { String(cString: $0) } ?? "")
485490
}
486491
var labels = [String]()
492+
// swiftlint:disable identifier_name
487493
if let select_keys = ctx.menu.select_keys {
488-
labels = Array(arrayLiteral: String(cString: select_keys))
494+
labels = String(cString: select_keys).map { String($0) }
489495
} else if let select_labels = ctx.select_labels {
490496
let pageSize = Int(ctx.menu.page_size)
491497
for i in 0..<pageSize {
492498
labels.append(select_labels[i].map { String(cString: $0) } ?? "")
493499
}
494500
}
495-
showPanel(preedit: inlinePreedit ? "" : preedit, selRange: NSRange(location: start.utf16Offset(in: preedit), length: preedit.utf16.distance(from: start, to: end)), caretPos: caretPos.utf16Offset(in: preedit), candidates: candidates, comments: comments, labels: labels, highlighted: Int(ctx.menu.highlighted_candidate_index))
501+
// swiftlint:enable identifier_name
502+
503+
let selRange = NSRange(location: start.utf16Offset(in: preedit), length: preedit.utf16.distance(from: start, to: end))
504+
showPanel(preedit: inlinePreedit ? "" : preedit, selRange: selRange, caretPos: caretPos.utf16Offset(in: preedit),
505+
candidates: candidates, comments: comments, labels: labels, highlighted: Int(ctx.menu.highlighted_candidate_index))
496506
_ = rimeAPI.free_context(&ctx)
497507
} else {
498508
hidePalettes()
@@ -531,6 +541,7 @@ private extension SquirrelInputController {
531541
client.setMarkedText(attrString, selectionRange: NSRange(location: caretPos, length: 0), replacementRange: NSRange(location: NSNotFound, length: 0))
532542
}
533543

544+
// swiftlint:disable:next function_parameter_count
534545
func showPanel(preedit: String, selRange: NSRange, caretPos: Int, candidates: [String], comments: [String], labels: [String], highlighted: Int) {
535546
// print("[DEBUG] showPanelWithPreedit:...:")
536547
guard let client = client else { return }

sources/SquirrelPanel.swift

+7-3
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ final class SquirrelPanel: NSPanel {
6464
view.currentTheme.inlineCandidate
6565
}
6666

67+
// swiftlint:disable:next cyclomatic_complexity
6768
override func sendEvent(_ event: NSEvent) {
6869
switch event.type {
6970
case .leftMouseDown:
@@ -98,15 +99,15 @@ final class SquirrelPanel: NSPanel {
9899
case .scrollWheel:
99100
if event.phase == .began {
100101
scrollDirection = .zero
101-
// Scrollboard span
102+
// Scrollboard span
102103
} else if event.phase == .ended || (event.phase == .init(rawValue: 0) && event.momentumPhase != .init(rawValue: 0)) {
103104
if abs(scrollDirection.dx) > abs(scrollDirection.dy) && abs(scrollDirection.dx) > 10 {
104105
_ = inputController?.page(up: (scrollDirection.dx < 0) == vertical)
105106
} else if abs(scrollDirection.dx) < abs(scrollDirection.dy) && abs(scrollDirection.dy) > 10 {
106107
_ = inputController?.page(up: scrollDirection.dx > 0)
107108
}
108109
scrollDirection = .zero
109-
// Mouse scroll wheel
110+
// Mouse scroll wheel
110111
} else if event.phase == .init(rawValue: 0) && event.momentumPhase == .init(rawValue: 0) {
111112
if scrollTime.timeIntervalSinceNow < -1 {
112113
scrollDirection = .zero
@@ -139,6 +140,7 @@ final class SquirrelPanel: NSPanel {
139140
}
140141

141142
// Main function to add attributes to text output from librime
143+
// swiftlint:disable:next cyclomatic_complexity function_parameter_count
142144
func update(preedit: String, selRange: NSRange, caretPos: Int, candidates: [String], comments: [String], labels: [String], highlighted index: Int, update: Bool) {
143145
if update {
144146
self.preedit = preedit
@@ -335,6 +337,7 @@ private extension SquirrelPanel {
335337

336338
// Get the window size, the windows will be the dirtyRect in
337339
// SquirrelView.drawRect
340+
// swiftlint:disable:next cyclomatic_complexity
338341
func show() {
339342
currentScreen()
340343
let theme = view.currentTheme
@@ -437,7 +440,8 @@ private extension SquirrelPanel {
437440
text.addAttribute(.paragraphStyle, value: theme.paragraphStyle, range: NSRange(location: 0, length: text.length))
438441
view.textContentStorage.attributedString = text
439442
view.textView.setLayoutOrientation(vertical ? .vertical : .horizontal)
440-
view.drawView(candidateRanges: [NSRange(location: 0, length: text.length)], hilightedIndex: -1, preeditRange: NSRange(location: NSNotFound, length: 0), highlightedPreeditRange: NSRange(location: NSNotFound, length: 0))
443+
view.drawView(candidateRanges: [NSRange(location: 0, length: text.length)], hilightedIndex: -1,
444+
preeditRange: NSRange(location: NSNotFound, length: 0), highlightedPreeditRange: NSRange(location: NSNotFound, length: 0))
441445
show()
442446

443447
statusTimer?.invalidate()

sources/SquirrelTheme.swift

+6-6
Original file line numberDiff line numberDiff line change
@@ -294,12 +294,12 @@ private extension SquirrelTheme {
294294
func blendColor(foregroundColor: NSColor, backgroundColor: NSColor?) -> NSColor {
295295
let foregroundColor = foregroundColor.usingColorSpace(NSColorSpace.deviceRGB)!
296296
let backgroundColor = (backgroundColor ?? NSColor.gray).usingColorSpace(NSColorSpace.deviceRGB)!
297-
func blend(_ a: CGFloat, _ b: CGFloat) -> CGFloat {
298-
return (a * 2 + b) / 3
297+
func blend(foreground: CGFloat, background: CGFloat) -> CGFloat {
298+
return (foreground * 2 + background) / 3
299299
}
300-
return NSColor(deviceRed: blend(foregroundColor.redComponent, backgroundColor.redComponent),
301-
green: blend(foregroundColor.greenComponent, backgroundColor.greenComponent),
302-
blue: blend(foregroundColor.blueComponent, backgroundColor.blueComponent),
303-
alpha: blend(foregroundColor.alphaComponent, backgroundColor.alphaComponent))
300+
return NSColor(deviceRed: blend(foreground: foregroundColor.redComponent, background: backgroundColor.redComponent),
301+
green: blend(foreground: foregroundColor.greenComponent, background: backgroundColor.greenComponent),
302+
blue: blend(foreground: foregroundColor.blueComponent, background: backgroundColor.blueComponent),
303+
alpha: blend(foreground: foregroundColor.alphaComponent, background: backgroundColor.alphaComponent))
304304
}
305305
}

0 commit comments

Comments
 (0)