mirror of
https://github.com/Silverquark/dance.git
synced 2024-09-11 18:48:00 +03:00
misc: add registers view
This commit is contained in:
parent
042a044936
commit
db2fbbfb60
1
assets/dance-white.svg
Normal file
1
assets/dance-white.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 288 288" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><g id="Logo"><path id="D" d="M72,250.738l0,-213.476c0,0 144.788,-20.828 143.997,106.738c-0.791,127.566 -143.997,106.738 -143.997,106.738Z" style="fill:none;"/><clipPath id="_clip1"><path d="M72,250.738l0,-213.476c0,0 144.788,-20.828 143.997,106.738c-0.791,127.566 -143.997,106.738 -143.997,106.738Z"/></clipPath><g clip-path="url(#_clip1)"><g id="Lines"><g id="Black-lines" serif:id="Black lines"><g id="L6"><rect id="L1" x="72" y="218.4" width="144" height="25.2" style="fill:url(#_Linear2);"/></g><g id="L5"><rect id="L11" serif:id="L1" x="72" y="183.6" width="144" height="25.2" style="fill:url(#_Linear3);"/></g><g id="L4"><rect id="L12" serif:id="L1" x="72" y="148.8" width="144" height="25.2" style="fill:url(#_Linear4);"/></g><g id="L3"><rect id="L13" serif:id="L1" x="72" y="114" width="144" height="25.2" style="fill:url(#_Linear5);"/></g><g id="L2"><rect id="L14" serif:id="L1" x="72" y="79.2" width="144" height="25.2" style="fill:url(#_Linear6);"/></g><g id="L15" serif:id="L1"><rect id="L16" serif:id="L1" x="72" y="44.4" width="144" height="25.2" style="fill:url(#_Linear7);"/></g></g><g id="Gaps"><g id="L61" serif:id="L6"><rect x="127.2" y="217.2" width="9.6" height="27.6" style="fill:#f2f2f2;"/></g><g id="L51" serif:id="L5"><rect x="164.4" y="182.4" width="9.6" height="27.6" style="fill:#f2f2f2;"/></g><g id="L52" serif:id="L5"><rect x="100.8" y="182.4" width="9.6" height="27.6" style="fill:#f2f2f2;"/></g><g id="L41" serif:id="L4"><rect x="180" y="147.6" width="9.6" height="27.6" style="fill:#f2f2f2;"/></g><g id="L42" serif:id="L4"><rect x="112.8" y="147.6" width="9.6" height="27.6" style="fill:#f2f2f2;"/></g><g id="L21" serif:id="L2"><rect x="144" y="78" width="9.6" height="27.6" style="fill:#f2f2f2;"/></g><g id="L22" serif:id="L2"><rect x="93.6" y="78" width="9.6" height="27.6" style="fill:#f2f2f2;"/></g><g id="L17" serif:id="L1"><rect x="120" y="43.2" width="9.6" height="27.6" style="fill:#f2f2f2;"/></g></g></g></g><g id="Highlight"><g id="L53" serif:id="L5"><g><rect x="158.4" y="181.2" width="6" height="30" style="fill:#fff;"/></g><g><rect x="110.4" y="183.6" width="48" height="25.2" style="fill:#fff;"/></g></g><g id="L31" serif:id="L3"><g><rect x="216" y="111.6" width="6" height="30" style="fill:#fff;"/></g><g><rect x="72" y="114" width="144" height="25.2" style="fill:#fff;"/></g></g></g></g><defs><linearGradient id="_Linear2" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(288,0,0,40.0288,72,238.414)"><stop offset="0" style="stop-color:#fff;stop-opacity:1"/><stop offset="1" style="stop-color:#fff;stop-opacity:1"/></linearGradient><linearGradient id="_Linear3" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(288,0,0,40.0288,72,203.614)"><stop offset="0" style="stop-color:#fff;stop-opacity:1"/><stop offset="1" style="stop-color:#fff;stop-opacity:1"/></linearGradient><linearGradient id="_Linear4" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(288,0,0,40.0288,72,168.814)"><stop offset="0" style="stop-color:#fff;stop-opacity:1"/><stop offset="1" style="stop-color:#fff;stop-opacity:1"/></linearGradient><linearGradient id="_Linear5" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(288,0,0,40.0288,72,134.014)"><stop offset="0" style="stop-color:#fff;stop-opacity:1"/><stop offset="1" style="stop-color:#fff;stop-opacity:1"/></linearGradient><linearGradient id="_Linear6" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(288,0,0,40.0288,72,99.2144)"><stop offset="0" style="stop-color:#fff;stop-opacity:1"/><stop offset="1" style="stop-color:#fff;stop-opacity:1"/></linearGradient><linearGradient id="_Linear7" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(288,0,0,40.0288,72,64.4144)"><stop offset="0" style="stop-color:#fff;stop-opacity:1"/><stop offset="1" style="stop-color:#fff;stop-opacity:1"/></linearGradient></defs></svg>
|
After Width: | Height: | Size: 4.3 KiB |
Binary file not shown.
@ -666,6 +666,28 @@ export const pkg = (modules: Builder.ParsedModule[]) => ({
|
||||
},
|
||||
},
|
||||
|
||||
// Views.
|
||||
// ========================================================================
|
||||
|
||||
viewsContainers: {
|
||||
activitybar: [
|
||||
{
|
||||
id: "dance",
|
||||
title: "Dance",
|
||||
icon: "assets/dance-white.svg",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
views: {
|
||||
dance: [
|
||||
{
|
||||
id: "registers",
|
||||
name: "Registers",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Commands.
|
||||
// ========================================================================
|
||||
|
||||
|
17
package.json
generated
17
package.json
generated
@ -867,6 +867,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"viewsContainers": {
|
||||
"activitybar": [
|
||||
{
|
||||
"id": "dance",
|
||||
"title": "Dance",
|
||||
"icon": "assets/dance-white.svg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"views": {
|
||||
"dance": [
|
||||
{
|
||||
"id": "registers",
|
||||
"name": "Registers"
|
||||
}
|
||||
]
|
||||
},
|
||||
"commands": [
|
||||
{
|
||||
"command": "dance.dev.copyLastErrorMessage",
|
||||
|
@ -11,6 +11,7 @@ import { extensionName } from "../utils/constants";
|
||||
import { AutoDisposable } from "../utils/disposables";
|
||||
import { assert, CancellationError } from "../utils/errors";
|
||||
import { SettingsValidator } from "../utils/settings-validator";
|
||||
import { RegistersView } from "./registers-view";
|
||||
|
||||
// ===============================================================================================
|
||||
// == EXTENSION ================================================================================
|
||||
@ -167,6 +168,9 @@ export class Extension implements vscode.Disposable {
|
||||
for (const descriptor of Object.values(commands)) {
|
||||
this._subscriptions.push(descriptor.register(this));
|
||||
}
|
||||
|
||||
// Render views.
|
||||
this._subscriptions.push(new RegistersView(this.registers).register());
|
||||
}
|
||||
|
||||
/**
|
||||
|
244
src/state/registers-view.ts
Normal file
244
src/state/registers-view.ts
Normal file
@ -0,0 +1,244 @@
|
||||
import * as vscode from "vscode";
|
||||
import { Register, Registers, RegisterSet } from "./registers";
|
||||
|
||||
/**
|
||||
* A {@link vscode.TreeDataProvider} for Dance registers.
|
||||
*/
|
||||
export class RegistersView implements vscode.TreeDataProvider<TreeItem> {
|
||||
private readonly _onDidChangeTreeData = new vscode.EventEmitter<TreeItem | undefined>();
|
||||
|
||||
private readonly _registerToItemMap = new Map<Register, RegisterTreeItem>();
|
||||
private readonly _documentToItemMap = new Map<vscode.TextDocument, RegisterSetTreeItem>();
|
||||
private _globalDocumentItem?: RegisterSetTreeItem;
|
||||
|
||||
public readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
|
||||
|
||||
public get isActive() {
|
||||
return this._globalDocumentItem !== undefined;
|
||||
}
|
||||
|
||||
public constructor(
|
||||
public readonly registers: Registers,
|
||||
) {}
|
||||
|
||||
public getTreeItem(element: TreeItem) {
|
||||
return element;
|
||||
}
|
||||
|
||||
public async getChildren(element?: TreeItem): Promise<vscode.TreeItem[]> {
|
||||
if (element === undefined) {
|
||||
if (this._globalDocumentItem === undefined) {
|
||||
this._globalDocumentItem = new RegisterSetTreeItem(
|
||||
this.registers,
|
||||
() => this._onDidChangeTreeData.fire(this._globalDocumentItem),
|
||||
);
|
||||
}
|
||||
|
||||
const document = vscode.window.activeTextEditor?.document;
|
||||
|
||||
return document === undefined
|
||||
? [this._globalDocumentItem]
|
||||
: [this._globalDocumentItem, this._itemForDocument(document)];
|
||||
}
|
||||
|
||||
if (element instanceof ValueTreeItem) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (element instanceof RegisterTreeItem) {
|
||||
return await element.values();
|
||||
}
|
||||
|
||||
let registers = [...element.registers.registers]
|
||||
.filter((r) => r.iconName !== undefined && r.canRead());
|
||||
|
||||
if (element !== this._globalDocumentItem) {
|
||||
registers = registers.filter((r) => /^[a-zA-Z]|.{2,}$/.test(r.name));
|
||||
}
|
||||
|
||||
const items = registers
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
.map((r) => this._itemForRegister(r as Register & Register.Readable, element));
|
||||
const shouldShowItem = await Promise.all(items.map(async (item) => await item.shouldShow()));
|
||||
|
||||
return items.filter((_, i) => shouldShowItem[i]);
|
||||
}
|
||||
|
||||
private _itemForDocument(document: vscode.TextDocument) {
|
||||
const existing = this._documentToItemMap.get(document);
|
||||
|
||||
if (existing !== undefined) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
const item: RegisterSetTreeItem = new RegisterSetTreeItem(
|
||||
this.registers.forDocument(document),
|
||||
() => this._onDidChangeTreeData.fire(item),
|
||||
document,
|
||||
);
|
||||
|
||||
this._documentToItemMap.set(document, item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private _itemForRegister(register: Register & Register.Readable, documentItem: RegisterSetTreeItem) {
|
||||
const existing = this._registerToItemMap.get(register);
|
||||
|
||||
if (existing !== undefined) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
const item: RegisterTreeItem = new RegisterTreeItem(
|
||||
register,
|
||||
async (wasShown) => {
|
||||
this._onDidChangeTreeData.fire(item);
|
||||
|
||||
if (await item.shouldShow() !== wasShown) {
|
||||
this._onDidChangeTreeData.fire(documentItem);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
this._registerToItemMap.set(register, item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public register(): vscode.Disposable {
|
||||
const treeDataProviderDisposable = vscode.window.createTreeView("registers", {
|
||||
treeDataProvider: this,
|
||||
showCollapseAll: true,
|
||||
}),
|
||||
editorChangeDisposable = vscode.window.onDidChangeActiveTextEditor(() =>
|
||||
this.isActive && this._onDidChangeTreeData.fire(undefined),
|
||||
),
|
||||
documentChangeDisposable = vscode.workspace.onDidCloseTextDocument((e) => {
|
||||
if (!this.isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._documentToItemMap.get(e)?.dispose();
|
||||
this._documentToItemMap.delete(e);
|
||||
|
||||
for (const register of this.registers.forDocument(e).registers) {
|
||||
this._registerToItemMap.get(register)?.dispose();
|
||||
this._registerToItemMap.delete(register);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
dispose: () => {
|
||||
for (const item of this._documentToItemMap.values()) {
|
||||
item.dispose()
|
||||
}
|
||||
|
||||
for (const item of this._registerToItemMap.values()) {
|
||||
item.dispose();
|
||||
}
|
||||
|
||||
this._globalDocumentItem?.dispose();
|
||||
this._documentToItemMap.clear();
|
||||
this._registerToItemMap.clear();
|
||||
|
||||
treeDataProviderDisposable.dispose();
|
||||
editorChangeDisposable.dispose();
|
||||
documentChangeDisposable.dispose();
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class RegisterSetTreeItem extends vscode.TreeItem implements vscode.Disposable {
|
||||
private readonly _disposable: vscode.Disposable;
|
||||
|
||||
public constructor(
|
||||
public readonly registers: RegisterSet,
|
||||
notifyChange: () => void,
|
||||
document?: vscode.TextDocument,
|
||||
) {
|
||||
super(
|
||||
document?.fileName ?? "Global",
|
||||
vscode.TreeItemCollapsibleState.Expanded,
|
||||
);
|
||||
|
||||
this.iconPath = new vscode.ThemeIcon(document === undefined ? "root-folder" : "folder-active");
|
||||
|
||||
this._disposable = registers.onRegisterChange(() => notifyChange());
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
this._disposable.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class RegisterTreeItem extends vscode.TreeItem implements vscode.Disposable {
|
||||
private readonly _disposable: vscode.Disposable;
|
||||
|
||||
private _shouldShow?: Thenable<boolean>;
|
||||
private _values?: Thenable<ValueTreeItem[]>;
|
||||
|
||||
public constructor(
|
||||
public readonly register: Register & Register.Readable,
|
||||
notifyChange: (wasVisible: boolean) => void,
|
||||
) {
|
||||
super(
|
||||
register.name,
|
||||
/^(["/@^|.]|[a-zA-Z0-9]+)$/.test(register.name)
|
||||
? vscode.TreeItemCollapsibleState.Expanded
|
||||
: vscode.TreeItemCollapsibleState.Collapsed,
|
||||
);
|
||||
|
||||
if (register.iconName !== undefined) {
|
||||
this.iconPath = new vscode.ThemeIcon(register.iconName);
|
||||
}
|
||||
|
||||
this._disposable = register.onChange(async () => {
|
||||
const wasVisible = await this.shouldShow();
|
||||
|
||||
this._shouldShow = undefined;
|
||||
this._values = undefined;
|
||||
|
||||
notifyChange(wasVisible);
|
||||
});
|
||||
}
|
||||
|
||||
private _shouldShowEmpty() {
|
||||
return /^["/@^|.]$/.test(this.register.name);
|
||||
}
|
||||
|
||||
public async shouldShow() {
|
||||
if (this._shouldShowEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this._shouldShow === undefined) {
|
||||
this._shouldShow = this.values().then((values) => values.length > 0);
|
||||
}
|
||||
|
||||
return await this._shouldShow;
|
||||
}
|
||||
|
||||
public async values() {
|
||||
if (this._values === undefined) {
|
||||
this._values = this.register.get()
|
||||
.then((values) => values?.map((v) => new ValueTreeItem(v)) ?? []);
|
||||
}
|
||||
|
||||
return await this._values;
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
this._disposable.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class ValueTreeItem extends vscode.TreeItem {
|
||||
public constructor(label: string) {
|
||||
super(label, vscode.TreeItemCollapsibleState.None);
|
||||
|
||||
this.iconPath = new vscode.ThemeIcon("symbol-string");
|
||||
}
|
||||
}
|
||||
|
||||
type TreeItem = RegisterSetTreeItem | RegisterTreeItem | ValueTreeItem;
|
@ -10,16 +10,34 @@ import type * as TrackedSelection from "../utils/tracked-selection";
|
||||
* The base class for all registers.
|
||||
*/
|
||||
export abstract class Register {
|
||||
private readonly _onChangeEvent = new vscode.EventEmitter<Register.ChangeKind>();
|
||||
|
||||
/**
|
||||
* The name of the register.
|
||||
*/
|
||||
public readonly abstract name: string;
|
||||
|
||||
/**
|
||||
* The name of the icon of the register, as seen in the [VS Code product icon
|
||||
* reference](https://code.visualstudio.com/api/references/icons-in-labels#icon-listing).
|
||||
*
|
||||
* `undefined` if the register shouldn't be displayed.
|
||||
*/
|
||||
public readonly abstract iconName: string | undefined;
|
||||
|
||||
/**
|
||||
* The flags of the register, which define what a register can do.
|
||||
*/
|
||||
public readonly abstract flags: Register.Flags;
|
||||
|
||||
/**
|
||||
* Event fired when the contents, selections or recording of the register
|
||||
* change.
|
||||
*/
|
||||
public get onChange() {
|
||||
return this._onChangeEvent.event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the register is readable.
|
||||
*/
|
||||
@ -146,6 +164,13 @@ export abstract class Register {
|
||||
|
||||
return this as any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies listeners that a {@link onChange change} occured to the register.
|
||||
*/
|
||||
protected notifyChange(kind: Register.ChangeKind) {
|
||||
this._onChangeEvent.fire(kind);
|
||||
}
|
||||
}
|
||||
|
||||
export declare namespace Register {
|
||||
@ -210,6 +235,12 @@ export declare namespace Register {
|
||||
getRecording(): Recording | undefined;
|
||||
setRecording(recording: Recording): void;
|
||||
}
|
||||
|
||||
export const enum ChangeKind {
|
||||
Contents,
|
||||
Selections,
|
||||
Recording,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -232,12 +263,14 @@ class GeneralPurposeRegister extends Register implements Register.Readable,
|
||||
|
||||
public constructor(
|
||||
public readonly name: string,
|
||||
public readonly iconName: string,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
public set(values: readonly string[]) {
|
||||
this._values = values;
|
||||
this.notifyChange(Register.ChangeKind.Contents);
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
@ -258,6 +291,7 @@ class GeneralPurposeRegister extends Register implements Register.Readable,
|
||||
const previousSelectionSet = this._selections;
|
||||
|
||||
this._selections = trackedSelections;
|
||||
this.notifyChange(Register.ChangeKind.Selections);
|
||||
|
||||
return previousSelectionSet;
|
||||
}
|
||||
@ -268,6 +302,7 @@ class GeneralPurposeRegister extends Register implements Register.Readable,
|
||||
|
||||
public setRecording(recording: Recording) {
|
||||
this._recording = recording;
|
||||
this.notifyChange(Register.ChangeKind.Recording);
|
||||
}
|
||||
}
|
||||
|
||||
@ -280,10 +315,20 @@ class SpecialRegister extends Register implements Register.Readable,
|
||||
? Register.Flags.CanRead
|
||||
: Register.Flags.CanRead | Register.Flags.CanWrite;
|
||||
|
||||
public override get onChange(): vscode.Event<Register.ChangeKind> {
|
||||
if (this._listenToChanges === undefined) {
|
||||
return super.onChange;
|
||||
}
|
||||
|
||||
return (listener) => this._listenToChanges!(() => listener(Register.ChangeKind.Contents));
|
||||
}
|
||||
|
||||
public constructor(
|
||||
public readonly name: string,
|
||||
public readonly iconName: string | undefined,
|
||||
public readonly getter: () => Thenable<readonly string[]>,
|
||||
public readonly setter?: (values: readonly string[]) => Thenable<void>,
|
||||
private readonly _listenToChanges?: (fire: () => void) => vscode.Disposable,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@ -292,12 +337,14 @@ class SpecialRegister extends Register implements Register.Readable,
|
||||
return this.getter();
|
||||
}
|
||||
|
||||
public set(values: readonly string[]) {
|
||||
public async set(values: readonly string[]) {
|
||||
if (this.setter === undefined) {
|
||||
throw new Error("cannot set read-only register");
|
||||
}
|
||||
|
||||
return this.setter(values);
|
||||
await this.setter(values);
|
||||
|
||||
this.notifyChange(Register.ChangeKind.Contents);
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,6 +357,7 @@ class ClipboardRegister extends Register implements Register.Readable,
|
||||
private _lastRawText?: string;
|
||||
|
||||
public readonly name = '"';
|
||||
public readonly iconName = "clippy";
|
||||
public readonly flags = Register.Flags.CanRead | Register.Flags.CanWrite;
|
||||
|
||||
public get() {
|
||||
@ -327,6 +375,7 @@ class ClipboardRegister extends Register implements Register.Readable,
|
||||
|
||||
this._lastStrings = values;
|
||||
this._lastRawText = values.join(newline);
|
||||
this.notifyChange(Register.ChangeKind.Contents);
|
||||
|
||||
return vscode.env.clipboard.writeText(this._lastRawText);
|
||||
}
|
||||
@ -343,19 +392,43 @@ function activeEditor() {
|
||||
/**
|
||||
* A set of registers.
|
||||
*/
|
||||
export abstract class RegisterSet {
|
||||
export abstract class RegisterSet implements vscode.Disposable {
|
||||
private readonly _onRegisterChange
|
||||
= new vscode.EventEmitter<{ register: Register; kind: "added" | "removed"; }>();
|
||||
private readonly _onLastMatchesChange = new vscode.EventEmitter<void>();
|
||||
|
||||
private readonly _named = new Map<string, Register>();
|
||||
private readonly _letters = Array.from(
|
||||
{ length: 26 },
|
||||
(_, i) => new GeneralPurposeRegister(String.fromCharCode(97 + i)) as Register,
|
||||
(_, i) => new GeneralPurposeRegister(String.fromCharCode(97 + i), "symbol-text") as Register,
|
||||
);
|
||||
private readonly _digits = Array.from(
|
||||
{ length: 10 },
|
||||
(_, i) => new SpecialRegister((i + 1).toString(), () => Promise.resolve(this._lastMatches[i])),
|
||||
{ length: 9 },
|
||||
(_, i) => new SpecialRegister(
|
||||
(i + 1).toString(),
|
||||
"regex",
|
||||
() => Promise.resolve(this._lastMatches[i]),
|
||||
undefined,
|
||||
(fire) => this._onLastMatchesChange.event(fire),
|
||||
),
|
||||
);
|
||||
|
||||
private _lastMatches: readonly (readonly string[])[] = [];
|
||||
|
||||
/**
|
||||
* The set of registers.
|
||||
*/
|
||||
public get registers() {
|
||||
return new Set(this._named.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Event fired when a change to the set occurs.
|
||||
*/
|
||||
public get onRegisterChange() {
|
||||
return this._onRegisterChange.event;
|
||||
}
|
||||
|
||||
/**
|
||||
* The '"' (`dquote`) register, mapped to the system clipboard and default
|
||||
* register for edit operations.
|
||||
@ -365,29 +438,30 @@ export abstract class RegisterSet {
|
||||
/**
|
||||
* The "/" (`slash`) register, default register for search / regex operations.
|
||||
*/
|
||||
public readonly slash = new GeneralPurposeRegister("/");
|
||||
public readonly slash = new GeneralPurposeRegister("/", "search-view-icon");
|
||||
|
||||
/**
|
||||
* The "@" (`arobase`) register, default register for recordings (aka macros).
|
||||
*/
|
||||
public readonly arobase = new GeneralPurposeRegister("@");
|
||||
public readonly arobase = new GeneralPurposeRegister("@", "record");
|
||||
|
||||
/**
|
||||
* The "^" (`caret`) register, default register for saving selections.
|
||||
*/
|
||||
public readonly caret = new GeneralPurposeRegister("^");
|
||||
public readonly caret = new GeneralPurposeRegister("^", "save");
|
||||
|
||||
/**
|
||||
* The "|" (`pipe`) register, default register for outputs of external
|
||||
* commands.
|
||||
*/
|
||||
public readonly pipe = new GeneralPurposeRegister("|");
|
||||
public readonly pipe = new GeneralPurposeRegister("|", "console");
|
||||
|
||||
/**
|
||||
* The "%" (`percent`) register, mapped to the name of the current document.
|
||||
*/
|
||||
public readonly percent = new SpecialRegister(
|
||||
"%",
|
||||
"file",
|
||||
() => Promise.resolve([activeEditor().document.fileName]),
|
||||
(values) => {
|
||||
if (values.length !== 1) {
|
||||
@ -396,6 +470,7 @@ export abstract class RegisterSet {
|
||||
|
||||
return vscode.workspace.openTextDocument(values[0]).then(() => {});
|
||||
},
|
||||
(fire) => vscode.window.onDidChangeActiveTextEditor(fire),
|
||||
);
|
||||
|
||||
/**
|
||||
@ -403,6 +478,7 @@ export abstract class RegisterSet {
|
||||
*/
|
||||
public readonly dot = new SpecialRegister(
|
||||
".",
|
||||
"selection",
|
||||
() => {
|
||||
const editor = activeEditor(),
|
||||
document = editor.document,
|
||||
@ -432,20 +508,25 @@ export abstract class RegisterSet {
|
||||
}
|
||||
}, noUndoStops).then((succeeded) => EditNotAppliedError.throwIfNotApplied(succeeded));
|
||||
},
|
||||
(fire) => vscode.window.onDidChangeTextEditorSelection(fire),
|
||||
);
|
||||
|
||||
/**
|
||||
* The read-only "#" (`hash`) register, mapped to the indices of the current
|
||||
* selections.
|
||||
*/
|
||||
public readonly hash = new SpecialRegister("#", () =>
|
||||
Promise.resolve(activeEditor().selections.map((_, i) => i.toString())),
|
||||
public readonly hash = new SpecialRegister("#", "symbol-numeric",
|
||||
() => Promise.resolve(activeEditor().selections.map((_, i) => i.toString())),
|
||||
undefined,
|
||||
(fire) => vscode.window.onDidChangeTextEditorSelection(fire),
|
||||
);
|
||||
|
||||
/**
|
||||
* The read-only "_" (`underscore`) register, mapped to an empty string.
|
||||
*/
|
||||
public readonly underscore = new SpecialRegister("_", () => Promise.resolve([""]));
|
||||
public readonly underscore = new SpecialRegister("_", undefined, () =>
|
||||
Promise.resolve([""]),
|
||||
);
|
||||
|
||||
/**
|
||||
* The ":" (`colon`) register.
|
||||
@ -453,7 +534,7 @@ export abstract class RegisterSet {
|
||||
* In Kakoune it is mapped to the last entered command, but since we don't
|
||||
* have access to that information in Dance, we map it to a prompt.
|
||||
*/
|
||||
public readonly colon = new SpecialRegister(":", () =>
|
||||
public readonly colon = new SpecialRegister(":", undefined, () =>
|
||||
prompt({ prompt: ":" }).then((result) => [result]),
|
||||
);
|
||||
|
||||
@ -463,6 +544,7 @@ export abstract class RegisterSet {
|
||||
*/
|
||||
public readonly null = new SpecialRegister(
|
||||
"null",
|
||||
undefined,
|
||||
() => Promise.resolve([]),
|
||||
() => Promise.resolve(),
|
||||
);
|
||||
@ -484,10 +566,25 @@ export abstract class RegisterSet {
|
||||
this._named.set(longName, register);
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._digits.length; i++) {
|
||||
this._named.set(`${i + 1}`, this._digits[i]);
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._letters.length; i++) {
|
||||
const letter = this._letters[i];
|
||||
|
||||
this._named.set(String.fromCharCode(i + 65), letter);
|
||||
this._named.set(String.fromCharCode(i + 97), letter);
|
||||
}
|
||||
|
||||
this._named.set("", this.null);
|
||||
this._named.set("null", this.null);
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
this._onRegisterChange.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the register with the given name or identified by the given key if
|
||||
* the input is one-character long.
|
||||
@ -520,14 +617,6 @@ export abstract class RegisterSet {
|
||||
return this.colon;
|
||||
|
||||
default:
|
||||
if (charCode >= 97 /* a */ && charCode <= 122 /* z */) {
|
||||
return this._letters[charCode - 97];
|
||||
}
|
||||
|
||||
if (charCode >= 65 /* A */ && charCode <= 90 /* Z */) {
|
||||
return this._letters[charCode - 65];
|
||||
}
|
||||
|
||||
if (charCode >= 49 /* 1 */ && charCode <= 57 /* 9 */) {
|
||||
return this._digits[charCode - 49];
|
||||
}
|
||||
@ -536,10 +625,10 @@ export abstract class RegisterSet {
|
||||
|
||||
key = key.toLowerCase();
|
||||
|
||||
let register = this._named.get(key.toLowerCase());
|
||||
let register = this._named.get(key);
|
||||
|
||||
if (register === undefined) {
|
||||
this._named.set(key, register = new GeneralPurposeRegister(key));
|
||||
this._named.set(key, register = new GeneralPurposeRegister(key, "symbol-text"));
|
||||
}
|
||||
|
||||
return register;
|
||||
@ -568,6 +657,7 @@ export abstract class RegisterSet {
|
||||
}
|
||||
|
||||
this._lastMatches = transposed;
|
||||
this._onLastMatchesChange.fire();
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user