1from __future__ import unicode_literals 2 3import os 4import re 5import sys 6import time 7 8from lineedit import Mode, ModalPromptSession, ModalInMemoryHistory, ModalFileHistory 9from prompt_toolkit.formatted_text import ANSI 10from prompt_toolkit.layout.processors import HighlightMatchingBracketProcessor 11from prompt_toolkit.lexers import PygmentsLexer 12from prompt_toolkit.output import ColorDepth 13from prompt_toolkit.styles import style_from_pygments_cls 14from prompt_toolkit.utils import is_windows, get_term_environment_variable 15 16from pygments.styles import get_style_by_name 17 18from rchitect import rcopy, rcall 19from rchitect.interface import setoption, process_events, peek_event, polled_events 20 21from six import string_types 22 23from . import shell 24from .rutils import prase_text_complete 25from .key_bindings import create_r_key_bindings, create_shell_key_bindings, create_key_bindings 26from .completion import RCompleter, SmartPathCompleter 27from .io import CustomInput, CustomOutput 28from .lexer import CustomSLexer as SLexer 29 30 31PROMPT = "\x1b[34mr$>\x1b[0m " 32SHELL_PROMPT = "\x1b[31m#!>\x1b[0m " 33BROWSE_PROMPT = "\x1b[33mBrowse[{}]>\x1b[0m " 34BROWSE_PATTERN = re.compile(r"Browse\[([0-9]+)\]> $") 35VI_MODE_PROMPT = "\x1b[34m[{}]\x1b[0m " 36 37 38class RadianMode(Mode): 39 def __init__( 40 self, 41 name, 42 activator=None, 43 on_post_accept=None, 44 insert_new_line=False, 45 **kwargs): 46 self.activator = activator 47 self.on_post_accept = on_post_accept 48 self.insert_new_line = insert_new_line 49 super(RadianMode, self).__init__(name, **kwargs) 50 51 52def apply_settings(session, settings): 53 setoption("prompt", settings.prompt) 54 55 if settings.auto_width: 56 output_width = session.app.output.get_size().columns 57 if output_width: 58 setoption("width", output_width) 59 60 # necessary on windows 61 setoption("menu.graphics", False) 62 63 def askpass(message): 64 from prompt_toolkit import prompt 65 return prompt(message, is_password=True) 66 67 setoption("askpass", askpass) 68 69 # enables completion of installed package names 70 if rcopy(rcall(("utils", "rc.settings"), "ipck")) is None: 71 rcall(("utils", "rc.settings"), ipck=True) 72 73 74def create_radian_prompt_session(options, settings): 75 76 history_file = ".radian_history" 77 if options.no_history: 78 history = ModalInMemoryHistory() 79 elif not options.global_history and os.path.exists(history_file): 80 history = ModalFileHistory(os.path.abspath(history_file)) 81 else: 82 history = ModalFileHistory(os.path.join(os.path.expanduser("~"), history_file)) 83 84 if is_windows(): 85 output = None 86 else: 87 output = CustomOutput.from_pty(sys.stdout, term=get_term_environment_variable()) 88 89 def get_inputhook(): 90 # make testing more robust 91 if "CIRCLECI" in os.environ or "GITHUB_REPOSITORY" in os.environ: 92 return None 93 94 terminal_width = [None] 95 96 def _(context): 97 output_width = session.app.output.get_size().columns 98 if output_width and terminal_width[0] != output_width: 99 terminal_width[0] = output_width 100 setoption("width", max(terminal_width[0], 20)) 101 102 while True: 103 if context.input_is_ready(): 104 break 105 try: 106 if peek_event(): 107 with session.app.input.detach(): 108 with session.app.input.rare_mode(): 109 process_events() 110 else: 111 polled_events() 112 113 except Exception: 114 pass 115 time.sleep(1.0 / 30) 116 117 return _ 118 119 def vi_mode_prompt(): 120 if session.editing_mode.lower() == "vi" and settings.show_vi_mode_prompt: 121 im = session.app.vi_state.input_mode 122 vi_mode_prompt = settings.vi_mode_prompt 123 if isinstance(vi_mode_prompt, string_types): 124 return vi_mode_prompt.format(str(im)[3:6]) 125 else: 126 return vi_mode_prompt[str(im)[3:6]] 127 return "" 128 129 def message(): 130 if hasattr(session.current_mode, "get_message"): 131 return ANSI(vi_mode_prompt() + session.current_mode.get_message()) 132 elif hasattr(session.current_mode, "message"): 133 message = session.current_mode.message 134 if callable(message): 135 return ANSI(vi_mode_prompt() + message()) 136 else: 137 return ANSI(vi_mode_prompt() + message) 138 else: 139 return "" 140 141 session = ModalPromptSession( 142 message=message, 143 color_depth=ColorDepth.default(term=os.environ.get("TERM")), 144 style=style_from_pygments_cls(get_style_by_name(settings.color_scheme)), 145 editing_mode="VI" if settings.editing_mode in ["vim", "vi"] else "EMACS", 146 history=history, 147 enable_history_search=True, 148 history_search_no_duplicates=settings.history_search_no_duplicates, 149 search_ignore_case=settings.history_search_ignore_case, 150 enable_suspend=True, 151 tempfile_suffix=".R", 152 input=CustomInput(sys.stdin), 153 output=output, 154 inputhook=get_inputhook(), 155 mode_class=RadianMode 156 ) 157 158 apply_settings(session, settings) 159 160 def browse_activator(session): 161 message = session.prompt_text 162 if BROWSE_PATTERN.match(message): 163 session.browse_level = BROWSE_PATTERN.match(message).group(1) 164 return True 165 else: 166 return False 167 168 def browse_on_pre_accept(session): 169 if session.default_buffer.text.strip() in [ 170 "n", "s", "f", "c", "cont", "Q", "where", "help"]: 171 session.add_history = False 172 173 def shell_process_text(session): 174 text = session.default_buffer.text 175 shell.run_command(text) 176 177 input_processors = [] 178 if settings.highlight_matching_bracket: 179 input_processors.append(HighlightMatchingBracketProcessor()) 180 181 session.register_mode( 182 "r", 183 activator=lambda session: session.prompt_text == settings.prompt, 184 insert_new_line=True, 185 history_share_with="browse", 186 get_message=lambda: settings.prompt, 187 multiline=settings.indent_lines, 188 complete_while_typing=settings.complete_while_typing, 189 lexer=PygmentsLexer(SLexer), 190 completer=RCompleter(timeout=settings.completion_timeout), 191 key_bindings=create_key_bindings(), 192 input_processors=input_processors, 193 prompt_key_bindings=create_r_key_bindings(prase_text_complete) 194 ) 195 session.register_mode( 196 "shell", 197 on_post_accept=shell_process_text, 198 insert_new_line=True, 199 get_message=lambda: settings.shell_prompt, 200 multiline=settings.indent_lines, 201 complete_while_typing=settings.complete_while_typing, 202 lexer=None, 203 completer=SmartPathCompleter(), 204 prompt_key_bindings=create_shell_key_bindings() 205 ) 206 session.register_mode( 207 "browse", 208 activator=browse_activator, 209 # on_pre_accept=browse_on_pre_accept, # disable 210 insert_new_line=True, 211 history_share_with="r", 212 get_message=lambda: settings.browse_prompt.format(session.browse_level), 213 multiline=settings.indent_lines, 214 complete_while_typing=True, 215 lexer=PygmentsLexer(SLexer), 216 completer=RCompleter(timeout=settings.completion_timeout), 217 input_processors=input_processors, 218 prompt_key_bindings=create_r_key_bindings(prase_text_complete), 219 switchable_from=False, 220 switchable_to=False 221 ) 222 session.register_mode( 223 "unknown", 224 insert_new_line=False, 225 get_message=lambda: session.prompt_text, 226 complete_while_typing=False, 227 lexer=None, 228 completer=None, 229 prompt_key_bindings=None, 230 switchable_from=False, 231 switchable_to=False, 232 input_processors=[] 233 ) 234 235 return session 236