Use unknown return type for callbacks

This commit is contained in:
Andras Schmelczer 2025-08-17 15:12:31 +01:00
commit 81b81e30ff
No known key found for this signature in database
GPG key ID: FC8F2C3D3D1A718C
16 changed files with 95 additions and 76 deletions

View file

@ -120,7 +120,7 @@ export default class VaultLinkPlugin extends Plugin {
this.app.workspace.onLayoutReady(async () => { this.app.workspace.onLayoutReady(async () => {
this.registerEditorEvents(); this.registerEditorEvents();
void this.client.start(); await this.client.start();
const interval = setInterval(() => { const interval = setInterval(() => {
updateEditorStatusDisplay(this.app.workspace, this.client); updateEditorStatusDisplay(this.app.workspace, this.client);

View file

@ -14,7 +14,7 @@ export const updateSelection = ({
}): void => { }): void => {
spans.forEach((span) => { spans.forEach((span) => {
if (fromA <= span.start) { if (fromA <= span.start) {
// The change covers the entirety of the selection // the change covers the entirety of the selection
if (toA > span.end) { if (toA > span.end) {
span.start = toB; span.start = toB;
span.end = toB; span.end = toB;
@ -23,6 +23,8 @@ export const updateSelection = ({
let change = toB - toA; let change = toB - toA;
if (change < 0) { if (change < 0) {
// it's a deletion
// if overlaps with the start, we can't move it back more than the deleted range
change = Math.max(change, fromA - span.start); change = Math.max(change, fromA - span.start);
} }
@ -31,6 +33,7 @@ export const updateSelection = ({
} else if (toA <= span.end) { } else if (toA <= span.end) {
span.end += toB - toA; span.end += toB - toA;
} else if (toB <= span.end) { } else if (toB <= span.end) {
// a deletion overlaps with the end, so we move the end
span.end = toB; span.end = toB;
} }
}); });

View file

@ -24,13 +24,12 @@ export class HistoryView extends ItemView {
super(leaf); super(leaf);
this.icon = HistoryView.ICON; this.icon = HistoryView.ICON;
this.client.addSyncHistoryUpdateListener( this.client.addSyncHistoryUpdateListener(async () =>
() => this.updateView().catch((error: unknown) => {
void this.updateView().catch((error: unknown) => { this.client.logger.error(
this.client.logger.error( `Failed to update history view: ${error}`
`Failed to update history view: ${error}` );
); })
})
); );
} }
@ -109,7 +108,15 @@ export class HistoryView extends ItemView {
this.historyContainer = container.createDiv({ cls: "logs-container" }); this.historyContainer = container.createDiv({ cls: "logs-container" });
await this.updateView(); await this.updateView();
this.timer = setInterval(() => void this.updateView(), 1000); this.timer = setInterval(
() =>
void this.updateView().catch((error: unknown) => {
this.client.logger.error(
`Failed to update history view: ${error}`
);
}),
1000
);
} }
public async onClose(): Promise<void> { public async onClose(): Promise<void> {
@ -174,11 +181,17 @@ export class HistoryView extends ItemView {
null null
) { ) {
card.addEventListener("click", () => { card.addEventListener("click", () => {
void this.app.workspace.openLinkText( this.app.workspace
entry.details.relativePath, .openLinkText(
entry.details.relativePath, entry.details.relativePath,
false entry.details.relativePath,
); false
)
.catch((error: unknown) => {
this.client.logger.error(
`Failed to open link for ${entry.details.relativePath}: ${error}`
);
});
}); });
card.addClass("clickable"); card.addClass("clickable");

View file

@ -16,7 +16,7 @@ export class SyncSettingsTab extends PluginSettingTab {
private readonly plugin: VaultLinkPlugin; private readonly plugin: VaultLinkPlugin;
private readonly syncClient: SyncClient; private readonly syncClient: SyncClient;
private readonly statusDescription: StatusDescription; private readonly statusDescription: StatusDescription;
private statusDescriptionSubscription: (() => void) | undefined; private statusDescriptionSubscription: (() => unknown) | undefined;
public constructor({ public constructor({
app, app,
@ -90,11 +90,12 @@ export class SyncSettingsTab extends PluginSettingTab {
cls: "description" cls: "description"
}, },
(descriptionContainer) => { (descriptionContainer) => {
this.setStatusDescriptionSubscription((): void => { this.setStatusDescriptionSubscription(
this.statusDescription.renderStatusDescription( this.statusDescription.renderStatusDescription.bind(
this.statusDescription,
descriptionContainer descriptionContainer
); )
}); );
} }
); );
@ -339,7 +340,7 @@ export class SyncSettingsTab extends PluginSettingTab {
} }
private setStatusDescriptionSubscription( private setStatusDescriptionSubscription(
newSubscription?: () => void newSubscription?: () => unknown
): void { ): void {
if (this.statusDescriptionSubscription) { if (this.statusDescriptionSubscription) {
this.statusDescription.removeStatusChangeListener( this.statusDescription.removeStatusChangeListener(
@ -360,7 +361,7 @@ export class SyncSettingsTab extends PluginSettingTab {
settingName: keyof SyncSettings settingName: keyof SyncSettings
): [ ): [
DocumentFragment, DocumentFragment,
(newValue: SyncSettings[keyof SyncSettings]) => void (newValue: SyncSettings[keyof SyncSettings]) => unknown
] { ] {
const titleContainer = document.createDocumentFragment(); const titleContainer = document.createDocumentFragment();
const title = titleContainer.createEl("div", { const title = titleContainer.createEl("div", {

View file

@ -42,9 +42,7 @@ export class StatusBar {
text: "VaultLink is disabled, click to configure", text: "VaultLink is disabled, click to configure",
cls: "initialize-button" cls: "initialize-button"
}); });
button.onclick = (): void => { button.onclick = this.plugin.openSettings.bind(this.plugin);
this.plugin.openSettings();
};
return; return;
} }

View file

@ -28,12 +28,12 @@ export class StatusDescription {
} }
); );
this.syncClient.addWebSocketStatusChangeListener( this.syncClient.addWebSocketStatusChangeListener(async () =>
() => void this.updateConnectionState() this.updateConnectionState()
); );
this.syncClient.addOnSettingsChangeListener( this.syncClient.addOnSettingsChangeListener(async () =>
() => void this.updateConnectionState() this.updateConnectionState()
); );
} }
@ -42,10 +42,10 @@ export class StatusDescription {
this.updateDescription(); this.updateDescription();
} }
public addStatusChangeListener(listener: () => void): void { public addStatusChangeListener(listener: () => unknown): void {
this.statusChangeListeners.push(listener); this.statusChangeListeners.push(listener);
} }
public removeStatusChangeListener(listener: () => void): void { public removeStatusChangeListener(listener: () => unknown): void {
this.statusChangeListeners = this.statusChangeListeners.filter( this.statusChangeListeners = this.statusChangeListeners.filter(
(l) => l !== listener (l) => l !== listener
); );

View file

@ -331,6 +331,8 @@ export class Database {
), ),
lastSeenUpdateId: this.lastSeenUpdateIds.min, lastSeenUpdateId: this.lastSeenUpdateIds.min,
hasInitialSyncCompleted: this.hasInitialSyncCompleted hasInitialSyncCompleted: this.hasInitialSyncCompleted
}).catch((error: unknown) => {
this.logger.error(`Error saving data: ${error}`);
}); });
} }

View file

@ -28,7 +28,7 @@ export class Settings {
private readonly onSettingsChangeHandlers: (( private readonly onSettingsChangeHandlers: ((
newSettings: SyncSettings, newSettings: SyncSettings,
oldSettings: SyncSettings oldSettings: SyncSettings
) => void)[] = []; ) => unknown)[] = [];
public constructor( public constructor(
private readonly logger: Logger, private readonly logger: Logger,
@ -50,7 +50,7 @@ export class Settings {
} }
public addOnSettingsChangeListener( public addOnSettingsChangeListener(
handler: (settings: SyncSettings, oldSettings: SyncSettings) => void handler: (settings: SyncSettings, oldSettings: SyncSettings) => unknown
): void { ): void {
this.onSettingsChangeHandlers.push(handler); this.onSettingsChangeHandlers.push(handler);
} }

View file

@ -7,8 +7,8 @@ export class ConnectionStatus {
private static readonly UNTIL_RESOLUTION = Symbol(); private static readonly UNTIL_RESOLUTION = Symbol();
private canFetch: boolean; private canFetch: boolean;
private until: Promise<symbol>; private until: Promise<symbol>;
private resolveUntil: (result: symbol) => void; private resolveUntil: (result: symbol) => unknown;
private rejectUntil: (reason: unknown) => void; private rejectUntil: (reason: unknown) => unknown;
public constructor( public constructor(
settings: Settings, settings: Settings,

View file

@ -64,12 +64,12 @@ export class WebSocketManager {
); );
} }
public addWebSocketStatusChangeListener(listener: () => void): void { public addWebSocketStatusChangeListener(listener: () => unknown): void {
this.webSocketStatusChangeListeners.push(listener); this.webSocketStatusChangeListeners.push(listener);
} }
public addRemoteCursorsUpdateListener( public addRemoteCursorsUpdateListener(
listener: (cursors: ClientCursors[]) => void listener: (cursors: ClientCursors[]) => unknown
): void { ): void {
this.remoteCursorsUpdateListeners.push(listener); this.remoteCursorsUpdateListeners.push(listener);
} }

View file

@ -48,9 +48,9 @@ export class SyncClient {
private readonly fileOperations: FileOperations private readonly fileOperations: FileOperations
) { ) {
this.settings.addOnSettingsChangeListener( this.settings.addOnSettingsChangeListener(
(newSettings, oldSettings) => { async (newSettings, oldSettings) => {
if (newSettings.vaultName !== oldSettings.vaultName) { if (newSettings.vaultName !== oldSettings.vaultName) {
void this.reset(); await this.reset();
} }
} }
); );
@ -197,7 +197,7 @@ export class SyncClient {
} }
public addSyncHistoryUpdateListener( public addSyncHistoryUpdateListener(
listener: (stats: HistoryStats) => void listener: (stats: HistoryStats) => unknown
): void { ): void {
this.history.addSyncHistoryUpdateListener(listener); this.history.addSyncHistoryUpdateListener(listener);
} }
@ -227,7 +227,7 @@ export class SyncClient {
this.database.reset(); this.database.reset();
this._logger.reset(); this._logger.reset();
this.connectionStatus.finishReset(); this.connectionStatus.finishReset();
void this.start(); await this.start();
} }
public getSettings(): SyncSettings { public getSettings(): SyncSettings {
@ -246,18 +246,18 @@ export class SyncClient {
} }
public addOnSettingsChangeListener( public addOnSettingsChangeListener(
handler: (settings: SyncSettings, oldSettings: SyncSettings) => void handler: (settings: SyncSettings, oldSettings: SyncSettings) => unknown
): void { ): void {
this.settings.addOnSettingsChangeListener(handler); this.settings.addOnSettingsChangeListener(handler);
} }
public addRemainingSyncOperationsListener( public addRemainingSyncOperationsListener(
listener: (remainingOperations: number) => void listener: (remainingOperations: number) => unknown
): void { ): void {
this.syncer.addRemainingOperationsListener(listener); this.syncer.addRemainingOperationsListener(listener);
} }
public addWebSocketStatusChangeListener(listener: () => void): void { public addWebSocketStatusChangeListener(listener: () => unknown): void {
this.webSocketManager.addWebSocketStatusChangeListener(listener); this.webSocketManager.addWebSocketStatusChangeListener(listener);
} }
@ -344,7 +344,7 @@ export class SyncClient {
} }
public addRemoteCursorsUpdateListener( public addRemoteCursorsUpdateListener(
listener: (cursors: DocumentWithMaybeOutdatedClientCursors[]) => void listener: (cursors: DocumentWithMaybeOutdatedClientCursors[]) => unknown
): void { ): void {
this.webSocketManager.addRemoteCursorsUpdateListener(async () => { this.webSocketManager.addRemoteCursorsUpdateListener(async () => {
listener(await this.getRelevantClientCursors()); listener(await this.getRelevantClientCursors());

View file

@ -22,7 +22,7 @@ export class Syncer {
private readonly remoteDocumentsLock: Locks<DocumentId>; private readonly remoteDocumentsLock: Locks<DocumentId>;
private readonly remainingOperationsListeners: (( private readonly remainingOperationsListeners: ((
remainingOperations: number remainingOperations: number
) => void)[] = []; ) => unknown)[] = [];
private readonly syncQueue: PQueue; private readonly syncQueue: PQueue;
private runningScheduleSyncForOfflineChanges: Promise<void> | undefined; private runningScheduleSyncForOfflineChanges: Promise<void> | undefined;
@ -57,7 +57,7 @@ export class Syncer {
} }
public addRemainingOperationsListener( public addRemainingOperationsListener(
listener: (remainingOperations: number) => void listener: (remainingOperations: number) => unknown
): void { ): void {
this.remainingOperationsListeners.push(listener); this.remainingOperationsListeners.push(listener);
} }

View file

@ -23,9 +23,11 @@ export class LogLine {
export class Logger { export class Logger {
private static readonly MAX_MESSAGES = 100000; private static readonly MAX_MESSAGES = 100000;
private readonly messages: LogLine[] = []; private readonly messages: LogLine[] = [];
private readonly onMessageListeners: ((message: LogLine) => void)[] = []; private readonly onMessageListeners: ((message: LogLine) => unknown)[] = [];
public constructor(...onMessageListeners: ((message: LogLine) => void)[]) { public constructor(
...onMessageListeners: ((message: LogLine) => unknown)[]
) {
this.onMessageListeners = onMessageListeners; this.onMessageListeners = onMessageListeners;
} }
@ -53,7 +55,7 @@ export class Logger {
); );
} }
public addOnMessageListener(listener: (message: LogLine) => void): void { public addOnMessageListener(listener: (message: LogLine) => unknown): void {
this.onMessageListeners.push(listener); this.onMessageListeners.push(listener);
} }

View file

@ -70,7 +70,7 @@ export class SyncHistory {
private readonly syncHistoryUpdateListeners: (( private readonly syncHistoryUpdateListeners: ((
status: HistoryStats status: HistoryStats
) => void)[] = []; ) => unknown)[] = [];
private status: HistoryStats = { private status: HistoryStats = {
success: 0, success: 0,
@ -111,7 +111,7 @@ export class SyncHistory {
} }
public addSyncHistoryUpdateListener( public addSyncHistoryUpdateListener(
listener: (stats: HistoryStats) => void listener: (stats: HistoryStats) => unknown
): void { ): void {
this.syncHistoryUpdateListeners.push(listener); this.syncHistoryUpdateListeners.push(listener);
listener({ ...this.status }); listener({ ...this.status });

View file

@ -2,13 +2,13 @@
* A type-safe utility function to create a Promise with resolve and reject functions. * A type-safe utility function to create a Promise with resolve and reject functions.
* @returns A tuple containing a Promise, a resolve function, and a reject function. * @returns A tuple containing a Promise, a resolve function, and a reject function.
*/ */
export function createPromise<T = void>(): [ export function createPromise<T = unknown>(): [
Promise<T>, Promise<T>,
(value: T) => void, (value: T) => unknown,
(error: unknown) => void (error: unknown) => unknown
] { ] {
let resolve: undefined | ((resolved: T) => void) = undefined; let resolve: undefined | ((resolved: T) => unknown) = undefined;
let reject: undefined | ((error: unknown) => void) = undefined; let reject: undefined | ((error: unknown) => unknown) = undefined;
const creationPromise = new Promise<T>( const creationPromise = new Promise<T>(
(resolve_, reject_) => ((resolve = resolve_), (reject = reject_)) (resolve_, reject_) => ((resolve = resolve_), (reject = reject_))

View file

@ -37,7 +37,7 @@ export class MockClient implements FileSystemOperations {
fs: this, fs: this,
persistence: { persistence: {
load: async () => this.data, load: async () => this.data,
save: async (data) => void (this.data = data) save: async (data) => (this.data = data)
}, },
fetch: fetchImplementation, fetch: fetchImplementation,
webSocket: webSocketImplementation webSocket: webSocketImplementation
@ -78,9 +78,9 @@ export class MockClient implements FileSystemOperations {
); );
this.localFiles.set(path, newContent); this.localFiles.set(path, newContent);
this.executeFileOperation(() => { this.executeFileOperation(async () =>
void this.client.syncLocallyCreatedFile(path); this.client.syncLocallyCreatedFile(path)
}); );
} }
public async createDirectory(_path: RelativePath): Promise<void> { public async createDirectory(_path: RelativePath): Promise<void> {
@ -120,11 +120,11 @@ export class MockClient implements FileSystemOperations {
`Updated file ${path} with:\n current content: ${currentContent}\n new content: ${newContent}` `Updated file ${path} with:\n current content: ${currentContent}\n new content: ${newContent}`
); );
this.executeFileOperation(() => { this.executeFileOperation(async () =>
void this.client.syncLocallyUpdatedFile({ this.client.syncLocallyUpdatedFile({
relativePath: path relativePath: path
}); })
}); );
return newContent; return newContent;
} }
@ -137,13 +137,13 @@ export class MockClient implements FileSystemOperations {
`Updated file ${path} with:\n new content: ${new TextDecoder().decode(content)}` `Updated file ${path} with:\n new content: ${new TextDecoder().decode(content)}`
); );
this.executeFileOperation(() => { this.executeFileOperation(async () => {
if (hasExisted) { if (hasExisted) {
void this.client.syncLocallyUpdatedFile({ return this.client.syncLocallyUpdatedFile({
relativePath: path relativePath: path
}); });
} else { } else {
void this.client.syncLocallyCreatedFile(path); return this.client.syncLocallyCreatedFile(path);
} }
}); });
} }
@ -154,9 +154,9 @@ export class MockClient implements FileSystemOperations {
); );
this.localFiles.delete(path); this.localFiles.delete(path);
this.executeFileOperation(() => { this.executeFileOperation(async () =>
void this.client.syncLocallyDeletedFile(path); this.client.syncLocallyDeletedFile(path)
}); );
} }
public async rename( public async rename(
@ -176,15 +176,15 @@ export class MockClient implements FileSystemOperations {
`Renamed file: ${oldPath} -> ${newPath} with:\n content ${new TextDecoder().decode(file)}` `Renamed file: ${oldPath} -> ${newPath} with:\n content ${new TextDecoder().decode(file)}`
); );
this.executeFileOperation(() => { this.executeFileOperation(async () =>
void this.client.syncLocallyUpdatedFile({ this.client.syncLocallyUpdatedFile({
oldPath, oldPath,
relativePath: newPath relativePath: newPath
}); })
}); );
} }
private executeFileOperation(callback: () => void): void { private executeFileOperation(callback: () => unknown): void {
if (this.useSlowFileEvents) { if (this.useSlowFileEvents) {
// we aren't the best client and it takes some time to notice changes // we aren't the best client and it takes some time to notice changes
setTimeout(callback, Math.random() * 100); setTimeout(callback, Math.random() * 100);