summaryrefslogtreecommitdiff
path: root/source/features/indented-code-wrapping.tsx
blob: 176c05da46004ed206b14f93e3493fe52b39c93d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import './indented-code-wrapping.css';
import select from 'select-dom';
import features from '../libs/features';
import onPrFileLoad from '../libs/on-pr-file-load';
import onNewComments from '../libs/on-new-comments';

function run(): void {
	const tables = select.all([
		'.file table.diff-table:not(.rgh-softwrapped-code)', // Split and unified diffs
		'.file table.d-table:not(.rgh-softwrapped-code)' // "Suggested changes" in PRs
	].join());

	for (const table of tables) {
		table.classList.add('rgh-softwrapped-code');
		const tabSize = parseInt(table.style.getPropertyValue('--tab-size') || document.documentElement.style.getPropertyValue('tab-size'), 10);

		for (const line of select.all('.blob-code-inner:not(.blob-code-hunk)', table)) {
			if (line.textContent!.length < 20) {
				continue;
			}

			const leadingSpaceCharacters = /^\s+/.exec(line.firstChild!.textContent!);
			if (!leadingSpaceCharacters) {
				continue;
			}

			let spaceCount = 0;
			let tabCount = 0;
			for (const char of leadingSpaceCharacters[0]) {
				if (char === '\t') {
					tabCount += 1;
				} else {
					// Consider every other "space" character as one "blank space" character
					spaceCount += 1;
				}
			}

			// Code suggestions already had some padding
			const extraPadding = line.classList.contains('px-2') ? '8px + ' : '';

			// If there is mixed indentation on a line, spaces before a tab are collapsed with the following tab until `spaceCount` is more than `tabSize`
			// So, ignore any number of spaces that are below `tabSize` or more than any multiple of `tabSize`
			if (tabCount > 0) {
				if (spaceCount > tabSize) {
					spaceCount %= tabSize;
				} else {
					spaceCount = 0;
				}
			}

			// Move the whole line where it is supposed to be, then unindent the start of the line to compensate for indentation, preserving spaces
			// We might get `--tab-size` from compatible extensions like `github-custom-tab-size`
			line.style.setProperty('padding-left', `calc(${extraPadding}(var(--tab-size, 4) * ${tabCount}ch) + ${spaceCount}ch)`, 'important');
			line.style.setProperty('text-indent', `calc((var(--tab-size, 4) * -${tabCount}ch) - ${spaceCount}ch)`, 'important');
		}
	}
}

function init(): void {
	run();
	onNewComments(run);
	onPrFileLoad(run);

	document.body.classList.add('rgh-code-wrapping-enabled');
}

features.add({
	id: __featureName__,
	description: 'Indents wrapped code correctly.',
	screenshot: 'https://user-images.githubusercontent.com/37769974/60379474-0ba67e80-9a51-11e9-97f9-077d282e5bdb.png',
	include: [
		features.isPRFiles,
		features.isCommit,
		features.isPRConversation,
		features.isCompare
	],
	load: features.onAjaxedPages,
	init
});