Skip to content
Snippets Groups Projects
Commit 9b7d0ec4 authored by George Nachman's avatar George Nachman
Browse files

Support initial directory for tmux integration. Issue 5063.

When in tmux integration, send select-pane commands. This enables reusing the previous session's working directory.

Move tmuxBookmark into ProfileModel.

Add a new class iTermInitialDirectory to capture the state around a session's initial directory. It isn't yet used everywhere it could be; just tmux integration currently.
parent 1af6e142
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -2147,6 +2147,8 @@
A6EFF2401D21949D00806EEF /* iTermShortcut.m in Sources */ = {isa = PBXBuildFile; fileRef = A6EFF23E1D21949D00806EEF /* iTermShortcut.m */; };
A6EFF2471D21DD0700806EEF /* iTermSessionHotkeyController.h in Headers */ = {isa = PBXBuildFile; fileRef = A6EFF2451D21DD0700806EEF /* iTermSessionHotkeyController.h */; };
A6EFF2481D21DD0700806EEF /* iTermSessionHotkeyController.m in Sources */ = {isa = PBXBuildFile; fileRef = A6EFF2461D21DD0700806EEF /* iTermSessionHotkeyController.m */; };
A6F3E96E1D60E4F0000E97C4 /* iTermInitialDirectory.h in Headers */ = {isa = PBXBuildFile; fileRef = A6F3E96C1D60E4F0000E97C4 /* iTermInitialDirectory.h */; };
A6F3E96F1D60E4F0000E97C4 /* iTermInitialDirectory.m in Sources */ = {isa = PBXBuildFile; fileRef = A6F3E96D1D60E4F0000E97C4 /* iTermInitialDirectory.m */; };
A6F981CC1C13783C00EE0B80 /* iTermCPS.h in Headers */ = {isa = PBXBuildFile; fileRef = A6F981CB1C13783C00EE0B80 /* iTermCPS.h */; };
A6FC49861C3A31840061B3BA /* iTermSystemVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = A6FC49841C3A31840061B3BA /* iTermSystemVersion.h */; };
A6FC49871C3A31840061B3BA /* iTermSystemVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = A6FC49851C3A31840061B3BA /* iTermSystemVersion.m */; };
Loading
Loading
@@ -3488,6 +3490,8 @@
A6EFF23E1D21949D00806EEF /* iTermShortcut.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = iTermShortcut.m; sourceTree = "<group>"; };
A6EFF2451D21DD0700806EEF /* iTermSessionHotkeyController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iTermSessionHotkeyController.h; sourceTree = "<group>"; };
A6EFF2461D21DD0700806EEF /* iTermSessionHotkeyController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = iTermSessionHotkeyController.m; sourceTree = "<group>"; };
A6F3E96C1D60E4F0000E97C4 /* iTermInitialDirectory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iTermInitialDirectory.h; sourceTree = "<group>"; };
A6F3E96D1D60E4F0000E97C4 /* iTermInitialDirectory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = iTermInitialDirectory.m; sourceTree = "<group>"; };
A6F981CB1C13783C00EE0B80 /* iTermCPS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iTermCPS.h; sourceTree = "<group>"; };
A6FC49841C3A31840061B3BA /* iTermSystemVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iTermSystemVersion.h; sourceTree = "<group>"; };
A6FC49851C3A31840061B3BA /* iTermSystemVersion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = iTermSystemVersion.m; sourceTree = "<group>"; };
Loading
Loading
@@ -5154,6 +5158,8 @@
A6936B481D2D756200521B04 /* iTermWindowOcclusionChangeMonitor.m */,
A686F38B1D3951A800F08ED7 /* iTermBoxDrawingBezierCurveFactory.h */,
A686F38C1D3951A800F08ED7 /* iTermBoxDrawingBezierCurveFactory.m */,
A6F3E96C1D60E4F0000E97C4 /* iTermInitialDirectory.h */,
A6F3E96D1D60E4F0000E97C4 /* iTermInitialDirectory.m */,
);
name = Helpers;
sourceTree = "<group>";
Loading
Loading
@@ -6197,6 +6203,7 @@
A6FEA2881CF4B9AB00376F28 /* iTermHotkeyPreferencesWindowController.h in Headers */,
A6EFF2471D21DD0700806EEF /* iTermSessionHotkeyController.h in Headers */,
A6FEA2631CF0F33300376F28 /* iTermModifierRemapper.h in Headers */,
A6F3E96E1D60E4F0000E97C4 /* iTermInitialDirectory.h in Headers */,
A686F3621D32CD8100F08ED7 /* NSWindow+iTerm.h in Headers */,
1D72A4D51BE9707A0042174A /* iTermWebViewWrapperViewController.h in Headers */,
A62C3B2F1BCC24AB00B5629D /* iTermRecentDirectoryMO+CoreDataProperties.h in Headers */,
Loading
Loading
@@ -7629,6 +7636,7 @@
A6C763AF1B45C52B00E3C992 /* CoprocessTrigger.m in Sources */,
A6C763C21B45C52B00E3C992 /* VT100DCSParser.m in Sources */,
A6C762DD1B45C52B00E3C992 /* PTYTab.m in Sources */,
A6F3E96F1D60E4F0000E97C4 /* iTermInitialDirectory.m in Sources */,
A6C763CA1B45C52B00E3C992 /* VT100Terminal.m in Sources */,
A6C763541B45C52B00E3C992 /* iTermOpenQuicklyTableCellView.m in Sources */,
A62C3B2E1BCC24AB00B5629D /* iTermCommandHistoryCommandUseMO.m in Sources */,
Loading
Loading
Loading
Loading
@@ -18,6 +18,7 @@
#import "iTermController.h"
#import "iTermGrowlDelegate.h"
#import "iTermHotKeyController.h"
#import "iTermInitialDirectory.h"
#import "iTermKeyBindingMgr.h"
#import "iTermMouseCursor.h"
#import "iTermPasteHelper.h"
Loading
Loading
@@ -4088,6 +4089,9 @@ ITERM_WEAKLY_REFERENCEABLE
if ([_terminal reportFocus]) {
[self writeLatin1EncodedData:[_terminal.output reportFocusGained:focused] broadcastAllowed:NO];
}
if (focused && [self isTmuxClient]) {
[_tmuxController selectPane:_tmuxPane];
}
}
}
 
Loading
Loading
Loading
Loading
@@ -2763,25 +2763,12 @@ static void SetAgainstGrainDim(BOOL isVertical, NSSize *dest, CGFloat value) {
[self nameOfSession:self.activeSession didChangeTo:self.activeSession.name];
}
 
+ (Profile *)tmuxBookmark
{
Profile *bookmark = [[ProfileModel sharedInstance] bookmarkWithName:@"tmux"];
if (!bookmark) {
Profile *defaultBookmark = [[ProfileModel sharedInstance] defaultBookmark];
NSMutableDictionary *tmuxBookmark = [[defaultBookmark mutableCopy] autorelease];
[tmuxBookmark setObject:@"tmux" forKey:KEY_NAME];
[tmuxBookmark setObject:[ProfileModel freshGuid] forKey:KEY_GUID];
[tmuxBookmark setObject:[NSNumber numberWithInt:1000]
forKey:KEY_SCROLLBACK_LINES];
[[ProfileModel sharedInstance] addBookmark:tmuxBookmark];
[[ProfileModel sharedInstance] postChangeNotification];
bookmark = tmuxBookmark;
}
return bookmark;
+ (Profile *)tmuxBookmark {
return [[ProfileModel sharedInstance] tmuxProfile];
}
 
- (Profile *)tmuxBookmark {
return [PTYTab tmuxBookmark];
return [[ProfileModel sharedInstance] tmuxProfile];
}
 
+ (void)setTmuxFont:(NSFont *)font
Loading
Loading
Loading
Loading
@@ -116,6 +116,9 @@ typedef struct {
// Write to user defaults
- (void)flush;
 
// Returns the profile to be used for tmux sessions.
- (Profile *)tmuxProfile;
// Tell all listeners that the model has changed.
- (void)postChangeNotification;
 
Loading
Loading
Loading
Loading
@@ -97,6 +97,23 @@ int gMigrated;
[super dealloc];
}
 
- (Profile *)tmuxProfile {
Profile *profile = [self bookmarkWithName:@"tmux"];
if (!profile) {
Profile *defaultBookmark = [self defaultBookmark];
NSMutableDictionary *tmuxProfile = [[defaultBookmark mutableCopy] autorelease];
[tmuxProfile setObject:@"tmux" forKey:KEY_NAME];
[tmuxProfile setObject:[ProfileModel freshGuid] forKey:KEY_GUID];
[tmuxProfile setObject:[NSNumber numberWithInt:1000]
forKey:KEY_SCROLLBACK_LINES];
[self addBookmark:tmuxProfile];
[self postChangeNotification];
profile = tmuxProfile;
}
return profile;
}
- (int)numberOfBookmarks
{
return [bookmarks_ count];
Loading
Loading
Loading
Loading
@@ -1995,18 +1995,20 @@ ITERM_WEAKLY_REFERENCEABLE
return controller;
}
 
- (IBAction)newTmuxWindow:(id)sender
{
[[self currentTmuxController] newWindowWithAffinity:nil];
- (IBAction)newTmuxWindow:(id)sender {
[[self currentTmuxController] newWindowWithAffinity:nil
initialDirectory:[iTermInitialDirectory initialDirectoryFromProfile:self.currentSession.profile
objectType:iTermWindowObject]];
}
 
- (IBAction)newTmuxTab:(id)sender
{
- (IBAction)newTmuxTab:(id)sender {
int tmuxWindow = [[self currentTab] tmuxWindow];
if (tmuxWindow < 0) {
tmuxWindow = -(number_ + 1);
}
[[self currentTmuxController] newWindowWithAffinity:[NSString stringWithFormat:@"%d", tmuxWindow]];
[[self currentTmuxController] newWindowWithAffinity:[NSString stringWithFormat:@"%d", tmuxWindow]
initialDirectory:[iTermInitialDirectory initialDirectoryFromProfile:self.currentSession.profile
objectType:iTermTabObject]];
}
 
- (NSSize)tmuxCompatibleSize {
Loading
Loading
@@ -5051,28 +5053,15 @@ ITERM_WEAKLY_REFERENCEABLE
}
 
- (PTYSession *)splitVertically:(BOOL)isVertical withProfile:(Profile *)profile {
if ([[self currentTab] isTmuxTab]) {
[self willSplitTmuxPane];
[[[self currentSession] tmuxController] splitWindowPane:[[self currentSession] tmuxPane]
vertically:isVertical];
return nil;
}
return [self splitVertically:isVertical
withBookmark:profile
targetSession:[self currentSession]];
}
 
- (PTYSession *)splitVertically:(BOOL)isVertical withBookmarkGuid:(NSString*)guid {
if ([[self currentTab] isTmuxTab]) {
[self willSplitTmuxPane];
[[[self currentSession] tmuxController] splitWindowPane:[[self currentSession] tmuxPane] vertically:isVertical];
return nil;
}
Profile* bookmark = [[ProfileModel sharedInstance] bookmarkWithGuid:guid];
if (bookmark) {
return [self splitVertically:isVertical
withBookmark:bookmark
targetSession:[self currentSession]];
Profile *profile = [[ProfileModel sharedInstance] bookmarkWithGuid:guid];
if (profile) {
return [self splitVertically:isVertical withProfile:profile];
} else {
return nil;
}
Loading
Loading
@@ -5150,7 +5139,10 @@ ITERM_WEAKLY_REFERENCEABLE
targetSession:(PTYSession*)targetSession {
if ([targetSession isTmuxClient]) {
[self willSplitTmuxPane];
[[targetSession tmuxController] splitWindowPane:[targetSession tmuxPane] vertically:isVertical];
[[targetSession tmuxController] selectPane:targetSession.tmuxPane];
[[targetSession tmuxController] splitWindowPane:[targetSession tmuxPane]
vertically:isVertical
initialDirectory:[iTermInitialDirectory initialDirectoryFromProfile:targetSession.profile objectType:iTermPaneObject]];
return nil;
}
PtyLog(@"--------- splitVertically -----------");
Loading
Loading
Loading
Loading
@@ -6,6 +6,7 @@
//
 
#import <Cocoa/Cocoa.h>
#import "iTermInitialDirectory.h"
#import "TmuxGateway.h"
#import "WindowControllerInterface.h"
 
Loading
Loading
@@ -84,14 +85,21 @@ extern NSString *const kTmuxControllerSessionWasRenamed;
- (void)windowPane:(int)wp
resizedBy:(int)amount
horizontally:(BOOL)wasHorizontal;
- (void)splitWindowPane:(int)wp vertically:(BOOL)splitVertically;
- (void)newWindowInSession:(NSString *)targetSession afterWindowWithName:(NSString *)predecessorWindow;
- (void)splitWindowPane:(int)wp
vertically:(BOOL)splitVertically
initialDirectory:(iTermInitialDirectory *)initialDirectory;
- (void)newWindowInSession:(NSString *)targetSession
initialDirectory:(iTermInitialDirectory *)initialDirectory;
- (void)selectPane:(int)windowPane;
 
- (PseudoTerminal *)windowWithAffinityForWindowId:(int)wid;
// nil: Open in a new window
// A string of a non-negative integer (e.g., @"2") means to open alongside a tmux window with that ID
// A string of a negative integer (e.g., @"-2") means to open in an iTerm2 window with abs(windowId)==window number.
- (void)newWindowWithAffinity:(NSString *)windowId;
- (void)newWindowWithAffinity:(NSString *)windowId
initialDirectory:(iTermInitialDirectory *)initialDirectory;
- (void)movePane:(int)srcPane
intoPane:(int)destPane
isVertical:(BOOL)splitVertical
Loading
Loading
Loading
Loading
@@ -41,6 +41,59 @@ static NSString *kListWindowsFormat = @"\"#{session_name}\t#{window_id}\t"
"#{window_flags}\t"
"#{?window_active,1,0}\"";
 
@interface iTermInitialDirectory(Tmux)
@end
@implementation iTermInitialDirectory(Tmux)
- (NSString *)tmuxNewWindowCommandInSession:(NSString *)session {
NSArray *args = @[ @"new-window", @"-PF '#{window_id}'" ];
if (session) {
NSString *targetSessionArg = [NSString stringWithFormat:@"\"%@:+\"", [session stringByEscapingQuotes]];
NSArray *insertionArguments = @[ @"-a",
@"-t",
targetSessionArg ];
args = [args arrayByAddingObjectsFromArray:insertionArguments];
}
return [self tmuxCommandByAddingCustomDirectoryWithArgs:args];
}
- (NSString *)tmuxNewWindowCommand {
return [self tmuxNewWindowCommandInSession:nil];
}
- (NSString *)tmuxSplitWindowCommand:(int)wp vertically:(BOOL)splitVertically {
NSArray *args = @[ @"split-window",
splitVertically ? @"-h": @"-v",
@"-t",
[NSString stringWithFormat:@"%%%d", wp] ];
return [self tmuxCommandByAddingCustomDirectoryWithArgs:args];
}
- (NSString *)tmuxCustomDirectoryParameter {
switch (self.mode) {
case iTermInitialDirectoryModeHome:
return nil;
case iTermInitialDirectoryModeCustom:
return [self.customDirectory stringByReplacingOccurrencesOfString:@"'" withString:@"\\'"];
case iTermInitialDirectoryModeRecycle:
return @"#{pane_current_path}";
}
}
- (NSString *)tmuxCommandByAddingCustomDirectoryWithArgs:(NSArray *)args {
NSString *customDirectory = [self tmuxCustomDirectoryParameter];
if (customDirectory) {
NSString *escapedCustomDirectory= [customDirectory stringByReplacingOccurrencesOfString:@"'" withString:@"\\'"];
NSString *customDirectoryArgument = [NSString stringWithFormat:@"-c '%@'", escapedCustomDirectory];
args = [args arrayByAddingObject:customDirectoryArgument];
}
return [args componentsJoinedByString:@" "];
}
@end
 
@interface TmuxController ()
 
Loading
Loading
@@ -650,25 +703,32 @@ static NSString *kListWindowsFormat = @"\"#{session_name}\t#{window_id}\t"
}
 
// The splitVertically parameter uses the iTerm2 conventions.
- (void)splitWindowPane:(int)wp vertically:(BOOL)splitVertically {
- (void)splitWindowPane:(int)wp
vertically:(BOOL)splitVertically
initialDirectory:(iTermInitialDirectory *)initialDirectory {
// No need for a callback. We should get a layout-changed message and act on it.
[gateway_ sendCommand:[NSString stringWithFormat:@"split-window -%@ -t %%%d", splitVertically ? @"h": @"v", wp]
[gateway_ sendCommand:[initialDirectory tmuxSplitWindowCommand:wp vertically:splitVertically]
responseTarget:nil
responseSelector:nil];
}
- (void)selectPane:(int)windowPane {
NSString *command = [NSString stringWithFormat:@"select-pane -t %%%d", windowPane];
[gateway_ sendCommand:command
responseTarget:nil
responseSelector:nil];
}
 
- (void)newWindowInSession:(NSString *)targetSession
afterWindowWithName:(NSString *)predecessorWindow
{
[gateway_ sendCommand:[NSString stringWithFormat:@"new-window -a -t \"%@:+\" -PF '#{window_id}'",
[targetSession stringByEscapingQuotes]]
initialDirectory:(iTermInitialDirectory *)initialDirectory {
[gateway_ sendCommand:[initialDirectory tmuxNewWindowCommandInSession:targetSession]
responseTarget:nil
responseSelector:nil];
}
 
- (void)newWindowWithAffinity:(NSString *)windowIdString
{
[gateway_ sendCommand:@"new-window -PF '#{window_id}'"
initialDirectory:(iTermInitialDirectory *)initialDirectory {
[gateway_ sendCommand:[initialDirectory tmuxNewWindowCommand]
responseTarget:self
responseSelector:@selector(newWindowWithAffinityCreated:affinityWindow:)
responseObject:windowIdString
Loading
Loading
@@ -1324,8 +1384,7 @@ static NSString *kListWindowsFormat = @"\"#{session_name}\t#{window_id}\t"
}
 
- (void)newWindowWithAffinityCreated:(NSString *)responseStr
affinityWindow:(NSString *)affinityWindow // Value passed in to -newWindowWithAffinity:, may be nil
{
affinityWindow:(NSString *)affinityWindow { // Value passed in to -newWindowWithAffinity:, may be nil
if ([responseStr hasPrefix:@"@"]) {
NSString *windowId = [NSString stringWithInt:[[responseStr substringFromIndex:1] intValue]];
if (affinityWindow) {
Loading
Loading
Loading
Loading
@@ -7,6 +7,9 @@
//
 
#import "TmuxDashboardController.h"
#import "ITAddressBookMgr.h"
#import "iTermInitialDirectory.h"
#import "TmuxSessionsTable.h"
#import "TmuxController.h"
#import "TSVParser.h"
Loading
Loading
@@ -197,12 +200,12 @@
[self reloadWindows];
}
 
- (void)addWindow
{
- (void)addWindow {
NSString *lastName = [[windowsTable_ names] lastObject];
if (lastName) {
[[self tmuxController] newWindowInSession:[sessionsTable_ selectedSessionName]
afterWindowWithName:lastName];
initialDirectory:[iTermInitialDirectory initialDirectoryFromProfile:[[ProfileModel sharedInstance] tmuxProfile]
objectType:iTermWindowObject]];
}
}
 
Loading
Loading
//
// iTermInitialDirectory.h
// iTerm2
//
// Created by George Nachman on 8/14/16.
//
//
#import <Foundation/Foundation.h>
#import "Profile.h"
#import "ITAddressBookMgr.h"
typedef NS_ENUM(NSUInteger, iTermInitialDirectoryMode) {
iTermInitialDirectoryModeHome,
iTermInitialDirectoryModeRecycle,
iTermInitialDirectoryModeCustom
};
@interface iTermInitialDirectory : NSObject
@property(nonatomic, assign) iTermInitialDirectoryMode mode;
// Only used if mode is Custom
@property(nonatomic, copy) NSString *customDirectory;
+ (instancetype)initialDirectoryFromProfile:(Profile *)profile
objectType:(iTermObjectType)objectType;
@end
//
// iTermInitialDirectory.m
// iTerm2
//
// Created by George Nachman on 8/14/16.
//
//
#import "iTermInitialDirectory.h"
#import "iTermProfilePreferences.h"
@implementation iTermInitialDirectory
+ (iTermInitialDirectoryMode)modeForString:(NSString *)modeString
objectType:(iTermObjectType)objectType
profile:(Profile *)profile
customDirectory:(NSString **)customDirectory {
if ([modeString isEqualToString:kProfilePreferenceInitialDirectoryCustomValue]) {
if (customDirectory) {
*customDirectory = [iTermProfilePreferences stringForKey:KEY_WORKING_DIRECTORY inProfile:profile];
}
return iTermInitialDirectoryModeCustom;
} else if ([modeString isEqualToString:kProfilePreferenceInitialDirectoryRecycleValue]) {
return iTermInitialDirectoryModeRecycle;
} else if ([modeString isEqualToString:kProfilePreferenceInitialDirectoryHomeValue]) {
return iTermInitialDirectoryModeHome;
} else if ([modeString isEqualToString:kProfilePreferenceInitialDirectoryAdvancedValue]) {
NSString *key = nil;
switch (objectType) {
case iTermTabObject:
key = [iTermProfilePreferences stringForKey:KEY_AWDS_TAB_OPTION inProfile:profile];
if (customDirectory) {
*customDirectory = [iTermProfilePreferences stringForKey:KEY_AWDS_TAB_DIRECTORY inProfile:profile];
}
break;
case iTermPaneObject:
key = [iTermProfilePreferences stringForKey:KEY_AWDS_PANE_OPTION inProfile:profile];
if (customDirectory) {
*customDirectory = [iTermProfilePreferences stringForKey:KEY_AWDS_PANE_DIRECTORY inProfile:profile];
}
break;
case iTermWindowObject:
key = [iTermProfilePreferences stringForKey:KEY_AWDS_WIN_OPTION inProfile:profile];
if (customDirectory) {
*customDirectory = [iTermProfilePreferences stringForKey:KEY_AWDS_WIN_DIRECTORY inProfile:profile];
}
break;
}
assert(key);
return [self modeForString:key
objectType:objectType
profile:profile
customDirectory:nil];
}
assert(false);
return iTermInitialDirectoryModeHome;
}
+ (instancetype)initialDirectoryFromProfile:(Profile *)profile
objectType:(iTermObjectType)objectType {
iTermInitialDirectory *initialDirectory = [[[iTermInitialDirectory alloc] init] autorelease];
NSString *customDirectorySetting = [iTermProfilePreferences stringForKey:KEY_CUSTOM_DIRECTORY inProfile:profile];
NSString *customDirectory = nil;
initialDirectory.mode = [self modeForString:customDirectorySetting
objectType:objectType
profile:profile
customDirectory:&customDirectory];
if (initialDirectory.mode == iTermInitialDirectoryModeCustom) {
initialDirectory.customDirectory = customDirectory;
}
return initialDirectory;
}
- (void)dealloc {
[_customDirectory release];
[super dealloc];
}
@end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment