extension: code line numbers

This commit is contained in:
dragonwocky 2021-10-20 15:42:22 +11:00
parent 6d40349389
commit b294964b26
6 changed files with 304 additions and 0 deletions

View File

@ -0,0 +1,30 @@
/*
* code line numbers
* (c) 2020 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (c) 2020 CloudHill
* under the MIT license
*/
.notion-code-block.line-numbers {
position: relative;
}
.code-numbered {
padding-left: 48px !important;
}
#code-line-numbers {
font-size: var(--theme--font_code-size);
font-family: var(--theme--font_code);
color: var(--theme--text_ui_info);
background: var(--theme--code-background);
text-align: right;
position: absolute;
left: 0;
right: calc(100% - 48px);
padding-right: 18px;
overflow: hidden;
pointer-events: none;
}
#code-line-numbers:empty {
display: none;
}

View File

@ -0,0 +1,63 @@
/*
* notion-enhancer: code line numbers
* (c) 2020 CloudHill <rl.cloudhill@gmail.com> (https://github.com/CloudHill)
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
.notion-code-block.line-numbers {
position: relative;
}
.code_line_numbers--plain:not(:empty) + div,
.code_line_numbers--background:not(:empty) + div,
.code_line_numbers--border:not(:empty) + div {
padding-left: 64px !important;
}
.code_line_numbers--plain,
.code_line_numbers--background,
.code_line_numbers--border {
position: absolute;
left: 0;
right: calc(100% - 64px);
top: 34px;
bottom: 32px;
padding-right: 27px;
font-size: 85%;
font-family: var(--theme--font_code);
text-align: right;
line-height: 1.5;
opacity: 0.8;
color: var(--theme--text_secondary);
overflow: hidden;
pointer-events: none;
}
.code_line_numbers--plain:empty,
.code_line_numbers--background:empty,
.code_line_numbers--border:empty {
display: none;
}
.code_line_numbers--background::before {
content: '';
position: absolute;
top: 0;
left: 7.25px;
width: calc(100% - 27px);
height: 100%;
display: block;
background-color: var(--theme--bg);
border-radius: 4px;
z-index: -1;
}
.code_line_numbers--border::before {
content: '';
position: absolute;
top: 0;
right: calc(100% - 52px);
width: 2px;
height: 100%;
display: block;
background-color: var(--theme--ui_divider);
}

View File

@ -0,0 +1,62 @@
/*
* notion-enhancer: code line numbers
* (c) 2020 CloudHill <rl.cloudhill@gmail.com> (https://github.com/CloudHill)
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
export default async function ({ web }, db) {
const singleLined = await db.get(['single_lined']),
codeBlockSelector = '.notion-code-block.line-numbers',
numbersClass = `code_line_numbers--${await db.get(['style'])}`,
$temp = web.html`<span></span>`;
const numberCodeBlock = ($codeBlock) => {
const $numbers =
$codeBlock.querySelector(`.${numbersClass}`) ||
web.html`<span class="${numbersClass}">1</span>`;
if (!$codeBlock.contains($numbers)) $codeBlock.prepend($numbers);
const lines = $codeBlock.lastElementChild.innerText.split(/\r\n|\r|\n/),
wordWrap = $codeBlock.lastElementChild.style.wordBreak === 'break-all';
if (lines.at(-1) === '') lines.pop();
let lineNumbers = '';
for (let i = 1; i <= lines.length + 1; i++) {
lineNumbers += `${i}\n`;
if (wordWrap && lines[i - 1]) {
$temp.innerText = lines[i - 1];
$codeBlock.lastElementChild.append($temp);
const height = parseFloat($temp.getBoundingClientRect().height);
$temp.remove();
for (let j = 1; j < height / 20.4; j++) lineNumbers += '\n';
}
}
if (!singleLined && lines.length < 2) lineNumbers = '';
if ($numbers.innerText !== lineNumbers) $numbers.innerText = lineNumbers;
},
numberAllCodeBlocks = () => {
for (const $codeBlock of document.querySelectorAll(codeBlockSelector)) {
numberCodeBlock($codeBlock);
}
},
observeCodeBlocks = (event) => {
const tempEvent = [...event.addedNodes, ...event.removedNodes].includes($temp),
numbersEvent =
event.target.classList.contains(numbersClass) ||
[...event.addedNodes, ...event.removedNodes].some(($node) =>
$node?.classList?.contains(numbersClass)
),
codeEvent = event.target.matches(`${codeBlockSelector}, ${codeBlockSelector} *`);
if (tempEvent || numbersEvent || !codeEvent) return;
let $codeBlock = event.target;
while (!$codeBlock.matches(codeBlockSelector)) $codeBlock = $codeBlock.parentElement;
numberCodeBlock($codeBlock);
};
await web.whenReady();
numberAllCodeBlocks();
web.addDocumentObserver(observeCodeBlocks, [codeBlockSelector]);
}

View File

@ -0,0 +1,113 @@
/*
* code line numbers
* (c) 2020 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (c) 2020 CloudHill
* under the MIT license
*/
'use strict';
const { createElement } = require('../../pkg/helpers.js');
module.exports = {
id: 'd61dc8a7-b195-465b-935f-53eea9efe74e',
tags: ['extension'],
name: 'code line numbers',
desc: 'adds line numbers to code blocks.',
version: '1.2.0',
author: 'CloudHill',
options: [
{
key: 'single_lined',
label: 'show line numbers on single-lined code blocks',
type: 'toggle',
value: false,
},
],
hacks: {
'renderer/preload.js'(store, __exports) {
document.addEventListener('readystatechange', (event) => {
if (document.readyState !== 'complete') return false;
let queue = [];
const observer = new MutationObserver((list, observer) => {
if (!queue.length) requestAnimationFrame(() => handle(queue));
queue.push(...list);
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
const resizeObserver = new ResizeObserver((list, observer) => number(list[0].target));
function handle(list) {
queue = [];
for (let { addedNodes, target } of list) {
const block =
target.querySelector('.line-numbers.notion-code-block') ||
(addedNodes[0]?.classList?.contains('.notion-code-block') &&
addedNodes[0].querySelector('.line-numbers.notion-code-block'));
if (block) {
if (block.dataset.numbered) return;
number(block);
block.dataset.numbered = true;
resizeObserver.observe(block);
}
}
}
function number(block) {
let codeLineNumbers = '';
let numbers = block.querySelector('#code-line-numbers');
if (!numbers) {
numbers = createElement('<span id="code-line-numbers"></span>');
// set size
const blockStyle = window.getComputedStyle(block.children[0]);
numbers.style.top = blockStyle.paddingTop;
numbers.style.bottom = blockStyle.paddingBottom;
block.append(numbers);
// get lineHeight
const temp = createElement('<span>A</span>');
block.firstChild.append(temp);
block.lineHeight = temp.getBoundingClientRect().height;
temp.remove();
}
const lines = block.firstChild.innerText.split(/\r\n|\r|\n/);
if (lines[lines.length - 1] === '') lines.pop();
let lineCounter = 0;
const wordWrap = block.firstChild.style.wordBreak === 'break-all';
for (let i = 0; i < lines.length; i++) {
lineCounter++;
codeLineNumbers += `${lineCounter}\n`;
if (wordWrap) {
const temp = document.createElement('span');
temp.innerText = lines[i];
block.firstChild.append(temp);
const lineHeight = temp.getBoundingClientRect().height;
temp.remove();
for (let j = 1; j < lineHeight / block.lineHeight - 1; j++)
codeLineNumbers += '\n';
}
}
if (store().single_lined || codeLineNumbers.length > 2) {
block.firstChild.classList.add('code-numbered');
numbers.innerText = codeLineNumbers || 1;
} else {
block.firstChild.classList.remove('code-numbered');
numbers.innerText = '';
}
}
});
},
},
};

View File

@ -0,0 +1,35 @@
{
"name": "code line numbers",
"id": "d61dc8a7-b195-465b-935f-53eea9efe74e",
"version": "0.4.0",
"description": "adds line numbers to code blocks.",
"tags": ["extension", "usability"],
"authors": [
{
"name": "CloudHill",
"email": "rh.cloudhill@gmail.com",
"homepage": "https://github.com/CloudHill",
"avatar": "https://avatars.githubusercontent.com/u/54142180"
}
],
"js": {
"client": ["client.mjs"]
},
"css": {
"client": ["client.css"]
},
"options": [
{
"type": "toggle",
"key": "single_lined",
"label": "number single-lined code blocks",
"value": false
},
{
"type": "select",
"key": "style",
"label": "line number style",
"values": ["plain", "background", "border"]
}
]
}

View File

@ -26,6 +26,7 @@
"bypass-preview",
"topbar-icons",
"word-counter",
"code-line-numbers",
"calendar-scroll",
"weekly-view",
"collapse-properties",