Compare commits
12 Commits
e6a880d2a4
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| f4e6cd4ab0 | |||
| c90d3071ea | |||
| 9de735aace | |||
| 786a72126c | |||
| 70022a9b6c | |||
| 1f7b68e432 | |||
| 507a7e5d92 | |||
| cc5d737c16 | |||
| a7116d7206 | |||
| 4563a0f653 | |||
| e7a15ea070 | |||
| 34f8b3d733 |
@ -62,6 +62,7 @@ class MdlpInsertCommand(sublime_plugin.TextCommand):
|
|||||||
class OpenMarkdownPreviewCommand(sublime_plugin.TextCommand):
|
class OpenMarkdownPreviewCommand(sublime_plugin.TextCommand):
|
||||||
def run(self, edit):
|
def run(self, edit):
|
||||||
|
|
||||||
|
print("--- MarkdownLivePreview: OpenMarkdownPreviewCommand running ---")
|
||||||
""" If the file is saved exists on disk, we close it, and reopen it in a new
|
""" If the file is saved exists on disk, we close it, and reopen it in a new
|
||||||
window. Otherwise, we copy the content, erase it all (to close the file without
|
window. Otherwise, we copy the content, erase it all (to close the file without
|
||||||
a dialog) and re-insert it into a new view into a new window """
|
a dialog) and re-insert it into a new view into a new window """
|
||||||
@ -69,22 +70,30 @@ class OpenMarkdownPreviewCommand(sublime_plugin.TextCommand):
|
|||||||
original_view = self.view
|
original_view = self.view
|
||||||
original_window_id = original_view.window().id()
|
original_window_id = original_view.window().id()
|
||||||
file_name = original_view.file_name()
|
file_name = original_view.file_name()
|
||||||
|
print("--- MarkdownLivePreview: Original view ID: {}, File: {}, Window ID: {} ---".format(original_view.id(), file_name, original_window_id))
|
||||||
|
|
||||||
syntax_file = original_view.settings().get("syntax")
|
syntax_file = original_view.settings().get("syntax")
|
||||||
|
|
||||||
if file_name:
|
# don't close the original view; keep it in your main window:
|
||||||
original_view.close()
|
# if file_name:
|
||||||
else:
|
# original_view.close()
|
||||||
# the file isn't saved, we need to restore the content manually
|
# else:
|
||||||
|
# # the file isn't saved, we need to restore the content manually
|
||||||
|
# total_region = sublime.Region(0, original_view.size())
|
||||||
|
# content = original_view.substr(total_region)
|
||||||
|
# original_view.erase(edit, total_region)
|
||||||
|
# original_view.close()
|
||||||
|
# # FIXME: save the document to a temporary file, so that if we crash,
|
||||||
|
# # the user doesn't lose what he wrote
|
||||||
|
if not file_name:
|
||||||
|
# If the file isn't saved, we still need the content for the new view
|
||||||
total_region = sublime.Region(0, original_view.size())
|
total_region = sublime.Region(0, original_view.size())
|
||||||
content = original_view.substr(total_region)
|
content = original_view.substr(total_region)
|
||||||
original_view.erase(edit, total_region)
|
print("--- MarkdownLivePreview: Unsaved file, content length: {} ---".format(len(content)))
|
||||||
original_view.close()
|
|
||||||
# FIXME: save the document to a temporary file, so that if we crash,
|
|
||||||
# the user doesn't lose what he wrote
|
|
||||||
|
|
||||||
sublime.run_command("new_window")
|
# instead of making a new window, grab your existing one:
|
||||||
preview_window = sublime.active_window()
|
preview_window = original_view.window()
|
||||||
|
print("--- MarkdownLivePreview: Using existing window ID: {} ---".format(preview_window.id()))
|
||||||
|
|
||||||
preview_window.run_command(
|
preview_window.run_command(
|
||||||
"set_layout",
|
"set_layout",
|
||||||
@ -100,20 +109,44 @@ class OpenMarkdownPreviewCommand(sublime_plugin.TextCommand):
|
|||||||
preview_view.set_scratch(True)
|
preview_view.set_scratch(True)
|
||||||
preview_view.settings().set(PREVIEW_VIEW_INFOS, {})
|
preview_view.settings().set(PREVIEW_VIEW_INFOS, {})
|
||||||
preview_view.set_name("Preview")
|
preview_view.set_name("Preview")
|
||||||
|
print("--- MarkdownLivePreview: Created preview_view ID: {} ---".format(preview_view.id()))
|
||||||
# FIXME: hide number lines on preview
|
# FIXME: hide number lines on preview
|
||||||
|
|
||||||
preview_window.focus_group(0)
|
preview_window.focus_group(0)
|
||||||
if file_name:
|
if file_name:
|
||||||
markdown_view = preview_window.open_file(file_name)
|
markdown_view = preview_window.open_file(file_name)
|
||||||
|
print("--- MarkdownLivePreview: Opened markdown_view ID: {} for file: {} ---".format(markdown_view.id(), file_name))
|
||||||
else:
|
else:
|
||||||
markdown_view = preview_window.new_file()
|
markdown_view = preview_window.new_file()
|
||||||
markdown_view.run_command("mdlp_insert", {"point": 0, "string": content})
|
markdown_view.run_command("mdlp_insert", {"point": 0, "string": content})
|
||||||
markdown_view.set_scratch(True)
|
markdown_view.set_scratch(True)
|
||||||
|
print("--- MarkdownLivePreview: Created new markdown_view ID: {} for unsaved content ---".format(markdown_view.id()))
|
||||||
|
|
||||||
markdown_view.set_syntax_file(syntax_file)
|
markdown_view.set_syntax_file(syntax_file)
|
||||||
markdown_view.settings().set(
|
markdown_view.settings().set(
|
||||||
MARKDOWN_VIEW_INFOS, {"original_window_id": original_window_id,},
|
MARKDOWN_VIEW_INFOS, {"original_window_id": original_window_id, "preview_view_id": preview_view.id(),},
|
||||||
)
|
)
|
||||||
|
infos_to_log = markdown_view.settings().get(MARKDOWN_VIEW_INFOS)
|
||||||
|
print("--- MarkdownLivePreview: Stored infos on markdown_view {}: {} ---".format(markdown_view.id(), infos_to_log))
|
||||||
|
|
||||||
|
# Manually trigger the initial setup and render via the listener
|
||||||
|
# Use set_timeout_async to run it after the command finishes
|
||||||
|
print("--- MarkdownLivePreview: Scheduling setup_and_update_preview for md_view: {}, pv_view: {} ---".format(markdown_view.id(), preview_view.id()))
|
||||||
|
sublime.set_timeout_async(lambda: self.trigger_listener_setup(markdown_view.id(), preview_view.id()), 0)
|
||||||
|
|
||||||
|
# Helper method to find and call the listener instance method
|
||||||
|
# This is needed because the listener isn't easily accessible directly from the command instance
|
||||||
|
def trigger_listener_setup(self, md_view_id, pv_view_id):
|
||||||
|
print("--- MarkdownLivePreview: trigger_listener_setup running for md_view: {}, pv_view: {} ---".format(md_view_id, pv_view_id))
|
||||||
|
# Access the listener instance directly via its class variable
|
||||||
|
listener_instance = MarkdownLivePreviewListener.instance
|
||||||
|
|
||||||
|
if listener_instance:
|
||||||
|
print("--- MarkdownLivePreview: Found listener instance via class variable, calling setup_and_update_preview ---")
|
||||||
|
listener_instance.setup_and_update_preview(md_view_id, pv_view_id)
|
||||||
|
else:
|
||||||
|
# This should ideally not happen if the listener loaded correctly before the command ran
|
||||||
|
print("--- MarkdownLivePreview: ERROR: MarkdownLivePreviewListener.instance is None! Cannot trigger setup. ---")
|
||||||
|
|
||||||
def is_enabled(self):
|
def is_enabled(self):
|
||||||
# FIXME: is this the best way there is to check if the current syntax is markdown?
|
# FIXME: is this the best way there is to check if the current syntax is markdown?
|
||||||
@ -124,6 +157,7 @@ class OpenMarkdownPreviewCommand(sublime_plugin.TextCommand):
|
|||||||
|
|
||||||
|
|
||||||
class MarkdownLivePreviewListener(sublime_plugin.EventListener):
|
class MarkdownLivePreviewListener(sublime_plugin.EventListener):
|
||||||
|
instance = None # Class variable to hold the single instance
|
||||||
|
|
||||||
phantom_sets = {
|
phantom_sets = {
|
||||||
# markdown_view.id(): phantom set
|
# markdown_view.id(): phantom set
|
||||||
@ -133,6 +167,11 @@ class MarkdownLivePreviewListener(sublime_plugin.EventListener):
|
|||||||
# then, we update only if now() - last_update > DELAY
|
# then, we update only if now() - last_update > DELAY
|
||||||
last_update = 0
|
last_update = 0
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__() # Good practice to call super
|
||||||
|
MarkdownLivePreviewListener.instance = self
|
||||||
|
print("--- MarkdownLivePreview: Listener instance created and registered. ---")
|
||||||
|
|
||||||
# FIXME: maybe we shouldn't restore the file in the original window...
|
# FIXME: maybe we shouldn't restore the file in the original window...
|
||||||
|
|
||||||
def on_pre_close(self, markdown_view):
|
def on_pre_close(self, markdown_view):
|
||||||
@ -155,11 +194,24 @@ class MarkdownLivePreviewListener(sublime_plugin.EventListener):
|
|||||||
def on_load_async(self, markdown_view):
|
def on_load_async(self, markdown_view):
|
||||||
infos = markdown_view.settings().get(MARKDOWN_VIEW_INFOS)
|
infos = markdown_view.settings().get(MARKDOWN_VIEW_INFOS)
|
||||||
if not infos:
|
if not infos:
|
||||||
|
# print("--- MarkdownLivePreview: on_load_async ignored for view {} - no infos ---".format(markdown_view.id())) # Optional: very verbose
|
||||||
return
|
return
|
||||||
|
|
||||||
preview_view = markdown_view.window().active_view_in_group(1)
|
print("--- MarkdownLivePreview: on_load_async triggered for markdown_view {} ---".format(markdown_view.id()))
|
||||||
|
preview_view_id = infos.get("preview_view_id")
|
||||||
|
if not preview_view_id:
|
||||||
|
print("--- MarkdownLivePreview: ERROR in on_load_async: No preview_view_id found in infos: {} ---".format(infos))
|
||||||
|
return # Should not happen if setup was correct
|
||||||
|
|
||||||
self.phantom_sets[markdown_view.id()] = sublime.PhantomSet(preview_view)
|
preview_view = sublime.View(preview_view_id)
|
||||||
|
if not preview_view.is_valid():
|
||||||
|
print("--- MarkdownLivePreview: ERROR in on_load_async: Preview view {} is no longer valid ---".format(preview_view_id))
|
||||||
|
return # Preview view was closed before loading finished
|
||||||
|
|
||||||
|
print("--- MarkdownLivePreview: on_load_async found valid preview_view {} ---".format(preview_view.id()))
|
||||||
|
# PhantomSet creation is now handled by setup_and_update_preview triggered from the command
|
||||||
|
# self.phantom_sets[markdown_view.id()] = sublime.PhantomSet(preview_view)
|
||||||
|
# print("--- MarkdownLivePreview: PhantomSet created in on_load_async for preview_view {} ---".format(preview_view.id())) # Keep log commented
|
||||||
self._update_preview(markdown_view)
|
self._update_preview(markdown_view)
|
||||||
|
|
||||||
def on_close(self, markdown_view):
|
def on_close(self, markdown_view):
|
||||||
@ -172,7 +224,8 @@ class MarkdownLivePreviewListener(sublime_plugin.EventListener):
|
|||||||
if markdown_view.id() in self.phantom_sets:
|
if markdown_view.id() in self.phantom_sets:
|
||||||
del self.phantom_sets[markdown_view.id()]
|
del self.phantom_sets[markdown_view.id()]
|
||||||
|
|
||||||
self.preview_window.run_command("close_window")
|
# don't close the entire window—just let the user close the preview tab:
|
||||||
|
# self.preview_window.run_command("close_window")
|
||||||
|
|
||||||
# find the window with the right id
|
# find the window with the right id
|
||||||
original_window = next(
|
original_window = next(
|
||||||
@ -203,14 +256,17 @@ class MarkdownLivePreviewListener(sublime_plugin.EventListener):
|
|||||||
# @min_time_between_call(.5)
|
# @min_time_between_call(.5)
|
||||||
def on_modified_async(self, markdown_view):
|
def on_modified_async(self, markdown_view):
|
||||||
|
|
||||||
|
# print("--- MarkdownLivePreview: on_modified_async triggered for view {} ---".format(markdown_view.id())) # Optional: very verbose
|
||||||
infos = markdown_view.settings().get(MARKDOWN_VIEW_INFOS)
|
infos = markdown_view.settings().get(MARKDOWN_VIEW_INFOS)
|
||||||
if not infos:
|
if not infos:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
print("--- MarkdownLivePreview: Scheduling update for markdown_view {} ---".format(markdown_view.id()))
|
||||||
# we schedule an update, which won't run if an
|
# we schedule an update, which won't run if an
|
||||||
sublime.set_timeout(partial(self._update_preview, markdown_view), DELAY)
|
sublime.set_timeout(partial(self._update_preview, markdown_view), DELAY)
|
||||||
|
|
||||||
def _update_preview(self, markdown_view):
|
def _update_preview(self, markdown_view):
|
||||||
|
print("--- MarkdownLivePreview: _update_preview called for markdown_view {} ---".format(markdown_view.id()))
|
||||||
# if the buffer id is 0, that means that the markdown_view has been closed
|
# if the buffer id is 0, that means that the markdown_view has been closed
|
||||||
# This check is needed since a this function is used as a callback for when images
|
# This check is needed since a this function is used as a callback for when images
|
||||||
# are loaded from the internet (ie. it could finish loading *after* the user
|
# are loaded from the internet (ie. it could finish loading *after* the user
|
||||||
@ -222,28 +278,50 @@ class MarkdownLivePreviewListener(sublime_plugin.EventListener):
|
|||||||
print("--- MarkdownLivePreview: Using font_scale: {} ---".format(font_scale))
|
print("--- MarkdownLivePreview: Using font_scale: {} ---".format(font_scale))
|
||||||
|
|
||||||
if time.time() - self.last_update < delay / 1000:
|
if time.time() - self.last_update < delay / 1000:
|
||||||
|
print("--- MarkdownLivePreview: Update skipped for view {} due to time delay ---".format(markdown_view.id()))
|
||||||
return
|
return
|
||||||
|
|
||||||
if markdown_view.buffer_id() == 0:
|
if markdown_view.buffer_id() == 0:
|
||||||
|
print("--- MarkdownLivePreview: Update skipped for view {}: buffer_id is 0 (view closed) ---".format(markdown_view.id()))
|
||||||
return
|
return
|
||||||
|
|
||||||
# Check if the phantom set still exists for this view ID
|
# Check if the phantom set still exists for this view ID
|
||||||
if markdown_view.id() not in self.phantom_sets:
|
if markdown_view.id() not in self.phantom_sets:
|
||||||
|
print("--- MarkdownLivePreview: Update skipped for view {}: No phantom set found ---".format(markdown_view.id()))
|
||||||
# View might have been closed between modification and update
|
# View might have been closed between modification and update
|
||||||
return
|
return
|
||||||
|
|
||||||
|
print("--- MarkdownLivePreview: Update proceeding for view {} ---".format(markdown_view.id()))
|
||||||
self.last_update = time.time()
|
self.last_update = time.time()
|
||||||
|
|
||||||
total_region = sublime.Region(0, markdown_view.size())
|
total_region = sublime.Region(0, markdown_view.size())
|
||||||
markdown = markdown_view.substr(total_region)
|
markdown = markdown_view.substr(total_region)
|
||||||
|
print("--- MarkdownLivePreview: Read markdown content (length: {}) ---".format(len(markdown)))
|
||||||
|
|
||||||
preview_view = markdown_view.window().active_view_in_group(1)
|
infos = markdown_view.settings().get(MARKDOWN_VIEW_INFOS)
|
||||||
|
if not infos:
|
||||||
|
print("--- MarkdownLivePreview: ERROR in _update_preview: No infos found for view {} ---".format(markdown_view.id()))
|
||||||
|
return # Should not happen
|
||||||
|
|
||||||
|
preview_view_id = infos.get("preview_view_id")
|
||||||
|
if not preview_view_id:
|
||||||
|
print("--- MarkdownLivePreview: ERROR in _update_preview: No preview_view_id found in infos: {} ---".format(infos))
|
||||||
|
return # Should not happen
|
||||||
|
|
||||||
|
preview_view = sublime.View(preview_view_id)
|
||||||
|
if not preview_view.is_valid():
|
||||||
|
print("--- MarkdownLivePreview: ERROR in _update_preview: Preview view {} is no longer valid ---".format(preview_view_id))
|
||||||
|
return # Preview view was closed
|
||||||
|
|
||||||
|
print("--- MarkdownLivePreview: Found valid preview_view {} for update ---".format(preview_view.id()))
|
||||||
# Get viewport_width, default to a large value if view isn't ready
|
# Get viewport_width, default to a large value if view isn't ready
|
||||||
viewport_extent = preview_view.viewport_extent()
|
viewport_extent = preview_view.viewport_extent()
|
||||||
viewport_width = viewport_extent[0] if viewport_extent else 1024
|
viewport_width = viewport_extent[0] if viewport_extent else 1024
|
||||||
|
print("--- MarkdownLivePreview: Viewport width: {} ---".format(viewport_width))
|
||||||
|
|
||||||
|
|
||||||
basepath = os.path.dirname(markdown_view.file_name()) if markdown_view.file_name() else '.' # Handle unsaved files
|
basepath = os.path.dirname(markdown_view.file_name()) if markdown_view.file_name() else '.' # Handle unsaved files
|
||||||
|
print("--- MarkdownLivePreview: Calling markdown2html with basepath: {} ---".format(basepath))
|
||||||
html = markdown2html(
|
html = markdown2html(
|
||||||
markdown,
|
markdown,
|
||||||
basepath,
|
basepath,
|
||||||
@ -253,6 +331,10 @@ class MarkdownLivePreviewListener(sublime_plugin.EventListener):
|
|||||||
font_scale,
|
font_scale,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Truncate HTML safely for logging
|
||||||
|
html_preview = html[:100].replace('\n', ' ') # Avoid breaking log lines
|
||||||
|
print("--- MarkdownLivePreview: Generated HTML (starts with): {}... ---".format(html_preview))
|
||||||
|
|
||||||
self.phantom_sets[markdown_view.id()].update(
|
self.phantom_sets[markdown_view.id()].update(
|
||||||
[
|
[
|
||||||
sublime.Phantom(
|
sublime.Phantom(
|
||||||
@ -263,6 +345,32 @@ class MarkdownLivePreviewListener(sublime_plugin.EventListener):
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
print("--- MarkdownLivePreview: Updated phantoms in preview_view {} ---".format(preview_view.id()))
|
||||||
|
|
||||||
|
def setup_and_update_preview(self, markdown_view_id, preview_view_id):
|
||||||
|
print("--- MarkdownLivePreview: setup_and_update_preview called for md_view: {}, pv_view: {} ---".format(markdown_view_id, preview_view_id))
|
||||||
|
markdown_view = sublime.View(markdown_view_id)
|
||||||
|
preview_view = sublime.View(preview_view_id)
|
||||||
|
|
||||||
|
if not markdown_view.is_valid():
|
||||||
|
print("--- MarkdownLivePreview: ERROR in setup_and_update: markdown_view {} is not valid ---".format(markdown_view_id))
|
||||||
|
return
|
||||||
|
if not preview_view.is_valid():
|
||||||
|
print("--- MarkdownLivePreview: ERROR in setup_and_update: preview_view {} is not valid ---".format(preview_view_id))
|
||||||
|
return
|
||||||
|
|
||||||
|
# Create PhantomSet if it doesn't exist (shouldn't at this point)
|
||||||
|
if markdown_view_id not in self.phantom_sets:
|
||||||
|
print("--- MarkdownLivePreview: Creating PhantomSet for preview_view {} in setup ---".format(preview_view.id()))
|
||||||
|
self.phantom_sets[markdown_view_id] = sublime.PhantomSet(preview_view)
|
||||||
|
else:
|
||||||
|
# This case might occur if the command is run multiple times rapidly? Ensure it's associated correctly.
|
||||||
|
print("--- MarkdownLivePreview: Warning: PhantomSet already existed for markdown_view {} in setup. Re-associating with preview_view {}. ---".format(markdown_view_id, preview_view.id()))
|
||||||
|
self.phantom_sets[markdown_view_id].view = preview_view # Ensure it points to the correct view
|
||||||
|
|
||||||
|
# Trigger the first update
|
||||||
|
print("--- MarkdownLivePreview: Triggering initial _update_preview from setup ---")
|
||||||
|
self._update_preview(markdown_view)
|
||||||
|
|
||||||
|
|
||||||
def get_settings():
|
def get_settings():
|
||||||
|
|||||||
Binary file not shown.
22
README.md
22
README.md
@ -1,5 +1,25 @@
|
|||||||
# MarkdownLivePreview
|
# MarkdownLivePreview
|
||||||
|
|
||||||
|
## Acknowledgments
|
||||||
|
|
||||||
|
This project is a fork of [MarkdownLivePreview](https://github.com/math2001/MarkdownLivePreview) by **math2001**. I'm grateful for the original implementation, which provided a solid foundation for live Markdown preview in Sublime Text.
|
||||||
|
|
||||||
|
Many thanks to math2001 for the original code—this fork wouldn't have been possible without their work.
|
||||||
|
|
||||||
|
## Changes contained in this Fork
|
||||||
|
|
||||||
|
In this fork ([christian.morpurgo/MarkdownLivePreview](https://git.0x42.cloud/christian.morpurgo/MarkdownLivePreview)), I've made the following enhancements:
|
||||||
|
|
||||||
|
- **Bundled `bs4` and `soupsieve`**
|
||||||
|
Repackaged both libraries as standalone modules by rewriting their imports, resolving errors when Sublime Text 4 attempted to install these old versions as external dependencies.
|
||||||
|
|
||||||
|
- **`font_scale` option**
|
||||||
|
Added a new `font_scale: number` setting to allow users to increase or decrease the preview text size directly from their Sublime Text settings.
|
||||||
|
|
||||||
|
- **In-window preview**
|
||||||
|
Changed the preview behavior so that starting a preview reuses the current window instead of opening a new one.
|
||||||
|
|
||||||
|
|
||||||
A simple plugin to preview your markdown as you type right in Sublime Text.
|
A simple plugin to preview your markdown as you type right in Sublime Text.
|
||||||
No dependencies!
|
No dependencies!
|
||||||
|
|
||||||
@ -31,7 +51,7 @@ could be working on, then there are a bunch of `FIXME`s all over this package.
|
|||||||
Just pick one and fix it :-)
|
Just pick one and fix it :-)
|
||||||
|
|
||||||
```
|
```
|
||||||
$ git clone https://github.com/math2001/MarkdownLivePreview
|
$ git clone https://git.0x42.cloud/christian.morpurgo/MarkdownLivePreview
|
||||||
$ cd MarkdownLivePreview
|
$ cd MarkdownLivePreview
|
||||||
$ grep -R FIXME
|
$ grep -R FIXME
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user