mirror of
https://github.com/We-Dont-Byte/Mind_Reader.git
synced 2025-02-04 10:38:42 +00:00
Added Senior Design Day Spring 2022 suggestion of selecting the leading whitespace characters. Had to refactor getLeadingWhitespace to include a helper function. Also added imports for vscode. Finally shifted helper functions together and added comments.
377 lines
14 KiB
TypeScript
Executable File
377 lines
14 KiB
TypeScript
Executable File
"use strict";
|
|
import pl = require("../pylex");
|
|
import { CommandEntry } from './commandEntry';
|
|
import { Position, Selection, TextEditor, TextLine, window, workspace } from "vscode";
|
|
|
|
export const textCommands: CommandEntry[] = [
|
|
{
|
|
name: 'mind-reader.getLineNumber',
|
|
callback: getLineNumber,
|
|
},
|
|
{
|
|
name: 'mind-reader.getIndent',
|
|
callback: getIndent,
|
|
},
|
|
{
|
|
name: 'mind-reader.getLeadingSpaces',
|
|
callback: getLeadingSpaces,
|
|
},
|
|
{
|
|
name: 'mind-reader.selectLeadingWhitespace',
|
|
callback: selectLeadingWhitespace
|
|
},
|
|
{
|
|
name: 'mind-reader.getNumberOfSelectedLines',
|
|
callback: getNumberOfSelectedLines,
|
|
},
|
|
{
|
|
name: 'mind-reader.runLineContext',
|
|
callback: runLineContext,
|
|
},
|
|
{
|
|
name: 'mind-reader.runCursorContext',
|
|
callback: runCursorContext
|
|
}
|
|
];
|
|
|
|
/** Helper Function
|
|
*
|
|
* @param editor
|
|
* @returns numSpaces
|
|
** There are two methods that can be used to find the leading spaces:
|
|
** method 1:
|
|
** calculates the number of leading spaces by finding the length of the current line
|
|
** then subtracting from that the length of the text after trimming the whitespace at the start
|
|
** which will equal the number of whitespace characters
|
|
**
|
|
** TO-USE: set calculateLeadingSpaces to true
|
|
**
|
|
** method 2 (default):
|
|
** finds the index position of the first non-whitespace character in a 0-index
|
|
** this number will equal the number of spaces preceding the non-whitespace character
|
|
** due to the nature of 0-indexes.
|
|
**
|
|
** TO-USE: set calculateLeadingSpaces to false
|
|
*/
|
|
function fetchNumberOfLeadingSpaces(editor: TextEditor | undefined): number {
|
|
let numSpaces: number = 0;
|
|
|
|
|
|
if (editor) {
|
|
/*
|
|
* set true to use method 1: find the number of leading spaces through arithmetic
|
|
* set false to use method 2: find the index position of the first non-whitespace character in a 0-index
|
|
* default: false
|
|
*/
|
|
const calculateLeadingSpaces: boolean = false; // change boolean value to change method
|
|
const lineNum: number = (fetchLineNumber(editor) - 1); // We want the line index, so we remove the 1 we added to the result in fetchLineNumber
|
|
const line : any = editor.document.lineAt(lineNum);
|
|
|
|
/* If true, calculate by arithmetic otherwise get index */
|
|
numSpaces = (calculateLeadingSpaces)
|
|
? pl.Lexer.getLeadingSpacesByArithmetic(line)
|
|
: pl.Lexer.getLeadingSpacesByIndex(line);
|
|
}
|
|
|
|
return numSpaces;
|
|
}
|
|
|
|
/** Helper Function
|
|
* * This function returns the number of selected lines in the active text editor window
|
|
@param editor
|
|
@returns numberOfSelectedLines
|
|
*/
|
|
function fetchNumberOfSelectedLines(editor: TextEditor | undefined): number {
|
|
let numberOfSelectedLines: number = 0;
|
|
|
|
if (editor) {
|
|
numberOfSelectedLines = editor.selections.reduce((prev, curr) => prev + (curr.end.line - curr.start.line), 1);
|
|
}
|
|
|
|
return numberOfSelectedLines;
|
|
}
|
|
|
|
/** Helper Function
|
|
** This function returns the line number of the active text editor window
|
|
* @param editor
|
|
* @returns editor!.selection.active.line + 1
|
|
*/
|
|
function fetchLineNumber(editor: TextEditor | undefined): number {
|
|
return editor!.selection.active.line + 1; // line numbers start at 1, not 0, so we add 1 to the result
|
|
}
|
|
|
|
/** Helper Function
|
|
** This function returns the text from the current line of the active text editor window
|
|
* @param editor
|
|
* @returns editor.document.lineAt(fetchLineNumber(editor) - 1)
|
|
*/
|
|
function fetchLine(editor: TextEditor | undefined): TextLine {
|
|
return editor!.document.lineAt(fetchLineNumber(editor) - 1); // We want the line index, so we remove the 1 we added to the result in fetchLineNumber
|
|
}
|
|
|
|
/* Function
|
|
* Function to return the number of selected (highlighted) lines
|
|
* Changes output to 'Line' for 1 line and 'Lines' for all other instances
|
|
*/
|
|
function getNumberOfSelectedLines(): void {
|
|
const editor: any = window.activeTextEditor;
|
|
|
|
if (editor) {
|
|
const numberOfSelectedLines: number = fetchNumberOfSelectedLines(editor);
|
|
|
|
(numberOfSelectedLines !== 1)
|
|
? window.showInformationMessage(`${numberOfSelectedLines.toString()} Lines Selected`)
|
|
: window.showInformationMessage(`${numberOfSelectedLines.toString()} Line Selected`);
|
|
}
|
|
else {
|
|
window.showErrorMessage('No document currently active');
|
|
}
|
|
}
|
|
|
|
/* Function
|
|
* Outputs the current line number the cursor is on
|
|
*/
|
|
function getLineNumber(): void {
|
|
const editor: any = window.activeTextEditor;
|
|
|
|
if (editor) {
|
|
const lineNum: number = fetchLineNumber(editor);
|
|
|
|
window.showInformationMessage(`Line ${lineNum.toString()}`);
|
|
}
|
|
else {
|
|
window.showErrorMessage('No document currently active');
|
|
}
|
|
}
|
|
|
|
/* Function
|
|
* Used to get the number of indents on a line
|
|
*/
|
|
function getIndent(): void {
|
|
const editor: any = window.activeTextEditor;
|
|
|
|
if (editor) {
|
|
const lineNum: number = (fetchLineNumber(editor) - 1); // We want the line index, so we remove the 1 we added to the result in fetchLineNumber
|
|
const line : any = editor.document.lineAt(lineNum);
|
|
|
|
if (line.isEmptyOrWhitespace) {
|
|
window.showInformationMessage(`Line ${lineNum.toString()} is Empty`);
|
|
}
|
|
else {
|
|
// Grab tab format from open document
|
|
const tabFmt: any = {
|
|
size: editor.options.tabSize,
|
|
hard: !editor.options.insertSpaces
|
|
};
|
|
const i: number = pl.Lexer.getIndent(line.text, tabFmt);
|
|
|
|
(i !== 1)
|
|
? window.showInformationMessage(`Line ${lineNum.toString()}: ${i.toString()} indents`)
|
|
: window.showInformationMessage(`Line ${lineNum.toString()}: ${i.toString()} indent`);
|
|
}
|
|
}
|
|
else {
|
|
window.showErrorMessage('No document currently active');
|
|
}
|
|
}
|
|
|
|
/* Function
|
|
* Returns the number of leading spaces on the line the cursor is on
|
|
*/
|
|
function getLeadingSpaces(): void {
|
|
const editor: any = window.activeTextEditor;
|
|
|
|
if (editor) {
|
|
const lineNum : number = fetchLineNumber(editor);
|
|
const line : any = fetchLine(editor);
|
|
|
|
if (line.isEmptyOrWhitespace) {
|
|
window.showInformationMessage(`Line ${lineNum.toString()} is empty`);
|
|
}
|
|
else {
|
|
const numSpaces = fetchNumberOfLeadingSpaces(editor);
|
|
|
|
/* Ternary operator to change the tense of 'space' to 'spaces' for the output if numSpaces is 0 or greater than 1 */
|
|
(numSpaces !== 1)
|
|
? window.showInformationMessage(`Line ${lineNum.toString()}: ${numSpaces.toString()} spaces`)
|
|
: window.showInformationMessage(`Line ${lineNum.toString()}: ${numSpaces.toString()} space`);
|
|
}
|
|
}
|
|
else {
|
|
window.showErrorMessage('No document currently active');
|
|
}
|
|
}
|
|
|
|
/* Function
|
|
* Selects the leading whitespace at the beginning of a line
|
|
* This feature was a request from Senior Design Day Spring 2022
|
|
*/
|
|
function selectLeadingWhitespace(): void {
|
|
const editor : any = window.activeTextEditor;
|
|
|
|
if (editor) {
|
|
const numSpaces = fetchNumberOfLeadingSpaces(editor); // This will be used for the output message
|
|
const lineNum : number = (fetchLineNumber(editor) - 1); // We want the line index, so we remove the 1 we added to the result in fetchLineNumber
|
|
|
|
/* If numSpaces isn't greater than 1, then there is no leading whitespace to select */
|
|
if (numSpaces >= 1) {
|
|
const line : any = editor.document.lineAt(lineNum); // Get our line
|
|
const startPos: any = line.range.start.character; // Start at the starting character position
|
|
const endPos : any = line.firstNonWhitespaceCharacterIndex; // End at the first non whitespace character index
|
|
|
|
/* Apply our selection */
|
|
editor.selection = new Selection(new Position(lineNum, startPos), new Position(lineNum, endPos));
|
|
/* After the selection is made, the editor loses focus. We need to re-focus the editor so typing isn't interrupted */
|
|
window.showTextDocument(editor.document);
|
|
|
|
|
|
/* Ternary operator to change the tense of 'space' to 'spaces' for the output if numSpaces is 0 or greater than 1 */
|
|
(numSpaces !== 1)
|
|
? window.showInformationMessage(`Line ${lineNum.toString()}: ${numSpaces.toString()} spaces selected`)
|
|
: window.showInformationMessage(`Line ${lineNum.toString()}: ${numSpaces.toString()} space selected`);
|
|
}
|
|
else {
|
|
window.showErrorMessage(`Line ${lineNum.toString()}: No leading spaces to select!`); // No whitespace to select
|
|
window.showTextDocument(editor.document); // Refocus editor
|
|
}
|
|
}
|
|
else {
|
|
window.showErrorMessage('No document currently active'); // No active document
|
|
}
|
|
}
|
|
|
|
function runLineContext(): void {
|
|
const editor: any = window.activeTextEditor;
|
|
|
|
if (editor) {
|
|
// current text and line number
|
|
const editorText: string = editor.document.getText();
|
|
const line : string = editor.selection.active.line;
|
|
// get tab info settings
|
|
const size : number = parseInt(editor.options.tabSize);
|
|
const hard : boolean = !editor.options.insertSpaces;
|
|
// initialize parser
|
|
const parser : any = new pl.Parser(editorText, {
|
|
size,
|
|
hard
|
|
});
|
|
|
|
parser.parse();
|
|
const context: string = parser.context(line);
|
|
// build text
|
|
const contentString: string = createContextString(context, line);
|
|
|
|
window.showInformationMessage(contentString);
|
|
}
|
|
else {
|
|
window.showErrorMessage('No document currently active');
|
|
}
|
|
}
|
|
|
|
function createContextString(context: any, line: string): string {
|
|
if (context.length < 1) {
|
|
throw new Error('Cannot create context string for empty context');
|
|
}
|
|
|
|
let contextString: string = `Line ${line + 1}`; // 1 based
|
|
|
|
if (context[0].token && context[0].token.attr) {
|
|
contextString += ': ' + context[0].token.type.toString() + ' ' + context[0].token.attr.toString();
|
|
}
|
|
|
|
for (let i: number = 1; i < context.length; i++) {
|
|
const node: any = context[i];
|
|
|
|
if (node.label === 'root') {
|
|
// root
|
|
contextString += ' in the Document Root';
|
|
continue;
|
|
}
|
|
|
|
if (node.token.type !== pl.PylexSymbol.EMPTY &&
|
|
node.token.type !== pl.PylexSymbol.INDENT) {
|
|
contextString += ' inside ' + node.token.type.toString();
|
|
if (node.token.attr) {
|
|
contextString += ' ' + node.token.attr.toString();
|
|
}
|
|
}
|
|
}
|
|
|
|
return contextString;
|
|
}
|
|
|
|
/*
|
|
* find up to `n` words around the cursor, where `n` is
|
|
* the value of `#mindReader.reader.contextWindow`
|
|
*/
|
|
function runCursorContext(): void {
|
|
const editor: any = window.activeTextEditor;
|
|
|
|
if (!editor) {
|
|
window.showErrorMessage('RunCursorContext: No Active Editor');
|
|
return;
|
|
}
|
|
|
|
const cursorPos : any = editor.selection.active;
|
|
const text : string = editor.document.lineAt(cursorPos).text;
|
|
const windowSize : any = workspace.getConfiguration('mindReader').get('reader.contextWindow');
|
|
let trimmedText: string = text.trimStart(); // trim leading whitespace
|
|
const leadingWS : number = text.length - trimmedText.length; // # of characters of leading whitespace
|
|
let pos : number = leadingWS;
|
|
const maxPos : number = text.length;
|
|
// clamp cursor start/end to new range
|
|
let col : number = cursorPos.character; // effective column of the cursor position
|
|
|
|
trimmedText = trimmedText.trimEnd(); // trim trailing whitespace
|
|
|
|
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
|
|
const spaceWords: any[] = [];
|
|
|
|
while (pos < maxPos && trimmedText.length > 0) {
|
|
const word: string = trimmedText.replace(/ .*/, '');
|
|
|
|
spaceWords.push({
|
|
word,
|
|
start: pos,
|
|
end: pos + word.length
|
|
});
|
|
|
|
// remove processed word from trimmed text
|
|
const oldText: string = 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;
|
|
let contextEnd : number = -1;
|
|
|
|
for (let i: number = 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: string = '';
|
|
|
|
for (let i: any = contextStart; i < contextEnd; i++) {
|
|
contextString += spaceWords[i].word + ' ';
|
|
}
|
|
// output cursor context string
|
|
window.showInformationMessage(contextString);
|
|
return;
|
|
}
|
|
}
|
|
}
|