From 1c69060c5b4fa16010e9bc79d7b462fbeb5f480e Mon Sep 17 00:00:00 2001 From: Antonio <54741970+Antonio-R1@users.noreply.github.com> Date: Sun, 13 Nov 2022 01:35:07 +0100 Subject: [PATCH 1/2] Add a `python3-markdown` plugin for including source line position attributes --- markdown_preview/source_lines.py | 60 ++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 markdown_preview/source_lines.py diff --git a/markdown_preview/source_lines.py b/markdown_preview/source_lines.py new file mode 100644 index 0000000..abbf097 --- /dev/null +++ b/markdown_preview/source_lines.py @@ -0,0 +1,60 @@ +# python3 -m markdown -x source_lines:SourceLinesExtension example.md + +from markdown.extensions import Extension +from markdown.blockparser import BlockParser +from markdown.blockprocessors import BlockProcessor +import re +import types +import xml.etree.ElementTree as etree + +def set_source_position(block, line): + class BlockWithSourcePosition(type(block)): + def set_source_position(self, line): + self.source_line = line + return self + return BlockWithSourcePosition(block).set_source_position(line) + +def parseBlocks(self, parent, _blocks): + if not BlockParser.first_run: + self._parseBlocks(parent, _blocks) + return + BlockParser.first_run = False + source_line = 1 + blocks = [] + for b in _blocks: + new_lines = re.search('[^\n]|$', b, re.MULTILINE).start() + blocks.append(set_source_position(b, source_line+new_lines)) + source_line += b.count('\n')+2 + self._parseBlocks(parent, blocks) + +def block_processor_run(self, parent, blocks): + if len(parent)>0 and not parent[-1].get("source-line"): + parent[-1].set("source-line", str(BlockParser.source_line)) + setattr(BlockParser, "source_line", None) + try: + setattr(BlockParser, "source_line", blocks[0].source_line) + except Exception: + pass + result = self._run(parent, blocks) + if len(parent)>0 and not parent[-1].get("source-line") and BlockParser.source_line: + parent[-1].set("source-line", str(BlockParser.source_line)) + setattr(BlockParser, "source_line", None) + return result + +_parseBlocks = BlockParser.parseBlocks +BlockProcessor_run = BlockProcessor.run + +class SourceLinesExtension(Extension): + def extendMarkdown(self, md): + BlockParser._parseBlocks = _parseBlocks + BlockParser.parseBlocks = parseBlocks + BlockParser.first_run = True + md.parser._parseBlocks = types.MethodType(BlockParser._parseBlocks, md.parser) + md.parser.parseBlocks = types.MethodType(parseBlocks, md.parser) + setattr(BlockParser, "source_line", None) + + BlockProcessor._run = BlockProcessor_run + BlockProcessor.run = block_processor_run + for b in md.parser.blockprocessors: + b._run = b.run + b.run = types.MethodType(block_processor_run, b) From 0401dd85a604838cefe3205ac4671128d34f42bf Mon Sep 17 00:00:00 2001 From: Antonio <54741970+Antonio-R1@users.noreply.github.com> Date: Sun, 13 Nov 2022 01:41:43 +0100 Subject: [PATCH 2/2] Add support for syncing the scroll position with the cursor position --- markdown_preview/main_container.py | 7 +- markdown_preview/prefs/prefs_dialog.py | 2 +- markdown_preview/webview_manager.py | 76 ++++++++++++++++++- ...gedit.plugins.markdown_preview.gschema.xml | 2 +- 4 files changed, 82 insertions(+), 5 deletions(-) diff --git a/markdown_preview/main_container.py b/markdown_preview/main_container.py index 3996efb..ca306ab 100644 --- a/markdown_preview/main_container.py +++ b/markdown_preview/main_container.py @@ -6,6 +6,7 @@ from .utils import get_backends_dict, init_gettext, recognize_format from .webview_manager import MdWebViewManager from .constants import MD_PREVIEW_KEY_BASE, MARKDOWN_SPLITTERS, BASE_TEMP_NAME +from .source_lines import SourceLinesExtension AVAILABLE_BACKENDS = get_backends_dict() if AVAILABLE_BACKENDS['p3md']: @@ -278,6 +279,9 @@ def _on_reload_unsafe(self): return start, end = doc.get_bounds() unsaved_text = doc.get_text(start, end, True) + cursor_position = doc.get_property("cursor-position") + text_iter = doc.get_iter_at_offset(cursor_position) + self._webview_manager.set_cursor_position (text_iter.get_line()+1, text_iter.get_line_offset()+1) unsaved_text = unsaved_text.encode('utf-8').decode() if self._file_format == 'html': html_content = self.get_html_from_html(unsaved_text) @@ -343,9 +347,10 @@ def get_html_from_p3md(self, unsaved_text): 'rel="stylesheet" href="' + self._stylesheet + '" />' post_string = '' + extensions = [SourceLinesExtension()]+self._p3md_extensions html_string = markdown.markdown( unsaved_text, - extensions=self._p3md_extensions + extensions=extensions ) html_content = pre_string + html_string + post_string return html_content diff --git a/markdown_preview/prefs/prefs_dialog.py b/markdown_preview/prefs/prefs_dialog.py index 8ac4d93..8871db4 100644 --- a/markdown_preview/prefs/prefs_dialog.py +++ b/markdown_preview/prefs/prefs_dialog.py @@ -160,7 +160,7 @@ def set_command_for_format(self, output_format): return command = 'pandoc $INPUT_FILE %s' - options = '--metadata pagetitle=Preview' + options = '-f commonmark+sourcepos --metadata pagetitle=Preview' accept_css = True # TODO........ diff --git a/markdown_preview/webview_manager.py b/markdown_preview/webview_manager.py index 1321c34..f4152b7 100644 --- a/markdown_preview/webview_manager.py +++ b/markdown_preview/webview_manager.py @@ -9,12 +9,79 @@ _ = init_gettext() +SCRIPT_SCROLL_TO_CURSOR = """ +window.document.body.scrollTop = %d; +var cursorRow = %d; +var cursorColumn = %d; +function setCursorPositionPandoc () { + var dataPosElements = window.document.querySelectorAll("[data-pos]"); + var dataPosRegExp = new RegExp(".*@(?[0-9]*):(?[0-9]*)-(?[0-9]*):(?[0-9]*)"); + var values = ""; + for (let e of dataPosElements) { + var dataPos = e.getAttribute("data-pos"); + var matches = dataPosRegExp.exec(dataPos, "g"); + + if (!matches) { + continue; + } + + values += cursorRow+" "+cursorColumn+"\\n"; + values += startRow+" "+startColumn+" "+endRow+" "+endColumn+"\\n"; + + var startRow = parseInt(matches.groups.start_row); + var startColumn = parseInt(matches.groups.start_column); + var endRow = parseInt(matches.groups.end_row); + var endColumn = parseInt(matches.groups.end_column); + if (startRow<=cursorRow && cursorRow<=endRow && + (startRow!==cursorRow || startColumn<=cursorColumn) && + (endRow!==cursorRow || cursorColumn<=endColumn)) { + e.scrollIntoViewIfNeeded(); + return true; + } + } + return false; +} + +function setCursorPositionPythonMarkdown() { + var sourceLineElements = window.document.querySelectorAll("[source-line]"); + var highestLowerBoundLine = -1; + var highestLowerBoundElement = null; + for (let e of sourceLineElements) { + let sourceLine = parseInt(e.getAttribute("source-line")); + if (sourceLine<=cursorRow && highestLowerBoundLine - ['pandoc', '-s', '$INPUT_FILE', '--metadata', 'pagetitle=Preview'] + ['pandoc', '-f', 'commonmark+sourcepos', '-s', '$INPUT_FILE', '--metadata', 'pagetitle=Preview'] Pandoc rendering command line The command line used for pandoc rendering. It has to return HTML code