mirror of
https://github.com/jasonwilliams/vscode-helix.git
synced 2024-08-17 04:10:46 +03:00
Compare commits
5 Commits
fbf35b4e1d
...
a2417223bb
Author | SHA1 | Date | |
---|---|---|---|
|
a2417223bb | ||
|
79195ce1b4 | ||
|
4ec1caf239 | ||
|
e17dcb808f | ||
|
9b7bd51351 |
17
package.json
17
package.json
@ -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": {
|
||||
|
@ -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());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
10
src/index.ts
10
src/index.ts
@ -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);
|
||||
|
@ -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');
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user