@@ -738,6 +738,151 @@ def command_shell(session_name, window_name, socket_name, socket_path, command):
738738 tmuxp_breakpoint ()
739739
740740
741+ @cli .command (name = 'shell_plus' )
742+ @click .argument ('session_name' , nargs = 1 , required = False )
743+ @click .argument ('window_name' , nargs = 1 , required = False )
744+ @click .option ('-S' , 'socket_path' , help = 'pass-through for tmux -S' )
745+ @click .option ('-L' , 'socket_name' , help = 'pass-through for tmux -L' )
746+ @click .option (
747+ '-c' ,
748+ 'command' ,
749+ help = 'Instead of opening shell, execute python code in libtmux and exit' ,
750+ )
751+ @click .option (
752+ '--use-pythonrc/--no-startup' ,
753+ 'use_pythonrc' ,
754+ help = 'Load the PYTHONSTARTUP environment variable and ~/.pythonrc.py script.' ,
755+ default = False ,
756+ )
757+ def command_shell_plus (
758+ session_name ,
759+ window_name ,
760+ socket_name ,
761+ socket_path ,
762+ command ,
763+ use_pythonrc ,
764+ ):
765+ """shell w/ tab completion.
766+
767+ Credits: django-extensions shell_plus.py 51fef74 (MIT License)
768+ """
769+ server = Server (socket_name = socket_name , socket_path = socket_path )
770+
771+ current_pane = None
772+ if os .getenv ('TMUX_PANE' ) is not None :
773+ current_pane = next (
774+ (
775+ p
776+ for p in server ._list_panes ()
777+ if p .get ('pane_id' ) == os .getenv ('TMUX_PANE' )
778+ ),
779+ None ,
780+ )
781+
782+ try :
783+ if session_name :
784+ session = server .find_where ({'session_name' : session_name })
785+ elif current_pane is not None :
786+ session = server .find_where ({'session_id' : current_pane ['session_id' ]})
787+ else :
788+ session = server .list_sessions ()[0 ]
789+
790+ if not session :
791+ raise exc .TmuxpException ('Session not found: %s' % session_name )
792+ except exc .TmuxpException as e :
793+ print (e )
794+ return
795+
796+ try :
797+ if window_name :
798+ window = session .find_where ({'window_name' : window_name })
799+ if not window :
800+ raise exc .TmuxpException ('Window not found: %s' % window_name )
801+ elif current_pane is not None :
802+ window = session .find_where ({'window_id' : current_pane ['window_id' ]})
803+ else :
804+ window = session .list_windows ()[0 ]
805+
806+ except exc .TmuxpException as e :
807+ print (e )
808+ return
809+
810+ try :
811+ if current_pane is not None :
812+ pane = window .find_where ({'pane_id' : current_pane ['pane_id' ]}) # NOQA: F841
813+ else :
814+ pane = window .attached_pane # NOQA: F841
815+ except exc .TmuxpException as e :
816+ print (e )
817+ return
818+
819+ if command is not None :
820+ exec (command )
821+ else :
822+ # Using normal Python shell
823+ import code
824+
825+ # imported_objects = [pane, window, session, server]
826+ import libtmux
827+
828+ imported_objects = {
829+ 'libtmux' : libtmux ,
830+ 'Server' : libtmux .Server ,
831+ 'Session' : libtmux .Session ,
832+ 'Window' : libtmux .Window ,
833+ 'Pane' : libtmux .Pane ,
834+ 'server' : server ,
835+ 'session' : session ,
836+ 'window' : window ,
837+ 'pane' : pane ,
838+ }
839+
840+ try :
841+ # Try activating rlcompleter, because it's handy.
842+ import readline
843+ except ImportError :
844+ pass
845+ else :
846+ # We don't have to wrap the following import in a 'try', because
847+ # we already know 'readline' was imported successfully.
848+ import rlcompleter
849+
850+ readline .set_completer (rlcompleter .Completer (imported_objects ).complete )
851+ # Enable tab completion on systems using libedit (e.g. macOS).
852+ # These lines are copied from Lib/site.py on Python 3.4.
853+ readline_doc = getattr (readline , '__doc__' , '' )
854+ if readline_doc is not None and 'libedit' in readline_doc :
855+ readline .parse_and_bind ("bind ^I rl_complete" )
856+ else :
857+ readline .parse_and_bind ("tab:complete" )
858+
859+ # We want to honor both $PYTHONSTARTUP and .pythonrc.py, so follow system
860+ # conventions and get $PYTHONSTARTUP first then .pythonrc.py.
861+ if use_pythonrc :
862+ for pythonrc in set (
863+ [os .environ .get ("PYTHONSTARTUP" ), os .path .expanduser ('~/.pythonrc.py' )]
864+ ):
865+ if not pythonrc :
866+ continue
867+ if not os .path .isfile (pythonrc ):
868+ continue
869+ with open (pythonrc ) as handle :
870+ pythonrc_code = handle .read ()
871+ # Match the behavior of the cpython shell where an error in
872+ # PYTHONSTARTUP prints an exception and continues.
873+ try :
874+ exec (compile (pythonrc_code , pythonrc , 'exec' ), imported_objects )
875+ except Exception :
876+ import traceback
877+
878+ traceback .print_exc ()
879+
880+ def run_plain ():
881+ code .interact (local = imported_objects )
882+
883+ run_plain ()
884+
885+
741886@cli .command (name = 'freeze' )
742887@click .argument ('session_name' , nargs = 1 , required = False )
743888@click .option ('-S' , 'socket_path' , help = 'pass-through for tmux -S' )
0 commit comments