From 7289e22c2e513cd85062c696a92841f43dac7644 Mon Sep 17 00:00:00 2001 From: Ron Meldiner Date: Wed, 30 Oct 2024 16:01:22 -0700 Subject: [PATCH] add event classification --- src/extension.test.ts | 110 ++++++++++++++++++++++++++++++++++++++++-- src/extension.ts | 94 ++++++++++++++++++++++-------------- 2 files changed, 165 insertions(+), 39 deletions(-) diff --git a/src/extension.test.ts b/src/extension.test.ts index 4ca0ab4..206c0e1 100644 --- a/src/extension.test.ts +++ b/src/extension.test.ts @@ -3,13 +3,115 @@ import * as assert from 'assert'; // You can import and use all API from the 'vscode' module // as well as import your extension to test it import * as vscode from 'vscode'; +import { classifyTextChange, TextChangeType } from './extension'; // import * as myExtension from '../../extension'; suite('Extension Test Suite', () => { - vscode.window.showInformationMessage('Start all tests.'); + test('classifyTextChange - Undo', () => { + const event = { + reason: vscode.TextDocumentChangeReason.Undo, + contentChanges: [], + document: {} as vscode.TextDocument + } as vscode.TextDocumentChangeEvent; + assert.strictEqual(classifyTextChange(event, "", ""), TextChangeType.Undo); + }); + + test('classifyTextChange - Redo', () => { + const event = { + reason: vscode.TextDocumentChangeReason.Redo, + contentChanges: [], + document: {} as vscode.TextDocument + } as vscode.TextDocumentChangeEvent; + assert.strictEqual(classifyTextChange(event, "", ""), TextChangeType.Redo); + }); + test('classifyTextChange - NoChange', () => { + const event = { + reason: undefined, + contentChanges: [], + document: {} as vscode.TextDocument + } as vscode.TextDocumentChangeEvent; + assert.strictEqual(classifyTextChange(event, "", ""), TextChangeType.NoChange); + }); + test('classifyTextChange - Deletion', () => { + const event = { + reason: undefined, + contentChanges: [{ + range: new vscode.Range(0, 0, 0, 1), + rangeOffset: 0, + rangeLength: 1, + text: "" + }], + document: {} as vscode.TextDocument + } as vscode.TextDocumentChangeEvent; + assert.strictEqual(classifyTextChange(event, "", "a"), TextChangeType.Deletion); + }); + + test('classifyTextChange - HandwrittenChar', () => { + const event = { + reason: undefined, + contentChanges: [{ + range: new vscode.Range(0, 0, 0, 0), + rangeOffset: 0, + rangeLength: 0, + text: "a" + }], + document: {} as vscode.TextDocument + } as vscode.TextDocumentChangeEvent; + assert.strictEqual(classifyTextChange(event, "a", ""), TextChangeType.HandwrittenChar); + }); + + test('classifyTextChange - Space', () => { + const event = { + reason: undefined, + contentChanges: [{ + range: new vscode.Range(0, 0, 0, 0), + rangeOffset: 0, + rangeLength: 0, + text: " " + }], + document: {} as vscode.TextDocument + } as vscode.TextDocumentChangeEvent; + assert.strictEqual(classifyTextChange(event, " ", ""), TextChangeType.Space); + }); - test('Sample test', () => { - assert.strictEqual(-1, [1, 2, 3].indexOf(5)); - assert.strictEqual(-1, [1, 2, 3].indexOf(0)); + test('classifyTextChange - AutoCloseBracket', () => { + const event = { + reason: undefined, + contentChanges: [{ + range: new vscode.Range(0, 0, 0, 0), + rangeOffset: 0, + rangeLength: 0, + text: "()" + }], + document: {} as vscode.TextDocument + } as vscode.TextDocumentChangeEvent; + assert.strictEqual(classifyTextChange(event, "()", ""), TextChangeType.AutoCloseBracket); + }); + + test('classifyTextChange - AutoCompletion', () => { + const event = { + reason: undefined, + contentChanges: [{ + range: new vscode.Range(0, 0, 0, 0), + rangeOffset: 0, + rangeLength: 0, + text: "console.log" + }], + document: {} as vscode.TextDocument + } as vscode.TextDocumentChangeEvent; + assert.strictEqual(classifyTextChange(event, "console.log", "cons"), TextChangeType.AutoCompletion); + }); + test('classifyTextChange - Unknown', () => { + const event = { + reason: undefined, + contentChanges: [{ + range: new vscode.Range(0, 0, 0, 0), + rangeOffset: 0, + rangeLength: 0, + text: "" + }], + document: {} as vscode.TextDocument + } as vscode.TextDocumentChangeEvent; + assert.strictEqual(classifyTextChange(event, "multiple\nlines\nof\ntext", ""), TextChangeType.Unknown); }); }); diff --git a/src/extension.ts b/src/extension.ts index 62faa1d..74c17ba 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -28,12 +28,38 @@ function checkAndLogEvents() { // Set interval to check and log events every minute (60000 milliseconds) setInterval(checkAndLogEvents, farosConfig.batchInterval()); -function isTabPress(change: vscode.TextDocumentContentChangeEvent): boolean { - // return change.text.length > 1 && change.rangeLength !== 0; - // this condition will be true for any document change event that introduces more than - // one character into the document. This will capture all auto-completions, but unfortunately - // will also capture copy/paste events. - return change.text.length > 1; +export enum TextChangeType { + Undo, + Redo, + NoChange, + Deletion, + HandwrittenChar, + Space, + AutoCloseBracket, + AutoCompletion, + Unknown +} + +export function classifyTextChange(event: vscode.TextDocumentChangeEvent, updatedText: string, previousText: string): TextChangeType { + if (event.reason === vscode.TextDocumentChangeReason.Undo) { + return TextChangeType.Undo; + } else if (event.reason === vscode.TextDocumentChangeReason.Redo) { + return TextChangeType.Redo; + } else if (event.contentChanges.length === 0) { + return TextChangeType.NoChange; + } else if (event.contentChanges.length === 1 && event.contentChanges[0].rangeLength > 0 && event.contentChanges[0].text.length === 0) { + return TextChangeType.Deletion; + } else if (event.contentChanges.length === 1 && event.contentChanges[0].text.length === 1) { + return TextChangeType.HandwrittenChar; + } else if (event.contentChanges.length > 0 && event.contentChanges.every(change => change.text.length > 0 && change.text.trim().length === 0)) { + return TextChangeType.Space; + } else if (event.contentChanges.length === 1 && event.contentChanges[0].text.length === 2 && ['()', '[]', '{}', '""', "''", '``'].includes(event.contentChanges[0].text)) { + return TextChangeType.AutoCloseBracket; + } else if (event.contentChanges.length > 0 && event.contentChanges.some(change => change.text.replace(/\s/g, "").length > 0) && updatedText.length > previousText.length) { + return TextChangeType.AutoCompletion; + } else { + return TextChangeType.Unknown; + } } function registerSuggestionListener() { @@ -53,37 +79,35 @@ function registerSuggestionListener() { if (!activeEditor || event.document !== activeEditor.document) { return; } - const change = event.contentChanges[0]; const updatedText = activeEditor.document.getText(); - if (updatedText.length > previousText.length) { - if (isTabPress(change)) { - const currentLengthChange = change.text.replace(/\s/g, "").length; - - if (currentLengthChange > 0) { - suggestionsCount++; - charCount += currentLengthChange; - statusBarItem.text = - "Auto-completions: " + - suggestionsCount + - " (" + - charCount + - " chars)"; - - // Store the event in memory - addAutoCompletionEvent({ - timestamp: new Date(), - charCountChange: currentLengthChange, - filename: activeEditor.document.fileName, - extension: path.extname(activeEditor.document.fileName), - language: activeEditor.document.languageId, - repository: getGitRepoName(activeEditor.document.fileName), - branch: getGitBranch(activeEditor.document.fileName), - }); - - farosPanel?.refresh(); - } - } + const changeType = classifyTextChange(event, updatedText, previousText); + + if (changeType === TextChangeType.AutoCompletion) { + const currentLengthChange = event.contentChanges[0].text.replace(/\s/g, "").length; + + suggestionsCount++; + charCount += currentLengthChange; + statusBarItem.text = + "Auto-completions: " + + suggestionsCount + + " (" + + charCount + + " chars)"; + + // Store the event in memory + addAutoCompletionEvent({ + timestamp: new Date(), + charCountChange: currentLengthChange, + filename: activeEditor.document.fileName, + extension: path.extname(activeEditor.document.fileName), + language: activeEditor.document.languageId, + repository: getGitRepoName(activeEditor.document.fileName), + branch: getGitBranch(activeEditor.document.fileName), + }); + + farosPanel?.refresh(); } + previousText = updatedText; }); }