Skip to content

Commit ab126d5

Browse files
Autocomplete Coordinator
1 parent 8fe1b28 commit ab126d5

8 files changed

Lines changed: 268 additions & 56 deletions

File tree

CodeEdit.xcodeproj/project.pbxproj

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
archiveVersion = 1;
44
classes = {
55
};
6-
objectVersion = 55;
6+
objectVersion = 70;
77
objects = {
88

99
/* Begin PBXBuildFile section */
@@ -64,6 +64,7 @@
6464
3000516A2BBD3A8200A98562 /* ServiceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 300051692BBD3A8200A98562 /* ServiceType.swift */; };
6565
3000516C2BBD3A9500A98562 /* ServiceWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3000516B2BBD3A9500A98562 /* ServiceWrapper.swift */; };
6666
3026F50F2AC006C80061227E /* InspectorAreaViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3026F50E2AC006C80061227E /* InspectorAreaViewModel.swift */; };
67+
3046374E2CB15FA900180667 /* AutoCompleteCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3046374D2CB15F9200180667 /* AutoCompleteCoordinator.swift */; };
6768
30AB4EBB2BF718A100ED4431 /* DeveloperSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AB4EBA2BF718A100ED4431 /* DeveloperSettings.swift */; };
6869
30AB4EBD2BF71CA800ED4431 /* DeveloperSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AB4EBC2BF71CA800ED4431 /* DeveloperSettingsView.swift */; };
6970
30AB4EC22BF7253200ED4431 /* KeyValueTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AB4EC12BF7253200ED4431 /* KeyValueTable.swift */; };
@@ -463,10 +464,10 @@
463464
6CD26C7B2C8EA8A500ADBA38 /* LSPCache+Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CD26C792C8EA8A500ADBA38 /* LSPCache+Data.swift */; };
464465
6CD26C7D2C8EA8F400ADBA38 /* LanguageServer+DocumentSync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CD26C7C2C8EA8F400ADBA38 /* LanguageServer+DocumentSync.swift */; };
465466
6CD26C812C8F8A4400ADBA38 /* LanguageIdentifier+CodeLanguage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CD26C802C8F8A4400ADBA38 /* LanguageIdentifier+CodeLanguage.swift */; };
466-
6CD26C852C8F907800ADBA38 /* CodeEditSourceEditor in Frameworks */ = {isa = PBXBuildFile; productRef = 6CD26C842C8F907800ADBA38 /* CodeEditSourceEditor */; };
467+
6CD26C852C8F907800ADBA38 /* (null) in Frameworks */ = {isa = PBXBuildFile; };
467468
6CD26C872C8F90FD00ADBA38 /* LazyServiceWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CD26C862C8F90FD00ADBA38 /* LazyServiceWrapper.swift */; };
468469
6CD26C8A2C8F91ED00ADBA38 /* LanguageServer+DocumentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CD26C892C8F91ED00ADBA38 /* LanguageServer+DocumentTests.swift */; };
469-
6CD3CA552C8B508200D83DCD /* CodeEditSourceEditor in Frameworks */ = {isa = PBXBuildFile; productRef = 6CD3CA542C8B508200D83DCD /* CodeEditSourceEditor */; };
470+
6CD3CA552C8B508200D83DCD /* (null) in Frameworks */ = {isa = PBXBuildFile; };
470471
6CDA84AD284C1BA000C1CC3A /* EditorTabBarContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CDA84AC284C1BA000C1CC3A /* EditorTabBarContextMenu.swift */; };
471472
6CE21E812C643D8F0031B056 /* CETerminalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CE21E802C643D8F0031B056 /* CETerminalView.swift */; };
472473
6CE21E872C650D2C0031B056 /* SwiftTerm in Frameworks */ = {isa = PBXBuildFile; productRef = 6CE21E862C650D2C0031B056 /* SwiftTerm */; };
@@ -742,6 +743,7 @@
742743
300051692BBD3A8200A98562 /* ServiceType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceType.swift; sourceTree = "<group>"; };
743744
3000516B2BBD3A9500A98562 /* ServiceWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceWrapper.swift; sourceTree = "<group>"; };
744745
3026F50E2AC006C80061227E /* InspectorAreaViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorAreaViewModel.swift; sourceTree = "<group>"; };
746+
3046374D2CB15F9200180667 /* AutoCompleteCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoCompleteCoordinator.swift; sourceTree = "<group>"; };
745747
30AB4EBA2BF718A100ED4431 /* DeveloperSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperSettings.swift; sourceTree = "<group>"; };
746748
30AB4EBC2BF71CA800ED4431 /* DeveloperSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperSettingsView.swift; sourceTree = "<group>"; };
747749
30AB4EC12BF7253200ED4431 /* KeyValueTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyValueTable.swift; sourceTree = "<group>"; };
@@ -1284,6 +1286,10 @@
12841286
EC0870F62A455F6400EB8692 /* ProjectNavigatorViewController+NSMenuDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProjectNavigatorViewController+NSMenuDelegate.swift"; sourceTree = "<group>"; };
12851287
/* End PBXFileReference section */
12861288

1289+
/* Begin PBXFileSystemSynchronizedRootGroup section */
1290+
302EFC1F2CC3C034004A74DF /* Views */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Views; sourceTree = "<group>"; };
1291+
/* End PBXFileSystemSynchronizedRootGroup section */
1292+
12871293
/* Begin PBXFrameworksBuildPhase section */
12881294
2BE487E928245162003F3F64 /* Frameworks */ = {
12891295
isa = PBXFrameworksBuildPhase;
@@ -1301,7 +1307,7 @@
13011307
58F2EB1E292FB954004A9BDE /* Sparkle in Frameworks */,
13021308
6C147C4529A329350089B630 /* OrderedCollections in Frameworks */,
13031309
6CE21E872C650D2C0031B056 /* SwiftTerm in Frameworks */,
1304-
6CD3CA552C8B508200D83DCD /* CodeEditSourceEditor in Frameworks */,
1310+
6CD3CA552C8B508200D83DCD /* (null) in Frameworks */,
13051311
6C0617D62BDB4432008C9C42 /* LogStream in Frameworks */,
13061312
6CC17B4F2C432AE000834E2C /* CodeEditSourceEditor in Frameworks */,
13071313
30CB64912C16CA8100CC8A9E /* LanguageServerProtocol in Frameworks */,
@@ -1310,7 +1316,7 @@
13101316
6C85BB442C210EFD00EB5DEF /* SwiftUIIntrospect in Frameworks */,
13111317
6CB446402B6DFF3A00539ED0 /* CodeEditSourceEditor in Frameworks */,
13121318
2816F594280CF50500DD548B /* CodeEditSymbols in Frameworks */,
1313-
6CD26C852C8F907800ADBA38 /* CodeEditSourceEditor in Frameworks */,
1319+
6CD26C852C8F907800ADBA38 /* (null) in Frameworks */,
13141320
30CB64942C16CA9100CC8A9E /* LanguageClient in Frameworks */,
13151321
6C6BD6F829CD14D100235D17 /* CodeEditKit in Frameworks */,
13161322
6C0824A12C5C0C9700A0751E /* SwiftTerm in Frameworks */,
@@ -1560,6 +1566,7 @@
15601566
children = (
15611567
6CD26C732C8EA71F00ADBA38 /* LanguageServer */,
15621568
6CD26C742C8EA79100ADBA38 /* Service */,
1569+
302EFC1F2CC3C034004A74DF /* Views */,
15631570
30B087FA2C0D53080063A882 /* LSPUtil.swift */,
15641571
);
15651572
path = LSP;
@@ -2823,6 +2830,7 @@
28232830
287776EB27E350BA00D46668 /* TabBar */,
28242831
B67660642AA970ED00CD56B0 /* Models */,
28252832
B67660632AA970E300CD56B0 /* Views */,
2833+
3046374D2CB15F9200180667 /* AutoCompleteCoordinator.swift */,
28262834
);
28272835
path = Editor;
28282836
sourceTree = "<group>";
@@ -3675,6 +3683,9 @@
36753683
6C7B1C762A1D57CE005CBBFC /* PBXTargetDependency */,
36763684
2BE487F328245162003F3F64 /* PBXTargetDependency */,
36773685
);
3686+
fileSystemSynchronizedGroups = (
3687+
302EFC1F2CC3C034004A74DF /* Views */,
3688+
);
36783689
name = CodeEdit;
36793690
packageProductDependencies = (
36803691
2816F593280CF50500DD548B /* CodeEditSymbols */,
@@ -3692,8 +3703,6 @@
36923703
6C0824A02C5C0C9700A0751E /* SwiftTerm */,
36933704
6CE21E862C650D2C0031B056 /* SwiftTerm */,
36943705
6C4E37FB2C73E00700AEE7B5 /* SwiftTerm */,
3695-
6CD3CA542C8B508200D83DCD /* CodeEditSourceEditor */,
3696-
6CD26C842C8F907800ADBA38 /* CodeEditSourceEditor */,
36973706
6CB94D022CA1205100E8651C /* AsyncAlgorithms */,
36983707
);
36993708
productName = CodeEdit;
@@ -3791,8 +3800,8 @@
37913800
303E88452C276FD100EEA8D9 /* XCRemoteSwiftPackageReference "LanguageClient" */,
37923801
303E88462C276FD600EEA8D9 /* XCRemoteSwiftPackageReference "LanguageServerProtocol" */,
37933802
6C4E37FA2C73E00700AEE7B5 /* XCRemoteSwiftPackageReference "SwiftTerm" */,
3794-
6CD26C832C8F907800ADBA38 /* XCRemoteSwiftPackageReference "CodeEditSourceEditor" */,
37953803
6CB94D012CA1205100E8651C /* XCRemoteSwiftPackageReference "swift-async-algorithms" */,
3804+
302EFBFB2CC3284D004A74DF /* XCLocalSwiftPackageReference "../CodeEditSourceEditor" */,
37963805
);
37973806
productRefGroup = B658FB2D27DA9E0F00EA4DBD /* Products */;
37983807
projectDirPath = "";
@@ -4162,6 +4171,7 @@
41624171
58A5DFA229339F6400D1BD5D /* KeybindingManager.swift in Sources */,
41634172
B62AEDB32A1FD95B009A9F52 /* UtilityAreaTerminalView.swift in Sources */,
41644173
661EF7BD2BEE215300C3E577 /* LoadingFileView.swift in Sources */,
4174+
3046374E2CB15FA900180667 /* AutoCompleteCoordinator.swift in Sources */,
41654175
58AFAA2E2933C69E00482B53 /* EditorTabRepresentable.swift in Sources */,
41664176
6C4104E6297C884F00F472BA /* AboutDetailView.swift in Sources */,
41674177
6C6BD6F129CD13FA00235D17 /* ExtensionDiscovery.swift in Sources */,
@@ -5558,6 +5568,13 @@
55585568
};
55595569
/* End XCConfigurationList section */
55605570

5571+
/* Begin XCLocalSwiftPackageReference section */
5572+
302EFBFB2CC3284D004A74DF /* XCLocalSwiftPackageReference "../CodeEditSourceEditor" */ = {
5573+
isa = XCLocalSwiftPackageReference;
5574+
relativePath = ../CodeEditSourceEditor;
5575+
};
5576+
/* End XCLocalSwiftPackageReference section */
5577+
55615578
/* Begin XCRemoteSwiftPackageReference section */
55625579
2816F592280CF50500DD548B /* XCRemoteSwiftPackageReference "CodeEditSymbols" */ = {
55635580
isa = XCRemoteSwiftPackageReference;
@@ -5687,14 +5704,6 @@
56875704
version = 1.0.1;
56885705
};
56895706
};
5690-
6CD26C832C8F907800ADBA38 /* XCRemoteSwiftPackageReference "CodeEditSourceEditor" */ = {
5691-
isa = XCRemoteSwiftPackageReference;
5692-
repositoryURL = "https://github.com/CodeEditApp/CodeEditSourceEditor";
5693-
requirement = {
5694-
kind = upToNextMajorVersion;
5695-
minimumVersion = 0.8.1;
5696-
};
5697-
};
56985707
/* End XCRemoteSwiftPackageReference section */
56995708

57005709
/* Begin XCSwiftPackageProductDependency section */
@@ -5789,15 +5798,6 @@
57895798
isa = XCSwiftPackageProductDependency;
57905799
productName = CodeEditSourceEditor;
57915800
};
5792-
6CD26C842C8F907800ADBA38 /* CodeEditSourceEditor */ = {
5793-
isa = XCSwiftPackageProductDependency;
5794-
package = 6CD26C832C8F907800ADBA38 /* XCRemoteSwiftPackageReference "CodeEditSourceEditor" */;
5795-
productName = CodeEditSourceEditor;
5796-
};
5797-
6CD3CA542C8B508200D83DCD /* CodeEditSourceEditor */ = {
5798-
isa = XCSwiftPackageProductDependency;
5799-
productName = CodeEditSourceEditor;
5800-
};
58015801
6CE21E862C650D2C0031B056 /* SwiftTerm */ = {
58025802
isa = XCSwiftPackageProductDependency;
58035803
productName = SwiftTerm;

CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 8 additions & 26 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

CodeEdit/Features/Documents/CodeFileDocument/CodeFileDocument.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import Foundation
1010
import SwiftUI
1111
import UniformTypeIdentifiers
1212
import CodeEditSourceEditor
13-
import CodeEditTextView
1413
import CodeEditLanguages
1514
import Combine
1615
import OSLog
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
//
2+
// AutoCompleteCoordinator.swift
3+
// CodeEdit
4+
//
5+
// Created by Abe Malla on 9/20/24.
6+
//
7+
8+
import AppKit
9+
import CodeEditTextView
10+
import CodeEditSourceEditor
11+
import LanguageServerProtocol
12+
13+
class AutoCompleteCoordinator: TextViewCoordinator {
14+
private weak var textViewController: TextViewController?
15+
private var localEventMonitor: Any?
16+
17+
private let itemBoxController = ItemBoxWindowController()
18+
19+
func prepareCoordinator(controller: TextViewController) {
20+
itemBoxController.close()
21+
self.textViewController = controller
22+
23+
localEventMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { event in
24+
// `ctrl + space` keyboard shortcut listener for the item box to show
25+
if event.modifierFlags.contains(.control) && event.charactersIgnoringModifiers == " " {
26+
self.showAutocompleteWindow()
27+
return nil
28+
}
29+
return event
30+
}
31+
}
32+
33+
func showAutocompleteWindow() {
34+
guard let cursorPos = textViewController?.cursorPositions.last,
35+
let textView = textViewController?.textView,
36+
let window = NSApplication.shared.keyWindow,
37+
!itemBoxController.isVisible
38+
else {
39+
return
40+
}
41+
42+
itemBoxController.items = [
43+
CompletionItem(label: "item1", kind: .class),
44+
CompletionItem(label: "item2", kind: .enum),
45+
CompletionItem(label: "item3", kind: .function),
46+
CompletionItem(label: "item4", kind: .color),
47+
CompletionItem(label: "item5", kind: .constant),
48+
CompletionItem(label: "item6", kind: .constructor),
49+
CompletionItem(label: "item7", kind: .enumMember),
50+
CompletionItem(label: "item8", kind: .field),
51+
CompletionItem(label: "item9", kind: .file),
52+
CompletionItem(label: "item10", kind: .folder),
53+
CompletionItem(label: "item11", kind: .snippet),
54+
CompletionItem(label: "item12", kind: .reference),
55+
]
56+
57+
// Reset the size of the window
58+
let windowSize = ItemBoxWindowController.DEFAULT_SIZE
59+
itemBoxController.window?.setContentSize(windowSize)
60+
61+
let cursorRect = textView.firstRect(forCharacterRange: cursorPos.range, actualRange: nil)
62+
let screenFrame = window.screen!.visibleFrame
63+
let padding: CGFloat = 22
64+
var autocompleteWindowOrigin = NSPoint(
65+
x: cursorRect.origin.x,
66+
y: cursorRect.origin.y
67+
)
68+
69+
// Keep the horizontal position within the screen and some padding
70+
let minX = screenFrame.minX + padding
71+
let maxX = screenFrame.maxX - windowSize.width - padding
72+
73+
if autocompleteWindowOrigin.x < minX {
74+
autocompleteWindowOrigin.x = minX
75+
} else if autocompleteWindowOrigin.x > maxX {
76+
autocompleteWindowOrigin.x = maxX
77+
}
78+
79+
// Check if the window will go below the screen
80+
// We determine whether the window drops down or upwards by choosing which
81+
// corner of the window we will position: `setFrameOrigin` or `setFrameTopLeftPoint`
82+
if autocompleteWindowOrigin.y - windowSize.height < screenFrame.minY {
83+
// If the cursor itself if below the screen, then position the window
84+
// at the bottom of the screen with some padding
85+
if autocompleteWindowOrigin.y < screenFrame.minY {
86+
autocompleteWindowOrigin.y = screenFrame.minY + padding
87+
} else {
88+
// Place above the cursor
89+
autocompleteWindowOrigin.y += cursorRect.height
90+
}
91+
92+
itemBoxController.window?.setFrameOrigin(autocompleteWindowOrigin)
93+
} else {
94+
// If the window goes above the screen, position it below the screen with padding
95+
let maxY = screenFrame.maxY - padding
96+
if autocompleteWindowOrigin.y > maxY {
97+
autocompleteWindowOrigin.y = maxY
98+
}
99+
100+
itemBoxController.window?.setFrameTopLeftPoint(autocompleteWindowOrigin)
101+
}
102+
103+
itemBoxController.showWindow(attachedTo: window)
104+
}
105+
106+
deinit {
107+
print("Destroyed AutoCompleteCoordinator")
108+
itemBoxController.close()
109+
if let localEventMonitor = localEventMonitor {
110+
NSEvent.removeMonitor(localEventMonitor)
111+
self.localEventMonitor = nil
112+
}
113+
}
114+
}

CodeEdit/Features/Editor/Views/CodeFileView.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ struct CodeFileView: View {
2121

2222
/// Any coordinators passed to the view.
2323
private var textViewCoordinators: [TextViewCoordinator]
24+
/// The coordinator that manages the autocomplete window (item box)
25+
private let autocompleteCoordinator = AutoCompleteCoordinator()
2426

2527
@AppSettings(\.textEditing.defaultTabWidth)
2628
var defaultTabWidth
@@ -58,7 +60,8 @@ struct CodeFileView: View {
5860
self._codeFile = .init(wrappedValue: codeFile)
5961
self.textViewCoordinators = textViewCoordinators + [
6062
codeFile.contentCoordinator,
61-
codeFile.languageServerCoordinator
63+
codeFile.languageServerCoordinator,
64+
autocompleteCoordinator,
6265
]
6366
self.isEditable = isEditable
6467

@@ -139,7 +142,6 @@ struct CodeFileView: View {
139142
undoManager: undoManager,
140143
coordinators: textViewCoordinators
141144
)
142-
143145
.id(codeFile.fileURL)
144146
.background {
145147
if colorScheme == .dark {

0 commit comments

Comments
 (0)