reiji-h 2 лет назад
Родитель
Сommit
92bf930f45

+ 25 - 113
apps/app/src/components/PageEditor/PageEditor.tsx

@@ -60,11 +60,9 @@ import loggerFactory from '~/utils/logger';
 // import Editor from './Editor';
 // import Editor from './Editor';
 import EditorNavbarBottom from './EditorNavbarBottom';
 import EditorNavbarBottom from './EditorNavbarBottom';
 import Preview from './Preview';
 import Preview from './Preview';
-import scrollSyncHelper from './ScrollSyncHelper';
-import { scrollEditor, scrollPreview } from './ScrollSyncHelperTest';
+import { scrollEditor, scrollPreview } from './ScrollSyncHelper';
 
 
 import '@growi/editor/dist/style.css';
 import '@growi/editor/dist/style.css';
-import { preview } from 'vite';
 
 
 
 
 const logger = loggerFactory('growi:PageEditor');
 const logger = loggerFactory('growi:PageEditor');
@@ -75,13 +73,11 @@ declare global {
   var globalEmitter: EventEmitter;
   var globalEmitter: EventEmitter;
 }
 }
 
 
-
 // for scrolling
 // for scrolling
-const lastScrolledDateWithCursor: Date | null = null;
+let lastScrolledDateWithCursor: Date | null = null;
 let isOriginOfScrollSyncEditor = false;
 let isOriginOfScrollSyncEditor = false;
 let isOriginOfScrollSyncPreview = false;
 let isOriginOfScrollSyncPreview = false;
 
 
-
 type Props = {
 type Props = {
   visibility?: boolean,
   visibility?: boolean,
 }
 }
@@ -358,112 +354,46 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
     return AcceptedUploadFileType.IMAGE;
     return AcceptedUploadFileType.IMAGE;
   }, [isUploadAllFileAllowed, isUploadEnabled]);
   }, [isUploadAllFileAllowed, isUploadEnabled]);
 
 
-  const scrollPreviewByEditorLine = useCallback((line: number) => {
-    if (previewRef.current == null) {
-      return;
-    }
 
 
-    // prevent circular invocation
-    if (isOriginOfScrollSyncPreview) {
-      isOriginOfScrollSyncPreview = false; // turn off the flag
+  const scrollEditorHandler = useCallback(() => {
+    if (codeMirrorEditor?.view?.scrollDOM == null || previewRef.current == null) {
       return;
       return;
     }
     }
 
 
-    // turn on the flag
-    isOriginOfScrollSyncEditor = true;
-    scrollSyncHelper.scrollPreview(previewRef.current, line);
-  }, []);
-  const scrollPreviewByEditorLineWithThrottle = useMemo(() => throttle(20, scrollPreviewByEditorLine), [scrollPreviewByEditorLine]);
-
-  /**
-   * the scroll event handler from codemirror
-   * @param {any} data {left, top, width, height, clientWidth, clientHeight} object that represents the current scroll position,
-   *                    the size of the scrollable area, and the size of the visible area (minus scrollbars).
-   *                    And data.line is also available that is added by Editor component
-   * @see https://codemirror.net/doc/manual.html#events
-   */
-  const editorScrolledHandler = useCallback(({ line }: { line: number }) => {
-    // prevent scrolling
-    //   if the elapsed time from last scroll with cursor is shorter than 40ms
     const now = new Date();
     const now = new Date();
-    if (lastScrolledDateWithCursor != null && now.getTime() - lastScrolledDateWithCursor.getTime() < 40) {
-      return;
-    }
-
-    scrollPreviewByEditorLineWithThrottle(line);
-  }, [scrollPreviewByEditorLineWithThrottle]);
-
-  /**
-   * scroll Preview element by cursor moving
-   * @param {number} line
-   */
-  const scrollPreviewByCursorMoving = useCallback((line: number) => {
-    if (previewRef.current == null) {
+    if (lastScrolledDateWithCursor != null && now.getTime() - lastScrolledDateWithCursor.getTime() < 50) {
+      lastScrolledDateWithCursor = now;
       return;
       return;
     }
     }
 
 
-    // prevent circular invocation
     if (isOriginOfScrollSyncPreview) {
     if (isOriginOfScrollSyncPreview) {
-      isOriginOfScrollSyncPreview = false; // turn off the flag
+      isOriginOfScrollSyncPreview = false;
       return;
       return;
     }
     }
 
 
-    // turn on the flag
     isOriginOfScrollSyncEditor = true;
     isOriginOfScrollSyncEditor = true;
-    if (previewRef.current != null) {
-      scrollSyncHelper.scrollPreviewToRevealOverflowing(previewRef.current, line);
-    }
-  }, []);
-  const scrollPreviewByCursorMovingWithThrottle = useMemo(() => throttle(20, scrollPreviewByCursorMoving), [scrollPreviewByCursorMoving]);
-
-  /**
-   * the scroll event handler from codemirror
-   * @param {number} line
-   * @see https://codemirror.net/doc/manual.html#events
-   */
-  // const editorScrollCursorIntoViewHandler = useCallback((line: number) => {
-  //   // record date
-  //   lastScrolledDateWithCursor = new Date();
-  //   scrollPreviewByCursorMovingWithThrottle(line);
-  // }, [scrollPreviewByCursorMovingWithThrottle]);
-
-  /**
-   * scroll Editor component by scroll event of Preview component
-   * @param {number} offset
-   */
-  // const scrollEditorByPreviewScroll = useCallback((offset: number) => {
-  //   if (codeMirrorEditorContainerRef.current == null || previewRef.current == null) {
-  //     return;
-  //   }
-
-  //   // prevent circular invocation
-  //   if (isOriginOfScrollSyncEditor) {
-  //     isOriginOfScrollSyncEditor = false; // turn off the flag
-  //     return;
-  //   }
-
-  //   // turn on the flag
-  //   // eslint-disable-next-line @typescript-eslint/no-unused-vars
-  //   isOriginOfScrollSyncPreview = true;
-
-  //   scrollSyncHelper.scrollEditor(codeMirrorEditorContainerRef.current, previewRef.current, offset);
-  // }, []);
-  // const scrollEditorByPreviewScrollWithThrottle = useMemo(() => throttle(20, scrollEditorByPreviewScroll), [scrollEditorByPreviewScroll]);
-
-  const scrollEditorHandler = useCallback(() => {
-    console.log('ScrollEditor!');
-    if (codeMirrorEditor?.view?.scrollDOM != null && previewRef.current != null) {
-      scrollEditor(codeMirrorEditor.view.scrollDOM, previewRef.current);
-    }
+    scrollEditor(codeMirrorEditor.view.scrollDOM, previewRef.current);
   }, [codeMirrorEditor, previewRef]);
   }, [codeMirrorEditor, previewRef]);
 
 
-  const scrollEditorHandlerThrottle = useMemo(() => throttle(150, scrollEditorHandler), [scrollEditorHandler]);
+  const scrollEditorHandlerThrottle = useMemo(() => throttle(25, scrollEditorHandler), [scrollEditorHandler]);
 
 
   const scrollPreviewHandler = useCallback(() => {
   const scrollPreviewHandler = useCallback(() => {
-    console.log('ScrollPreview!');
-    if (codeMirrorEditor?.view?.scrollDOM != null && previewRef.current != null) {
-      scrollPreview(codeMirrorEditor.view.scrollDOM, previewRef.current);
+    if (codeMirrorEditor?.view?.scrollDOM == null || previewRef.current == null) {
+      return;
     }
     }
+
+    const now = new Date();
+    if (lastScrolledDateWithCursor != null && now.getTime() - lastScrolledDateWithCursor.getTime() < 50) {
+      lastScrolledDateWithCursor = now;
+      return;
+    }
+    if (isOriginOfScrollSyncEditor) {
+      isOriginOfScrollSyncEditor = false;
+      return;
+    }
+
+    isOriginOfScrollSyncPreview = true;
+    scrollPreview(codeMirrorEditor.view.scrollDOM, previewRef.current);
   }, [codeMirrorEditor, previewRef]);
   }, [codeMirrorEditor, previewRef]);
 
 
   const scrollPreviewHandlerThrottle = useMemo(() => throttle(150, scrollPreviewHandler), [scrollPreviewHandler]);
   const scrollPreviewHandlerThrottle = useMemo(() => throttle(150, scrollPreviewHandler), [scrollPreviewHandler]);
@@ -501,21 +431,6 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
     codeMirrorEditor?.setCaretLine();
     codeMirrorEditor?.setCaretLine();
   }, [codeMirrorEditor]);
   }, [codeMirrorEditor]);
 
 
-  // set handler to set caret line
-  useEffect(() => {
-    const handler = (line) => {
-      codeMirrorEditor?.setCaretLine(line);
-
-      if (previewRef.current != null) {
-        scrollSyncHelper.scrollPreview(previewRef.current, line);
-      }
-    };
-    globalEmitter.on('setCaretLine', handler);
-
-    return function cleanup() {
-      globalEmitter.removeListener('setCaretLine', handler);
-    };
-  }, [codeMirrorEditor]);
 
 
   // set handler to save and return to View
   // set handler to save and return to View
   useEffect(() => {
   useEffect(() => {
@@ -600,16 +515,13 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
             acceptedFileType={acceptedFileType}
             acceptedFileType={acceptedFileType}
           />
           />
         </div>
         </div>
-        <div ref={previewRef} className="page-editor-preview-container flex-expand-vert d-none d-lg-flex">
+        <div ref={previewRef} onScroll={scrollPreviewHandlerThrottle} className="page-editor-preview-container flex-expand-vert d-none d-lg-flex">
           <Preview
           <Preview
             rendererOptions={rendererOptions}
             rendererOptions={rendererOptions}
             markdown={markdownToPreview}
             markdown={markdownToPreview}
             pagePath={currentPagePath}
             pagePath={currentPagePath}
             expandContentWidth={shouldExpandContent}
             expandContentWidth={shouldExpandContent}
             pastEnd={previewRef.current?.getBoundingClientRect().height}
             pastEnd={previewRef.current?.getBoundingClientRect().height}
-            // TODO: implement
-            // refs: https://redmine.weseek.co.jp/issues/126519
-            // onScroll={(offset) => { console.log('Preview'); console.log(offset) }}
           />
           />
         </div>
         </div>
         {/*
         {/*

+ 0 - 206
apps/app/src/components/PageEditor/ScrollSyncHelper.js

@@ -1,206 +0,0 @@
-/**
- * This class is copied from Microsoft/vscode repository
- * @see https://github.com/Microsoft/vscode/blob/0532a3429a18688a0c086a4212e7e5b4888b2a48/extensions/markdown/media/main.js
- */
-class ScrollSyncHelper {
-
-  /**
-   * @typedef {{ element: Element, line: number }} CodeLineElement
-   */
-
-  getCodeLineElements(parentElement) {
-    /** @type {CodeLineElement[]} */
-    let elements;
-    if (!elements) {
-      elements = Array.prototype.map.call(
-        parentElement.getElementsByClassName('has-data-line'),
-        (element) => {
-          const line = +element.getAttribute('data-line');
-          return { element, line };
-        },
-      )
-        .filter((x) => { return !Number.isNaN(x.line) });
-    }
-    return elements;
-  }
-
-  /**
-   * Find the html elements that map to a specific target line in the editor.
-   *
-   * If an exact match, returns a single element. If the line is between elements,
-   * returns the element prior to and the element after the given line.
-   *
-   * @param {Element} element
-   * @param {number} targetLine
-   *
-   * @returns {{ previous: CodeLineElement, next?: CodeLineElement }}
-   */
-  getElementsForSourceLine(element, targetLine) {
-    const lines = this.getCodeLineElements(element);
-    let previous = lines[0] || null;
-    for (const entry of lines) {
-      if (entry.line === targetLine) {
-        return { previous: entry, next: null };
-      }
-      if (entry.line > targetLine) {
-        return { previous, next: entry };
-      }
-      previous = entry;
-    }
-    return { previous };
-  }
-
-  /**
-   * Find the html elements that are at a specific pixel offset on the page.
-   *
-   * @param {Element} parentElement
-   * @param {number} offset
-   *
-   * @returns {{ previous: CodeLineElement, next?: CodeLineElement }}
-   */
-  getLineElementsAtPageOffset(parentElement, offset) {
-    const lines = this.getCodeLineElements(parentElement);
-
-    const position = offset - parentElement.scrollTop + this.getParentElementOffset(parentElement);
-
-    let lo = -1;
-    let hi = lines.length - 1;
-    while (lo + 1 < hi) {
-      const mid = Math.floor((lo + hi) / 2);
-      const bounds = lines[mid].element.getBoundingClientRect();
-      if (bounds.top + bounds.height >= position) {
-        hi = mid;
-      }
-      else {
-        lo = mid;
-      }
-    }
-
-    const hiElement = lines[hi];
-
-    if (hiElement == null) {
-      return {};
-    }
-
-    if (hi >= 1 && hiElement.element.getBoundingClientRect().top > position) {
-      const loElement = lines[lo];
-      const bounds = loElement.element.getBoundingClientRect();
-      const previous = { element: loElement.element, line: loElement.line };
-      if (bounds.height > 0) {
-        previous.line += (position - bounds.top) / (bounds.height);
-      }
-      const next = { element: hiElement.element, line: hiElement.line, fractional: 0 };
-      return { previous, next };
-    }
-
-    const bounds = hiElement.element.getBoundingClientRect();
-    const previous = { element: hiElement.element, line: hiElement.line + (position - bounds.top) / (bounds.height) };
-    return { previous };
-  }
-
-  getEditorLineNumberForPageOffset(parentElement, offset) {
-    const { previous, next } = this.getLineElementsAtPageOffset(parentElement, offset);
-    if (previous != null) {
-      if (next) {
-        const betweenProgress = (
-          offset - parentElement.scrollTop - previous.element.getBoundingClientRect().top)
-          / (next.element.getBoundingClientRect().top - previous.element.getBoundingClientRect().top);
-        return previous.line + betweenProgress * (next.line - previous.line);
-      }
-
-      return previous.line;
-
-    }
-    return null;
-  }
-
-  /**
-   * return the sum of the offset position of parent element and paddingTop
-   * @param {Element} parentElement
-   */
-  getParentElementOffset(parentElement) {
-    const offsetY = parentElement.getBoundingClientRect().top;
-    // get paddingTop
-    const style = window.getComputedStyle(parentElement, null);
-    const paddingTop = +(style.paddingTop.replace('px', ''));
-
-    return offsetY + paddingTop;
-  }
-
-  /**
-   * Attempt to scroll preview element for a source line in the editor.
-   *
-   * @param {Element} previewElement
-   * @param {number} line
-   */
-  scrollPreview(previewElement, line) {
-    const { previous, next } = this.getElementsForSourceLine(previewElement, line);
-    if (previous) {
-      let scrollTo = 0;
-      if (next) {
-        // Between two elements. Go to percentage offset between them.
-        const betweenProgress = (line - previous.line) / (next.line - previous.line);
-        const elementOffset = next.element.getBoundingClientRect().top - previous.element.getBoundingClientRect().top;
-        scrollTo = previous.element.getBoundingClientRect().top + betweenProgress * elementOffset;
-      }
-      else {
-        scrollTo = previous.element.getBoundingClientRect().top;
-      }
-
-      scrollTo -= this.getParentElementOffset(previewElement);
-
-      previewElement.scrollTop += scrollTo;
-    }
-  }
-
-  /**
-   * Attempt to reveal the element that is overflowing from previewElement.
-   *
-   * @param {Element} previewElement
-   * @param {number} line
-   */
-  scrollPreviewToRevealOverflowing(previewElement, line) {
-    // eslint-disable-next-line no-unused-vars
-    const { previous, next } = this.getElementsForSourceLine(previewElement, line);
-    if (previous) {
-      const parentElementOffset = this.getParentElementOffset(previewElement);
-      const prevElmTop = previous.element.getBoundingClientRect().top - parentElementOffset;
-      const prevElmBottom = previous.element.getBoundingClientRect().bottom - parentElementOffset;
-
-      let scrollTo = null;
-      if (prevElmTop < 0) {
-        // set the top of 'previous.element' to the top of 'previewElement'
-        scrollTo = previewElement.scrollTop + prevElmTop;
-      }
-      else if (prevElmBottom > previewElement.clientHeight) {
-        // set the bottom of 'previous.element' to the bottom of 'previewElement'
-        scrollTo = previewElement.scrollTop + prevElmBottom - previewElement.clientHeight + 20;
-      }
-
-      if (scrollTo == null) {
-        return;
-      }
-
-      previewElement.scrollTop = scrollTo;
-    }
-  }
-
-  /**
-   * Attempt to scroll Editor component for the offset of the element in the Preview component.
-   *
-   * @param {Editor} editor
-   * @param {Element} previewElement
-   * @param {number} offset
-   */
-  scrollEditor(editor, previewElement, offset) {
-    let line = this.getEditorLineNumberForPageOffset(previewElement, offset);
-    line = Math.floor(line);
-    editor.setScrollTopByLine(line);
-  }
-
-}
-
-// singleton pattern
-const instance = new ScrollSyncHelper();
-Object.freeze(instance);
-export default instance;

+ 152 - 0
apps/app/src/components/PageEditor/ScrollSyncHelper.ts

@@ -0,0 +1,152 @@
+let defaultTop = 0;
+const padding = 5;
+
+const setDefaultTop = (top: number): void => {
+  defaultTop = top;
+};
+const getDefaultTop = (): number => {
+  return defaultTop + padding;
+};
+
+
+const getDataLine = (element: Element | null): number => {
+  return element ? +(element.getAttribute('data-line') ?? '0') - 1 : 0;
+};
+
+const getEditorElements = (editorRootElement: HTMLElement): Array<Element> => {
+  return Array.from(editorRootElement.getElementsByClassName('cm-line'))
+    .filter((element) => { return !Number.isNaN(element.getAttribute('data-line')) });
+};
+
+const getPreviewElements = (previewRootElement: HTMLElement): Array<Element> => {
+  return Array.from(previewRootElement.getElementsByClassName('has-data-line'))
+    .filter((element) => { return !Number.isNaN(element.getAttribute('data-line')) });
+};
+
+
+const elementBinarySearch = (list: Array<Element>, fn: (index: number) => boolean): number => {
+  let ok = 0;
+  let ng = list.length;
+  while (ok + 1 < ng) {
+    const mid = Math.floor((ok + ng) / 2);
+    if (fn(mid)) {
+      ok = mid;
+    }
+    else {
+      ng = mid;
+    }
+  }
+  return ok;
+};
+
+const findTopElementIndex = (elements: Array<Element>): number => {
+
+  const find = (index: number): boolean => {
+    return elements[index].getBoundingClientRect().top < getDefaultTop();
+  };
+
+  return elementBinarySearch(elements, find);
+};
+
+const findElementIndexFromDataLine = (previewElements: Array<Element>, dataline: number): number => {
+
+  const find = (index: number): boolean => {
+    return getDataLine(previewElements[index]) <= dataline;
+  };
+
+  return elementBinarySearch(previewElements, find);
+};
+
+
+type SourceElement = {
+  start: DOMRect,
+  top: DOMRect,
+  next: DOMRect | undefined,
+}
+
+type TargetElement = {
+  start: DOMRect,
+  next: DOMRect | undefined,
+}
+
+const calcScrollElementToTop = (element: Element): number => {
+  return element.getBoundingClientRect().top - getDefaultTop();
+};
+
+const calcScorllElementByRatio = (sourceElement: SourceElement, targetElement: TargetElement): number => {
+  if (sourceElement.start === sourceElement.next || sourceElement.next == null || targetElement.next == null) {
+    return 0;
+  }
+  const sourceAllHeight = sourceElement.next.top - sourceElement.start.top;
+  const sourceOutHeight = sourceElement.top.top - sourceElement.start.top;
+  const sourceTopHeight = getDefaultTop() - sourceElement.top.top;
+  const sourceRaito = (sourceOutHeight + sourceTopHeight) / sourceAllHeight;
+
+  const targetAllHeight = targetElement.next.top - targetElement.start.top;
+
+  return targetAllHeight * sourceRaito;
+};
+
+
+export const scrollEditor = (editorRootElement: HTMLElement, previewRootElement: HTMLElement): void => {
+
+  setDefaultTop(editorRootElement.getBoundingClientRect().top);
+
+  const editorElements = getEditorElements(editorRootElement);
+  const previewElements = getPreviewElements(previewRootElement);
+
+  const topEditorElementIndex = findTopElementIndex(editorElements);
+  const topPreviewElementIndex = findElementIndexFromDataLine(previewElements, getDataLine(editorElements[topEditorElementIndex]));
+
+  const startEditorElementIndex = findElementIndexFromDataLine(editorElements, getDataLine(previewElements[topPreviewElementIndex]));
+  const nextEditorElementIndex = findElementIndexFromDataLine(editorElements, getDataLine(previewElements[topPreviewElementIndex + 1]));
+
+  let newScrollTop = previewRootElement.scrollTop;
+
+  newScrollTop += calcScrollElementToTop(previewElements[topPreviewElementIndex]);
+  newScrollTop += calcScorllElementByRatio(
+    {
+      start: editorElements[startEditorElementIndex].getBoundingClientRect(),
+      top: editorElements[topEditorElementIndex].getBoundingClientRect(),
+      next: editorElements[nextEditorElementIndex]?.getBoundingClientRect(),
+    },
+    {
+      start: previewElements[topPreviewElementIndex].getBoundingClientRect(),
+      next: previewElements[topPreviewElementIndex + 1]?.getBoundingClientRect(),
+    },
+  );
+
+  previewRootElement.scrollTop = newScrollTop;
+
+};
+
+export const scrollPreview = (editorRootElement: HTMLElement, previewRootElement: HTMLElement): void => {
+
+  setDefaultTop(previewRootElement.getBoundingClientRect().y);
+
+  const previewElements = getPreviewElements(previewRootElement);
+  const editorElements = getEditorElements(editorRootElement);
+
+  const topPreviewElementIndex = findTopElementIndex(previewElements);
+
+  const startEditorElementIndex = findElementIndexFromDataLine(editorElements, getDataLine(previewElements[topPreviewElementIndex]));
+  const nextEditorElementIndex = findElementIndexFromDataLine(editorElements, getDataLine(previewElements[topPreviewElementIndex + 1]));
+
+  let newScrollTop = editorRootElement.scrollTop;
+
+  newScrollTop += calcScrollElementToTop(editorElements[startEditorElementIndex]);
+  newScrollTop += calcScorllElementByRatio(
+    {
+      start: previewElements[topPreviewElementIndex].getBoundingClientRect(),
+      top: previewElements[topPreviewElementIndex].getBoundingClientRect(),
+      next: previewElements[topPreviewElementIndex + 1]?.getBoundingClientRect(),
+    },
+    {
+      start: editorElements[startEditorElementIndex].getBoundingClientRect(),
+      next: editorElements[nextEditorElementIndex]?.getBoundingClientRect(),
+    },
+  );
+
+  editorRootElement.scrollTop = newScrollTop;
+
+};

+ 0 - 151
apps/app/src/components/PageEditor/ScrollSyncHelperTest.ts

@@ -1,151 +0,0 @@
-// console.log(previewRootElement.scrollTop);
-// for (const element of previewElements) {
-//   console.log(element.getBoundingClientRect());
-// }
-
-// element.getBoundingClientRect
-// {
-//     "x": 85,
-//     "y": 907.203125,
-//     "width": 650.5,
-//     "height": 22.390625,
-//     "top": 907.203125,
-//     "right": 735.5,
-//     "bottom": 929.59375,
-//     "left": 85
-// }
-
-// fn return true when arg nubmer's comparison is lower.
-// list: [1, 3, 4, 6, 7, 9]
-// fn: (args) => {return list[args] < 5}
-// output: 4
-
-let defaultTop = 0;
-const padding = 5;
-
-const getDataLineIndex = (previewElement: Element): number => {
-  return +(previewElement.getAttribute('data-line') ?? '0') - 1;
-};
-
-const getEditorElements = (editorRootElement: HTMLElement): Array<Element> => {
-  return Array.from(editorRootElement.getElementsByClassName('cm-line'));
-};
-
-const getPreviewElements = (previewRootElement: HTMLElement): Array<Element> => {
-  return Array.from(previewRootElement.getElementsByClassName('has-data-line'))
-    .filter((element) => { return !Number.isNaN(element.getAttribute('data-line')) });
-};
-
-const elementBinarySearch = (list: Array<Element>, fn: (index: number) => boolean): number => {
-  let ok = 0;
-  let ng = list.length;
-  while (ok + 1 < ng) {
-    const mid = Math.floor((ok + ng) / 2);
-    if (fn(mid)) {
-      ok = mid;
-    }
-    else {
-      ng = mid;
-    }
-  }
-  return ok;
-};
-
-const findTopElementIndex = (elements: Array<Element>): number => {
-
-  const find = (index: number): boolean => {
-    return elements[index].getBoundingClientRect().top < defaultTop + padding;
-  };
-
-  return elementBinarySearch(elements, find);
-};
-
-const findPreviewElementIndex = (previewElements: Array<Element>, editorElementLineIndex: number): number => {
-
-  const find = (index: number): boolean => {
-    const data = getDataLineIndex(previewElements[index]);
-    return data <= editorElementLineIndex;
-  };
-
-  return elementBinarySearch(previewElements, find);
-};
-
-const calcScrollElementToTop = (element: Element): number => {
-  return element.getBoundingClientRect().top - (defaultTop + padding);
-};
-
-type SourceElement = {
-  start: Element,
-  top: Element,
-  next: Element | undefined,
-}
-
-type TargetElement = {
-  start: Element,
-  next: Element | undefined,
-}
-
-const calcScorllElementByRatio = (sourceElement: SourceElement, targetElement: TargetElement): number => {
-  if (sourceElement.start === sourceElement.next || sourceElement.next == null || targetElement.next == null) {
-    return 0;
-  }
-  const sourceAllHeight = sourceElement.next.getBoundingClientRect().top - sourceElement.start.getBoundingClientRect().top;
-  const sourceUseHeight = sourceElement.top.getBoundingClientRect().top - sourceElement.start.getBoundingClientRect().top;
-  const sourceTopHeight = defaultTop + padding - sourceElement.top.getBoundingClientRect().top;
-  const sourceRaito = (sourceUseHeight + sourceTopHeight) / sourceAllHeight;
-
-  const targetAllHeight = targetElement.next.getBoundingClientRect().top - targetElement.start.getBoundingClientRect().top;
-
-  return targetAllHeight * sourceRaito;
-};
-
-export const scrollEditor = (editorRootElement: HTMLElement, previewRootElement: HTMLElement): void => {
-
-  defaultTop = editorRootElement.getBoundingClientRect().top;
-
-  const editorElements = getEditorElements(editorRootElement);
-  const previewElements = getPreviewElements(previewRootElement);
-
-  const topEditorElementIndex = findTopElementIndex(editorElements);
-  const targetPreviewElementIndex = findPreviewElementIndex(previewElements, topEditorElementIndex);
-
-  const startEditorElementIndex = getDataLineIndex(previewElements[targetPreviewElementIndex]);
-  const nextEditorElementIndex = getDataLineIndex(previewElements[targetPreviewElementIndex + 1]);
-
-  let newScrollTop = previewRootElement.scrollTop;
-
-  newScrollTop += calcScrollElementToTop(previewElements[targetPreviewElementIndex]);
-  newScrollTop += calcScorllElementByRatio(
-    {
-      start: editorElements[startEditorElementIndex],
-      top: editorElements[topEditorElementIndex],
-      next: editorElements[nextEditorElementIndex],
-    },
-    {
-      start: previewElements[targetPreviewElementIndex],
-      next: previewElements[targetPreviewElementIndex + 1],
-    },
-  );
-
-  previewRootElement.scrollTop = newScrollTop;
-
-};
-
-
-export const scrollPreview = (editorRootElement: HTMLElement, previewRootElement: HTMLElement): void => {
-
-  // defaultTop = previewRootElement.getBoundingClientRect().y;
-
-  // const previewElements = getPreviewElements(previewRootElement);
-
-  // const topPreviewElementIndex = findTopElementIndex(previewElements);
-
-  // console.log(previewRootElement.scrollTop);
-  // console.log(previewElements[topPreviewElementIndex]);
-  // console.log(previewElements[topPreviewElementIndex].getBoundingClientRect());
-
-  // console.log(topPreviewElement);
-  // console.log(topPreviewElement.getBoundingClientRect());
-  // console.log(previewRootElement.scrollTop);
-
-};