Skip to content

Commit

Permalink
Have something working
Browse files Browse the repository at this point in the history
  • Loading branch information
Jason3S committed Oct 4, 2023
1 parent 0577b78 commit 253e3b4
Show file tree
Hide file tree
Showing 15 changed files with 196 additions and 161 deletions.
14 changes: 14 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export abstract class AbstractSubscribable<T> extends InheritableDisposable impl
protected _isNotifyBusy = false;

constructor() {
super([() => this.done(), () => this._eventListeners.clear()]);
super([() => this.done(), () => this._eventListeners.clear()], 'AbstractSubscribable');
}

protected _hasSubscribers() {
Expand Down Expand Up @@ -42,7 +42,7 @@ export abstract class AbstractSubscribable<T> extends InheritableDisposable impl

private _unSub(s: SubscriberLike<T>) {
this._subscriptions.delete(s);
this._markAsDone(s);
// this._markAsDone(s);
this._tryToStop();
}

Expand Down Expand Up @@ -71,7 +71,7 @@ export abstract class AbstractSubscribable<T> extends InheritableDisposable impl
public subscribe(s: SubscriberLike<T>): Disposable {
this._subscriptions.add(s);
this._start();
return createDisposable(() => this._unSub(s));
return createDisposable(() => this._unSub(s), undefined, 'subscribe');
}

protected notify(value: T): void {
Expand All @@ -97,7 +97,7 @@ export abstract class AbstractSubscribable<T> extends InheritableDisposable impl
public onEvent(etOrL: EventType | EventListener, listener?: EventListener): Disposable {
if (typeof etOrL === 'function') {
this._eventListeners.add(etOrL);
return createDisposable(() => this._eventListeners.delete(etOrL));
return createDisposable(() => this._eventListeners.delete(etOrL), undefined, 'onEvent');
}

const eventType = etOrL;
Expand All @@ -106,6 +106,6 @@ export abstract class AbstractSubscribable<T> extends InheritableDisposable impl
listener?.(e);
};
this._eventListeners.add(eventlistener);
return createDisposable(() => this._eventListeners.delete(eventlistener));
return createDisposable(() => this._eventListeners.delete(eventlistener), undefined, `onEvent ${eventType}`);
}
}
2 changes: 1 addition & 1 deletion packages/client/src/webview/AppState/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { getWebviewGlobalStore } from './store';
export { calcDocSettings, getWebviewGlobalStore } from './store';
export { awaitSubscribable } from './Subscribables/helpers/awaitSubscribable';
78 changes: 41 additions & 37 deletions packages/client/src/webview/AppState/store.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { DisposableClassic, DisposableHybrid } from 'utils-disposables';
import { createDisposableFromList, disposeOf } from 'utils-disposables';
import type { DisposableClassic, DisposableHybrid, DisposableLike } from 'utils-disposables';
import { createDisposable, createDisposableFromList, disposeOf, injectDisposable } from 'utils-disposables';
import type { TextDocument, TextEditor, Uri } from 'vscode';
import { window } from 'vscode';
import { getLogLevel, LogLevel, setLogLevel } from 'vscode-webview-rpc/logger';
Expand All @@ -8,68 +8,77 @@ import type { WatchFieldList, WatchFields } from 'webview-api';
import { getDependencies } from '../../di';
import { calcSettings } from '../../infoViewer/infoHelper';
import type { AppStateData } from '../apiTypes';
import { awaitPromise, createSubscribableView, delayUnsubscribe, map, pipe, rx, throttle } from './Subscribables';
import { createSubscribableView, pipe, rx, throttle } from './Subscribables';
import { toSubscriberFn } from './Subscribables/helpers/toSubscriber';
import type { MakeSubscribable, StoreValue } from './Subscribables/StoreValue';
import { createStoreValue } from './Subscribables/StoreValue';
import type { Subscribable, SubscriberLike } from './Subscribables/Subscribables';
import type { SubscriberLike } from './Subscribables/Subscribables';

export interface Storage {
seq: number;
state: MakeSubscribable<AppStateData, 'currentDocument' | 'docSettings'>;
state: MakeSubscribable<AppStateData, 'currentDocument'>;
dispose(): void;
}

const debug = false;

debug && setLogLevel(LogLevel.debug);

const writableState = {
logLevel: createStoreValue(getLogLevel()),
todos: createStoreValue<AppStateData['todos']>([]),
} as const;

let store: Storage | undefined = undefined;

export function getWebviewGlobalStore(): Storage {
if (store) return store;

const currentDocument = rx(subscribeToCurrentDocument, createSubscribableView, delayUnsubscribe(5000), throttle(500));
const currentDocumentSub = rx(subscribeToCurrentDocument);
const currentDocument = pipe(currentDocumentSub, throttle(500), /* delayUnsubscribe(5000), */ createSubscribableView);
currentDocument.onEvent('onNotify', (event) => console.log('current document update: %o', event));

function dispose() {
disposeOf(currentDocumentSub);
const _store = store;
store = undefined;
if (!_store) return;
Object.values(_store.state).forEach((s) => disposeOf(s));
}

const _store: Storage = {
seq: 1,
state: {
...writableState,
currentDocument,
docSettings: rx(currentDocument, subscribeToDocSettings, createSubscribableView, delayUnsubscribe(5000), throttle(500)),
const writableState = {
logLevel: createStoreValue(getLogLevel()),
todos: createStoreValue<AppStateData['todos']>([]),
} as const;

const _store: Storage = injectDisposable(
{
seq: 1,
state: {
...writableState,
currentDocument,
},
},
dispose,
};
'getWebviewGlobalStore',
);

return (store = _store);
}

function subscribeToCurrentDocument(subscriber: SubscriberLike<AppStateData['currentDocument']>): DisposableHybrid {
const emitter = toSubscriberFn(subscriber);
const disposables: DisposableClassic[] = [];
const disposables: DisposableLike[] = [createDisposable(() => console.error('Dispose Last'), undefined, 'Dispose Last')];
const disposable = createDisposableFromList(disposables);

setCurrentDocument(window.activeTextEditor);
window.onDidChangeActiveTextEditor(setCurrentDocument, undefined, disposables);
window.onDidChangeTextEditorSelection(
(event) => event.textEditor === window.activeTextEditor && setCurrentDocument(event.textEditor),
undefined,
disposables,
disposables.push(disposeClassic(window.onDidChangeActiveTextEditor(setCurrentDocument, undefined)));
disposables.push(
disposeClassic(
window.onDidChangeTextEditorSelection(
(event) => event.textEditor === window.activeTextEditor && setCurrentDocument(event.textEditor),
undefined,
),
),
);

disposables.push(createDisposable(() => console.error('Dispose First'), undefined, 'Dispose First'));

return disposable;

function setCurrentDocument(textEditor: TextEditor | undefined) {
Expand All @@ -88,19 +97,10 @@ function subscribeToCurrentDocument(subscriber: SubscriberLike<AppStateData['cur
}
}

function subscribeToDocSettings(src: Subscribable<AppStateData['currentDocument']>): Subscribable<AppStateData['docSettings']> {
async function calcDocSettings(doc: AppStateData['currentDocument']): Promise<AppStateData['docSettings']> {
const textDoc = (doc && findMatchTextDocument(doc?.url)) || undefined;
const di = getDependencies();
return calcSettings(textDoc, undefined, di.client, console.log);
}

return pipe(
src,
throttle(1000),
map(calcDocSettings),
awaitPromise((err, emitter) => (console.error(err), emitter(null))),
);
export async function calcDocSettings(doc?: string) {
const textDoc = (doc && findMatchTextDocument(doc)) || undefined;
const di = getDependencies();
return calcSettings(textDoc, undefined, di.client, console.log);
}

export interface StateUpdate<T> {
Expand Down Expand Up @@ -154,3 +154,7 @@ function normalizeUrlToString(url: UrlLike): string {
const decoded = decodeURIComponent(decodeURIComponent(url.toString())).normalize('NFC');
return decoded.replace(/^file:\/\/\/[a-z]:/i, (fileUrl) => fileUrl.toLowerCase());
}

function disposeClassic(disposable: DisposableClassic): DisposableHybrid {
return createDisposable(() => disposable.dispose(), undefined, 'disposeClassic');
}
14 changes: 7 additions & 7 deletions packages/client/src/webview/api/api.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { createDisposeMethodFromList, type DisposableLike, disposeOf, injectDisposable } from 'utils-disposables';
import { createDisposableList, type DisposableLike, disposeOf, injectDisposable, makeDisposable } from 'utils-disposables';
import { window } from 'vscode';
import { type MessageConnection } from 'vscode-jsonrpc/node';
import type { RequestResult, SetValueRequest, SetValueResult, WatchFieldList, WatchFields } from 'webview-api';
import { createServerSideSpellInfoWebviewApi } from 'webview-api';

import type { ServerSideApi, ServerSideApiDef } from '../apiTypes';
import { awaitSubscribable, getWebviewGlobalStore } from '../AppState';
import { type Storage, updateState, watchFieldList } from '../AppState/store';
import { calcDocSettings, type Storage, updateState, watchFieldList } from '../AppState/store';
import type { StoreValue } from '../AppState/Subscribables/StoreValue';
import type { Subscribable } from '../AppState/Subscribables/Subscribables';
import { sampleList } from './staticTestData';
Expand All @@ -18,16 +18,16 @@ export function createApi(connection: MessageConnection) {
export function bindApiAndStore(connection: MessageConnection, store: Storage): ServerSideApi {
let watcher: DisposableLike | undefined = undefined;
const fieldsToWatch = new Set<WatchFields>();
const disposables: DisposableLike[] = [() => disposeOf(watcher)];
const dispose = createDisposeMethodFromList(disposables);
const disposables = createDisposableList([() => disposeOf(watcher)], 'bindApiAndStore');
const dispose = disposables.dispose;

const api: ServerSideApiDef = {
serverRequests: {
whatTimeIsIt,
getLogLevel: () => resolveRequest(store.state.logLevel),
getTodos: () => resolveRequest(store.state.todos),
getCurrentDocument: () => resolveRequest(store.state.currentDocument),
getDocSettings: () => resolveRequest(store.state.docSettings),
getDocSettings: calcDocSettings,
setLogLevel: (r) => updateStateRequest(r, store.state.logLevel),
setTodos: (r) => updateStateRequest(r, store.state.todos),
watchFields,
Expand All @@ -43,9 +43,9 @@ export function bindApiAndStore(connection: MessageConnection, store: Storage):
};

const serverSideApi = createServerSideSpellInfoWebviewApi(connection, api);
disposables.push(serverSideApi);
disposables.push(makeDisposable(serverSideApi));

return injectDisposable({ ...serverSideApi }, dispose);
return injectDisposable({ ...serverSideApi }, dispose, 'bindApiAndStore');

/** Add fields to be watched. */
function watchFields(req: WatchFieldList) {
Expand Down
43 changes: 1 addition & 42 deletions packages/client/src/webview/index.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1 @@
import type { ExtensionContext } from 'vscode';
import { commands, window } from 'vscode';
import { supportedViewsByName } from 'webview-api';

import { getWebviewGlobalStore } from './AppState/store';
import { HelloWorldPanel } from './panels/HelloWorldPanel';
import { TodoViewProvider } from './providers/TodoViewProvider';
import { WebviewApiViewProvider } from './providers/viewProviders';

export const registeredCommands = ['cspell-info.showHelloWorld'] as const;

type CommandNames = (typeof registeredCommands)[number];

type RegisteredCommandNames = {
[P in CommandNames]: P;
};

const rCommands = Object.fromEntries(registeredCommands.map((name) => [name, name] as const)) as RegisteredCommandNames;

export function activate(context: ExtensionContext) {
const { subscriptions, extensionUri } = context;

const views = [
new TodoViewProvider(extensionUri),
new WebviewApiViewProvider(extensionUri, supportedViewsByName['cspell-info'], 'cspell-info.infoView'),
];

for (const view of views) {
subscriptions.push(window.registerWebviewViewProvider(view.viewType, view));
}

// Create the show hello world command
const showHelloWorldCommand = commands.registerCommand(rCommands['cspell-info.showHelloWorld'], () => {
HelloWorldPanel.render(context.extensionUri);
});

// Add command to the extension context
subscriptions.push(showHelloWorldCommand, { dispose: () => HelloWorldPanel.currentPanel?.dispose() });

// Add state clean up.
subscriptions.push(getWebviewGlobalStore());
}
export { activate, registeredCommands } from './webview';
15 changes: 5 additions & 10 deletions packages/client/src/webview/panels/HelloWorldPanel.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Disposable, Uri, WebviewPanel } from 'vscode';
import { createDisposableList } from 'utils-disposables';
import type { Uri, WebviewPanel } from 'vscode';
import { ViewColumn, window } from 'vscode';

import { HelloWorldView } from '../views/HelloWorldView';
Expand All @@ -16,7 +17,7 @@ import { HelloWorldView } from '../views/HelloWorldView';
export class HelloWorldPanel {
public static currentPanel: HelloWorldPanel | undefined;
private readonly _panel: WebviewPanel;
private _disposables: Disposable[] = [];
private _disposables = createDisposableList(undefined, 'HelloWorldPanel');

/**
* The HelloWorldPanel class private constructor (called only from the render method).
Expand All @@ -30,8 +31,7 @@ export class HelloWorldPanel {

// Set an event listener to listen for when the panel is disposed (i.e. when the user closes
// the panel or when the panel is closed programmatically)
this._panel.onDidDispose(() => this.dispose(), null, this._disposables);

this._disposables.push(this._panel.onDidDispose(() => this.dispose()));
this._disposables.push(HelloWorldView.bindView(this._panel.webview, extensionUri));
}

Expand Down Expand Up @@ -75,11 +75,6 @@ export class HelloWorldPanel {
// this._panel.dispose() is the first element on the list.;

// Dispose of all disposables (i.e. commands) for the current webview panel
while (this._disposables.length) {
const disposable = this._disposables.pop();
if (disposable) {
disposable.dispose();
}
}
this._disposables.dispose();
}
}
Loading

0 comments on commit 253e3b4

Please sign in to comment.