pyscript-programming-language 1.14.0__tar.gz → 1.15.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. {pyscript_programming_language-1.14.0/pyscript_programming_language.egg-info → pyscript_programming_language-1.15.0}/PKG-INFO +6 -5
  2. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/README.md +5 -4
  3. pyscript_programming_language-1.15.0/changelog.md +7 -0
  4. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyproject.toml +1 -1
  5. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/__main__.py +4 -4
  6. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/analyzer.py +31 -40
  7. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/buffer.py +6 -10
  8. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/cache.py +4 -3
  9. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/checks.py +5 -21
  10. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/constants.py +1 -1
  11. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/editor/bases.py +1 -1
  12. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/editor/gui.py +96 -7
  13. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/editor/terminal.py +14 -1
  14. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/highlight.py +22 -3
  15. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/interpreter.py +38 -48
  16. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/lexer.py +46 -44
  17. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/mapping.py +3 -0
  18. pyscript_programming_language-1.15.0/pyscript/core/nodes.py +885 -0
  19. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/parser.py +144 -208
  20. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/pysbuiltins.py +4 -37
  21. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/pystypes.py +50 -8
  22. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/results.py +2 -2
  23. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/shell.py +42 -63
  24. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/utils/ansi.py +45 -23
  25. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/utils/decorators.py +8 -8
  26. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/utils/generic.py +1 -4
  27. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/utils/module.py +6 -3
  28. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/utils/string.py +3 -3
  29. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/version.py +13 -2
  30. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/ast/ast_dump.py +20 -32
  31. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/ast/ast_literal_eval.py +5 -12
  32. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/ast/ast_unparse.py +10 -10
  33. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/ast/ast_walk.py +20 -26
  34. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/brainfuck.pys +7 -2
  35. pyscript_programming_language-1.15.0/pyscript/lib/dual/__init__.pys +17 -0
  36. pyscript_programming_language-1.15.0/pyscript/lib/dual/py_dual.py +246 -0
  37. pyscript_programming_language-1.15.0/pyscript/lib/getch/__init__.pys +10 -0
  38. pyscript_programming_language-1.15.0/pyscript/lib/getch/py_getch.py +99 -0
  39. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/history.pys +10 -8
  40. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/inspect.pys +1 -1
  41. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0/pyscript_programming_language.egg-info}/PKG-INFO +6 -5
  42. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript_programming_language.egg-info/SOURCES.txt +2 -0
  43. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/setup.py +2 -2
  44. pyscript_programming_language-1.14.0/changelog.md +0 -21
  45. pyscript_programming_language-1.14.0/pyscript/core/nodes.py +0 -673
  46. pyscript_programming_language-1.14.0/pyscript/lib/getch/__init__.pys +0 -5
  47. pyscript_programming_language-1.14.0/pyscript/lib/getch/py_getch.py +0 -18
  48. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/MANIFEST.in +0 -0
  49. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/__init__.py +0 -0
  50. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/__init__.pyi +0 -0
  51. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/__init__.py +0 -0
  52. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/bases.py +0 -0
  53. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/context.py +0 -0
  54. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/editor/__init__.py +0 -0
  55. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/exceptions.py +0 -0
  56. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/handlers.py +0 -0
  57. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/position.py +0 -0
  58. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/runner.py +0 -0
  59. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/symtab.py +0 -0
  60. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/token.py +0 -0
  61. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/utils/__init__.py +0 -0
  62. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/utils/debug.py +0 -0
  63. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/utils/path.py +0 -0
  64. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/core/utils/similarity.py +0 -0
  65. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/__hello__.pys +0 -0
  66. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/ansi.pys +0 -0
  67. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/ast/__init__.pys +0 -0
  68. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/code.pys +0 -0
  69. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/dis.pys +0 -0
  70. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/explorer.pys +0 -0
  71. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/fpstimer/__init__.pys +0 -0
  72. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/fpstimer/py_fpstimer.py +0 -0
  73. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/jsdict.pys +0 -0
  74. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/keyword.pys +0 -0
  75. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/opcode.pys +0 -0
  76. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/parser.pys +0 -0
  77. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/pdisplay.pys +0 -0
  78. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/site.pys +0 -0
  79. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/symtable.pys +0 -0
  80. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/token.pys +0 -0
  81. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/tokenize/__init__.pys +0 -0
  82. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/lib/tokenize/tok_untokenize.py +0 -0
  83. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/other/.nomedia +0 -0
  84. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/other/PyScript.ico +0 -0
  85. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/other/copyright +0 -0
  86. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/other/credits +0 -0
  87. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/other/license +0 -0
  88. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/site-packages/this.pys +0 -0
  89. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript/this.py +0 -0
  90. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript_programming_language.egg-info/dependency_links.txt +0 -0
  91. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript_programming_language.egg-info/requires.txt +0 -0
  92. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/pyscript_programming_language.egg-info/top_level.txt +0 -0
  93. {pyscript_programming_language-1.14.0 → pyscript_programming_language-1.15.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyscript-programming-language
3
- Version: 1.14.0
3
+ Version: 1.15.0
4
4
  Summary: PyScript Programming Language
5
5
  Home-page: https://azzammuhyala.github.io/pyscript
6
6
  Author: AzzamMuhyala
@@ -128,10 +128,11 @@ Familiar? There it is!
128
128
  | 2 | `msvcrt` | **required (library)** |
129
129
  | 3 | `pprint` | **required (library)** |
130
130
  | 4 | `shutil` | **required (library)** |
131
- | 5 | `stat` | **required (library)** |
132
- | 6 | `termios` | **required (library)** |
133
- | 7 | `time` | **required (library)** |
134
- | 8 | `tty` | **required (library)** |
131
+ | 5 | `signal` | **required (library)** |
132
+ | 6 | `stat` | **required (library)** |
133
+ | 7 | `termios` | **required (library)** |
134
+ | 8 | `time` | **required (library)** |
135
+ | 9 | `tty` | **required (library)** |
135
136
  | 1 | `_colorize` | **optional** |
136
137
  | 2 | `beartype` | **optional** |
137
138
  | 3 | `prompt_toolkit` | **optional** |
@@ -96,10 +96,11 @@ Familiar? There it is!
96
96
  | 2 | `msvcrt` | **required (library)** |
97
97
  | 3 | `pprint` | **required (library)** |
98
98
  | 4 | `shutil` | **required (library)** |
99
- | 5 | `stat` | **required (library)** |
100
- | 6 | `termios` | **required (library)** |
101
- | 7 | `time` | **required (library)** |
102
- | 8 | `tty` | **required (library)** |
99
+ | 5 | `signal` | **required (library)** |
100
+ | 6 | `stat` | **required (library)** |
101
+ | 7 | `termios` | **required (library)** |
102
+ | 8 | `time` | **required (library)** |
103
+ | 9 | `tty` | **required (library)** |
103
104
  | 1 | `_colorize` | **optional** |
104
105
  | 2 | `beartype` | **optional** |
105
106
  | 3 | `prompt_toolkit` | **optional** |
@@ -0,0 +1,7 @@
1
+ # Change Logs
2
+
3
+ ## [1.15.0] - 23/10/2026
4
+
5
+ ### Fixed
6
+ - Fixed several bugs.
7
+ - _etc._
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pyscript-programming-language"
7
- version = "1.14.0"
7
+ version = "1.15.0"
8
8
  description = "PyScript Programming Language"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  requires-python = ">=3.10"
@@ -9,7 +9,7 @@ from .core.highlight import (
9
9
  PYGMENTS, HLFMT_HTML, HLFMT_ANSI, HLFMT_BBCODE, pys_highlight, PygmentsPyScriptStyle, PygmentsPyScriptLexer
10
10
  )
11
11
  from .core.runner import _namespace_to_symbol_table, pys_runner, pys_shell
12
- from .core.utils.debug import USE_NOTEBOOK
12
+ from .core.utils.debug import USE_NOTEBOOK, custom_print
13
13
  from .core.utils.generic import is_environ
14
14
  from .core.utils.module import find_module_path, remove_python_path
15
15
  from .core.utils.path import getcwd, base, normpath
@@ -357,7 +357,7 @@ def execute(file: PysFileBuffer) -> None:
357
357
  if sys.stdout.closed or sys.stderr.closed:
358
358
  code = 1
359
359
  elif sys.stdin.closed:
360
- print("Can't run interactive shell: sys.stdin closed", file=sys.stderr)
360
+ custom_print("Can't run interactive shell: sys.stdin closed", file=sys.stderr)
361
361
  code = 1
362
362
  else:
363
363
  code = pys_shell(
@@ -382,14 +382,14 @@ if args.file is not None:
382
382
  elif args.highlight:
383
383
  try:
384
384
  if args.highlight in FORMATER_HIGHLIGHT_MAP:
385
- print(
385
+ custom_print(
386
386
  pys_highlight(
387
387
  source=file,
388
388
  formatter=FORMATER_HIGHLIGHT_MAP[args.highlight]
389
389
  )
390
390
  )
391
391
  else:
392
- print(
392
+ custom_print(
393
393
  highlight(
394
394
  code=file.text,
395
395
  lexer=PygmentsPyScriptLexer(),
@@ -102,18 +102,17 @@ class PysAnalyzer(Pys):
102
102
 
103
103
  keyword_names = set()
104
104
 
105
- for element in node.arguments:
105
+ for argument in node.arguments:
106
106
 
107
- if element.__class__ is tuple:
108
- token, value = element
107
+ if argument.__class__ is tuple:
108
+ token, value = argument
109
109
  name = token.value
110
110
  if name in keyword_names:
111
111
  self.throw(f"duplicate argument {name!r} in call definition", token.position)
112
112
  return
113
113
  keyword_names.add(name)
114
-
115
114
  else:
116
- value = element
115
+ value = argument
117
116
 
118
117
  self.visit(value)
119
118
  if self.error:
@@ -163,7 +162,7 @@ class PysAnalyzer(Pys):
163
162
  self.visit_declaration_from_AssignmentNode(node.target, f"cannot {operator} literal", operator)
164
163
 
165
164
  def visit_StatementsNode(self, node: PysStatementsNode) -> None:
166
- for element in node.body:
165
+ for element in node.elements:
167
166
  self.visit(element)
168
167
  if self.error:
169
168
  return
@@ -179,7 +178,7 @@ class PysAnalyzer(Pys):
179
178
  self.visit(node.value)
180
179
 
181
180
  def visit_IfNode(self, node: PysIfNode) -> None:
182
- for condition, body in node.cases_body:
181
+ for condition, body in node.cases:
183
182
  self.visit(condition)
184
183
  if self.error:
185
184
  return
@@ -188,8 +187,8 @@ class PysAnalyzer(Pys):
188
187
  if self.error:
189
188
  return
190
189
 
191
- if node.else_body:
192
- self.visit(node.else_body)
190
+ if node.else_:
191
+ self.visit(node.else_)
193
192
 
194
193
  def visit_SwitchNode(self, node: PysSwitchNode) -> None:
195
194
  self.visit(node.target)
@@ -198,8 +197,8 @@ class PysAnalyzer(Pys):
198
197
 
199
198
  self.in_switch += 1
200
199
 
201
- for condition, body in node.case_cases:
202
- self.visit(condition)
200
+ for case, body in node.cases:
201
+ self.visit(case)
203
202
  if self.error:
204
203
  return
205
204
 
@@ -207,8 +206,8 @@ class PysAnalyzer(Pys):
207
206
  if self.error:
208
207
  return
209
208
 
210
- if node.default_body:
211
- self.visit(node.default_body)
209
+ if node.default:
210
+ self.visit(node.default)
212
211
  if self.error:
213
212
  return
214
213
 
@@ -220,8 +219,8 @@ class PysAnalyzer(Pys):
220
219
  if self.error:
221
220
  return
222
221
 
223
- for condition, value in node.cases:
224
- self.visit(condition)
222
+ for case, value in node.cases:
223
+ self.visit(case)
225
224
  if self.error:
226
225
  return
227
226
 
@@ -237,18 +236,18 @@ class PysAnalyzer(Pys):
237
236
  if self.error:
238
237
  return
239
238
 
240
- for _, body in node.catch_cases:
239
+ for _, body in node.catches:
241
240
  self.visit(body)
242
241
  if self.error:
243
242
  return
244
243
 
245
- if node.else_body:
246
- self.visit(node.else_body)
244
+ if node.else_:
245
+ self.visit(node.else_)
247
246
  if self.error:
248
247
  return
249
248
 
250
- if node.finally_body:
251
- self.visit(node.finally_body)
249
+ if node.finally_:
250
+ self.visit(node.finally_)
252
251
 
253
252
  def visit_WithNode(self, node: PysWithNode) -> None:
254
253
  for context, _ in node.contexts:
@@ -284,8 +283,8 @@ class PysAnalyzer(Pys):
284
283
 
285
284
  self.in_loop -= 1
286
285
 
287
- if node.else_body:
288
- self.visit(node.else_body)
286
+ if node.else_:
287
+ self.visit(node.else_)
289
288
 
290
289
  def visit_WhileNode(self, node: PysWhileNode) -> None:
291
290
  self.visit(node.condition)
@@ -300,8 +299,8 @@ class PysAnalyzer(Pys):
300
299
 
301
300
  self.in_loop -= 1
302
301
 
303
- if node.else_body:
304
- self.visit(node.else_body)
302
+ if node.else_:
303
+ self.visit(node.else_)
305
304
 
306
305
  def visit_DoWhileNode(self, node: PysDoWhileNode) -> None:
307
306
  self.in_loop += 1
@@ -316,11 +315,10 @@ class PysAnalyzer(Pys):
316
315
  if self.error:
317
316
  return
318
317
 
319
- if node.else_body:
320
- self.visit(node.else_body)
318
+ if node.else_:
319
+ self.visit(node.else_)
321
320
 
322
321
  def visit_RepeatNode(self, node: PysRepeatNode) -> None:
323
-
324
322
  if node.body:
325
323
  self.in_loop += 1
326
324
 
@@ -334,8 +332,8 @@ class PysAnalyzer(Pys):
334
332
  if self.error:
335
333
  return
336
334
 
337
- if node.else_body:
338
- self.visit(node.else_body)
335
+ if node.else_:
336
+ self.visit(node.else_)
339
337
 
340
338
  def visit_ClassNode(self, node: PysClassNode) -> None:
341
339
  for decorator in node.decorators:
@@ -378,9 +376,9 @@ class PysAnalyzer(Pys):
378
376
 
379
377
  parameter_names = set()
380
378
 
381
- for element in node.parameters:
382
- is_keyword = element.__class__ is tuple
383
- token = element[0] if is_keyword else element
379
+ for parameter in node.parameters:
380
+ is_keyword = parameter.__class__ is tuple
381
+ token = parameter[0] if is_keyword else parameter
384
382
  name = token.value
385
383
 
386
384
  if name in parameter_names:
@@ -389,7 +387,7 @@ class PysAnalyzer(Pys):
389
387
  parameter_names.add(name)
390
388
 
391
389
  if is_keyword:
392
- self.visit(element[1])
390
+ self.visit(parameter[1])
393
391
  if self.error:
394
392
  return
395
393
 
@@ -464,10 +462,6 @@ class PysAnalyzer(Pys):
464
462
  if self.error:
465
463
  return
466
464
 
467
- elif type is PysKeywordNode:
468
- self.throw(f"cannot delete {target.name.value}", target.position)
469
- return
470
-
471
465
  elif type is PysDebugNode:
472
466
  self.throw("cannot delete to __debug__", node.position)
473
467
  return
@@ -537,9 +531,6 @@ class PysAnalyzer(Pys):
537
531
  if self.error:
538
532
  return
539
533
 
540
- elif type is PysKeywordNode:
541
- self.throw(f"cannot {operator_name} to {node.name.value}", node.position)
542
-
543
534
  elif type is PysDebugNode:
544
535
  self.throw(f"cannot {operator_name} to __debug__", node.position)
545
536
 
@@ -5,7 +5,7 @@ from .utils.string import normstr
5
5
 
6
6
  from io import IOBase
7
7
  from types import BuiltinMethodType
8
- from typing import Iterable, Optional, Union
8
+ from typing import Iterable, Optional
9
9
 
10
10
  @immutable
11
11
  class PysBuffer(Pys):
@@ -17,27 +17,23 @@ class PysFileBuffer(PysBuffer):
17
17
 
18
18
  def __new__(
19
19
  cls,
20
- text: Union[str, bytes, bytearray, Iterable, BuiltinMethodType, IOBase, 'PysFileBuffer'],
20
+ text: 'str | bytes | bytearray | Iterable | BuiltinMethodType | IOBase | PysFileBuffer',
21
21
  name: Optional[str | bytes] = None
22
22
  ) -> 'PysFileBuffer':
23
23
 
24
24
  if isinstance(text, PysFileBuffer):
25
25
  return text
26
-
27
26
  elif isinstance(text, IOBase):
28
27
  name = normstr(getattr(text, 'name', '<io>') if name is None else name)
29
28
  text = normstr(text)
30
-
31
29
  else:
32
30
  name = '<string>' if name is None else normstr(name)
33
31
  text = normstr(text)
34
32
 
35
- instance = super().__new__(cls)
36
-
37
- setimuattr(instance, 'text', text)
38
- setimuattr(instance, 'name', name)
39
-
40
- return instance
33
+ self = super().__new__(cls)
34
+ setimuattr(self, 'text', text)
35
+ setimuattr(self, 'name', name)
36
+ return self
41
37
 
42
38
  def __repr__(self) -> str:
43
39
  return f'<FileBuffer from {self.name!r}>'
@@ -18,6 +18,9 @@ pys_sys = ModuleType(
18
18
  "Python sys module."
19
19
  )
20
20
 
21
+ def intern_object(object: Any) -> Any:
22
+ return pys_sys.interns.setdefault((type(object), object), object)
23
+
21
24
  pys_sys.__running_shell__ = False
22
25
  pys_sys.__running_breakpoint__ = False
23
26
  pys_sys.builtin_module_names = tuple(
@@ -33,6 +36,7 @@ pys_sys.singletons = {}
33
36
  pys_sys.argv = ['']
34
37
  pys_sys.encoding = 'utf-8'
35
38
  pys_sys.flags = DEFAULT
39
+ pys_sys.intern_object = intern_object
36
40
  pys_sys.displayhook = print_display
37
41
  pys_sys.excepthook = print_traceback
38
42
  pys_sys.clearhook = clear_shell
@@ -58,9 +62,6 @@ for name in (
58
62
  if hasattr(sys, name):
59
63
  setattr(pys_sys, name, getattr(sys, name))
60
64
 
61
- def intern_object(object: Any) -> Any:
62
- return pys_sys.interns.setdefault((type(object), object), object)
63
-
64
65
  # sentinel
65
66
  @singleton
66
67
  @inheritable
@@ -4,10 +4,9 @@ from .nodes import *
4
4
  from .token import TOKENS
5
5
 
6
6
  is_expression = frozenset([
7
- PysNumberNode, PysStringNode, PysKeywordNode, PysDebugNode, PysIdentifierNode, PysDictionaryNode, PysSetNode,
8
- PysListNode, PysTupleNode, PysAttributeNode, PysSubscriptNode, PysCallNode, PysChainOperatorNode,
9
- PysTernaryOperatorNode, PysBinaryOperatorNode, PysUnaryOperatorNode, PysIncrementalNode, PysMatchNode,
10
- PysFunctionNode, PysEllipsisNode
7
+ PysConstantNode, PysDebugNode, PysIdentifierNode, PysDictionaryNode, PysSetNode, PysListNode, PysTupleNode,
8
+ PysAttributeNode, PysSubscriptNode, PysCallNode, PysChainOperatorNode, PysTernaryOperatorNode,
9
+ PysBinaryOperatorNode, PysUnaryOperatorNode, PysIncrementalNode, PysMatchNode, PysFunctionNode, PysEllipsisNode
11
10
  ]).__contains__
12
11
 
13
12
  is_statement = frozenset([
@@ -16,31 +15,16 @@ is_statement = frozenset([
16
15
  PysAssertNode, PysDeleteNode, PysContinueNode, PysBreakNode
17
16
  ]).__contains__
18
17
 
19
- is_sequence = frozenset([
20
- PysSetNode, PysListNode, PysTupleNode
21
- ]).__contains__
22
-
23
- is_literal = frozenset([
24
- PysNumberNode, PysStringNode
25
- ]).__contains__
26
-
18
+ is_sequence = frozenset([PysSetNode, PysListNode, PysTupleNode]).__contains__
27
19
  is_keyword = frozenset(KEYWORDS).__contains__
28
20
  is_constant_keyword = frozenset(CONSTANT_KEYWORDS).__contains__
29
-
30
- is_equal = frozenset([
31
- TOKENS['EQUAL'], TOKENS['EQUAL_COLON']
32
- ]).__contains__
21
+ is_equal = frozenset([TOKENS['EQUAL'], TOKENS['EQUAL_COLON']]).__contains__
33
22
 
34
23
  is_blacklist_python_builtin = frozenset([
35
24
  'IndentationError', 'TabError', 'breakpoint', 'compile', 'copyright', 'credits', 'dir', 'eval', 'exec', 'help',
36
25
  'globals', 'license', 'locals', 'vars'
37
26
  ]).__contains__
38
27
 
39
- is_python_extension = frozenset([
40
- '.cgi', '.fcgi', '.gyp', '.gypi', '.ipy', '.lmi', '.pxd', '.pxi', '.py', '.py3', '.pyc', '.pyd', '.pyde', '.pyi',
41
- '.pyo', '.pyp', '.pyproj', '.pyt', '.pyw', '.pyx', '.pyz', '.rpy', '.spec', '.tac', '.wsgi', '.xpy'
42
- ]).__contains__
43
-
44
28
  is_left_bracket = frozenset(BRACKETS_MAP.keys()).__contains__
45
29
  is_right_bracket = frozenset(BRACKETS_MAP.values()).__contains__
46
30
  is_bracket = frozenset(BRACKETS_MAP.keys() | BRACKETS_MAP.values()).__contains__
@@ -17,7 +17,7 @@ ENV_PYSCRIPT_NO_COLOR_PROMPT = 'PYSCRIPT_NO_COLOR_PROMPT'
17
17
  ENV_PYSCRIPT_MAXIMUM_TRACEBACK_LINE = 'PYSCRIPT_MAXIMUM_TRACEBACK_LINE'
18
18
  ENV_PYSCRIPT_CLASSIC_LINE_SHELL = 'PYSCRIPT_CLASSIC_LINE_SHELL'
19
19
  ENV_PYSCRIPT_HISTORY_PATH = 'PYSCRIPT_HISTORY_PATH'
20
- ENV_PYSCRIPT_MAXIMUM_HISTORY_LINE = 'PYSCRIPT_MAXIMUM_HISTORY_LINE'
20
+ ENV_PYSCRIPT_HISTORY_LIMIT = 'PYSCRIPT_HISTORY_LIMIT'
21
21
 
22
22
  KEYWORDS = (
23
23
  '__debug__', 'False', 'None', 'True', 'and', 'as', 'assert', 'break', 'case', 'catch', 'class', 'constructor',
@@ -15,8 +15,8 @@ class PysEditor(Pys):
15
15
  @typecheck
16
16
  def __init__(self, file: PysFileBuffer, colored: bool = True) -> None:
17
17
  self.file = file
18
- self.colored = bool(colored)
19
18
  self.basename = basename(self.file.name)
19
+ self.colored = bool(colored)
20
20
  self.used = False
21
21
  self.modified = False
22
22
 
@@ -6,8 +6,17 @@ from ..utils.generic import boundary
6
6
  from ..version import __version__
7
7
 
8
8
  try:
9
- from tkinter import Tk, Scrollbar, Text, messagebox
10
- from tkinter.font import Font
9
+ from tkinter import Tk, Scrollbar, Text, Canvas, messagebox
10
+ from tkinter.font import families, Font
11
+
12
+ MONO_FALLBACKS = (
13
+ 'Fira Code',
14
+ 'Cascadia Code', 'SF Mono', 'Ubuntu Mono', 'Liberation Mono',
15
+ 'Consolas', 'Menlo', 'DejaVu Sans Mono',
16
+ 'Courier New', 'Lucida Console', 'Monaco', 'Andale Mono',
17
+ 'Courier', 'Terminal',
18
+ 'monospace'
19
+ )
11
20
 
12
21
  class PysGUIEditor(PysEditor, Tk):
13
22
 
@@ -45,6 +54,7 @@ try:
45
54
  if self.text.edit_modified():
46
55
  self.modified = True
47
56
  update()
57
+ refresh_line_numbers()
48
58
  self.text.edit_modified(False)
49
59
 
50
60
  def on_change_font(value):
@@ -53,6 +63,7 @@ try:
53
63
  size = boundary(font.cget('size') + value, 1, 127)
54
64
  font.config(size=size)
55
65
  update_tab_size()
66
+ refresh_line_numbers()
56
67
  self.set_configuration('gui-size-font', size)
57
68
  return 'break'
58
69
  return wrapper
@@ -61,6 +72,7 @@ try:
61
72
  text = self.text
62
73
  wrap = text.cget('wrap') != 'char'
63
74
  text.configure(wrap='char' if wrap else 'none')
75
+ refresh_line_numbers()
64
76
  self.set_configuration('wrap', wrap)
65
77
  return 'break'
66
78
 
@@ -86,9 +98,71 @@ try:
86
98
  text.see('insert')
87
99
  return 'break'
88
100
 
89
- self.lexer = PygmentsPyScriptLexer()
90
- self.font = Font(family='Consolas', size=boundary(self.get_configuration('gui-size-font', 10), 1, 127))
101
+ def draw_line_numbers():
102
+ self.line_job = None
103
+ text = self.text
104
+ canvas = self.line_numbers
105
+ font = self.font
106
+
107
+ bold = (font.cget('family'), font.cget('size'), 'bold')
108
+ digits = len(text.index('end-1c').split('.')[0])
109
+ padding = font.measure(' ')
110
+ width = font.measure('0' * digits) + padding * 2
111
+
112
+ canvas.delete('all')
113
+ canvas.configure(width=width, bg='#1f1f1f')
114
+
115
+ current = text.index('insert').split('.')[0]
116
+ index = text.index('@0,0')
117
+ previous = None
118
+
119
+ while True:
120
+ info = text.dlineinfo(index)
121
+ if info is None:
122
+ break
123
+
124
+ number = index.split('.')[0]
125
+ if number == previous:
126
+ break
127
+
128
+ canvas.create_text(
129
+ width - padding, info[1],
130
+ anchor='ne',
131
+ text=number,
132
+ font=bold if number == current else font,
133
+ fill='#ffffff'
134
+ )
135
+
136
+ previous = number
137
+ index = text.index(f'{index} +1 lines')
138
+
139
+ def refresh_line_numbers(event=None):
140
+ if self.line_job is None:
141
+ self.line_job = self.after_idle(draw_line_numbers)
142
+
143
+ def on_scroll(first, last):
144
+ self.scrollbar.set(first, last)
145
+ refresh_line_numbers()
146
+
147
+ installed = frozenset(families())
148
+ for font_name in MONO_FALLBACKS:
149
+ if font_name in installed:
150
+ break
151
+ else:
152
+ font_name = 'monospace'
153
+
154
+ self.line_job = None
155
+ self.font = Font(
156
+ family=font_name,
157
+ size=boundary(self.get_configuration('gui-size-font', 10), 1, 127)
158
+ )
91
159
  self.scrollbar = Scrollbar(self)
160
+ self.line_numbers = Canvas(
161
+ self,
162
+ width=0,
163
+ highlightthickness=0,
164
+ bd=0
165
+ )
92
166
  self.text = Text(
93
167
  self,
94
168
  undo=True,
@@ -98,7 +172,7 @@ try:
98
172
  bg='#000000',
99
173
  fg='#ffffff',
100
174
  insertbackground='white',
101
- yscrollcommand=self.scrollbar.set
175
+ yscrollcommand=on_scroll
102
176
  )
103
177
 
104
178
  update_tab_size()
@@ -106,6 +180,8 @@ try:
106
180
  self.scrollbar.pack_configure(side='right', fill='y')
107
181
  self.scrollbar.configure(command=self.text.yview)
108
182
 
183
+ self.line_numbers.pack_configure(side='left', fill='y')
184
+ self.text.pack_configure(side='left', expand=True, fill='both')
109
185
  self.text.pack_configure(side='left', expand=True, fill='both')
110
186
 
111
187
  self.text.insert('1.0', self.file.text)
@@ -115,6 +191,9 @@ try:
115
191
  self.text.bind('<Return>', on_enter)
116
192
  self.text.bind('<Prior>', on_page_up)
117
193
  self.text.bind('<Next>', on_page_down)
194
+ self.text.bind('<KeyRelease>', refresh_line_numbers)
195
+ self.text.bind('<ButtonRelease-1>', refresh_line_numbers)
196
+ self.text.bind('<Configure>', refresh_line_numbers)
118
197
 
119
198
  self.bind('<Configure>', on_configure)
120
199
  self.bind_all('<Control-S>', on_save)
@@ -145,14 +224,17 @@ try:
145
224
  self.wm_protocol('WM_DELETE_WINDOW', on_close)
146
225
 
147
226
  if PYGMENTS and colored:
227
+ self.lexer = PygmentsPyScriptLexer()
228
+ self.refresh_job = None
229
+
148
230
  tag_configure = self.text.tag_configure
149
231
  for token, style in PygmentsPyScriptStyle.list_styles():
150
232
  color = style['color']
151
233
  if color:
152
234
  tag_configure(str(token), foreground=f'#{color}')
153
235
 
154
- def update():
155
- self.title(f'PyScript {__version__} - {self.basename}{"*" if self.modified else ""}')
236
+ def refresh_highlight():
237
+ self.refresh_job = None
156
238
 
157
239
  index = '1.0'
158
240
 
@@ -172,11 +254,18 @@ try:
172
254
  text_tag_add(to_string(type), index, end_index)
173
255
  index = end_index
174
256
 
257
+ def update():
258
+ self.title(f'PyScript {__version__} - {self.basename}{"*" if self.modified else ""}')
259
+ if self.refresh_job is not None:
260
+ self.after_cancel(self.refresh_job)
261
+ self.refresh_job = self.after(500, refresh_highlight)
262
+
175
263
  else:
176
264
  def update():
177
265
  self.title(f'PyScript {__version__} - {self.basename}{"*" if self.modified else ""}')
178
266
 
179
267
  update()
268
+ refresh_line_numbers()
180
269
 
181
270
  def run(self) -> None:
182
271
  PysEditor.run(self)
@@ -65,6 +65,13 @@ try:
65
65
  title = file
66
66
  return ANSI(f'\x1b[7m{title}{" " * max(0, columns - len(title))}\x1b[0m')
67
67
 
68
+ def create_line_prefix(line_number, wrap_count):
69
+ width = len(str(self.text.document.line_count))
70
+ number = str(line_number + 1) if wrap_count == 0 else ''
71
+ is_current = line_number == self.text.document.cursor_position_row
72
+ style = 'bg:#ffffff fg:#000000' + (' bold' if is_current else '')
73
+ return [(style, f' {number:>{width}} '), ('', ' ')]
74
+
68
75
  else:
69
76
  app_other_keyword.update({
70
77
  'color_depth': ColorDepth.DEPTH_1_BIT
@@ -85,10 +92,16 @@ try:
85
92
  title = file
86
93
  return title + ' ' * max(0, columns - len(title))
87
94
 
95
+ def create_line_prefix(line_number, wrap_count):
96
+ width = len(str(self.text.document.line_count))
97
+ number = str(line_number + 1) if wrap_count == 0 else ''
98
+ return f' {number:>{width}} | '
99
+
88
100
  self.text = TextArea(
89
101
  multiline=True,
90
102
  wrap_lines=on_wrap,
91
103
  input_processors=[TabsProcessor(tabstop=4)],
104
+ get_line_prefix=create_line_prefix,
92
105
  **text_other_keyword
93
106
  )
94
107
 
@@ -188,7 +201,7 @@ try:
188
201
  )
189
202
 
190
203
  self.text.text = self.file.text
191
- self.text.buffer.on_text_changed += on_change
204
+ self.text.buffer.on_text_changed.add_handler(on_change)
192
205
 
193
206
  Application.__init__(
194
207
  self,