diff --git a/package.json b/package.json index 323ec5a..0a4af18 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,11 @@ { "command": "mind-reader.runLineContext", "title": "Run Line Context" + }, + + { + "command": "mind-reader.runCursorContext", + "title": "Run Cursor Context" } ], @@ -151,7 +156,7 @@ "LEGO® Education SPIKE™ Prime Set (45678)" ] }, - "mindreader.screenReader": { + "mindreader.reader.screenReader": { "type": "string", "description": "Specifies which screen reader to optimize for.", "default": "NVDA", @@ -166,6 +171,11 @@ "Apple VoiceOver (macOS)" ] }, + "mindreader.reader.contextWindow": { + "type": "number", + "description": "The number of words around the cursor to use when reading the cursor context", + "default": 1 + }, "mindreader.connection.connectAutomatically": { "type": "boolean", "description": "Specifies whether to try to automatically detect and communicate with a connected Hub.", diff --git a/src/commands.ts b/src/commands.ts index 72c5d57..efbaded 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -54,6 +54,11 @@ const commands: Command[] = [ name: 'mind-reader.runLineContext', callback: runLineContext, }, + { + name: 'mind-reader.runCursorContext', + + callback: runCursorContext + } ]; // COMMAND CALLBACK IMPLEMENTATIONS @@ -135,4 +140,70 @@ function createContextString(context: pl.LexNode[], line: number): string { return contextString; } +// find up to `n` words around the cursor, where `n` is +// the value of `#mindreader.reader.contextWindow` +function runCursorContext(): void { + let editor = vscode.window.activeTextEditor; + if (!editor) { + vscode.window.showErrorMessage("RunCursorContext: No Active Editor"); + return; + } + + const cursorPos: vscode.Position = editor.selection.active; + const text: string = editor.document.lineAt(cursorPos).text; + const windowSize: number = vscode.workspace.getConfiguration('mindreader').get('reader.contextWindow')!; + + let trimmedText = text.trimStart(); // trim leading whitespace + let leadingWS = text.length - trimmedText.length; // # of characters of leading whitespace + trimmedText = trimmedText.trimEnd(); // trim trailing whitespace + let pos = leadingWS; + let maxPos = text.length; + + // clamp cursor start/end to new range + let col = cursorPos.character; // effective column of the cursor position + if (col < leadingWS) { + // move effective start to first non-whitespace character in the line + col = leadingWS; + } else if (col > leadingWS + trimmedText.length - 1) { + // move effective end to last non-whitespace character in the line + col = leadingWS + trimmedText.length - 1; + } + + // generate list of space separate words with range data (start, end) + // TODO: can find user position to be done in one pass + let spaceWords: {word: string, start: number, end: number}[] = []; + while (pos < maxPos && trimmedText.length > 0) { + let word = trimmedText.replace(/ .*/, ''); + spaceWords.push({word, start: pos, end: pos+word.length}); + + // remove processed word from trimmed text + const oldText = trimmedText; + trimmedText = trimmedText.replace(/[^ ]+/, '').trimStart(); + + // update pos to start of next word + pos += oldText.length - trimmedText.length; + } + + // find word the user is in + let contextStart: number = -1, contextEnd: number = -1; + for (let i = 0; i < spaceWords.length; i++) { + if (col >= spaceWords[i].start && col <= spaceWords[i].end) { + // found the word + contextStart = Math.max(0, i - windowSize); // clamp start index + contextEnd = Math.min(spaceWords.length, i + windowSize + 1); // clamp end index + + // construct cursor context string + let contextString = ''; + for (let i = contextStart; i < contextEnd; i++) { + contextString += spaceWords[i].word + ' '; + } + + // output cursor context string + vscode.window.showInformationMessage(contextString); + + return; + } + } +} + export default commands;