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.
- meltygui/__init__.py +107 -0
- meltygui/accounts/__init__.py +0 -0
- meltygui/accounts/internet_accounts.py +1355 -0
- meltygui/chat/__init__.py +91 -0
- meltygui/chat/activity.py +75 -0
- meltygui/chat/backends.py +36 -0
- meltygui/chat/chat_interface.py +732 -0
- meltygui/chat/chat_proxy.py +352 -0
- meltygui/chat/codex_proxy.py +592 -0
- meltygui/chat/codex_settings.py +100 -0
- meltygui/chat/codex_transport.py +60 -0
- meltygui/chat/command_parser.py +204 -0
- meltygui/chat/images.py +227 -0
- meltygui/chat/messages.py +417 -0
- meltygui/chat/metadata.py +139 -0
- meltygui/chat/writer_locks.py +64 -0
- meltygui/code/__init__.py +0 -0
- meltygui/code/basic_converters.py +533 -0
- meltygui/code/chain_converters.py +2111 -0
- meltygui/code/code_checks.py +2209 -0
- meltygui/code/core_syntax.py +1430 -0
- meltygui/code/file_converters.py +1933 -0
- meltygui/code/fileref.py +702 -0
- meltygui/code/hotswap_guard.py +144 -0
- meltygui/code/libcst_conversion.py +9724 -0
- meltygui/code/live_instrument.py +392 -0
- meltygui/code/live_view.py +2490 -0
- meltygui/code/melty_scan.py +2684 -0
- meltygui/code/new_codecs.py +1255 -0
- meltygui/code/new_converters.py +3017 -0
- meltygui/code/project_code.py +278 -0
- meltygui/code/source_context.py +63 -0
- meltygui/code/symbol_roster.py +1588 -0
- meltygui/code/syntax_check.py +34 -0
- meltygui/code/syntax_check_worker.py +114 -0
- meltygui/completion/__init__.py +0 -0
- meltygui/completion/fim.py +1232 -0
- meltygui/completion/fim_context.py +481 -0
- meltygui/completion/providers/__init__.py +0 -0
- meltygui/completion/providers/anthropic_oauth.py +446 -0
- meltygui/completion/providers/anthropic_requests.py +69 -0
- meltygui/completion/providers/claude.py +203 -0
- meltygui/completion/providers/claude_usage.py +499 -0
- meltygui/completion/providers/codex_accounts.py +180 -0
- meltygui/completion/providers/copilot.py +617 -0
- meltygui/completion/providers/oauth_popup.py +220 -0
- meltygui/completion/providers/ollama.py +320 -0
- meltygui/completion/providers/profiles.py +23 -0
- meltygui/core/README.md +88 -0
- meltygui/core/__init__.py +1 -0
- meltygui/core/automation/__init__.py +1 -0
- meltygui/core/automation/action_core.py +153 -0
- meltygui/core/automation/collection_action.py +45 -0
- meltygui/core/automation/mcp_eval.py +167 -0
- meltygui/core/automation/mcp_hotswap.py +107 -0
- meltygui/core/automation/mcp_query.py +552 -0
- meltygui/core/automation/mcp_server.py +657 -0
- meltygui/core/automation/orchestration_core.py +2636 -0
- meltygui/core/automation/query_core.py +124 -0
- meltygui/core/automation/search_core.py +26 -0
- meltygui/core/automation/selector_core.py +340 -0
- meltygui/core/automation/value_core.py +1752 -0
- meltygui/core/cache/__init__.py +1 -0
- meltygui/core/cache/cache_diagnostics.py +0 -0
- meltygui/core/cache/invalidation_decoration.py +153 -0
- meltygui/core/cache/invalidation_tracker.py +43 -0
- meltygui/core/cache/tile_cache.py +5968 -0
- meltygui/core/conversion/__init__.py +1 -0
- meltygui/core/conversion/bubbling.py +599 -0
- meltygui/core/conversion/cache_tree.py +188 -0
- meltygui/core/conversion/chain.py +113 -0
- meltygui/core/conversion/converter_register.py +145 -0
- meltygui/core/conversion/data_decoration.py +67 -0
- meltygui/core/conversion/dict_conversion.py +1815 -0
- meltygui/core/conversion/dict_conversion_util.py +177 -0
- meltygui/core/conversion/dynamic_obj.py +89 -0
- meltygui/core/conversion/graph_compare.py +183 -0
- meltygui/core/conversion/load_save_v2.py +1032 -0
- meltygui/core/conversion/missing_saved_class.py +39 -0
- meltygui/core/conversion/path_finder.py +606 -0
- meltygui/core/conversion/render_host.py +1083 -0
- meltygui/core/core_render.py +6537 -0
- meltygui/core/definition_hotswap.py +231 -0
- meltygui/core/diagnostics/__init__.py +1 -0
- meltygui/core/diagnostics/attribute_churn.py +38 -0
- meltygui/core/diagnostics/fps_counter.py +39 -0
- meltygui/core/diagnostics/gpu_frame_timer.py +103 -0
- meltygui/core/diagnostics/inspection_core.py +169 -0
- meltygui/core/diagnostics/monitor_core.py +105 -0
- meltygui/core/diagnostics/notifications.py +706 -0
- meltygui/core/diagnostics/perf_trace.py +281 -0
- meltygui/core/diagnostics/profile_decoration.py +88 -0
- meltygui/core/diagnostics/resize_trace.py +62 -0
- meltygui/core/diagnostics/screenshot_core.py +247 -0
- meltygui/core/diagnostics/session_status.py +98 -0
- meltygui/core/diagnostics/trace_core.py +445 -0
- meltygui/core/files/__init__.py +1 -0
- meltygui/core/files/file_core.py +208 -0
- meltygui/core/files/file_explorer_core.py +104 -0
- meltygui/core/files/file_tree_core.py +198 -0
- meltygui/core/files/file_watch_core.py +43 -0
- meltygui/core/files/import_graph_core.py +43 -0
- meltygui/core/files/metadata_core.py +51 -0
- meltygui/core/graphics/__init__.py +1 -0
- meltygui/core/graphics/cuda_context_core.py +166 -0
- meltygui/core/graphics/cuda_interop_core.py +136 -0
- meltygui/core/graphics/cuda_kernel_core.py +91 -0
- meltygui/core/graphics/framebuffer_recorder.py +337 -0
- meltygui/core/graphics/gl_state.py +658 -0
- meltygui/core/graphics/lut_core.py +52 -0
- meltygui/core/graphics/overlay_renderer.py +984 -0
- meltygui/core/graphics/scene_target.py +180 -0
- meltygui/core/graphics/screenshot.py +439 -0
- meltygui/core/graphics/shader_func.py +478 -0
- meltygui/core/graphics/tensor_core.py +45 -0
- meltygui/core/graphics/text_texture.py +329 -0
- meltygui/core/graphics/wayland_color.py +635 -0
- meltygui/core/input/__init__.py +1 -0
- meltygui/core/input/collision.py +165 -0
- meltygui/core/input/drag_drop_core.py +1525 -0
- meltygui/core/input/hypr_left_drag.py +323 -0
- meltygui/core/input/input_core.py +245 -0
- meltygui/core/input/input_handler.py +1101 -0
- meltygui/core/input/mouse_cursor.py +355 -0
- meltygui/core/input/pynput_backend.py +1054 -0
- meltygui/core/input/space_mouse.py +338 -0
- meltygui/core/input/touchpad_backend.py +393 -0
- meltygui/core/input/view_selection.py +177 -0
- meltygui/core/layout/__init__.py +1 -0
- meltygui/core/layout/column_core.py +2153 -0
- meltygui/core/layout/cursor_core.py +161 -0
- meltygui/core/layout/dropdown_core.py +278 -0
- meltygui/core/layout/edge_constraints.py +155 -0
- meltygui/core/layout/grid_core.py +117 -0
- meltygui/core/layout/header_core.py +23 -0
- meltygui/core/layout/header_runtime.py +67 -0
- meltygui/core/layout/layout_core.py +87 -0
- meltygui/core/layout/tile_manager_core.py +591 -0
- meltygui/core/melty.py +6726 -0
- meltygui/core/module_map.json +896 -0
- meltygui/core/module_names.py +19 -0
- meltygui/core/rendering/__init__.py +1 -0
- meltygui/core/rendering/core_decoration.py +431 -0
- meltygui/core/rendering/core_render_helpers.py +328 -0
- meltygui/core/rendering/func_metadata.py +398 -0
- meltygui/core/rendering/mode.py +818 -0
- meltygui/core/rendering/mode_defaults.py +41 -0
- meltygui/core/rendering/modes.py +136 -0
- meltygui/core/rendering/parameter_core.py +1665 -0
- meltygui/core/rendering/render_dispatch.py +1891 -0
- meltygui/core/rendering/render_funcs.py +273 -0
- meltygui/core/rendering/shaped.py +312 -0
- meltygui/core/rendering/window_decoration.py +25 -0
- meltygui/core/runtime/__init__.py +1 -0
- meltygui/core/runtime/app.py +735 -0
- meltygui/core/runtime/app_session.py +140 -0
- meltygui/core/runtime/background.py +564 -0
- meltygui/core/runtime/extensions.py +53 -0
- meltygui/core/runtime/gc_manager.py +1163 -0
- meltygui/core/runtime/lifecycle.py +21 -0
- meltygui/core/runtime/paths.py +27 -0
- meltygui/core/runtime/settings.py +14 -0
- meltygui/core/runtime/singleton.py +16 -0
- meltygui/core/runtime/thread_safe_bool.py +24 -0
- meltygui/core/runtime/thread_signal.py +30 -0
- meltygui/core/runtime/toggles.py +3115 -0
- meltygui/core/services/__init__.py +1 -0
- meltygui/core/services/account_core.py +10 -0
- meltygui/core/services/chat_core.py +19 -0
- meltygui/core/services/claude_terminal_core.py +346 -0
- meltygui/core/services/terminal_core.py +458 -0
- meltygui/core/services/terminal_runtime.py +78 -0
- meltygui/core/styling/__init__.py +1 -0
- meltygui/core/styling/color_core.py +46 -0
- meltygui/core/styling/fonts.py +639 -0
- meltygui/core/styling/global_style.py +338 -0
- meltygui/core/styling/style.py +198 -0
- meltygui/core/styling/style_core.py +522 -0
- meltygui/core/styling/warm_start.py +149 -0
- meltygui/core/windowing/__init__.py +1 -0
- meltygui/core/windowing/backends/PYIMGUI_LICENSE +28 -0
- meltygui/core/windowing/backends/__init__.py +1 -0
- meltygui/core/windowing/backends/imgui_renderer.py +138 -0
- meltygui/core/windowing/backends/native_wayland.py +850 -0
- meltygui/core/windowing/backends/protocols/xdg-decoration-unstable-v1.xml +156 -0
- meltygui/core/windowing/backends/protocols/xdg-shell.xml +1420 -0
- meltygui/core/windowing/backends/wayland_protocol.py +101 -0
- meltygui/core/windowing/dock_core.py +182 -0
- meltygui/core/windowing/frame_geometry.py +42 -0
- meltygui/core/windowing/geometry_feed.py +864 -0
- meltygui/core/windowing/glfw_utils.py +1343 -0
- meltygui/core/windowing/os_frame.py +1552 -0
- meltygui/core/windowing/surface.py +643 -0
- meltygui/core/windowing/titlebar.py +1560 -0
- meltygui/core/windowing/titlebar_buttons.py +281 -0
- meltygui/core/windowing/wayland_move.py +932 -0
- meltygui/core/windowing/window_api.py +62 -0
- meltygui/core/windowing/window_constants.py +339 -0
- meltygui/core/windowing/window_visibility.py +162 -0
- meltygui/debug/__init__.py +0 -0
- meltygui/debug/app_view_utils.py +9 -0
- meltygui/editor/__init__.py +0 -0
- meltygui/editor/bash_syntax.py +30 -0
- meltygui/editor/code_line_fast.py +152 -0
- meltygui/editor/diff.py +139 -0
- meltygui/editor/external_changes.py +159 -0
- meltygui/editor/file_header.py +41 -0
- meltygui/editor/live_usage.py +169 -0
- meltygui/editor/live_view_views.py +1803 -0
- meltygui/editor/pending_save.py +1351 -0
- meltygui/editor/roster_tints.py +547 -0
- meltygui/editor/source_preview.py +16 -0
- meltygui/editor/source_tools.py +10 -0
- meltygui/editor/source_ui.py +70 -0
- meltygui/editor/spell_check.py +77 -0
- meltygui/editor/text_editor.py +9076 -0
- meltygui/editor/usage_picker.py +538 -0
- meltygui/events/__init__.py +0 -0
- meltygui/events/example.py +118 -0
- meltygui/examples/__init__.py +0 -0
- meltygui/examples/columns_demo.py +33 -0
- meltygui/examples/columns_window_demo.py +60 -0
- meltygui/examples/context_menu_demo.py +24 -0
- meltygui/examples/context_menu_window_demo.py +49 -0
- meltygui/examples/gui_playground.py +127 -0
- meltygui/examples/live_view_playground.py +256 -0
- meltygui/examples/lora.py +43 -0
- meltygui/examples/lora_data.py +59 -0
- meltygui/examples/lora_policies.py +67 -0
- meltygui/examples/mode_demo.py +49 -0
- meltygui/examples/modifies_demo.py +119 -0
- meltygui/examples/scalar_policies.py +60 -0
- meltygui/examples/style_layouts.py +149 -0
- meltygui/examples/tile_manager_demo.py +74 -0
- meltygui/examples/tint_demo.py +160 -0
- meltygui/examples/tint_functions.py +56 -0
- meltygui/examples/trace_demo.py +88 -0
- meltygui/examples/two_windows.py +36 -0
- meltygui/files/__init__.py +0 -0
- meltygui/files/fast_file_explorer.py +439 -0
- meltygui/gnome_extension/lsd-window-geometry@latent-descent/extension.js +237 -0
- meltygui/gnome_extension/lsd-window-geometry@latent-descent/lsd-window-geometry@latent-descent.iml +9 -0
- meltygui/gnome_extension/lsd-window-geometry@latent-descent/metadata.json +7 -0
- meltygui/graphics/__init__.py +6 -0
- meltygui/graphics/base.py +85 -0
- meltygui/graphics/examples.py +507 -0
- meltygui/graphics/executor.py +520 -0
- meltygui/graphics/filter.py +667 -0
- meltygui/graphics/filter.pyi +856 -0
- meltygui/graphics/generate_stubs.py +22 -0
- meltygui/graphics/registry.py +203 -0
- meltygui/graphics/shader_compiler.py +155 -0
- meltygui/graphics/shaders.py +1302 -0
- meltygui/graphics/stub_generator.py +250 -0
- meltygui/graphics/texture_manager.py +170 -0
- meltygui/graphics/texture_min_max.py +295 -0
- meltygui/hdr_color.py +757 -0
- meltygui/image_load.py +308 -0
- meltygui/model/__init__.py +1 -0
- meltygui/model/account_model.py +142 -0
- meltygui/model/camera_model.py +159 -0
- meltygui/model/chat_model.py +86 -0
- meltygui/model/code_model.py +62 -0
- meltygui/model/code_proxy_model.py +876 -0
- meltygui/model/collection_model.py +41 -0
- meltygui/model/color_model.py +127 -0
- meltygui/model/cuda_tensor_model.py +36 -0
- meltygui/model/cuda_texture_model.py +149 -0
- meltygui/model/dropdown_model.py +137 -0
- meltygui/model/file_metadata_model.py +73 -0
- meltygui/model/file_model.py +289 -0
- meltygui/model/format_model.py +390 -0
- meltygui/model/graph_model.py +98 -0
- meltygui/model/icon_model.py +1024 -0
- meltygui/model/import_graph_model.py +468 -0
- meltygui/model/layout_model.py +15 -0
- meltygui/model/lut_model.py +266 -0
- meltygui/model/search_model.py +173 -0
- meltygui/model/tensor_model.py +402 -0
- meltygui/model/terminal_model.py +329 -0
- meltygui/model/texture_model.py +146 -0
- meltygui/model/tile_model.py +24 -0
- meltygui/model/trace_model.py +198 -0
- meltygui/model/trace_report_model.py +146 -0
- meltygui/models/__init__.py +0 -0
- meltygui/models/file_meta.py +613 -0
- meltygui/models/function_console.py +184 -0
- meltygui/models/orchestration.py +48 -0
- meltygui/pbr.py +1576 -0
- meltygui/png_unfilter.c +49 -0
- meltygui/resources/JetBrainsMono-Regular.ttf +0 -0
- meltygui/resources/THIRD_PARTY_NOTICES.md +21 -0
- meltygui/resources/dejavu/DejaVuSans-Bold.ttf +0 -0
- meltygui/resources/dejavu/DejaVuSans-ExtraLight.ttf +0 -0
- meltygui/resources/dejavu/DejaVuSans.ttf +0 -0
- meltygui/resources/dejavu/LICENSE.txt +78 -0
- meltygui/resources/fontawesome-LICENSE.txt +121 -0
- meltygui/resources/fontawesome-webfont.ttf +0 -0
- meltygui/resources/hdri/studio_small_09_1k.hdr +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Bold.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraBold.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraLight.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Light.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Medium.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-SemiBold.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Thin.ttf +0 -0
- meltygui/resources/jetbrains-weights/OFL.txt +93 -0
- meltygui/resources/jetbrains-weights/README.md +2 -0
- meltygui/state/__init__.py +0 -0
- meltygui/state/account_state.py +22 -0
- meltygui/state/animation_state.py +97 -0
- meltygui/state/annotation_state.py +22 -0
- meltygui/state/chat_state.py +36 -0
- meltygui/state/code_state.py +10 -0
- meltygui/state/core_enums.py +59 -0
- meltygui/state/core_markers.py +115 -0
- meltygui/state/core_undo.py +1075 -0
- meltygui/state/file_state.py +75 -0
- meltygui/state/graph_state.py +26 -0
- meltygui/state/inspection_state.py +47 -0
- meltygui/state/menu_state.py +10 -0
- meltygui/state/model_enums.py +11 -0
- meltygui/state/new_core_model.py +2394 -0
- meltygui/state/orchestration_state.py +14 -0
- meltygui/state/query_state.py +24 -0
- meltygui/state/tensor_state.py +10 -0
- meltygui/state/terminal_state.py +23 -0
- meltygui/state/trace_state.py +32 -0
- meltygui/state/voxel_state.py +12 -0
- meltygui/text_index.py +816 -0
- meltygui/utils/__init__.py +0 -0
- meltygui/utils/jump_to_code.py +344 -0
- meltygui/utils/pkl_inspect.py +90 -0
- meltygui/utils/render_utils.py +1419 -0
- meltygui/view/__init__.py +1 -0
- meltygui/view/account_view.py +524 -0
- meltygui/view/action_view.py +73 -0
- meltygui/view/chat_decoration_view.py +112 -0
- meltygui/view/chat_view.py +1703 -0
- meltygui/view/code_view.py +2677 -0
- meltygui/view/collection_view.py +1185 -0
- meltygui/view/color_view.py +824 -0
- meltygui/view/control_view.py +559 -0
- meltygui/view/decoration_view.py +439 -0
- meltygui/view/diagnostic_view.py +177 -0
- meltygui/view/dropdown_view.py +1082 -0
- meltygui/view/file_view.py +1599 -0
- meltygui/view/graph_cuda_view.py +105 -0
- meltygui/view/graph_view.py +882 -0
- meltygui/view/header_view.py +848 -0
- meltygui/view/input_view.py +238 -0
- meltygui/view/inspection_view.py +1783 -0
- meltygui/view/layout_view.py +596 -0
- meltygui/view/lut_view.py +45 -0
- meltygui/view/menu_view.py +212 -0
- meltygui/view/orchestration_view.py +658 -0
- meltygui/view/query_view.py +118 -0
- meltygui/view/search_view.py +342 -0
- meltygui/view/tab_view.py +258 -0
- meltygui/view/tensor_view.py +430 -0
- meltygui/view/terminal_view.py +355 -0
- meltygui/view/text_view.py +8031 -0
- meltygui/view/texture_view.py +480 -0
- meltygui/view/tile_view.py +38 -0
- meltygui/view/trace_view.py +923 -0
- meltygui/view/voxel_cuda_view.py +824 -0
- meltygui/view/voxel_view.py +1860 -0
- meltygui/view/window_view.py +111 -0
- meltygui-0.1.0.dist-info/METADATA +143 -0
- meltygui-0.1.0.dist-info/RECORD +372 -0
- meltygui-0.1.0.dist-info/WHEEL +4 -0
- 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]
|