Compare commits

...

5 Commits

Author SHA1 Message Date
jasonwilliams
a2417223bb v0.6.1 2024-02-04 00:38:09 +00:00
jasonwilliams
79195ce1b4 increment / decrement 2024-02-02 00:01:18 +00:00
jasonwilliams
4ec1caf239 Fix bug with bracket matching and improve it with counts 2024-02-01 23:01:27 +00:00
jasonwilliams
e17dcb808f Handle casings (upper case, lowercase ,swtich case) 2024-02-01 22:23:07 +00:00
jasonwilliams
9b7bd51351 Fix insert after or before 2024-02-01 22:04:30 +00:00
7 changed files with 145 additions and 84 deletions

View File

@ -1,6 +1,6 @@
{
"name": "vscode-helix-emulation",
"version": "0.6.0",
"version": "0.6.1",
"displayName": "Helix For VS Code",
"description": "Helix emulation for Visual Studio Code",
"publisher": "jasew",
@ -273,6 +273,21 @@
"key": "ctrl+x",
"command": "acceptSelectedSuggestion",
"when": "suggestWidgetHasFocusedSuggestion && suggestWidgetVisible && extension.helixKeymap.insertMode"
},
{
"key": "alt+OEM_8",
"command": "extension.helixKeymap.switchToUppercase",
"when": "(extension.helixKeymap.normalMode || extension.helixKeymap.visualMode)"
},
{
"key": "ctrl+a",
"command": "extension.helixKeymap.increment",
"when": "(extension.helixKeymap.normalMode || extension.helixKeymap.visualMode)"
},
{
"key": "ctrl+x",
"command": "extension.helixKeymap.decrement",
"when": "(extension.helixKeymap.normalMode || extension.helixKeymap.visualMode)"
}
],
"configuration": {

View File

@ -99,6 +99,29 @@ export const actions: Action[] = [
vscode.commands.executeCommand('editor.action.formatSelection');
}),
parseKeysExact(['`'], [Mode.Normal], (vimState, editor) => {
// Take the selection and make it all lowercase
editor.edit((editBuilder) => {
editor.selections.forEach((selection) => {
const text = editor.document.getText(selection);
editBuilder.replace(selection, text.toLowerCase());
});
});
}),
parseKeysExact(['~'], [Mode.Normal], (vimState, editor) => {
// Switch the case of the selection (so if upper case make lower case and vice versa)
editor.edit((editBuilder) => {
editor.selections.forEach((selection) => {
const text = editor.document.getText(selection);
editBuilder.replace(
selection,
text.replace(/./g, (c) => (c === c.toUpperCase() ? c.toLowerCase() : c.toUpperCase())),
);
});
});
}),
// replace
parseKeysRegex(/^r(.)/, /^r/, [Mode.Normal], (helixState, editor, match) => {
const position = editor.selection.active;
@ -131,12 +154,7 @@ export const actions: Action[] = [
}),
parseKeysExact(['a'], [Mode.Normal], (vimState, editor) => {
editor.selections = editor.selections.map((selection) => {
const newPosition = positionUtils.right(editor.document, selection.active);
return new vscode.Selection(newPosition, newPosition);
});
enterInsertMode(vimState);
enterInsertMode(vimState, false);
setModeCursorStyle(vimState.mode, editor);
removeTypeSubscription(vimState);
}),
@ -436,3 +454,42 @@ function yankLine(vimState: HelixState, editor: vscode.TextEditor): void {
linewise: true,
};
}
export function switchToUppercase(editor: vscode.TextEditor): void {
editor.edit((editBuilder) => {
editor.selections.forEach((selection) => {
const text = editor.document.getText(selection);
editBuilder.replace(selection, text.toUpperCase());
});
});
}
export function incremenet(editor: vscode.TextEditor): void {
// Move the cursor to the first number and incremene the number
// If the cursor is not on a number, then do nothing
editor.edit((editBuilder) => {
editor.selections.forEach((selection) => {
const translatedSelection = selection.with(selection.start, selection.start.translate(0, 1));
const text = editor.document.getText(translatedSelection);
const number = parseInt(text, 10);
if (!isNaN(number)) {
editBuilder.replace(translatedSelection, (number + 1).toString());
}
});
});
}
export function decrement(editor: vscode.TextEditor): void {
// Move the cursor to the first number and incremene the number
// If the cursor is not on a number, then do nothing
editor.edit((editBuilder) => {
editor.selections.forEach((selection) => {
const translatedSelection = selection.with(selection.start, selection.start.translate(0, 1));
const text = editor.document.getText(translatedSelection);
const number = parseInt(text, 10);
if (!isNaN(number)) {
editBuilder.replace(translatedSelection, (number - 1).toString());
}
});
});
}

View File

@ -3,6 +3,7 @@ import { Action } from '../action_types';
import { enterInsertMode, setModeCursorStyle } from '../modes';
import { Mode } from '../modes_types';
import { parseKeysExact, parseKeysRegex } from '../parse_keys';
import { searchBackwardBracket, searchForwardBracket } from '../search_utils';
import { removeTypeSubscription } from '../type_subscription';
import { delete_ } from './operators';
@ -55,8 +56,20 @@ export const matchActions: Action[] = [
const [startCharNew, endCharNew] = getMatchPairs(replacement);
const num = helixState.resolveCount();
const forwardPosition = searchFowardForChar(endCharOrig, editor.selection.active, num);
const backwardPosition = searchBackwardForChar(startCharOrig, editor.selection.active, num);
const forwardPosition = searchForwardBracket(
editor.document,
startCharOrig,
endCharOrig,
editor.selection.active,
num,
);
const backwardPosition = searchBackwardBracket(
editor.document,
startCharOrig,
endCharOrig,
editor.selection.active,
num,
);
if (forwardPosition === undefined || backwardPosition === undefined) return;
@ -80,8 +93,8 @@ export const matchActions: Action[] = [
const [startChar, endChar] = getMatchPairs(char);
const num = helixState.resolveCount();
const forwardPosition = searchFowardForChar(endChar, editor.selection.active, num);
const backwardPosition = searchBackwardForChar(startChar, editor.selection.active, num);
const forwardPosition = searchForwardBracket(editor.document, startChar, endChar, editor.selection.active, num);
const backwardPosition = searchBackwardBracket(editor.document, startChar, endChar, editor.selection.active, num);
if (forwardPosition === undefined || backwardPosition === undefined) return;
@ -98,58 +111,6 @@ export const matchActions: Action[] = [
}),
];
const searchFowardForChar = (char: string, fromPosition: vscode.Position, num: number): vscode.Position | undefined => {
const editor = vscode.window.activeTextEditor;
if (!editor) return;
const document = editor.document;
// num starts at 1 so we should drop down to 0, as 1 is the default
// even if count wasn't specified
let count = --num;
for (let i = fromPosition.line; i < document.lineCount; ++i) {
const lineText = document.lineAt(i).text;
const fromIndex = i === fromPosition.line ? fromPosition.character : 0;
for (let j = fromIndex; j < lineText.length; ++j) {
if (lineText[j] === char && count === 0) {
return new vscode.Position(i, j);
} else if (lineText[j] === char) {
--count;
}
}
}
return undefined;
};
const searchBackwardForChar = (
char: string,
fromPosition: vscode.Position,
num: number,
): vscode.Position | undefined => {
const editor = vscode.window.activeTextEditor;
if (!editor) return;
const document = editor.document;
// num starts at 1 so we should drop down to 0, as 1 is the default
// even if count wasn't specified
let count = --num;
for (let i = fromPosition.line; i >= 0; --i) {
const lineText = document.lineAt(i).text;
const fromIndex = i === fromPosition.line ? fromPosition.character : lineText.length - 1;
for (let j = fromIndex; j >= 0; --j) {
if (lineText[j] === char && count === 0) {
return new vscode.Position(i, j);
} else if (lineText[j] === char) {
--count;
}
}
}
return undefined;
};
const getMatchPairs = (char: string) => {
let startChar: string;
let endChar: string;

View File

@ -304,8 +304,9 @@ function createInnerBracketHandler(
openingChar: string,
closingChar: string,
): (vimState: HelixState, document: vscode.TextDocument, position: vscode.Position) => vscode.Range | undefined {
return (vimState, document, position) => {
const bracketRange = getBracketRange(document, position, openingChar, closingChar);
return (helixState, document, position) => {
const count = helixState.resolveCount();
const bracketRange = getBracketRange(document, position, openingChar, closingChar, count);
if (bracketRange) {
return new vscode.Range(
@ -324,8 +325,9 @@ function createOuterBracketHandler(
openingChar: string,
closingChar: string,
): (vimState: HelixState, document: vscode.TextDocument, position: vscode.Position) => vscode.Range | undefined {
return (vimState, document, position) => {
const bracketRange = getBracketRange(document, position, openingChar, closingChar);
return (helixState, document, position) => {
const count = helixState.resolveCount();
const bracketRange = getBracketRange(document, position, openingChar, closingChar, count);
if (bracketRange) {
return new vscode.Range(bracketRange.start, bracketRange.end.with({ character: bracketRange.end.character + 1 }));
@ -340,6 +342,7 @@ function getBracketRange(
position: vscode.Position,
openingChar: string,
closingChar: string,
offset?: number,
): vscode.Range | undefined {
const lineText = document.lineAt(position.line).text;
const currentChar = lineText[position.character];
@ -348,13 +351,19 @@ function getBracketRange(
let end;
if (currentChar === openingChar) {
start = position;
end = searchForwardBracket(document, openingChar, closingChar, positionUtils.rightWrap(document, position));
end = searchForwardBracket(document, openingChar, closingChar, positionUtils.rightWrap(document, position), offset);
} else if (currentChar === closingChar) {
start = searchBackwardBracket(document, openingChar, closingChar, positionUtils.leftWrap(document, position));
start = searchBackwardBracket(
document,
openingChar,
closingChar,
positionUtils.leftWrap(document, position),
offset,
);
end = position;
} else {
start = searchBackwardBracket(document, openingChar, closingChar, position);
end = searchForwardBracket(document, openingChar, closingChar, position);
start = searchBackwardBracket(document, openingChar, closingChar, position, offset);
end = searchForwardBracket(document, openingChar, closingChar, position, offset);
}
if (start && end) {
@ -507,17 +516,18 @@ function createOuterWordHandler(
* This should ensure that we're fetching the nearest bracket pair.
**/
function createInnerMatchHandler(): (
vimState: HelixState,
helixState: HelixState,
document: vscode.TextDocument,
position: vscode.Position,
) => vscode.Range | undefined {
return (_, document, position) => {
return (helixState, document, position) => {
const count = helixState.resolveCount();
// Get all ranges from our position then reduce down to the shortest one
const bracketRange = [
getBracketRange(document, position, '(', ')'),
getBracketRange(document, position, '{', '}'),
getBracketRange(document, position, '<', '>'),
getBracketRange(document, position, '[', ']'),
getBracketRange(document, position, '(', ')', count),
getBracketRange(document, position, '{', '}', count),
getBracketRange(document, position, '<', '>', count),
getBracketRange(document, position, '[', ']', count),
].reduce((acc, range) => {
if (range) {
if (!acc) {

View File

@ -1,6 +1,7 @@
import * as vscode from 'vscode';
import { symbolProvider } from './SymbolProvider';
import { decrement, incremenet, switchToUppercase } from './actions/actions';
import { commandLine } from './commandLine';
import { escapeHandler } from './escape_handler';
import { onDidChangeActiveTextEditor, onDidChangeTextDocument } from './eventHandlers';
@ -87,6 +88,15 @@ export function activate(context: vscode.ExtensionContext): void {
vscode.commands.registerCommand('extension.helixKeymap.repeatLastMotion', () => {
globalhelixState.repeatLastMotion(globalhelixState, vscode.window.activeTextEditor!);
}),
vscode.commands.registerCommand('extension.helixKeymap.switchToUppercase', () => {
switchToUppercase(vscode.window.activeTextEditor!);
}),
vscode.commands.registerCommand('extension.helixKeymap.increment', () => {
incremenet(vscode.window.activeTextEditor!);
}),
vscode.commands.registerCommand('extension.helixKeymap.decrement', () => {
decrement(vscode.window.activeTextEditor!);
}),
);
enterNormalMode(globalhelixState);

View File

@ -4,12 +4,14 @@ import { HelixState } from './helix_state_types';
import { Mode } from './modes_types';
import { removeTypeSubscription } from './type_subscription';
export function enterInsertMode(helixState: HelixState): void {
export function enterInsertMode(helixState: HelixState, before = true): void {
// To fix https://github.com/jasonwilliams/vscode-helix/issues/14 we should clear selections on entering insert mode
// Helix doesn't clear selections on insert but doesn't overwrite the selection either, so our best option is to just clear them
const editor = helixState.editorState.activeEditor!;
const activeSelection = editor.selection.active;
editor.selections = [new vscode.Selection(activeSelection, activeSelection)];
editor.selections = editor.selections.map((selection) => {
const position = before ? selection.anchor : selection.active;
return new vscode.Selection(position, position);
});
helixState.mode = Mode.Insert;
setModeContext('extension.helixKeymap.insertMode');

View File

@ -55,15 +55,18 @@ export function searchForwardBracket(
openingChar: string,
closingChar: string,
fromPosition: vscode.Position,
offset?: number,
): vscode.Position | undefined {
let n = 0;
let n = offset ? offset - 1 : 0;
for (let i = fromPosition.line; i < document.lineCount; ++i) {
const lineText = document.lineAt(i).text;
const fromIndex = i === fromPosition.line ? fromPosition.character : 0;
for (let j = fromIndex; j < lineText.length; ++j) {
if (lineText[j] === openingChar) {
// If closing and opening are the same, don't bother deducting n
// However if they are different, we need to deduct n when we see an opening char
if (lineText[j] === openingChar && openingChar !== closingChar) {
++n;
} else if (lineText[j] === closingChar) {
if (n === 0) {
@ -83,15 +86,18 @@ export function searchBackwardBracket(
openingChar: string,
closingChar: string,
fromPosition: vscode.Position,
offset?: number,
): vscode.Position | undefined {
let n = 0;
let n = offset ? offset - 1 : 0;
for (let i = fromPosition.line; i >= 0; --i) {
const lineText = document.lineAt(i).text;
const fromIndex = i === fromPosition.line ? fromPosition.character : lineText.length - 1;
for (let j = fromIndex; j >= 0; --j) {
if (lineText[j] === closingChar) {
// If closing and opening are the same, don't bother deducting n
// However if they are different, we need to deduct n when we see an opening char
if (lineText[j] === closingChar && closingChar !== openingChar) {
++n;
} else if (lineText[j] === openingChar) {
if (n === 0) {