From 7258088b5690d3929389d480e042d96b2eaa59e9 Mon Sep 17 00:00:00 2001 From: Luck Date: Fri, 21 May 2021 11:56:05 +0100 Subject: [PATCH] line highlight functionality --- src/components/EditorTextArea.js | 108 +++++++++++++++++++++++++++---- src/style/themes.js | 4 ++ 2 files changed, 99 insertions(+), 13 deletions(-) diff --git a/src/components/EditorTextArea.js b/src/components/EditorTextArea.js index 9c26796..5507aa0 100644 --- a/src/components/EditorTextArea.js +++ b/src/components/EditorTextArea.js @@ -1,17 +1,27 @@ -import { useState, useRef } from 'react'; +import { useState, useEffect, useRef } from 'react'; import styled from 'styled-components'; import ReactEditor from 'react-simple-code-editor'; import EditorPrismStyle from './EditorPrismStyle'; import { getHighlighter } from '../util/highlighting'; export default function EditorTextArea({ code, setCode, language, fontSize }) { + const [isSelected, toggleSelected] = useSelectedLine(); const highlight = getHighlighter(language); function highlightWithLineNumbers(input, grammar) { return highlight(input, grammar) .split('\n') - .map((line, i) => `${i + 1}${line}`) - .join('\n'); + .map((line, i) => ( + + + + + )) + .reduce((prev, curr) => [prev, '\n', curr]); } const autoBracketState = useState(null); @@ -38,6 +48,18 @@ export default function EditorTextArea({ code, setCode, language, fontSize }) { ); } +const LineNumber = ({ lineNo, selected, toggleSelected }) => { + function click(e) { + toggleSelected(lineNo, e.shiftKey); + } + + return selected ? ( + {lineNo} + ) : ( + {lineNo} + ); +}; + const StyledReactEditor = styled(ReactEditor)` counter-reset: line; font-size: ${props => props.size}px; @@ -52,18 +74,78 @@ const StyledReactEditor = styled(ReactEditor)` pre { padding-left: 60px !important; } - - .editorLineNumber { - position: absolute; - left: 0px; - color: ${props => props.theme.editor.lineNumber}; - text-align: right; - width: 40px; - font-weight: 100; - user-select: none; - } `; +const PlainLineNumber = styled.span` + position: absolute; + left: 0px; + color: ${props => props.theme.editor.lineNumber}; + text-align: right; + width: 40px; + font-weight: 100; + user-select: none; + + // override parent
+  pointer-events: auto;
+  cursor: pointer;
+`;
+
+const HighlightedLineNumber = styled(PlainLineNumber)`
+  color: ${props => props.theme.editor.lineNumberHl};
+  background-color: ${props => props.theme.editor.lineNumberHlBackground};
+  font-weight: bold;
+`;
+
+function useSelectedLine() {
+  // extract highlighted lines from window hash
+  const [selected, setSelected] = useState(() => {
+    const hash = window.location.hash;
+    if (/^#L\d+(-\d+)?$/.test(hash)) {
+      const [start, end] = hash.substring(2).split('-').map(Number);
+      return [start, isNaN(end) ? -1 : end];
+    } else {
+      return [-1, -1];
+    }
+  });
+
+  // update window hash when a new line is highlighted
+  useEffect(() => {
+    if (selected[0] !== -1) {
+      if (selected[1] !== -1) {
+        const [s, e] = selected.sort();
+        window.location.hash = `#L${s}-${e}`;
+      } else {
+        window.location.hash = `#L${selected[0]}`;
+      }
+    }
+  }, [selected]);
+
+  // toggle the highlighting for a given line
+  function toggleSelected(lineNo, shift) {
+    if (selected[0] === lineNo && selected[1] === -1) {
+      setSelected([-1, -1]);
+    } else if (selected[0] === -1 || !shift) {
+      setSelected([lineNo, -1]);
+    } else {
+      setSelected([selected[0], lineNo]);
+    }
+  }
+
+  // should a line be highlighted in the viewer?
+  function isSelected(lineNo) {
+    if (selected[0] === -1) {
+      return false;
+    }
+    if (selected[1] === -1) {
+      return selected[0] === lineNo;
+    }
+
+    return lineNo >= Math.min(...selected) && lineNo <= Math.max(...selected);
+  }
+
+  return [isSelected, toggleSelected];
+}
+
 const KEYCODE_ENTER = 13;
 const KEYCODE_PARENS = 57;
 const KEYCODE_PARENS_CLOSE = 48;
diff --git a/src/style/themes.js b/src/style/themes.js
index 9a811d8..eee9af4 100644
--- a/src/style/themes.js
+++ b/src/style/themes.js
@@ -7,6 +7,8 @@ const themes = {
     editor: {
       background: 'none',
       lineNumber: '#cccccc',
+      lineNumberHl: 'black',
+      lineNumberHlBackground: '#e0f6ff',
       primary: 'black',
       selection: '#b3d4fc',
       comment: 'slategray',
@@ -33,6 +35,8 @@ const themes = {
     editor: {
       background: '#041f29',
       lineNumber: '#81969A',
+      lineNumberHl: '#fff',
+      lineNumberHlBackground: '#0e303e',
       primary: '#E0E2E4',
       selection: '#E0E2E4',
       comment: '#7D8C93',