solorice/vscode/extensions/vscodevim.vim-1.22.2/out/extensionBase.js
2022-04-28 20:54:44 +03:00

498 lines
23 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.registerEventListener = exports.registerCommand = exports.activate = exports.getAndUpdateModeHandler = void 0;
const vscode = require("vscode");
const compositionState_1 = require("./src/state/compositionState");
const globals_1 = require("./src/globals");
const jump_1 = require("./src/jumps/jump");
const modeHandlerMap_1 = require("./src/mode/modeHandlerMap");
const mode_1 = require("./src/mode/mode");
const notation_1 = require("./src/configuration/notation");
const logger_1 = require("./src/util/logger");
const statusBar_1 = require("./src/statusBar");
const vscodeContext_1 = require("./src/util/vscodeContext");
const commandLine_1 = require("./src/cmd_line/commandLine");
const configuration_1 = require("./src/configuration/configuration");
const globalState_1 = require("./src/state/globalState");
const taskQueue_1 = require("./src/taskQueue");
const register_1 = require("./src/register/register");
const specialKeys_1 = require("./src/util/specialKeys");
const historyTracker_1 = require("./src/history/historyTracker");
let extensionContext;
let previousActiveEditorUri;
let lastClosedModeHandler = null;
async function getAndUpdateModeHandler(forceSyncAndUpdate = false) {
const activeTextEditor = vscode.window.activeTextEditor;
if (activeTextEditor === undefined || activeTextEditor.document.isClosed) {
return undefined;
}
const uri = activeTextEditor.document.uri;
const [curHandler, isNew] = await modeHandlerMap_1.ModeHandlerMap.getOrCreate(uri);
if (isNew) {
extensionContext.subscriptions.push(curHandler);
}
curHandler.vimState.editor = activeTextEditor;
if (forceSyncAndUpdate || !previousActiveEditorUri || previousActiveEditorUri !== uri) {
// We sync the cursors here because ModeHandler is specific to a document, not an editor, so we
// need to update our representation of the cursors when switching between editors for the same document.
// This will be unnecessary once #4889 is fixed.
curHandler.syncCursors();
await curHandler.updateView({ drawSelection: false, revealRange: false });
}
previousActiveEditorUri = uri;
if (curHandler.focusChanged) {
curHandler.focusChanged = false;
if (previousActiveEditorUri) {
const prevHandler = modeHandlerMap_1.ModeHandlerMap.get(previousActiveEditorUri);
prevHandler.focusChanged = true;
}
}
return curHandler;
}
exports.getAndUpdateModeHandler = getAndUpdateModeHandler;
/**
* Loads and validates the user's configuration
*/
async function loadConfiguration() {
const validatorResults = await configuration_1.configuration.load();
logger_1.Logger.configChanged(configuration_1.configuration);
const logger = logger_1.Logger.get('Configuration');
logger.debug(`${validatorResults.numErrors} errors found with vim configuration`);
if (validatorResults.numErrors > 0) {
for (const validatorResult of validatorResults.get()) {
switch (validatorResult.level) {
case 'error':
logger.error(validatorResult.message);
break;
case 'warning':
logger.warn(validatorResult.message);
break;
}
}
}
}
/**
* The extension's entry point
*/
async function activate(context, handleLocal = true) {
// before we do anything else, we need to load the configuration
await loadConfiguration();
const logger = logger_1.Logger.get('Extension Startup');
logger.debug('Start');
extensionContext = context;
extensionContext.subscriptions.push(statusBar_1.StatusBar);
// Load state
register_1.Register.loadFromDisk(handleLocal);
await Promise.all([commandLine_1.ExCommandLine.loadHistory(context), commandLine_1.SearchCommandLine.loadHistory(context)]);
if (vscode.window.activeTextEditor) {
const filepathComponents = vscode.window.activeTextEditor.document.fileName.split(/\\|\//);
register_1.Register.setReadonlyRegister('%', filepathComponents[filepathComponents.length - 1]);
}
// workspace events
registerEventListener(context, vscode.workspace.onDidChangeConfiguration, async () => {
await loadConfiguration();
}, false);
registerEventListener(context, vscode.workspace.onDidChangeTextDocument, async (event) => {
const textWasDeleted = (changeEvent) => changeEvent.contentChanges.length === 1 &&
changeEvent.contentChanges[0].text === '' &&
changeEvent.contentChanges[0].range.start.line !==
changeEvent.contentChanges[0].range.end.line;
const textWasAdded = (changeEvent) => changeEvent.contentChanges.length === 1 &&
(changeEvent.contentChanges[0].text === '\n' ||
changeEvent.contentChanges[0].text === '\r\n') &&
changeEvent.contentChanges[0].range.start.line ===
changeEvent.contentChanges[0].range.end.line;
if (textWasDeleted(event)) {
globalState_1.globalState.jumpTracker.handleTextDeleted(event.document, event.contentChanges[0].range);
}
else if (textWasAdded(event)) {
globalState_1.globalState.jumpTracker.handleTextAdded(event.document, event.contentChanges[0].range, event.contentChanges[0].text);
}
// Change from VSCode editor should set document.isDirty to true but they initially don't!
// There is a timing issue in VSCode codebase between when the isDirty flag is set and
// when registered callbacks are fired. https://github.com/Microsoft/vscode/issues/11339
const contentChangeHandler = (modeHandler) => {
if (modeHandler.vimState.currentMode === mode_1.Mode.Insert) {
if (modeHandler.vimState.historyTracker.currentContentChanges === undefined) {
modeHandler.vimState.historyTracker.currentContentChanges = [];
}
modeHandler.vimState.historyTracker.currentContentChanges =
modeHandler.vimState.historyTracker.currentContentChanges.concat(event.contentChanges);
}
};
modeHandlerMap_1.ModeHandlerMap.getAll()
.filter((modeHandler) => modeHandler.vimState.documentUri === event.document.uri)
.forEach((modeHandler) => {
contentChangeHandler(modeHandler);
});
if (handleLocal) {
setTimeout(() => {
if (!event.document.isDirty &&
!event.document.isUntitled &&
event.document.uri.scheme !== 'vscode-notebook-cell' && // TODO: Notebooks never seem to be marked dirty...
event.contentChanges.length) {
handleContentChangedFromDisk(event.document);
}
}, 0);
}
});
registerEventListener(context, vscode.workspace.onDidCloseTextDocument, async (closedDocument) => {
const documents = vscode.workspace.textDocuments;
// Delete modehandler once all tabs of this document have been closed
for (const uri of modeHandlerMap_1.ModeHandlerMap.keys()) {
const modeHandler = modeHandlerMap_1.ModeHandlerMap.get(uri);
let shouldDelete = false;
if (modeHandler == null || modeHandler.vimState.editor === undefined) {
shouldDelete = true;
}
else {
const document = modeHandler.vimState.document;
if (!documents.includes(document)) {
shouldDelete = true;
if (closedDocument === document) {
lastClosedModeHandler = modeHandler;
}
}
}
if (shouldDelete) {
modeHandlerMap_1.ModeHandlerMap.delete(uri);
}
}
}, false);
// window events
registerEventListener(context, vscode.window.onDidChangeActiveTextEditor, async () => {
var _a;
const mhPrevious = previousActiveEditorUri
? modeHandlerMap_1.ModeHandlerMap.get(previousActiveEditorUri)
: undefined;
// Track the closed editor so we can use it the next time an open event occurs.
// When vscode changes away from a temporary file, onDidChangeActiveTextEditor first twice.
// First it fires when leaving the closed editor. Then onDidCloseTextDocument first, and we delete
// the old ModeHandler. Then a new editor opens.
//
// This also applies to files that are merely closed, which allows you to jump back to that file similarly
// once a new file is opened.
lastClosedModeHandler = mhPrevious || lastClosedModeHandler;
if (vscode.window.activeTextEditor === undefined) {
register_1.Register.setReadonlyRegister('%', '');
return;
}
const oldFileRegister = (_a = (await register_1.Register.get('%'))) === null || _a === void 0 ? void 0 : _a.text;
const relativePath = vscode.workspace.asRelativePath(vscode.window.activeTextEditor.document.uri, false);
if (relativePath !== oldFileRegister) {
if (oldFileRegister && oldFileRegister !== '') {
register_1.Register.setReadonlyRegister('#', oldFileRegister);
}
register_1.Register.setReadonlyRegister('%', relativePath);
}
taskQueue_1.taskQueue.enqueueTask(async () => {
const mh = await getAndUpdateModeHandler(true);
if (mh) {
globalState_1.globalState.jumpTracker.handleFileJump(lastClosedModeHandler ? jump_1.Jump.fromStateNow(lastClosedModeHandler.vimState) : null, jump_1.Jump.fromStateNow(mh.vimState));
}
});
}, true, true);
registerEventListener(context, vscode.window.onDidChangeTextEditorSelection, async (e) => {
if (vscode.window.activeTextEditor === undefined ||
e.textEditor.document !== vscode.window.activeTextEditor.document) {
// We don't care if user selection changed in a paneled window (e.g debug console/terminal)
return;
}
const mh = modeHandlerMap_1.ModeHandlerMap.get(vscode.window.activeTextEditor.document.uri);
if (mh === undefined) {
// We don't care if there is no active editor
return;
}
if (e.kind !== vscode.TextEditorSelectionChangeKind.Mouse) {
const selectionsHash = e.selections.reduce((hash, s) => hash +
`[${s.anchor.line}, ${s.anchor.character}; ${s.active.line}, ${s.active.character}]`, '');
const idx = mh.vimState.selectionsChanged.ourSelections.indexOf(selectionsHash);
if (idx > -1) {
mh.vimState.selectionsChanged.ourSelections.splice(idx, 1);
logger.debug(`Selections: Ignoring selection: ${selectionsHash}, Count left: ${mh.vimState.selectionsChanged.ourSelections.length}`);
return;
}
else if (mh.vimState.selectionsChanged.ignoreIntermediateSelections) {
logger.debug(`Selections: ignoring intermediate selection change: ${selectionsHash}`);
return;
}
else if (mh.vimState.selectionsChanged.ourSelections.length > 0) {
// Some intermediate selection must have slipped in after setting the
// 'ignoreIntermediateSelections' to false. Which means we didn't count
// for it yet, but since we have selections to be ignored then we probably
// wanted this one to be ignored as well.
logger.debug(`Selections: Ignoring slipped selection: ${selectionsHash}`);
return;
}
}
// We may receive changes from other panels when, having selections in them containing the same file
// and changing text before the selection in current panel.
if (e.textEditor !== mh.vimState.editor) {
return;
}
if (mh.focusChanged) {
mh.focusChanged = false;
return;
}
if (mh.currentMode === mode_1.Mode.EasyMotionMode) {
return;
}
taskQueue_1.taskQueue.enqueueTask(() => mh.handleSelectionChange(e));
}, true, false);
registerEventListener(context, vscode.window.onDidChangeTextEditorVisibleRanges, async (e) => {
taskQueue_1.taskQueue.enqueueTask(async () => {
// Scrolling the viewport clears any status bar message, even errors.
const mh = await getAndUpdateModeHandler();
if (mh && statusBar_1.StatusBar.lastMessageTime) {
// TODO: Using the time elapsed works most of the time, but is a bit of a hack
const timeElapsed = Number(new Date()) - Number(statusBar_1.StatusBar.lastMessageTime);
if (timeElapsed > 100) {
statusBar_1.StatusBar.clear(mh.vimState, true);
}
}
});
});
const compositionState = new compositionState_1.CompositionState();
// Override VSCode commands
overrideCommand(context, 'type', async (args) => {
taskQueue_1.taskQueue.enqueueTask(async () => {
const mh = await getAndUpdateModeHandler();
if (mh) {
if (compositionState.isInComposition) {
compositionState.composingText += args.text;
if (mh.vimState.currentMode === mode_1.Mode.Insert) {
compositionState.insertedText = true;
vscode.commands.executeCommand('default:type', { text: args.text });
}
}
else {
await mh.handleKeyEvent(args.text);
}
}
});
});
overrideCommand(context, 'replacePreviousChar', async (args) => {
taskQueue_1.taskQueue.enqueueTask(async () => {
const mh = await getAndUpdateModeHandler();
if (mh) {
if (compositionState.isInComposition) {
compositionState.composingText =
compositionState.composingText.substr(0, compositionState.composingText.length - args.replaceCharCnt) + args.text;
}
if (compositionState.insertedText) {
await vscode.commands.executeCommand('default:replacePreviousChar', {
text: args.text,
replaceCharCnt: args.replaceCharCnt,
});
mh.vimState.cursorStopPosition = mh.vimState.editor.selection.start;
mh.vimState.cursorStartPosition = mh.vimState.editor.selection.start;
}
}
else {
await vscode.commands.executeCommand('default:replacePreviousChar', {
text: args.text,
replaceCharCnt: args.replaceCharCnt,
});
}
});
});
overrideCommand(context, 'compositionStart', async () => {
taskQueue_1.taskQueue.enqueueTask(async () => {
compositionState.isInComposition = true;
});
});
overrideCommand(context, 'compositionEnd', async () => {
taskQueue_1.taskQueue.enqueueTask(async () => {
const mh = await getAndUpdateModeHandler();
if (mh) {
if (compositionState.insertedText) {
mh.vimState.selectionsChanged.ignoreIntermediateSelections = true;
await vscode.commands.executeCommand('default:replacePreviousChar', {
text: '',
replaceCharCnt: compositionState.composingText.length,
});
mh.vimState.cursorStopPosition = mh.vimState.editor.selection.active;
mh.vimState.cursorStartPosition = mh.vimState.editor.selection.active;
mh.vimState.selectionsChanged.ignoreIntermediateSelections = false;
}
const text = compositionState.composingText;
await mh.handleMultipleKeyEvents(text.split(''));
}
compositionState.reset();
});
});
// Register extension commands
registerCommand(context, 'vim.showQuickpickCmdLine', async () => {
const mh = await getAndUpdateModeHandler();
if (mh) {
const cmd = await vscode.window.showInputBox({
prompt: 'Vim command line',
value: '',
ignoreFocusOut: false,
valueSelection: [0, 0],
});
if (cmd) {
await new commandLine_1.ExCommandLine(cmd, mh.vimState.currentMode).run(mh.vimState);
}
mh.updateView();
}
});
registerCommand(context, 'vim.remap', async (args) => {
taskQueue_1.taskQueue.enqueueTask(async () => {
const mh = await getAndUpdateModeHandler();
if (mh === undefined) {
return;
}
if (!args) {
throw new Error("'args' is undefined. For this remap to work it needs to have 'args' with an '\"after\": string[]' and/or a '\"commands\": { command: string; args: any[] }[]'");
}
if (args.after) {
for (const key of args.after) {
await mh.handleKeyEvent(notation_1.Notation.NormalizeKey(key, configuration_1.configuration.leader));
}
}
if (args.commands) {
for (const command of args.commands) {
// Check if this is a vim command by looking for :
if (command.command.startsWith(':')) {
await new commandLine_1.ExCommandLine(command.command.slice(1, command.command.length), mh.vimState.currentMode).run(mh.vimState);
mh.updateView();
}
else {
vscode.commands.executeCommand(command.command, command.args);
}
}
}
});
});
registerCommand(context, 'toggleVim', async () => {
configuration_1.configuration.disableExtension = !configuration_1.configuration.disableExtension;
toggleExtension(configuration_1.configuration.disableExtension, compositionState);
});
for (const boundKey of configuration_1.configuration.boundKeyCombinations) {
const command = ['<Esc>', '<C-c>'].includes(boundKey.key)
? async () => {
const mh = await getAndUpdateModeHandler();
if (mh && !(await forceStopRecursiveRemap(mh))) {
await mh.handleKeyEvent(`${boundKey.key}`);
}
}
: async () => {
const mh = await getAndUpdateModeHandler();
if (mh) {
await mh.handleKeyEvent(`${boundKey.key}`);
}
};
registerCommand(context, boundKey.command, async () => {
taskQueue_1.taskQueue.enqueueTask(command);
});
}
{
// Initialize mode handler for current active Text Editor at startup.
const modeHandler = await getAndUpdateModeHandler();
if (modeHandler) {
if (!configuration_1.configuration.startInInsertMode) {
const vimState = modeHandler.vimState;
// Make sure no cursors start on the EOL character (which is invalid in normal mode)
// This can happen if we quit last session in insert mode at the end of the line
vimState.cursors = vimState.cursors.map((cursor) => {
const eolColumn = vimState.document.lineAt(cursor.stop).text.length;
if (cursor.stop.character >= eolColumn) {
const character = Math.max(eolColumn - 1, 0);
return cursor.withNewStop(cursor.stop.with({ character }));
}
else {
return cursor;
}
});
}
// This is called last because getAndUpdateModeHandler() will change cursor
modeHandler.updateView({ drawSelection: true, revealRange: false });
}
}
// Disable automatic keyboard navigation in lists, so it doesn't interfere
// with our list navigation keybindings
await vscodeContext_1.VSCodeContext.set('listAutomaticKeyboardNavigation', false);
await toggleExtension(configuration_1.configuration.disableExtension, compositionState);
logger.debug('Finish.');
}
exports.activate = activate;
/**
* Toggles the VSCodeVim extension between Enabled mode and Disabled mode. This
* function is activated by calling the 'toggleVim' command from the Command Palette.
*
* @param isDisabled if true, sets VSCodeVim to Disabled mode; else sets to enabled mode
*/
async function toggleExtension(isDisabled, compositionState) {
await vscodeContext_1.VSCodeContext.set('vim.active', !isDisabled);
const mh = await getAndUpdateModeHandler();
if (mh) {
if (isDisabled) {
await mh.handleKeyEvent(specialKeys_1.SpecialKeys.ExtensionDisable);
compositionState.reset();
modeHandlerMap_1.ModeHandlerMap.clear();
}
else {
await mh.handleKeyEvent(specialKeys_1.SpecialKeys.ExtensionEnable);
}
}
}
function overrideCommand(context, command, callback) {
const disposable = vscode.commands.registerCommand(command, async (args) => {
if (configuration_1.configuration.disableExtension) {
return vscode.commands.executeCommand('default:' + command, args);
}
if (!vscode.window.activeTextEditor) {
return;
}
if (vscode.window.activeTextEditor.document &&
vscode.window.activeTextEditor.document.uri.toString() === 'debug:input') {
return vscode.commands.executeCommand('default:' + command, args);
}
return callback(args);
});
context.subscriptions.push(disposable);
}
function registerCommand(context, command, callback, requiresActiveEditor = true) {
const disposable = vscode.commands.registerCommand(command, async (args) => {
if (requiresActiveEditor && !vscode.window.activeTextEditor) {
return;
}
callback(args);
});
context.subscriptions.push(disposable);
}
exports.registerCommand = registerCommand;
function registerEventListener(context, event, listener, exitOnExtensionDisable = true, exitOnTests = false) {
const disposable = event(async (e) => {
if (exitOnExtensionDisable && configuration_1.configuration.disableExtension) {
return;
}
if (exitOnTests && globals_1.Globals.isTesting) {
return;
}
listener(e);
});
context.subscriptions.push(disposable);
}
exports.registerEventListener = registerEventListener;
/**
* @returns true if there was a remap being executed to stop
*/
async function forceStopRecursiveRemap(mh) {
if (mh.remapState.isCurrentlyPerformingRecursiveRemapping) {
mh.remapState.forceStopRecursiveRemapping = true;
return true;
}
return false;
}
function handleContentChangedFromDisk(document) {
modeHandlerMap_1.ModeHandlerMap.getAll()
.filter((modeHandler) => modeHandler.vimState.documentUri === document.uri)
.forEach((modeHandler) => {
modeHandler.vimState.historyTracker = new historyTracker_1.HistoryTracker(modeHandler.vimState);
});
}
//# sourceMappingURL=extensionBase.js.map