Skip to content

Commit a107a34

Browse files
committed
Renaming theme file when displayName is changed. Persisting changes to theme files.
1 parent a8126e3 commit a107a34

File tree

4 files changed

+205
-179
lines changed

4 files changed

+205
-179
lines changed

CodeEdit/Features/Settings/Pages/ThemeSettings/Models/ThemeModel.swift

Lines changed: 87 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -67,23 +67,17 @@ final class ThemeModel: ObservableObject {
6767
}
6868
}
6969

70+
@Published var presentingDetails: Bool = false
71+
72+
@Published var detailsTheme: Theme?
73+
7074
/// The selected appearance in the sidebar.
7175
/// - **0**: dark mode themes
7276
/// - **1**: light mode themes
7377
@Published var selectedAppearance: Int = 0
7478

75-
/// The selected tab in the main section.
76-
/// - **0**: Preview
77-
/// - **1**: Editor
78-
/// - **2**: Terminal
79-
@Published var selectedTab: Int = 1
80-
8179
/// An array of loaded ``Theme``.
82-
@Published var themes: [Theme] = [] {
83-
didSet {
84-
saveThemes()
85-
}
86-
}
80+
@Published var themes: [Theme] = []
8781

8882
/// The currently selected ``Theme``.
8983
@Published var selectedTheme: Theme? {
@@ -244,23 +238,6 @@ final class ThemeModel: ObservableObject {
244238
}
245239
}
246240

247-
/// Removes all overrides of the given theme in
248-
/// `~/Library/Application Support/CodeEdit/settings.json`
249-
///
250-
/// After removing overrides, themes are reloaded
251-
/// from `~/Library/Application Support/CodeEdit/Themes`. See ``loadThemes()``
252-
/// for more information.
253-
///
254-
/// - Parameter theme: The theme to reset
255-
func reset(_ theme: Theme) {
256-
Settings.shared.preferences.theme.overrides[theme.name] = [:]
257-
do {
258-
try self.loadThemes()
259-
} catch {
260-
print(error)
261-
}
262-
}
263-
264241
/// Removes the given theme from `–/Library/Application Support/CodeEdit/themes`
265242
///
266243
/// After removing the theme, themes are reloaded
@@ -285,51 +262,6 @@ final class ThemeModel: ObservableObject {
285262
}
286263
}
287264

288-
/// Saves changes on theme properties to `overrides`
289-
/// in `~/Library/Application Support/CodeEdit/settings.json`.
290-
private func saveThemes() {
291-
let url = themesURL
292-
themes.forEach { theme in
293-
do {
294-
// load the original theme from `~/Library/Application Support/CodeEdit/Themes/`
295-
let originalUrl = url.appendingPathComponent(theme.name).appendingPathExtension("cetheme")
296-
let originalData = try Data(contentsOf: originalUrl)
297-
let originalTheme = try JSONDecoder().decode(Theme.self, from: originalData)
298-
299-
// get properties of the current theme as well as the original
300-
guard let terminalColors = try theme.terminal.allProperties() as? [String: Theme.Attributes],
301-
let editorColors = try theme.editor.allProperties() as? [String: Theme.Attributes],
302-
let oTermColors = try originalTheme.terminal.allProperties() as? [String: Theme.Attributes],
303-
let oEditColors = try originalTheme.editor.allProperties() as? [String: Theme.Attributes]
304-
else {
305-
// TODO: Throw a proper error
306-
throw NSError() // swiftlint:disable:this discouraged_direct_init
307-
}
308-
309-
// compare the properties and if there are differences, save to overrides
310-
// in `settings.json
311-
var newAttr: [String: [String: Theme.Attributes]] = ["terminal": [:], "editor": [:]]
312-
terminalColors.forEach { (key, value) in
313-
if value != oTermColors[key] {
314-
newAttr["terminal"]?[key] = value
315-
}
316-
}
317-
318-
editorColors.forEach { (key, value) in
319-
if value != oEditColors[key] {
320-
newAttr["editor"]?[key] = value
321-
}
322-
}
323-
DispatchQueue.main.async {
324-
Settings.shared.preferences.theme.overrides[theme.name] = newAttr
325-
}
326-
327-
} catch {
328-
print(error)
329-
}
330-
}
331-
}
332-
333265
func importTheme() {
334266
let openPanel = NSOpenPanel()
335267
let allowedTypes = [UTType(filenameExtension: "cetheme")!]
@@ -349,26 +281,82 @@ final class ThemeModel: ObservableObject {
349281
}
350282
}
351283

284+
func rename(to newName: String, theme: Theme) {
285+
do {
286+
guard let oldURL = theme.fileURL else {
287+
throw NSError(
288+
domain: "ThemeModel",
289+
code: 1,
290+
userInfo: [NSLocalizedDescriptionKey: "Theme file URL not found"]
291+
)
292+
}
293+
294+
var iterator = 1
295+
var finalName = newName
296+
var finalURL = themesURL.appendingPathComponent(finalName).appendingPathExtension("cetheme")
297+
298+
// Check for existing display names in themes
299+
while themes.contains(where: { theme != $0 && $0.displayName == finalName }) {
300+
finalName = "\(newName) \(iterator)"
301+
finalURL = themesURL.appendingPathComponent(finalName).appendingPathExtension("cetheme")
302+
iterator += 1
303+
}
304+
305+
try filemanager.moveItem(at: oldURL, to: finalURL)
306+
307+
try self.loadThemes()
308+
309+
if let index = themes.firstIndex(where: { $0.fileURL == finalURL }) {
310+
themes[index].displayName = finalName
311+
themes[index].fileURL = finalURL
312+
themes[index].name = finalName.lowercased().replacingOccurrences(of: " ", with: "-")
313+
}
314+
315+
} catch {
316+
print("Error renaming theme: \(error.localizedDescription)")
317+
}
318+
}
319+
352320
func duplicate(_ url: URL) {
353321
do {
354322
// Construct the destination file URL
355323
var destinationFileURL = self.themesURL.appendingPathComponent(url.lastPathComponent)
356324

325+
// Extract the base filename and extension
326+
let fileExtension = destinationFileURL.pathExtension
327+
328+
var fileName = destinationFileURL.deletingPathExtension().lastPathComponent
329+
var newFileName = fileName
330+
357331
// Check if the file already exists
358332
var iterator = 1
333+
334+
let isBundled = url.absoluteString.hasPrefix(bundledThemesURL?.absoluteString ?? "")
335+
let isImporting =
336+
!url.absoluteString.hasPrefix(bundledThemesURL?.absoluteString ?? "")
337+
&& !url.absoluteString.hasPrefix(themesURL.absoluteString)
338+
339+
if isBundled {
340+
newFileName = "\(fileName) \(iterator)"
341+
destinationFileURL = self.themesURL
342+
.appendingPathComponent(newFileName)
343+
.appendingPathExtension(fileExtension)
344+
}
345+
359346
while FileManager.default.fileExists(atPath: destinationFileURL.path) {
360-
// Extract the base filename and extension
361-
let fileExtension = destinationFileURL.pathExtension
362-
var fileName = destinationFileURL.deletingPathExtension().lastPathComponent
347+
fileName = destinationFileURL.deletingPathExtension().lastPathComponent
363348

364349
// Remove any existing iterator
365350
if let range = fileName.range(of: " \\d+$", options: .regularExpression) {
366351
fileName = String(fileName[..<range.lowerBound])
367352
}
368353

369354
// Generate a new filename with an iterator
370-
let newFileName = "\(fileName) \(iterator)"
371-
destinationFileURL = self.themesURL.appendingPathComponent(newFileName).appendingPathExtension(fileExtension)
355+
newFileName = "\(fileName) \(iterator)"
356+
destinationFileURL = self.themesURL
357+
.appendingPathComponent(newFileName)
358+
.appendingPathExtension(fileExtension)
359+
372360
iterator += 1
373361
}
374362

@@ -377,13 +365,31 @@ final class ThemeModel: ObservableObject {
377365

378366
try self.loadThemes()
379367

380-
if var newTheme = self.themes.first(where: { $0.fileURL == destinationFileURL }) {
381-
newTheme.name = newTheme.fileURL?.lastPathComponent ?? ""
382-
self.selectedTheme = newTheme
368+
if var index = self.themes.firstIndex(where: { $0.fileURL == destinationFileURL }) {
369+
self.themes[index].displayName = newFileName
370+
self.themes[index].name = newFileName.lowercased().replacingOccurrences(of: " ", with: "-")
371+
if isImporting != true {
372+
self.themes[index].author = NSFullUserName()
373+
}
374+
self.selectedTheme = self.themes[index]
375+
self.detailsTheme = self.themes[index]
383376
}
384377
} catch {
385378
print("Error adding theme: \(error.localizedDescription)")
386379
}
387380
}
388381

382+
/// Save theme to file
383+
func save(_ theme: Theme) {
384+
do {
385+
if let fileURL = theme.fileURL {
386+
let data = try JSONEncoder().encode(theme)
387+
let json = try JSONSerialization.jsonObject(with: data)
388+
let prettyJSON = try JSONSerialization.data(withJSONObject: json, options: [.prettyPrinted])
389+
try prettyJSON.write(to: fileURL, options: .atomic)
390+
}
391+
} catch {
392+
print("Error saving theme: \(error.localizedDescription)")
393+
}
394+
}
389395
}

CodeEdit/Features/Settings/Pages/ThemeSettings/ThemeSettingThemeRow.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ struct ThemeSettingsThemeRow: View {
4242
ThemeSettingsColorPreview(theme)
4343
Menu {
4444
Button("Details...") {
45-
presentingDetails = true
45+
themeModel.detailsTheme = theme
4646
}
4747
Button("Duplicate") {
4848
if let fileURL = theme.fileURL {
@@ -64,8 +64,5 @@ struct ThemeSettingsThemeRow: View {
6464
.onHover { hovering in
6565
isHovering = hovering
6666
}
67-
.sheet(isPresented: $presentingDetails) {
68-
ThemeSettingsThemeDetails($theme)
69-
}
7067
}
7168
}

0 commit comments

Comments
 (0)