meltygui 0.1.0__py3-none-any.whl

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 (372) hide show
  1. meltygui/__init__.py +107 -0
  2. meltygui/accounts/__init__.py +0 -0
  3. meltygui/accounts/internet_accounts.py +1355 -0
  4. meltygui/chat/__init__.py +91 -0
  5. meltygui/chat/activity.py +75 -0
  6. meltygui/chat/backends.py +36 -0
  7. meltygui/chat/chat_interface.py +732 -0
  8. meltygui/chat/chat_proxy.py +352 -0
  9. meltygui/chat/codex_proxy.py +592 -0
  10. meltygui/chat/codex_settings.py +100 -0
  11. meltygui/chat/codex_transport.py +60 -0
  12. meltygui/chat/command_parser.py +204 -0
  13. meltygui/chat/images.py +227 -0
  14. meltygui/chat/messages.py +417 -0
  15. meltygui/chat/metadata.py +139 -0
  16. meltygui/chat/writer_locks.py +64 -0
  17. meltygui/code/__init__.py +0 -0
  18. meltygui/code/basic_converters.py +533 -0
  19. meltygui/code/chain_converters.py +2111 -0
  20. meltygui/code/code_checks.py +2209 -0
  21. meltygui/code/core_syntax.py +1430 -0
  22. meltygui/code/file_converters.py +1933 -0
  23. meltygui/code/fileref.py +702 -0
  24. meltygui/code/hotswap_guard.py +144 -0
  25. meltygui/code/libcst_conversion.py +9724 -0
  26. meltygui/code/live_instrument.py +392 -0
  27. meltygui/code/live_view.py +2490 -0
  28. meltygui/code/melty_scan.py +2684 -0
  29. meltygui/code/new_codecs.py +1255 -0
  30. meltygui/code/new_converters.py +3017 -0
  31. meltygui/code/project_code.py +278 -0
  32. meltygui/code/source_context.py +63 -0
  33. meltygui/code/symbol_roster.py +1588 -0
  34. meltygui/code/syntax_check.py +34 -0
  35. meltygui/code/syntax_check_worker.py +114 -0
  36. meltygui/completion/__init__.py +0 -0
  37. meltygui/completion/fim.py +1232 -0
  38. meltygui/completion/fim_context.py +481 -0
  39. meltygui/completion/providers/__init__.py +0 -0
  40. meltygui/completion/providers/anthropic_oauth.py +446 -0
  41. meltygui/completion/providers/anthropic_requests.py +69 -0
  42. meltygui/completion/providers/claude.py +203 -0
  43. meltygui/completion/providers/claude_usage.py +499 -0
  44. meltygui/completion/providers/codex_accounts.py +180 -0
  45. meltygui/completion/providers/copilot.py +617 -0
  46. meltygui/completion/providers/oauth_popup.py +220 -0
  47. meltygui/completion/providers/ollama.py +320 -0
  48. meltygui/completion/providers/profiles.py +23 -0
  49. meltygui/core/README.md +88 -0
  50. meltygui/core/__init__.py +1 -0
  51. meltygui/core/automation/__init__.py +1 -0
  52. meltygui/core/automation/action_core.py +153 -0
  53. meltygui/core/automation/collection_action.py +45 -0
  54. meltygui/core/automation/mcp_eval.py +167 -0
  55. meltygui/core/automation/mcp_hotswap.py +107 -0
  56. meltygui/core/automation/mcp_query.py +552 -0
  57. meltygui/core/automation/mcp_server.py +657 -0
  58. meltygui/core/automation/orchestration_core.py +2636 -0
  59. meltygui/core/automation/query_core.py +124 -0
  60. meltygui/core/automation/search_core.py +26 -0
  61. meltygui/core/automation/selector_core.py +340 -0
  62. meltygui/core/automation/value_core.py +1752 -0
  63. meltygui/core/cache/__init__.py +1 -0
  64. meltygui/core/cache/cache_diagnostics.py +0 -0
  65. meltygui/core/cache/invalidation_decoration.py +153 -0
  66. meltygui/core/cache/invalidation_tracker.py +43 -0
  67. meltygui/core/cache/tile_cache.py +5968 -0
  68. meltygui/core/conversion/__init__.py +1 -0
  69. meltygui/core/conversion/bubbling.py +599 -0
  70. meltygui/core/conversion/cache_tree.py +188 -0
  71. meltygui/core/conversion/chain.py +113 -0
  72. meltygui/core/conversion/converter_register.py +145 -0
  73. meltygui/core/conversion/data_decoration.py +67 -0
  74. meltygui/core/conversion/dict_conversion.py +1815 -0
  75. meltygui/core/conversion/dict_conversion_util.py +177 -0
  76. meltygui/core/conversion/dynamic_obj.py +89 -0
  77. meltygui/core/conversion/graph_compare.py +183 -0
  78. meltygui/core/conversion/load_save_v2.py +1032 -0
  79. meltygui/core/conversion/missing_saved_class.py +39 -0
  80. meltygui/core/conversion/path_finder.py +606 -0
  81. meltygui/core/conversion/render_host.py +1083 -0
  82. meltygui/core/core_render.py +6537 -0
  83. meltygui/core/definition_hotswap.py +231 -0
  84. meltygui/core/diagnostics/__init__.py +1 -0
  85. meltygui/core/diagnostics/attribute_churn.py +38 -0
  86. meltygui/core/diagnostics/fps_counter.py +39 -0
  87. meltygui/core/diagnostics/gpu_frame_timer.py +103 -0
  88. meltygui/core/diagnostics/inspection_core.py +169 -0
  89. meltygui/core/diagnostics/monitor_core.py +105 -0
  90. meltygui/core/diagnostics/notifications.py +706 -0
  91. meltygui/core/diagnostics/perf_trace.py +281 -0
  92. meltygui/core/diagnostics/profile_decoration.py +88 -0
  93. meltygui/core/diagnostics/resize_trace.py +62 -0
  94. meltygui/core/diagnostics/screenshot_core.py +247 -0
  95. meltygui/core/diagnostics/session_status.py +98 -0
  96. meltygui/core/diagnostics/trace_core.py +445 -0
  97. meltygui/core/files/__init__.py +1 -0
  98. meltygui/core/files/file_core.py +208 -0
  99. meltygui/core/files/file_explorer_core.py +104 -0
  100. meltygui/core/files/file_tree_core.py +198 -0
  101. meltygui/core/files/file_watch_core.py +43 -0
  102. meltygui/core/files/import_graph_core.py +43 -0
  103. meltygui/core/files/metadata_core.py +51 -0
  104. meltygui/core/graphics/__init__.py +1 -0
  105. meltygui/core/graphics/cuda_context_core.py +166 -0
  106. meltygui/core/graphics/cuda_interop_core.py +136 -0
  107. meltygui/core/graphics/cuda_kernel_core.py +91 -0
  108. meltygui/core/graphics/framebuffer_recorder.py +337 -0
  109. meltygui/core/graphics/gl_state.py +658 -0
  110. meltygui/core/graphics/lut_core.py +52 -0
  111. meltygui/core/graphics/overlay_renderer.py +984 -0
  112. meltygui/core/graphics/scene_target.py +180 -0
  113. meltygui/core/graphics/screenshot.py +439 -0
  114. meltygui/core/graphics/shader_func.py +478 -0
  115. meltygui/core/graphics/tensor_core.py +45 -0
  116. meltygui/core/graphics/text_texture.py +329 -0
  117. meltygui/core/graphics/wayland_color.py +635 -0
  118. meltygui/core/input/__init__.py +1 -0
  119. meltygui/core/input/collision.py +165 -0
  120. meltygui/core/input/drag_drop_core.py +1525 -0
  121. meltygui/core/input/hypr_left_drag.py +323 -0
  122. meltygui/core/input/input_core.py +245 -0
  123. meltygui/core/input/input_handler.py +1101 -0
  124. meltygui/core/input/mouse_cursor.py +355 -0
  125. meltygui/core/input/pynput_backend.py +1054 -0
  126. meltygui/core/input/space_mouse.py +338 -0
  127. meltygui/core/input/touchpad_backend.py +393 -0
  128. meltygui/core/input/view_selection.py +177 -0
  129. meltygui/core/layout/__init__.py +1 -0
  130. meltygui/core/layout/column_core.py +2153 -0
  131. meltygui/core/layout/cursor_core.py +161 -0
  132. meltygui/core/layout/dropdown_core.py +278 -0
  133. meltygui/core/layout/edge_constraints.py +155 -0
  134. meltygui/core/layout/grid_core.py +117 -0
  135. meltygui/core/layout/header_core.py +23 -0
  136. meltygui/core/layout/header_runtime.py +67 -0
  137. meltygui/core/layout/layout_core.py +87 -0
  138. meltygui/core/layout/tile_manager_core.py +591 -0
  139. meltygui/core/melty.py +6726 -0
  140. meltygui/core/module_map.json +896 -0
  141. meltygui/core/module_names.py +19 -0
  142. meltygui/core/rendering/__init__.py +1 -0
  143. meltygui/core/rendering/core_decoration.py +431 -0
  144. meltygui/core/rendering/core_render_helpers.py +328 -0
  145. meltygui/core/rendering/func_metadata.py +398 -0
  146. meltygui/core/rendering/mode.py +818 -0
  147. meltygui/core/rendering/mode_defaults.py +41 -0
  148. meltygui/core/rendering/modes.py +136 -0
  149. meltygui/core/rendering/parameter_core.py +1665 -0
  150. meltygui/core/rendering/render_dispatch.py +1891 -0
  151. meltygui/core/rendering/render_funcs.py +273 -0
  152. meltygui/core/rendering/shaped.py +312 -0
  153. meltygui/core/rendering/window_decoration.py +25 -0
  154. meltygui/core/runtime/__init__.py +1 -0
  155. meltygui/core/runtime/app.py +735 -0
  156. meltygui/core/runtime/app_session.py +140 -0
  157. meltygui/core/runtime/background.py +564 -0
  158. meltygui/core/runtime/extensions.py +53 -0
  159. meltygui/core/runtime/gc_manager.py +1163 -0
  160. meltygui/core/runtime/lifecycle.py +21 -0
  161. meltygui/core/runtime/paths.py +27 -0
  162. meltygui/core/runtime/settings.py +14 -0
  163. meltygui/core/runtime/singleton.py +16 -0
  164. meltygui/core/runtime/thread_safe_bool.py +24 -0
  165. meltygui/core/runtime/thread_signal.py +30 -0
  166. meltygui/core/runtime/toggles.py +3115 -0
  167. meltygui/core/services/__init__.py +1 -0
  168. meltygui/core/services/account_core.py +10 -0
  169. meltygui/core/services/chat_core.py +19 -0
  170. meltygui/core/services/claude_terminal_core.py +346 -0
  171. meltygui/core/services/terminal_core.py +458 -0
  172. meltygui/core/services/terminal_runtime.py +78 -0
  173. meltygui/core/styling/__init__.py +1 -0
  174. meltygui/core/styling/color_core.py +46 -0
  175. meltygui/core/styling/fonts.py +639 -0
  176. meltygui/core/styling/global_style.py +338 -0
  177. meltygui/core/styling/style.py +198 -0
  178. meltygui/core/styling/style_core.py +522 -0
  179. meltygui/core/styling/warm_start.py +149 -0
  180. meltygui/core/windowing/__init__.py +1 -0
  181. meltygui/core/windowing/backends/PYIMGUI_LICENSE +28 -0
  182. meltygui/core/windowing/backends/__init__.py +1 -0
  183. meltygui/core/windowing/backends/imgui_renderer.py +138 -0
  184. meltygui/core/windowing/backends/native_wayland.py +850 -0
  185. meltygui/core/windowing/backends/protocols/xdg-decoration-unstable-v1.xml +156 -0
  186. meltygui/core/windowing/backends/protocols/xdg-shell.xml +1420 -0
  187. meltygui/core/windowing/backends/wayland_protocol.py +101 -0
  188. meltygui/core/windowing/dock_core.py +182 -0
  189. meltygui/core/windowing/frame_geometry.py +42 -0
  190. meltygui/core/windowing/geometry_feed.py +864 -0
  191. meltygui/core/windowing/glfw_utils.py +1343 -0
  192. meltygui/core/windowing/os_frame.py +1552 -0
  193. meltygui/core/windowing/surface.py +643 -0
  194. meltygui/core/windowing/titlebar.py +1560 -0
  195. meltygui/core/windowing/titlebar_buttons.py +281 -0
  196. meltygui/core/windowing/wayland_move.py +932 -0
  197. meltygui/core/windowing/window_api.py +62 -0
  198. meltygui/core/windowing/window_constants.py +339 -0
  199. meltygui/core/windowing/window_visibility.py +162 -0
  200. meltygui/debug/__init__.py +0 -0
  201. meltygui/debug/app_view_utils.py +9 -0
  202. meltygui/editor/__init__.py +0 -0
  203. meltygui/editor/bash_syntax.py +30 -0
  204. meltygui/editor/code_line_fast.py +152 -0
  205. meltygui/editor/diff.py +139 -0
  206. meltygui/editor/external_changes.py +159 -0
  207. meltygui/editor/file_header.py +41 -0
  208. meltygui/editor/live_usage.py +169 -0
  209. meltygui/editor/live_view_views.py +1803 -0
  210. meltygui/editor/pending_save.py +1351 -0
  211. meltygui/editor/roster_tints.py +547 -0
  212. meltygui/editor/source_preview.py +16 -0
  213. meltygui/editor/source_tools.py +10 -0
  214. meltygui/editor/source_ui.py +70 -0
  215. meltygui/editor/spell_check.py +77 -0
  216. meltygui/editor/text_editor.py +9076 -0
  217. meltygui/editor/usage_picker.py +538 -0
  218. meltygui/events/__init__.py +0 -0
  219. meltygui/events/example.py +118 -0
  220. meltygui/examples/__init__.py +0 -0
  221. meltygui/examples/columns_demo.py +33 -0
  222. meltygui/examples/columns_window_demo.py +60 -0
  223. meltygui/examples/context_menu_demo.py +24 -0
  224. meltygui/examples/context_menu_window_demo.py +49 -0
  225. meltygui/examples/gui_playground.py +127 -0
  226. meltygui/examples/live_view_playground.py +256 -0
  227. meltygui/examples/lora.py +43 -0
  228. meltygui/examples/lora_data.py +59 -0
  229. meltygui/examples/lora_policies.py +67 -0
  230. meltygui/examples/mode_demo.py +49 -0
  231. meltygui/examples/modifies_demo.py +119 -0
  232. meltygui/examples/scalar_policies.py +60 -0
  233. meltygui/examples/style_layouts.py +149 -0
  234. meltygui/examples/tile_manager_demo.py +74 -0
  235. meltygui/examples/tint_demo.py +160 -0
  236. meltygui/examples/tint_functions.py +56 -0
  237. meltygui/examples/trace_demo.py +88 -0
  238. meltygui/examples/two_windows.py +36 -0
  239. meltygui/files/__init__.py +0 -0
  240. meltygui/files/fast_file_explorer.py +439 -0
  241. meltygui/gnome_extension/lsd-window-geometry@latent-descent/extension.js +237 -0
  242. meltygui/gnome_extension/lsd-window-geometry@latent-descent/lsd-window-geometry@latent-descent.iml +9 -0
  243. meltygui/gnome_extension/lsd-window-geometry@latent-descent/metadata.json +7 -0
  244. meltygui/graphics/__init__.py +6 -0
  245. meltygui/graphics/base.py +85 -0
  246. meltygui/graphics/examples.py +507 -0
  247. meltygui/graphics/executor.py +520 -0
  248. meltygui/graphics/filter.py +667 -0
  249. meltygui/graphics/filter.pyi +856 -0
  250. meltygui/graphics/generate_stubs.py +22 -0
  251. meltygui/graphics/registry.py +203 -0
  252. meltygui/graphics/shader_compiler.py +155 -0
  253. meltygui/graphics/shaders.py +1302 -0
  254. meltygui/graphics/stub_generator.py +250 -0
  255. meltygui/graphics/texture_manager.py +170 -0
  256. meltygui/graphics/texture_min_max.py +295 -0
  257. meltygui/hdr_color.py +757 -0
  258. meltygui/image_load.py +308 -0
  259. meltygui/model/__init__.py +1 -0
  260. meltygui/model/account_model.py +142 -0
  261. meltygui/model/camera_model.py +159 -0
  262. meltygui/model/chat_model.py +86 -0
  263. meltygui/model/code_model.py +62 -0
  264. meltygui/model/code_proxy_model.py +876 -0
  265. meltygui/model/collection_model.py +41 -0
  266. meltygui/model/color_model.py +127 -0
  267. meltygui/model/cuda_tensor_model.py +36 -0
  268. meltygui/model/cuda_texture_model.py +149 -0
  269. meltygui/model/dropdown_model.py +137 -0
  270. meltygui/model/file_metadata_model.py +73 -0
  271. meltygui/model/file_model.py +289 -0
  272. meltygui/model/format_model.py +390 -0
  273. meltygui/model/graph_model.py +98 -0
  274. meltygui/model/icon_model.py +1024 -0
  275. meltygui/model/import_graph_model.py +468 -0
  276. meltygui/model/layout_model.py +15 -0
  277. meltygui/model/lut_model.py +266 -0
  278. meltygui/model/search_model.py +173 -0
  279. meltygui/model/tensor_model.py +402 -0
  280. meltygui/model/terminal_model.py +329 -0
  281. meltygui/model/texture_model.py +146 -0
  282. meltygui/model/tile_model.py +24 -0
  283. meltygui/model/trace_model.py +198 -0
  284. meltygui/model/trace_report_model.py +146 -0
  285. meltygui/models/__init__.py +0 -0
  286. meltygui/models/file_meta.py +613 -0
  287. meltygui/models/function_console.py +184 -0
  288. meltygui/models/orchestration.py +48 -0
  289. meltygui/pbr.py +1576 -0
  290. meltygui/png_unfilter.c +49 -0
  291. meltygui/resources/JetBrainsMono-Regular.ttf +0 -0
  292. meltygui/resources/THIRD_PARTY_NOTICES.md +21 -0
  293. meltygui/resources/dejavu/DejaVuSans-Bold.ttf +0 -0
  294. meltygui/resources/dejavu/DejaVuSans-ExtraLight.ttf +0 -0
  295. meltygui/resources/dejavu/DejaVuSans.ttf +0 -0
  296. meltygui/resources/dejavu/LICENSE.txt +78 -0
  297. meltygui/resources/fontawesome-LICENSE.txt +121 -0
  298. meltygui/resources/fontawesome-webfont.ttf +0 -0
  299. meltygui/resources/hdri/studio_small_09_1k.hdr +0 -0
  300. meltygui/resources/jetbrains-weights/JetBrainsMono-Bold.ttf +0 -0
  301. meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraBold.ttf +0 -0
  302. meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraLight.ttf +0 -0
  303. meltygui/resources/jetbrains-weights/JetBrainsMono-Light.ttf +0 -0
  304. meltygui/resources/jetbrains-weights/JetBrainsMono-Medium.ttf +0 -0
  305. meltygui/resources/jetbrains-weights/JetBrainsMono-SemiBold.ttf +0 -0
  306. meltygui/resources/jetbrains-weights/JetBrainsMono-Thin.ttf +0 -0
  307. meltygui/resources/jetbrains-weights/OFL.txt +93 -0
  308. meltygui/resources/jetbrains-weights/README.md +2 -0
  309. meltygui/state/__init__.py +0 -0
  310. meltygui/state/account_state.py +22 -0
  311. meltygui/state/animation_state.py +97 -0
  312. meltygui/state/annotation_state.py +22 -0
  313. meltygui/state/chat_state.py +36 -0
  314. meltygui/state/code_state.py +10 -0
  315. meltygui/state/core_enums.py +59 -0
  316. meltygui/state/core_markers.py +115 -0
  317. meltygui/state/core_undo.py +1075 -0
  318. meltygui/state/file_state.py +75 -0
  319. meltygui/state/graph_state.py +26 -0
  320. meltygui/state/inspection_state.py +47 -0
  321. meltygui/state/menu_state.py +10 -0
  322. meltygui/state/model_enums.py +11 -0
  323. meltygui/state/new_core_model.py +2394 -0
  324. meltygui/state/orchestration_state.py +14 -0
  325. meltygui/state/query_state.py +24 -0
  326. meltygui/state/tensor_state.py +10 -0
  327. meltygui/state/terminal_state.py +23 -0
  328. meltygui/state/trace_state.py +32 -0
  329. meltygui/state/voxel_state.py +12 -0
  330. meltygui/text_index.py +816 -0
  331. meltygui/utils/__init__.py +0 -0
  332. meltygui/utils/jump_to_code.py +344 -0
  333. meltygui/utils/pkl_inspect.py +90 -0
  334. meltygui/utils/render_utils.py +1419 -0
  335. meltygui/view/__init__.py +1 -0
  336. meltygui/view/account_view.py +524 -0
  337. meltygui/view/action_view.py +73 -0
  338. meltygui/view/chat_decoration_view.py +112 -0
  339. meltygui/view/chat_view.py +1703 -0
  340. meltygui/view/code_view.py +2677 -0
  341. meltygui/view/collection_view.py +1185 -0
  342. meltygui/view/color_view.py +824 -0
  343. meltygui/view/control_view.py +559 -0
  344. meltygui/view/decoration_view.py +439 -0
  345. meltygui/view/diagnostic_view.py +177 -0
  346. meltygui/view/dropdown_view.py +1082 -0
  347. meltygui/view/file_view.py +1599 -0
  348. meltygui/view/graph_cuda_view.py +105 -0
  349. meltygui/view/graph_view.py +882 -0
  350. meltygui/view/header_view.py +848 -0
  351. meltygui/view/input_view.py +238 -0
  352. meltygui/view/inspection_view.py +1783 -0
  353. meltygui/view/layout_view.py +596 -0
  354. meltygui/view/lut_view.py +45 -0
  355. meltygui/view/menu_view.py +212 -0
  356. meltygui/view/orchestration_view.py +658 -0
  357. meltygui/view/query_view.py +118 -0
  358. meltygui/view/search_view.py +342 -0
  359. meltygui/view/tab_view.py +258 -0
  360. meltygui/view/tensor_view.py +430 -0
  361. meltygui/view/terminal_view.py +355 -0
  362. meltygui/view/text_view.py +8031 -0
  363. meltygui/view/texture_view.py +480 -0
  364. meltygui/view/tile_view.py +38 -0
  365. meltygui/view/trace_view.py +923 -0
  366. meltygui/view/voxel_cuda_view.py +824 -0
  367. meltygui/view/voxel_view.py +1860 -0
  368. meltygui/view/window_view.py +111 -0
  369. meltygui-0.1.0.dist-info/METADATA +143 -0
  370. meltygui-0.1.0.dist-info/RECORD +372 -0
  371. meltygui-0.1.0.dist-info/WHEEL +4 -0
  372. meltygui-0.1.0.dist-info/licenses/LICENSE +202 -0
@@ -0,0 +1,278 @@
1
+ """project_code — dict-proxy access to the studio's code, at PENDING truth.
2
+
3
+ from meltygui.code.project_code import project_code
4
+ span = project_code[path].get_lines(first, last) # 0-based, end-EXCLUSIVE
5
+ text = span["value"] # read: pending overlay over the code cache
6
+ span["value"] = new_text # write: queues into PendingSave, never disk
7
+
8
+ The lightweight sibling of the code-host RenderHosts: same ground truth, same
9
+ caches, no view machinery. Reads compose `PendingSave.current_file_text`
10
+ (disk via `Melty.code_cache` + every queued span/whole-file edit spliced in —
11
+ pending changes are the truth, disk is stale mid-session) and are memoized on
12
+ content-free signals only (`live_instrument._pending_gen` + the cached disk
13
+ text's identity — FileWatch pops the cache on external writes, so those show
14
+ up too; nothing is ever hashed). Writes splice the range into the pending
15
+ truth and queue ONE whole-file entry through `PendingSave.queue_save` — the
16
+ exact entry shape the code editor's own keystrokes queue — so every editor,
17
+ host and the merge window see the edit through the established cross-view
18
+ sync, the shutdown flush writes it with the same conflict guard, and no
19
+ span-coordinate drift can exist between this proxy and the queue.
20
+
21
+ A LineRange tracks its OWN writes (`end` moves with the line-count delta);
22
+ edits from elsewhere (an editor typing above the range) do not re-anchor it —
23
+ the caller owns cross-range bookkeeping, as the stack trace view does with
24
+ _sync_spans.
25
+ """
26
+
27
+ from pathlib import Path
28
+
29
+ from meltygui.code.fileref import Address
30
+
31
+
32
+ def _pending_generation(path):
33
+ from meltygui.code.live_instrument import _pending_gen
34
+ return _pending_gen(str(path))
35
+
36
+
37
+ class LineRange(dict):
38
+ """A line span of one file, read and written like a dict (the RenderHost
39
+ convention: `range[range.value_key]`). `start`/`end` are 0-based,
40
+ end-exclusive PENDING-truth line indices — the same slice convention as
41
+ `Address(path, start, end)` and the pending queue's span entries."""
42
+
43
+ value_key = "value"
44
+
45
+ def __init__(self, file_code, start, end):
46
+ super().__init__()
47
+ self._file = file_code
48
+ self.start = start
49
+ self.end = end
50
+
51
+ @property
52
+ def path(self):
53
+ return self._file.path
54
+
55
+ def __getitem__(self, key):
56
+ if key == self.value_key:
57
+ return self.text()
58
+ return dict.__getitem__(self, key)
59
+
60
+ def get(self, key, default=None):
61
+ if key == self.value_key:
62
+ text = self.text()
63
+ return text if text is not None else default
64
+ return dict.get(self, key, default)
65
+
66
+ def __setitem__(self, key, value):
67
+ if key == self.value_key:
68
+ self.write(value)
69
+ return
70
+ dict.__setitem__(self, key, value)
71
+
72
+ def text(self):
73
+ """The range's current text — pending truth, cache-served. Memoized
74
+ on the file text's identity so a steady-state frame pays two dict
75
+ hits and nothing O(file). None when the file is unreadable."""
76
+ whole = self._file.text()
77
+ if whole is None:
78
+ return None
79
+ memo = getattr(self, "_slice_memo", None)
80
+ if (memo is not None and memo[0] is whole
81
+ and memo[1] == (self.start, self.end)):
82
+ return memo[2]
83
+ sliced = "\n".join(self._file.lines()[self.start:self.end])
84
+ self._slice_memo = (whole, (self.start, self.end), sliced)
85
+ return sliced
86
+
87
+ def line_count(self):
88
+ return max(0, self.end - self.start)
89
+
90
+ def write(self, new_text):
91
+ """Replace the range with `new_text` in the file's PENDING truth:
92
+ splice into the composed file text and queue one whole-file entry —
93
+ exactly what an editor keystroke queues — then move `end` by the
94
+ line-count delta so this proxy keeps addressing the same code. A
95
+ no-op write queues nothing."""
96
+ if not isinstance(new_text, str):
97
+ return False
98
+ whole = self._file.text()
99
+ if whole is None:
100
+ return False
101
+ lines = whole.split("\n")
102
+ new_lines = new_text.split("\n")
103
+ if lines[self.start:self.end] == new_lines:
104
+ return False
105
+ lines[self.start:self.end] = new_lines
106
+ queued = self._file.write_text("\n".join(lines))
107
+ if queued:
108
+ self.end = self.start + len(new_lines)
109
+ return queued
110
+
111
+
112
+ class FileCode:
113
+ """One file's proxy: whole-text access plus `get_lines`. Composed text is
114
+ memoized on (pending generation, cached disk text identity) — recomputed
115
+ only when an edit queues or FileWatch pops the disk cache."""
116
+
117
+ def __init__(self, path):
118
+ self.path = Path(path)
119
+ self._memo = None # ((gen, disk_gen), composed_text)
120
+ self._lines_memo = None # (composed_text_obj, split lines)
121
+ self._ast_memo = None # (composed_text_obj, ast tree | None)
122
+ self._span_index_memo = None # (composed_text_obj, (defs, stmt_ends))
123
+
124
+ def get_lines(self, start, end):
125
+ """A LineRange over 0-based, end-exclusive line indices (the Address
126
+ slice convention). A fresh proxy each call — ranges are cheap and a
127
+ cached one would go stale when other edits shift the file."""
128
+ return LineRange(self, start, end)
129
+
130
+ def text(self):
131
+ """The whole file as the STUDIO sees it — `studio_text_for`: the
132
+ SYNC-FRAME base (the last disk state the pending queue was rebased
133
+ to; an external write parks at the merge banner as the INCOMING
134
+ side, it never swaps in here) with every queued pending edit
135
+ spliced in. This is the same truth the code hosts / func tab serve
136
+ (codec.load's baseline overlay) — composing over the raw disk cache
137
+ instead swapped drifted text under coordinate-anchored consumers
138
+ the moment an external edit landed (08-31). Memoized on the pending
139
+ generation + the disk/sync generation (bumped by FileWatch events
140
+ and sync-frame advances). None when unreadable."""
141
+ from meltygui.code.symbol_roster import disk_generation
142
+ from meltygui.editor.pending_save import PendingSave
143
+ sig = (_pending_generation(self.path), disk_generation())
144
+ memo = self._memo
145
+ if memo is not None and memo[0] == sig:
146
+ return memo[1]
147
+ composed = PendingSave.studio_text_for(self.path)
148
+ self._memo = (sig, composed)
149
+ return composed
150
+
151
+ def ast_tree(self):
152
+ """ast of EXACTLY the text this proxy serves (text()), memoized on
153
+ the text's identity — span consumers resolve line bounds against
154
+ the very text they will render, never a different cache's view.
155
+ None while the text doesn't parse."""
156
+ import ast
157
+ text = self.text()
158
+ if text is None:
159
+ return None
160
+ memo = self._ast_memo
161
+ if memo is not None and memo[0] is text:
162
+ return memo[1]
163
+ try:
164
+ tree = ast.parse(text)
165
+ except SyntaxError:
166
+ tree = None
167
+ self._ast_memo = (text, tree)
168
+ return tree
169
+
170
+ def span_index_ready(self):
171
+ """True when span_index() would serve its memo without parsing —
172
+ lets a consumer that backgrounds the build resolve synchronously
173
+ the moment the worker's memo lands."""
174
+ memo = self._span_index_memo
175
+ return memo is not None and memo[0] is self.text()
176
+
177
+ def span_index(self):
178
+ """(defs, stmt_ends) of the served text, ONE ast walk per text
179
+ version: defs = [(containment_start, def_line, end_line)] — the
180
+ containment start includes decorator lines — and stmt_ends =
181
+ {start_line: last line of the longest statement starting there}.
182
+ Consumers answer per-line span queries with a defs scan + dict hit
183
+ instead of walking the whole tree per query (an ast.walk per pane
184
+ was most of the Code tab's open lag). None while unparseable."""
185
+ import ast
186
+ tree = self.ast_tree()
187
+ if tree is None:
188
+ return None
189
+ text = self._ast_memo[0]
190
+ memo = self._span_index_memo
191
+ if memo is not None and memo[0] is text:
192
+ return memo[1]
193
+ defs, stmt_ends = [], {}
194
+ for node in ast.walk(tree):
195
+ start = getattr(node, "lineno", None)
196
+ end = getattr(node, "end_lineno", None)
197
+ if start is None or end is None:
198
+ continue
199
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
200
+ span_start = min((d.lineno for d in node.decorator_list),
201
+ default=start)
202
+ defs.append((span_start, start, end))
203
+ if isinstance(node, ast.stmt):
204
+ prev = stmt_ends.get(start)
205
+ if prev is None or end > prev:
206
+ stmt_ends[start] = end
207
+ index = (defs, stmt_ends)
208
+ self._span_index_memo = (text, index)
209
+ return index
210
+
211
+ def lines(self):
212
+ """The composed text's line list, memoized on the text's identity —
213
+ every LineRange of this file slices the same split."""
214
+ text = self.text()
215
+ if text is None:
216
+ return []
217
+ memo = self._lines_memo
218
+ if memo is None or memo[0] is not text:
219
+ memo = self._lines_memo = (text, text.split("\n"))
220
+ return memo[1]
221
+
222
+ def line_count(self):
223
+ text = self.text()
224
+ return 0 if text is None else text.count("\n") + 1
225
+
226
+ def write_text(self, new_text):
227
+ """Queue `new_text` as the file's pending truth (whole-file entry,
228
+ the editor-keystroke shape). Records the disk baseline on the first
229
+ queue (no-op detection + merge base) and arms the flush-time conflict
230
+ fingerprint, both exactly as a load through code_file_io would."""
231
+ from meltygui.core.melty import Melty
232
+ from meltygui.code.fileref import is_writable_file
233
+ from meltygui.code.new_codecs import codec_for_path
234
+ from meltygui.code.new_codecs import _span_fingerprint
235
+ from meltygui.editor.pending_save import PendingSave
236
+ codec = codec_for_path(self.path)
237
+ if codec is None or not codec.editable:
238
+ print(f"[project_code] refusing write: no editable codec for "
239
+ f"{self.path}")
240
+ return False
241
+ address = Address(self.path)
242
+ if is_writable_file(address.path):
243
+ address._allow_write = True # same way TextFileCodec stamps
244
+ if PendingSave.originals.get(address) is None:
245
+ disk = Melty.read_code(self.path)
246
+ if disk is not None:
247
+ PendingSave.mark_load(address, disk)
248
+ address._span_fp = _span_fingerprint(disk.split("\n"))
249
+ PendingSave.queue_save(address, codec, data=new_text)
250
+ self._memo = None
251
+ return True
252
+
253
+
254
+ class _ProjectCode:
255
+ """`project_code[path]` → the file's FileCode, memoized per resolved
256
+ path so every consumer shares one composed-text cache per file."""
257
+
258
+ def __init__(self):
259
+ self._files = {}
260
+
261
+ def _key(self, path):
262
+ try:
263
+ return str(Path(path).resolve())
264
+ except OSError:
265
+ return str(path)
266
+
267
+ def __getitem__(self, path):
268
+ key = self._key(path)
269
+ file_code = self._files.get(key)
270
+ if file_code is None:
271
+ file_code = self._files[key] = FileCode(key)
272
+ return file_code
273
+
274
+ def __contains__(self, path):
275
+ return Path(self._key(path)).is_file()
276
+
277
+
278
+ project_code = _ProjectCode()
@@ -0,0 +1,63 @@
1
+ """File-based symbol analysis in the running interpreter.
2
+
3
+ Applications may provide another context (for example an isolated interpreter).
4
+ The default has no project settings, environment selection, or package installs.
5
+ """
6
+ import os
7
+ import sys
8
+ from pathlib import Path
9
+ from meltygui.core.runtime.extensions import get
10
+
11
+
12
+ class SourceContext:
13
+ def __init__(self, root):
14
+ self.root = str(Path(root).expanduser().resolve())
15
+ self.source_paths = (self.root,)
16
+ if (Path(self.root) / 'src').is_dir():
17
+ self.source_paths += (str(Path(self.root) / 'src'),)
18
+ self.import_paths = tuple(dict.fromkeys((*self.source_paths, *sys.path)))
19
+ self.environment = None
20
+
21
+ @property
22
+ def key(self):
23
+ return self.root, self.import_paths, self.environment
24
+
25
+ def refresh(self):
26
+ return self
27
+
28
+ def contains(self, path):
29
+ return str(path) == self.root or str(path).startswith(self.root + os.sep)
30
+
31
+ def owns(self, path):
32
+ from meltygui.text_index import _SKIP_DIRS
33
+ return self.contains(path) and not any(
34
+ part in _SKIP_DIRS for part in Path(path).relative_to(self.root).parts[:-1]) and owning_root(path) == self.root
35
+
36
+ def resolves(self, path):
37
+ return self.owns(path) or any(
38
+ root and (str(path) == root or str(path).startswith(root + os.sep))
39
+ for root in self.import_paths if root not in self.source_paths)
40
+
41
+
42
+ def owning_root(path):
43
+ from meltygui.code.fileref import project_root_of
44
+ target = Path(path).expanduser().resolve()
45
+ return str(project_root_of(target) or (target if target.is_dir() else target.parent))
46
+
47
+
48
+ _contexts = {}
49
+
50
+
51
+ def analysis_project(project=None, path=None):
52
+ provider = get('source_context')
53
+ if provider is not None:
54
+ return provider(project=project, path=path)
55
+ if hasattr(project, 'refresh'):
56
+ return project.refresh()
57
+ if project is None:
58
+ from meltygui.core.runtime.paths import application_root
59
+ project = owning_root(path) if path is not None else application_root()
60
+ root = str(Path(project).expanduser().resolve())
61
+ if root not in _contexts:
62
+ _contexts[root] = SourceContext(root)
63
+ return _contexts[root]