Skip to content

Commit 3196883

Browse files
0.1.4: customizable debouce/throttle on left line numbers
1 parent 3536a32 commit 3196883

File tree

5 files changed

+714
-498
lines changed

5 files changed

+714
-498
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,8 @@ This version doesn't work
6161

6262
- Add customizable font settings
6363
- New extension icon!
64+
65+
## [0.1.4] - 2025-01-03
66+
67+
- Added delay functionality to prevent re-rendering on repeated vim motions (e.g. `9j` or `12k`)
68+
- Added delay configuration option to extension settings

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
This is a Visual Studio Code extension that allows you adjust the display of line numbers to a combination of absolute and relative settings.
44

5-
This should be helpful if you use one of the Vim plugins [VSCodeVim](#https://github.com/VSCodeVim/Vim), or are learning Vim like me.
5+
This should be helpful if you use one of the Vim plugins [VSCodeVim](https://github.com/VSCodeVim/Vim), or are learning Vim like me.
66

77
The Visual Studio Code API doesn't support a great way to display anything on the left of the built-in line numbers. Therefore, "gutterIcon" is used to display the left line numbers as SVGs.
88

package.json

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "Display both absolute and relative line numbers.",
55
"license": "GPL-3.0-or-later",
66
"icon": "icon.png",
7-
"version": "0.1.3",
7+
"version": "0.1.4",
88
"publisher": "slhsxcmy",
99
"extensionKind": [
1010
"ui"
@@ -22,6 +22,11 @@
2222
"contributes": {
2323
"configuration": {
2424
"properties": {
25+
"vscode-double-line-numbers.delay": {
26+
"type": "number",
27+
"default": 50,
28+
"markdownDescription": "Delay in milliseconds for throttling line number updates. Default is 50ms."
29+
},
2530
"vscode-double-line-numbers.font.color": {
2631
"type": "string",
2732
"default": "#858585",
@@ -88,23 +93,24 @@
8893
"test": "node ./out/test/runTest.js"
8994
},
9095
"devDependencies": {
91-
"@types/vscode": "^1.60.0",
9296
"@types/glob": "^7.1.4",
9397
"@types/mocha": "^9.0.0",
9498
"@types/node": "14.x",
99+
"@types/vscode": "^1.60.0",
95100
"@typescript-eslint/eslint-plugin": "^4.31.1",
96101
"@typescript-eslint/parser": "^4.31.1",
102+
"@vscode/test-electron": "^2.4.1",
97103
"eslint": "^7.32.0",
98104
"glob": "^7.1.7",
99105
"mocha": "^9.1.1",
100-
"typescript": "^4.4.3",
101106
"ts-loader": "^9.2.5",
102-
"webpack": "^5.52.1",
103-
"webpack-cli": "^4.8.0",
104-
"@vscode/test-electron": "^1.6.2"
107+
"typescript": "^4.4.3",
108+
"webpack": "^5.97.1",
109+
"webpack-cli": "^6.0.1"
105110
},
106111
"repository": {
107112
"type": "git",
108113
"url": "https://github.com/slhsxcmy/vscode-double-line-numbers/"
109-
}
114+
},
115+
"packageManager": "[email protected]"
110116
}

src/extension.ts

Lines changed: 67 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ class LineNumberManager {
3030
readonly NO_DECOR = -1;
3131

3232
context: vscode.ExtensionContext;
33-
3433
/* decorTypeMap
3534
{
3635
editor0: {
@@ -52,6 +51,7 @@ class LineNumberManager {
5251
Map<number, vscode.TextEditorDecorationType>
5352
>;
5453
decorNumMap: Map<vscode.TextEditor, Map<number, number>>;
54+
updateDecorDebounced!: (editor: vscode.TextEditor) => void;
5555

5656
/**
5757
* Constructor of LineNumberManager
@@ -63,9 +63,30 @@ class LineNumberManager {
6363
this.decorTypeMap = new Map();
6464
this.decorNumMap = new Map();
6565

66+
this.initializeUpdateDecorDebounced();
67+
68+
vscode.workspace.onDidChangeConfiguration((e) => {
69+
if (e.affectsConfiguration("vscode-double-line-numbers.delay")) {
70+
this.initializeUpdateDecorDebounced();
71+
}
72+
});
73+
6674
this.updateAllDecor();
6775
}
6876

77+
/**
78+
* Updates the delay time and debounced function based on configuration
79+
*/
80+
private initializeUpdateDecorDebounced() {
81+
const delayTime = vscode.workspace
82+
.getConfiguration()
83+
.get("vscode-double-line-numbers.delay", 50);
84+
this.updateDecorDebounced = this.debounce(
85+
this.updateDecor.bind(this),
86+
delayTime
87+
);
88+
}
89+
6990
/**
7091
* Getter of right state
7192
* @returns {vscode.TextEditorLineNumbersStyle} right state
@@ -114,7 +135,9 @@ class LineNumberManager {
114135
* @param {string} key left state key, one of "abs_rel", "rel_abs", "abs", "rel", "off"
115136
*/
116137
setLeftState(key: string) {
117-
if (this.getLeftState() === leftStateMap[key]) return; // Optimization
138+
if (this.getLeftState() === leftStateMap[key]) {
139+
return;
140+
}
118141
this.context.globalState.update(this.LEFT_STATE_STORE, leftStateMap[key]);
119142
this.updateAllDecor();
120143
}
@@ -124,7 +147,7 @@ class LineNumberManager {
124147
*/
125148
updateAllDecor() {
126149
vscode.window.visibleTextEditors.forEach((editor) => {
127-
this.updateDecor(editor);
150+
this.updateDecorDebounced(editor);
128151
});
129152
}
130153

@@ -141,9 +164,12 @@ class LineNumberManager {
141164
const activeLine = editor.selection.active.line;
142165

143166
// Create new map
144-
if (!this.decorTypeMap.has(editor))
167+
if (!this.decorTypeMap.has(editor)) {
145168
this.decorTypeMap.set(editor, new Map());
146-
if (!this.decorNumMap.has(editor)) this.decorNumMap.set(editor, new Map());
169+
}
170+
if (!this.decorNumMap.has(editor)) {
171+
this.decorNumMap.set(editor, new Map());
172+
}
147173

148174
for (let i = start; i <= end; ++i) {
149175
let num: number;
@@ -167,10 +193,11 @@ class LineNumberManager {
167193

168194
// apply decor
169195
for (let i = start; i <= end; ++i) {
170-
if (this.decorNumMap.get(editor)!.has(i))
196+
if (this.decorNumMap.get(editor)!.has(i)) {
171197
editor.setDecorations(this.decorTypeMap.get(editor)!.get(i)!, [
172198
new vscode.Range(i, 0, i, 0),
173199
]);
200+
}
174201
}
175202
}
176203

@@ -189,6 +216,35 @@ class LineNumberManager {
189216
return vscode.window.createTextEditorDecorationType({});
190217
}
191218
}
219+
220+
/**
221+
* Creates a debounced function that delays invoking the provided function until after a specified wait time has elapsed
222+
* since the last time the debounced function was invoked. Optionally, the function can be invoked immediately on the first call.
223+
*
224+
* @param func - The function to debounce.
225+
* @param wait - The number of milliseconds to delay.
226+
* @param immediate - If `true`, the function will be invoked on the leading edge of the timeout, instead of the trailing.
227+
* @returns A new debounced function.
228+
*/
229+
debounce(func: Function, wait: number, immediate: boolean = false) {
230+
let timeout: NodeJS.Timeout | null = null;
231+
return (...args: any[]) => {
232+
const later = () => {
233+
timeout = null;
234+
if (!immediate) {
235+
func(...args);
236+
}
237+
};
238+
const callNow = immediate && !timeout;
239+
if (timeout) {
240+
clearTimeout(timeout);
241+
}
242+
timeout = setTimeout(later, wait);
243+
if (callNow) {
244+
func(...args);
245+
}
246+
};
247+
}
192248
}
193249

194250
/**
@@ -271,17 +327,19 @@ export function activate(context: vscode.ExtensionContext) {
271327

272328
// scroll; create/delete new lines
273329
vscode.window.onDidChangeTextEditorVisibleRanges((event) => {
274-
mgr.updateDecor(event.textEditor);
330+
mgr.updateDecorDebounced(event.textEditor);
275331
});
276332

277333
// click into different editor
278334
vscode.window.onDidChangeActiveTextEditor((editor) => {
279-
if (editor) mgr.updateDecor(editor);
335+
if (editor) {
336+
mgr.updateDecorDebounced(editor);
337+
}
280338
});
281339

282340
// select new lines in editor
283341
vscode.window.onDidChangeTextEditorSelection((event) => {
284-
mgr.updateDecor(event.textEditor);
342+
mgr.updateDecorDebounced(event.textEditor);
285343
});
286344

287345
// open/close editors

0 commit comments

Comments
 (0)