@@ -92,15 +92,6 @@ struct TerminalEmulatorView: NSViewRepresentable {
9292 }
9393 }
9494
95- private func setupShellTitle( shell: String ) {
96- if let shellSetupScript = Bundle . main. url ( forResource: " codeedit_shell_integration " , withExtension: shell) {
97- let scriptPath = ( shellSetupScript. absoluteString [ 7 ..< shellSetupScript. absoluteString. count] ) ?? " "
98- terminal. send ( txt: " source \( scriptPath) \n " )
99- }
100-
101- terminal. send ( txt: " clear \n " )
102- }
103-
10495 private func getTerminalCursor( ) -> CursorStyle {
10596 let blink = terminalSettings. cursorBlink
10697 switch terminalSettings. cursorStyle {
@@ -128,6 +119,84 @@ struct TerminalEmulatorView: NSViewRepresentable {
128119 return String ( cString: pwd. pw_shell)
129120 }
130121
122+ /// Check if the source command for shell integration already exists
123+ /// Returns true if it already exists or encountered an error, no new commands will be added to user's source file
124+ /// Returns false if it's not there, new commands will be added to user's source file
125+ private func shellIntegrationInstalled( sourceScriptPath: String , command: String ) -> Bool {
126+ do {
127+ // Get user's shell's source file
128+ let sourceScript = try String ( contentsOfFile: sourceScriptPath)
129+ let sourceScriptSeperatedByLines = sourceScript. components ( separatedBy: . newlines)
130+ // Check line by line
131+ for line in sourceScriptSeperatedByLines where line == command {
132+ // If one line matches the command, no new commands are needed
133+ return true
134+ }
135+ // If no line matches the command, new command is needed
136+ return false
137+ } catch {
138+ if let error = error as NSError ? {
139+ switch error. _code {
140+ case 260 :
141+ // If error 260 is thrown, it's just the source file is missing
142+ // Create a new file and add new command
143+ FileManager . default. createFile ( atPath: sourceScriptPath, contents: nil , attributes: nil )
144+ return false
145+ default :
146+ // Otherwise just abort the shell integration setup
147+ print ( " Cannot setup shell integration, error: \( error) " )
148+ return true
149+ }
150+ }
151+ }
152+ }
153+
154+ /// Configure shell integration script
155+ private func setupShellIntegration( shell: String , environment: [ String ] ) {
156+ // Get user's home dir
157+ var homePath : String = " "
158+ environment. forEach { value in
159+ if value. starts ( with: " HOME= " ) {
160+ homePath = value
161+ }
162+ }
163+ homePath. removeSubrange ( homePath. startIndex..< homePath. index ( homePath. startIndex, offsetBy: 5 ) )
164+
165+ if let shellIntegrationScript = Bundle . main. url (
166+ forResource: " codeedit_shell_integration " , withExtension: shell
167+ ) {
168+ // Get the path of shell integration script
169+ let shellIntegrationScriptPath = (
170+ shellIntegrationScript. absoluteString [ 7 ..< shellIntegrationScript. absoluteString. count]
171+ ) ?? " "
172+
173+ // Get the path of user's shell's source file
174+ // Only zsh and bash are supported for now
175+ var sourceScriptPath : String = " "
176+ switch shell {
177+ case " bash " :
178+ sourceScriptPath = homePath + " /.profile "
179+ case " zsh " :
180+ sourceScriptPath = homePath + " /.zshrc "
181+ default :
182+ return
183+ }
184+
185+ // Get the command for setting up shell integration
186+ let sourceCommand = " [[ \" $TERM_PROGRAM \" == \" CodeEditApp_Terminal \" ]] && "
187+ + " . \" \( shellIntegrationScriptPath) \" "
188+
189+ // Add the shell integration setup command if needed
190+ if !shellIntegrationInstalled( sourceScriptPath: sourceScriptPath, command: sourceCommand) {
191+ if let handle = FileHandle ( forWritingAtPath: sourceScriptPath) {
192+ handle. seekToEndOfFile ( )
193+ handle. write ( " \n \( sourceCommand) \n " . data ( using: . utf8) !)
194+ handle. closeFile ( )
195+ }
196+ }
197+ }
198+ }
199+
131200 /// Returns true if the `option` key should be treated as the `meta` key.
132201 private var optionAsMeta : Bool {
133202 terminalSettings. optionAsMeta
@@ -214,7 +283,13 @@ struct TerminalEmulatorView: NSViewRepresentable {
214283 // multiple workspaces. This works for now but most probably will need
215284 // to be changed later on
216285 FileManager . default. changeCurrentDirectoryPath ( url. path)
217- terminal. startProcess ( executable: shell, execName: shellIdiom)
286+
287+ var terminalEnvironment : [ String ] = Terminal . getEnvironmentVariables ( )
288+ terminalEnvironment. append ( " TERM_PROGRAM=CodeEditApp_Terminal " )
289+
290+ setupShellIntegration ( shell: shellName, environment: terminalEnvironment)
291+
292+ terminal. startProcess ( executable: shell, environment: terminalEnvironment, execName: shellIdiom)
218293 terminal. font = font
219294 terminal. configureNativeColors ( )
220295 terminal. installColors ( self . colors)
@@ -225,8 +300,6 @@ struct TerminalEmulatorView: NSViewRepresentable {
225300 terminal. cursorStyleChanged ( source: terminal. getTerminal ( ) , newStyle: getTerminalCursor ( ) )
226301 terminal. layer? . backgroundColor = . clear
227302 terminal. optionAsMetaKey = optionAsMeta
228-
229- setupShellTitle ( shell: shellName)
230303 }
231304 terminal. appearance = colorAppearance
232305 scroller? . isHidden = true
0 commit comments