Skip to content

Commit

Permalink
feat: Add smart vault selection to NoteLookupCommand (#1174)
Browse files Browse the repository at this point in the history
* chore: handle race condition in schema lookup similar to note lookup

* feat: add smart vault selection mode

* chore: expose vaultSelectionMode to command run option

* chore: fix vault stub

* chore: clean up

* chore: update lookup selection

* chore: fix test

Co-authored-by: Kevin <kevin@dendron.so>
  • Loading branch information
hikchoi and kevinslin committed Aug 23, 2021
1 parent 7008d06 commit 742cab6
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 65 deletions.
3 changes: 3 additions & 0 deletions packages/plugin-core/src/commands/NoteLookupCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
LookupSelectionTypeEnum,
LookupSplitType,
LookupSplitTypeEnum,
VaultSelectionMode,
} from "../components/lookup/types";
import {
node2Uri,
Expand All @@ -64,6 +65,7 @@ export type CommandRunOpts = {
* NOTE: currently, only one filter is supported
*/
filterMiddleware?: LookupFilterType[];
vaultSelectionMode?: VaultSelectionMode;
};

/**
Expand Down Expand Up @@ -154,6 +156,7 @@ export class NoteLookupCommand extends BaseCommand<
ws.config,
"lookupConfirmVaultOnCreate"
),
vaultButtonPressed: copts.vaultSelectionMode === VaultSelectionMode.alwaysPrompt,
extraButtons: [
MultiSelectBtn.create(copts.multiSelect),
CopyNoteLinkBtn.create(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ export type LookupControllerV3CreateOpts = {
* When true, don't enable vault selection
*/
disableVaultSelection?: boolean;
/**
* if vault selection isn't disabled,
* press button on init if true
*/
vaultButtonPressed?: boolean;
/**
* Additional buttons
*/
Expand All @@ -61,8 +66,11 @@ export class LookupControllerV3 {
opts?.disableVaultSelection) ||
opts?.nodeType === "schema";
const isMultiVault = vaults.length > 1 && !disableVaultSelection;
const maybeVaultSelectButtonPressed = _.isUndefined(opts?.vaultButtonPressed)
? isMultiVault
: isMultiVault && opts!.vaultButtonPressed;
const maybeVaultSelectButton =
opts?.nodeType === "note" ? [VaultSelectButton.create(isMultiVault)] : [];
opts?.nodeType === "note" ? [VaultSelectButton.create(maybeVaultSelectButtonPressed)] : [];
const buttons = opts?.buttons || maybeVaultSelectButton;
const extraButtons = opts?.extraButtons || [];
return new LookupControllerV3({
Expand Down
48 changes: 39 additions & 9 deletions packages/plugin-core/src/components/lookup/LookupProviderV3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export class NoteLookupProvider implements ILookupProviderV3 {
lc: LookupControllerV3;
}) {
return async () => {
const ctx = "LookupProvider:onDidAccept";
const ctx = "NoteLookupProvider:onDidAccept";
const { quickpick: picker, lc } = opts;
let selectedItems = NotePickerUtils.getSelection(picker);
Logger.debug({
Expand All @@ -157,7 +157,8 @@ export class NoteLookupProvider implements ILookupProviderV3 {
) {
Logger.debug({ ctx, msg: "nextPicker:pre" });
picker.state = DendronQuickPickState.PENDING_NEXT_PICK;
picker.vault = await picker.nextPicker();

picker.vault = await picker.nextPicker({ note: selectedItems[0] });
// check if we exited from selecting a vault
if (_.isUndefined(picker.vault)) {
HistoryService.instance().add({
Expand Down Expand Up @@ -344,7 +345,9 @@ export class NoteLookupProvider implements ILookupProviderV3 {
!queryEndsWithDot &&
!picker.canSelectMany &&
!perfectMatch
? updatedItems.concat([NotePickerUtils.createNoActiveItem({} as any)])
? updatedItems.concat([
NotePickerUtils.createNoActiveItem({ fname: querystring }),
])
: updatedItems;

// check fuzz threshold. tune fuzzyness. currently hardcoded
Expand Down Expand Up @@ -408,12 +411,19 @@ export class SchemaLookupProvider implements ILookupProviderV3 {
}
);
quickpick.onDidChangeValue(onUpdateDebounced);
quickpick.onDidAccept(() => {
quickpick.onDidAccept(async () => {
Logger.info({
ctx: "SchemaLookupProvider:onDidAccept",
quickpick: quickpick.value,
});
onUpdateDebounced.cancel();
if (_.isEmpty(quickpick.selectedItems)) {
await onUpdatePickerItems({
picker: quickpick,
token: new CancellationTokenSource().token,
fuzzThreshold: lc.fuzzThreshold,
});
}
this.onDidAccept({ quickpick, lc })();
});
return;
Expand All @@ -429,10 +439,29 @@ export class SchemaLookupProvider implements ILookupProviderV3 {
lc: LookupControllerV3;
}) {
return async () => {
const ctx = "SchemaLookupProvider:onDidAccept";
const { quickpick: picker, lc } = opts;
const nextPicker = picker.nextPicker;
if (nextPicker) {
picker.vault = await nextPicker();
let selectedItems = NotePickerUtils.getSelection(picker);
Logger.debug({
ctx,
selectedItems: selectedItems.map((item) => NoteUtils.toLogObj(item)),
});
if (_.isEmpty(selectedItems)) {
selectedItems =
await SchemaPickerUtils.fetchPickerResultsWithCurrentValue({
picker,
});
}
if (
PickerUtilsV2.hasNextPicker(picker, {
selectedItems,
providerId: this.id,
})
) {
Logger.debug({ ctx, msg: "nextPicker:pre" });
picker.state = DendronQuickPickState.PENDING_NEXT_PICK;

picker.vault = await picker.nextPicker({ note: selectedItems[0] });
// check if we exited from selecting a vault
if (_.isUndefined(picker.vault)) {
HistoryService.instance().add({
Expand All @@ -444,7 +473,6 @@ export class SchemaLookupProvider implements ILookupProviderV3 {
return;
}
}
const selectedItems = NotePickerUtils.getSelection(picker);
const isMultiLevel = picker.value.split(".").length > 1;
if (isMultiLevel) {
window.showErrorMessage("schemas can only be one level deep");
Expand Down Expand Up @@ -548,7 +576,9 @@ export class SchemaLookupProvider implements ILookupProviderV3 {

updatedItems =
this.opts.allowNewNote && !perfectMatch
? updatedItems.concat([NotePickerUtils.createNoActiveItem({} as any)])
? updatedItems.concat([
NotePickerUtils.createNoActiveItem({ fname: querystring }),
])
: updatedItems;

picker.items = updatedItems;
Expand Down
34 changes: 27 additions & 7 deletions packages/plugin-core/src/components/lookup/buttons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
LookupNoteTypeEnum,
LookupSelectionType,
LookupSplitType,
VaultSelectionMode,
} from "./types";
import { NotePickerUtils, PickerUtilsV2 } from "./utils";

Expand Down Expand Up @@ -441,17 +442,36 @@ export class VaultSelectButton extends DendronBtn {
canToggle: false,
});
}

get tooltip(): string {
return `${this.title}, status: ${this.pressed ? "always prompt" : "smart"}`;
}

setNextPicker({quickPick, mode}: {quickPick: DendronQuickPickerV2, mode: VaultSelectionMode}) {
quickPick.nextPicker = async (opts: { note: NoteProps }) => {
const { note } = opts;
const currentVault = PickerUtilsV2.getVaultForOpenEditor();
const vaultSelection = await PickerUtilsV2.getOrPromptVaultForNewNote({
vault: currentVault,
fname: note.fname,
vaultSelectionMode: mode
});

if (_.isUndefined(vaultSelection)) {
vscode.window.showInformationMessage("Note creation cancelled.");
return;
}

return vaultSelection;
}
}

async onEnable({ quickPick }: ButtonHandleOpts) {
quickPick.nextPicker = async () => {
const vault = await PickerUtilsV2.promptVault();
return vault;
};
this.setNextPicker({ quickPick, mode: VaultSelectionMode.alwaysPrompt });
}

async onDisable({ quickPick }: ButtonHandleOpts) {
if (quickPick.nextPicker) {
delete quickPick["nextPicker"];
}
this.setNextPicker({ quickPick, mode: VaultSelectionMode.smart});
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-core/src/components/lookup/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export type DendronQuickPickerV2 = DendronQuickPickItemV2 & {
/**
* Should show a subsequent picker?
*/
nextPicker?: () => any;
nextPicker?: (opts: any) => any;
/**
* TODO: should be required
*/
Expand Down
57 changes: 36 additions & 21 deletions packages/plugin-core/src/components/lookup/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ import {
MORE_RESULTS_LABEL,
} from "./constants";
import { ILookupProviderV3, OnAcceptHook } from "./LookupProviderV3";
import { VaultSelectionMode, DendronQuickPickerV2, DendronQuickPickState } from "./types";
import {
DendronQuickPickerV2,
DendronQuickPickState,
VaultSelectionMode,
} from "./types";

const PAGINATE_LIMIT = 50;
export const UPDATET_SOURCE = {
Expand Down Expand Up @@ -217,22 +221,6 @@ export class ProviderAcceptHooks {
}

export class PickerUtilsV2 {
static createDefaultItems = ({
picker,
vault,
}: {
picker: DendronQuickPickerV2;
vault: DVault;
}) => {
const out = [];
if (_.find(picker.buttons, { type: "multiSelect" })?.pressed) {
return [];
} else {
out.push(NotePickerUtils.createNoActiveItem(vault));
}
return out;
};

static createDendronQuickPick(opts: CreateQuickPickOpts) {
const { title, placeholder, ignoreFocusOut, initialValue } = _.defaults(
opts,
Expand Down Expand Up @@ -688,11 +676,17 @@ export class PickerUtilsV2 {
}

export class NotePickerUtils {
static createNoActiveItem(vault: DVault): DNodePropsQuickInputV2 {
static createNoActiveItem({
fname,
}: {
fname: string;
}): DNodePropsQuickInputV2 {
const props = DNodeUtils.create({
fname: CREATE_NEW_LABEL,
id: CREATE_NEW_LABEL,
fname,
type: "note",
vault,
// @ts-ignore
vault: {},
});
return {
...props,
Expand Down Expand Up @@ -747,7 +741,7 @@ export class NotePickerUtils {
const note = resp[0];
const perfectMatch = note.fname === picker.value;
return !perfectMatch
? [NotePickerUtils.createNoActiveItem({} as any)]
? [NotePickerUtils.createNoActiveItem({ fname: picker.value })]
: [
DNodeUtils.enhancePropForQuickInputV3({
wsRoot: DendronWorkspace.wsRoot(),
Expand Down Expand Up @@ -817,6 +811,27 @@ export class NotePickerUtils {
}

export class SchemaPickerUtils {
static async fetchPickerResultsWithCurrentValue({
picker,
}: {
picker: DendronQuickPickerV2;
}) {
const engine = getEngine();
const resp = await engine.querySchema(picker.value);
const node = SchemaUtils.getModuleRoot(resp.data[0]);
const perfectMatch = node.fname === picker.value;
return !perfectMatch
? [NotePickerUtils.createNoActiveItem({ fname: picker.value })]
: [
DNodeUtils.enhancePropForQuickInputV3({
wsRoot: DendronWorkspace.wsRoot(),
props: node,
schemas: engine.schemas,
vaults: DendronWorkspace.instance().vaultsv4,
}),
];
}

static async fetchPickerResults(opts: {
picker: DendronQuickPickerV2;
qs: string;
Expand Down

0 comments on commit 742cab6

Please sign in to comment.