Skip to content

Commit e03cba7

Browse files
authored
Overhaul code (#106)
* Overhaul code: * DRY prompt-toolkit and GNU readline initialization code * Respect NO_COLOR environment setting * Fix up style=None so that it doesn't show colors on In/Out * Fix bugs in changing color styles * Remove f5-f6 keybindings. It's too clumsy and hard to hook in.
1 parent a3ab1ee commit e03cba7

File tree

6 files changed

+164
-217
lines changed

6 files changed

+164
-217
lines changed

mathicsscript/__main__.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
from mathics.core.evaluation import Evaluation, Output
2828
from mathics.core.expression import from_python
2929
from mathics.core.parser import MathicsFileLineFeeder
30-
from mathics.core.symbols import Symbol, SymbolFalse, SymbolTrue
30+
from mathics.core.symbols import Symbol, SymbolNull, SymbolFalse, SymbolTrue
3131
from mathics.core.systemsymbols import SymbolTeXForm
3232
from mathics.session import autoload_files
3333

@@ -113,7 +113,10 @@ def ensure_settings():
113113
return settings_file
114114

115115

116-
def load_settings(shell):
116+
def load_settings_file(shell):
117+
"""
118+
Read in or "autoload" Mathics3 code to initialize some settings.
119+
"""
117120
autoload_files(shell.definitions, get_srcdir(), "autoload")
118121
settings_file = ensure_settings()
119122
if settings_file == "":
@@ -182,6 +185,7 @@ def fmt_fun(query: Any) -> Any:
182185
style = style.get_string_value()
183186
if shell.terminal_formatter:
184187
fmt = fmt_fun
188+
shell.pygments_style = style or "None"
185189

186190
evaluation = Evaluation(shell.definitions, output=TerminalOutput(shell))
187191

@@ -200,7 +204,10 @@ def fmt_fun(query: Any) -> Any:
200204
):
201205
current_pos = GNU_readline.get_current_history_length()
202206
for pos in range(last_pos, current_pos - 1):
203-
GNU_readline.remove_history_item(pos)
207+
try:
208+
GNU_readline.remove_history_item(pos)
209+
except ValueError:
210+
pass
204211
wl_input = source_code.rstrip()
205212
if unicode:
206213
wl_input = replace_wl_with_plain_text(wl_input)
@@ -434,6 +441,10 @@ def main(
434441
f"Settings`{setting_name}", from_python(True if setting_value else False)
435442
)
436443

444+
if os.environ.get("NO_COLOR", False) and style not in (None, "None"):
445+
print('Environment variable NO_COLOR set when "style" option given.')
446+
print("NO_COLOR setting ignored.")
447+
437448
if post_mortem:
438449
try:
439450
from trepan.post_mortem import post_mortem_excepthook
@@ -448,15 +459,20 @@ def main(
448459
readline = "none" if (code or file and not persist) else readline.lower()
449460
if readline == "prompt":
450461
shell = TerminalShellPromptToolKit(
451-
definitions, style, completion, charset, prompt, edit_mode
462+
definitions, completion, charset, prompt, edit_mode
452463
)
453464
else:
454465
want_readline = readline == "gnu"
455466
shell = TerminalShellGNUReadline(
456-
definitions, style, want_readline, completion, charset, prompt
467+
definitions, want_readline, completion, charset, prompt
457468
)
458469

459-
load_settings(shell)
470+
load_settings_file(shell)
471+
style_from_settings_file = definitions.get_ownvalue("Settings`$PygmentsStyle")
472+
if style_from_settings_file is SymbolNull and style is None:
473+
style = style_from_settings_file
474+
shell.setup_pygments_style(style)
475+
460476
if file:
461477
with open(file, "r") as ifile:
462478
feeder = MathicsFileLineFeeder(ifile)

mathicsscript/bindkeys.py

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,11 @@
2222
from prompt_toolkit.enums import EditingMode
2323
from prompt_toolkit.key_binding import KeyBindings
2424
from prompt_toolkit.filters import Condition
25-
from prompt_toolkit.layout import Float, FloatContainer
26-
from prompt_toolkit.widgets import Dialog, Button, Label
2725
from sys import version_info
2826
import contextlib
2927
import pathlib
3028
import re
3129

32-
from mathicsscript.termshell import ALL_PYGMENTS_STYLES
3330
from mathicsscript.settings import definitions
3431
from mathics.session import get_settings_value
3532

@@ -149,36 +146,6 @@ def _group_autocomplete_toggle(event):
149146
app.group_autocomplete = not app.group_autocomplete
150147

151148

152-
# Add an additional key binding for toggling this flag.
153-
@bindings.add("f5")
154-
def _next_pygments_style(event):
155-
"""Set Pygments style to the next sytle in ALL_PYGMENTS_STYLE."""
156-
app = event.app
157-
158-
try:
159-
i = ALL_PYGMENTS_STYLES.index(app.pygments_style)
160-
except ValueError:
161-
pass
162-
else:
163-
i = (i + 1) % len(ALL_PYGMENTS_STYLES)
164-
app.pygments_style = ALL_PYGMENTS_STYLES[i]
165-
166-
167-
# Add an additional key binding for toggling this flag.
168-
@bindings.add("f6")
169-
def _prev_pygments_style(event):
170-
"""Set Pygments style to the previous sytle in ALL_PYGMENTS_STYLE."""
171-
app = event.app
172-
173-
try:
174-
i = ALL_PYGMENTS_STYLES.index(app.pygments_style)
175-
except ValueError:
176-
pass
177-
else:
178-
i = (i - 1) % len(ALL_PYGMENTS_STYLES)
179-
app.pygments_style = ALL_PYGMENTS_STYLES[i]
180-
181-
182149
def read_inputrc(read_init_file_fn: Callable, use_unicode: bool) -> None:
183150
"""
184151
Read GNU Readline style inputrc

mathicsscript/termshell.py

Lines changed: 92 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,12 @@
55
import os
66
import os.path as osp
77
import pathlib
8-
import sys
9-
from typing import Any, Optional, Union
8+
from typing import Any, Union
109

1110
import mathics_scanner.location
1211

13-
from colorama import init as colorama_init
1412
from columnize import columnize
15-
from mathics.core.atoms import String, Symbol
13+
from mathics.core.atoms import Symbol
1614
from mathics.core.attributes import attribute_string_to_number
1715
from mathics.core.expression import Expression, from_python # strip_context,
1816
from mathics.core.rules import Rule
@@ -32,7 +30,7 @@
3230

3331
mma_lexer = MathematicaLexer()
3432

35-
ALL_PYGMENTS_STYLES = list(get_all_styles())
33+
ALL_PYGMENTS_STYLES = list(get_all_styles()) + ["None"]
3634

3735
color_scheme = TERMINAL_COLORS.copy()
3836
color_scheme[MToken.SYMBOL] = ("yellow", "ansibrightyellow")
@@ -79,8 +77,7 @@ class TerminalShellCommon(MathicsLineFeeder):
7977
def __init__(
8078
self,
8179
definitions,
82-
style: Optional[str],
83-
_: bool,
80+
want_completion: bool,
8481
use_unicode: bool,
8582
prompt: bool,
8683
):
@@ -94,38 +91,16 @@ def __init__(
9491
self.lineno = 0
9592
self.terminal_formatter = None
9693
self.prompt = prompt
94+
self.want_completion = want_completion
9795

98-
colorama_init()
99-
if style == "None":
100-
self.terminal_formatter = None
101-
self.incolors = self.outcolors = ["", "", "", ""]
102-
else:
103-
# self.incolors = ["\033[34m", "\033[1m", "\033[22m", "\033[39m"]
104-
self.incolors = ["\033[32m", "\033[1m", "\033[22m", "\033[39m"]
105-
self.outcolors = ["\033[31m", "\033[1m", "\033[22m", "\033[39m"]
106-
if style is not None and not is_pygments_style(style):
107-
style = None
108-
109-
if style is None:
110-
dark_background = is_dark_background()
111-
if dark_background:
112-
style = "inkpot"
113-
else:
114-
style = "colorful"
115-
try:
116-
self.terminal_formatter = Terminal256Formatter(style=style)
117-
except ClassNotFound:
118-
print(f"Pygments style name '{style}' not found; No pygments style set")
119-
120-
self.pygments_style = style
12196
self.definitions = definitions
12297
self.definitions.set_ownvalue(
12398
"Settings`$PygmentsShowTokens", from_python(False)
12499
)
125-
self.definitions.set_ownvalue("Settings`$PygmentsStyle", from_python(style))
126100
self.definitions.set_ownvalue("Settings`$UseUnicode", from_python(use_unicode))
127101
self.definitions.set_ownvalue(
128-
"Settings`PygmentsStylesAvailable", from_python(ALL_PYGMENTS_STYLES)
102+
"Settings`PygmentsStylesAvailable",
103+
from_python(ALL_PYGMENTS_STYLES),
129104
)
130105

131106
self.definitions.add_message(
@@ -149,20 +124,28 @@ def __init__(
149124
"Settings`PygmentsStylesAvailable",
150125
attribute_string_to_number["System`Locked"],
151126
)
127+
152128
self.definitions.set_attribute(
153129
"Settings`$UseUnicode", attribute_string_to_number["System`Locked"]
154130
)
155131

156132
def change_pygments_style(self, style: str):
157133
if not style or style == self.pygments_style:
158134
return False
135+
if style == "None":
136+
self.terminal_formatter = None
137+
self.pygments_style = style
138+
self.incolors = self.outcolors = ["", "", "", ""]
139+
return True
159140
if is_pygments_style(style):
141+
self.incolors = ["\033[32m", "\033[1m", "\033[22m", "\033[39m"]
142+
self.outcolors = ["\033[31m", "\033[1m", "\033[22m", "\033[39m"]
160143
self.terminal_formatter = Terminal256Formatter(style=style)
161144
self.pygments_style = style
162145
return True
163-
else:
164-
print("Pygments style not changed")
165-
return False
146+
147+
print("Pygments style not changed")
148+
return False
166149

167150
def empty(self):
168151
return False
@@ -189,19 +172,30 @@ def get_out_prompt(self, form: str) -> Union[str, Any]:
189172
default form, or the name of the Form which was used in output preceded by "//"
190173
"""
191174
line_number = self.last_line_number
192-
return "{2}Out[{3}{0}{4}]{5}{1}= ".format(line_number, form, *self.outcolors)
175+
if self.is_styled:
176+
return "{2}Out[{3}{0}{4}]{5}{1}= ".format(
177+
line_number, form, *self.outcolors
178+
)
179+
else:
180+
return f"Out[{line_number}]= "
193181

194182
@property
195183
def in_prompt(self) -> Union[str, Any]:
196184
next_line_number = self.last_line_number + 1
197185
if self.lineno > 0:
198186
return " " * len(f"In[{next_line_number}]:= ")
199-
else:
187+
elif self.is_styled:
200188
return "{1}In[{2}{0}{3}]:= {4}".format(next_line_number, *self.incolors)
201-
# if have_full_readline:
202-
# return "{1}In[{2}{0}{3}]:= {4}".format(next_line_number, *self.incolors)
203-
# else:
204-
# return f"In[{next_line_number}]:= "
189+
else:
190+
return f"In[{next_line_number}]:= "
191+
192+
@property
193+
def is_styled(self):
194+
"""
195+
Returns True if a Pygments style (other than SymbolNull or "None" has been set.
196+
"""
197+
style = self.definitions.get_ownvalue("Settings`$PygmentsStyle")
198+
return not (style is SymbolNull or style.value == "None")
205199

206200
@property
207201
def last_line_number(self) -> int:
@@ -213,7 +207,8 @@ def last_line_number(self) -> int:
213207
def out_callback(self, out):
214208
print(self.to_output(str(out), form=""))
215209

216-
def read_line(self, prompt, completer=None, use_html=None):
210+
# noinspection PyUnusedLocal
211+
def read_line(self, prompt, _completer=None, _use_html: bool = False):
217212
if self.using_readline:
218213
line = self.rl_read_line(prompt)
219214
else:
@@ -253,6 +248,19 @@ def print_result(
253248
use_highlight = False
254249
else:
255250
out_str = '"' + out_str.replace('"', r"\"") + '"'
251+
252+
show_pygments_tokens = self.definitions.get_ownvalue(
253+
"Settings`$PygmentsShowTokens"
254+
).to_python()
255+
pygments_style = self.definitions.get_ownvalue(
256+
"Settings`$PygmentsStyle"
257+
).get_string_value()
258+
if pygments_style != self.pygments_style:
259+
if not self.change_pygments_style(pygments_style):
260+
self.definitions.set_ownvalue(
261+
"Settings`$PygmentsStyle", String(self.pygments_style)
262+
)
263+
256264
if eval_type == "System`Graph":
257265
out_str = "-Graph-"
258266
elif self.terminal_formatter: # pygmentize
@@ -273,7 +281,8 @@ def print_result(
273281
if show_pygments_tokens:
274282
print(list(lex(out_str, mma_lexer)))
275283
if use_highlight:
276-
out_str = highlight(out_str, mma_lexer, self.terminal_formatter)
284+
if self.terminal_formatter is not None:
285+
out_str = highlight(out_str, mma_lexer, self.terminal_formatter)
277286
form = (
278287
""
279288
if not hasattr(result, "form") or result.form is None
@@ -296,10 +305,49 @@ def rl_read_line(self, prompt):
296305
def reset_lineno(self):
297306
self.lineno = 0
298307

308+
def setup_pygments_style(self, style):
309+
"""Goes through what we need to do to setup or change a
310+
Pygments style.
311+
"""
312+
if (
313+
isinstance(style, str)
314+
and style.lower() == "none"
315+
or style is None
316+
and os.environ.get("NO_COLOR", False)
317+
):
318+
style = "None" # Canonicalize spelling
319+
self.terminal_formatter = None
320+
self.incolors = self.outcolors = ["", "", "", ""]
321+
else:
322+
# self.incolors = ["\033[34m", "\033[1m", "\033[22m", "\033[39m"]
323+
self.incolors = ["\033[32m", "\033[1m", "\033[22m", "\033[39m"]
324+
self.outcolors = ["\033[31m", "\033[1m", "\033[22m", "\033[39m"]
325+
if style is not None and not is_pygments_style(style):
326+
style = None
327+
328+
# If no style given, choose one based on the background.
329+
if style is None:
330+
dark_background = is_dark_background()
331+
if dark_background:
332+
style = "inkpot"
333+
else:
334+
style = "colorful"
335+
try:
336+
self.terminal_formatter = Terminal256Formatter(style=style)
337+
except ClassNotFound:
338+
print(f"Pygments style name '{style}' not found; No pygments style set")
339+
style = "None"
340+
341+
self.definitions.set_ownvalue("Settings`$PygmentsStyle", from_python(style))
342+
self.pygments_style = style
343+
299344
def to_output(self, text: str, form: str) -> str:
300345
"""
301346
Format an 'Out=' line that it lines after the first one indent properly.
302347
"""
303348
line_number = self.last_line_number
304-
newline = "\n" + " " * len(f"Out[{line_number}]{form}= ")
349+
if self.is_styled:
350+
newline = "\n" + " " * len(f"Out[{line_number}]{form}= ")
351+
else:
352+
newline = "\n" + " " * len(f"Out[{line_number}]= ")
305353
return newline.join(text.splitlines())

mathicsscript/termshell_gnu.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# -*- coding: utf-8 -*-
2-
# Copyright (C) 2020-2022 Rocky Bernstein <[email protected]>
2+
# Copyright (C) 2020-2022, 2025 Rocky Bernstein <[email protected]>
33

44
import atexit
55
import os
@@ -59,15 +59,12 @@ class TerminalShellGNUReadline(TerminalShellCommon):
5959
def __init__(
6060
self,
6161
definitions,
62-
style: str,
6362
want_readline: bool,
6463
want_completion: bool,
6564
use_unicode: bool,
6665
prompt: bool,
6766
):
68-
super(TerminalShellGNUReadline, self).__init__(
69-
definitions, style, want_completion, use_unicode, prompt
70-
)
67+
super().__init__(definitions, want_completion, use_unicode, prompt)
7168

7269
# Try importing readline to enable arrow keys support etc.
7370
self.using_readline = False

0 commit comments

Comments
 (0)