fix: pick status and priority show current
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("vscode", () => ({}));
|
||||
|
||||
import { getCurrentFieldValue } from "./editFrontmatterField";
|
||||
|
||||
const inlineSample = `---
|
||||
title: Test
|
||||
status: in-progress
|
||||
priority: high
|
||||
tags:
|
||||
- bug
|
||||
- urgent
|
||||
assignee: me
|
||||
---
|
||||
|
||||
Body
|
||||
`;
|
||||
|
||||
const quotedSample = `---
|
||||
status: "done"
|
||||
priority: 'low'
|
||||
---
|
||||
|
||||
Body
|
||||
`;
|
||||
|
||||
describe("getCurrentFieldValue", () => {
|
||||
it("returns undefined when field is missing", () => {
|
||||
expect(getCurrentFieldValue(inlineSample, "nonexistent")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("returns inline status value", () => {
|
||||
expect(getCurrentFieldValue(inlineSample, "status")).toBe("in-progress");
|
||||
});
|
||||
|
||||
it("returns inline priority value", () => {
|
||||
expect(getCurrentFieldValue(inlineSample, "priority")).toBe("high");
|
||||
});
|
||||
|
||||
it("returns assignee value", () => {
|
||||
expect(getCurrentFieldValue(inlineSample, "assignee")).toBe("me");
|
||||
});
|
||||
|
||||
it("strips double quotes", () => {
|
||||
expect(getCurrentFieldValue(quotedSample, "status")).toBe("done");
|
||||
});
|
||||
|
||||
it("strips single quotes", () => {
|
||||
expect(getCurrentFieldValue(quotedSample, "priority")).toBe("low");
|
||||
});
|
||||
|
||||
it("returns undefined for empty frontmatter", () => {
|
||||
expect(getCurrentFieldValue("---\n---\n\nBody", "status")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("returns undefined for document without frontmatter", () => {
|
||||
expect(getCurrentFieldValue("Just body text", "status")).toBeUndefined();
|
||||
});
|
||||
});
|
||||
+136
-21
@@ -8,7 +8,7 @@ import {
|
||||
setFrontmatterTags,
|
||||
} from "@/frontmatter/frontmatterFields";
|
||||
import { parseTaskTags } from "@/frontmatter/parseTaskTags";
|
||||
import { normalizeTag } from "@/model/projectConfig";
|
||||
import { getProjectConfigPath, normalizeTag } from "@/model/projectConfig";
|
||||
import {
|
||||
isTaskFileName,
|
||||
TASK_FILE_EXTENSION,
|
||||
@@ -19,6 +19,8 @@ import * as vscode from "vscode";
|
||||
import { getGitUserName, yamlInlineScalar } from "./getGitUserName";
|
||||
import { presentError } from "./presentError";
|
||||
import { resolveTaskFolderForPath } from "./resolveTaskFolder";
|
||||
import { type TaskPriority } from "@/model/taskPriority";
|
||||
import { type TaskStatus } from "@/model/taskStatus";
|
||||
import {
|
||||
TASK_PRIORITY_LABELS,
|
||||
TASK_PRIORITY_META,
|
||||
@@ -107,6 +109,20 @@ async function applyFieldValue(
|
||||
return vscode.workspace.applyEdit(edit);
|
||||
}
|
||||
|
||||
export function getCurrentFieldValue(text: string, field: string): string | undefined {
|
||||
const range = findFieldRange(text, field);
|
||||
if (!range) return undefined;
|
||||
const lines = text.split(/\r?\n/);
|
||||
const raw = lines[range.startLine]!.slice(range.startCol, range.endCol).trim();
|
||||
if (
|
||||
(raw.startsWith('"') && raw.endsWith('"')) ||
|
||||
(raw.startsWith("'") && raw.endsWith("'"))
|
||||
) {
|
||||
return raw.slice(1, raw.length - 1);
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
export async function runEditTaskStatus(uriArg?: string): Promise<void> {
|
||||
const document = await resolveDocument(uriArg);
|
||||
if (!document) {
|
||||
@@ -116,16 +132,49 @@ export async function runEditTaskStatus(uriArg?: string): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
const picked = await vscode.window.showQuickPick(
|
||||
TASK_STATUS_ORDER.filter(
|
||||
(status) => !TASK_STATUS_META[status].fallback,
|
||||
).map((status) => ({
|
||||
label: TASK_STATUS_LABELS[status],
|
||||
description: status,
|
||||
status,
|
||||
})),
|
||||
{ placeHolder: "Status" },
|
||||
);
|
||||
type StatusItem = vscode.QuickPickItem & { status: TaskStatus };
|
||||
|
||||
const text = document.getText();
|
||||
const items: StatusItem[] = TASK_STATUS_ORDER.filter(
|
||||
(status) => !TASK_STATUS_META[status].fallback,
|
||||
).map((status) => ({
|
||||
label: TASK_STATUS_LABELS[status],
|
||||
description: status,
|
||||
status,
|
||||
}));
|
||||
|
||||
const currentValue = getCurrentFieldValue(text, EditableField.STATUS);
|
||||
const activeItem = currentValue
|
||||
? items.find((item) => item.status === currentValue)
|
||||
: undefined;
|
||||
|
||||
const picked = await new Promise<StatusItem | undefined>((resolve) => {
|
||||
const qp = vscode.window.createQuickPick<StatusItem>();
|
||||
qp.items = items;
|
||||
qp.placeholder = "Status";
|
||||
if (activeItem) qp.activeItems = [activeItem];
|
||||
|
||||
let accepted = false;
|
||||
|
||||
qp.onDidAccept(() => {
|
||||
accepted = true;
|
||||
const picked = qp.activeItems[0];
|
||||
if (!picked) return;
|
||||
qp.hide();
|
||||
qp.dispose();
|
||||
resolve(picked);
|
||||
});
|
||||
|
||||
qp.onDidHide(() => {
|
||||
if (!accepted) {
|
||||
qp.dispose();
|
||||
resolve(undefined);
|
||||
}
|
||||
});
|
||||
|
||||
qp.show();
|
||||
});
|
||||
|
||||
if (!picked) return;
|
||||
|
||||
await applyFieldValue(document, EditableField.STATUS, picked.status);
|
||||
@@ -140,16 +189,49 @@ export async function runEditTaskPriority(uriArg?: string): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
const picked = await vscode.window.showQuickPick(
|
||||
TASK_PRIORITY_ORDER.filter(
|
||||
(priority) => !TASK_PRIORITY_META[priority].fallback,
|
||||
).map((priority) => ({
|
||||
label: TASK_PRIORITY_LABELS[priority],
|
||||
description: priority,
|
||||
priority,
|
||||
})),
|
||||
{ placeHolder: "Priority" },
|
||||
);
|
||||
type PriorityItem = vscode.QuickPickItem & { priority: TaskPriority };
|
||||
|
||||
const text = document.getText();
|
||||
const items: PriorityItem[] = TASK_PRIORITY_ORDER.filter(
|
||||
(priority) => !TASK_PRIORITY_META[priority].fallback,
|
||||
).map((priority) => ({
|
||||
label: TASK_PRIORITY_LABELS[priority],
|
||||
description: priority,
|
||||
priority,
|
||||
}));
|
||||
|
||||
const currentValue = getCurrentFieldValue(text, EditableField.PRIORITY);
|
||||
const activeItem = currentValue
|
||||
? items.find((item) => item.priority === currentValue)
|
||||
: undefined;
|
||||
|
||||
const picked = await new Promise<PriorityItem | undefined>((resolve) => {
|
||||
const qp = vscode.window.createQuickPick<PriorityItem>();
|
||||
qp.items = items;
|
||||
qp.placeholder = "Priority";
|
||||
if (activeItem) qp.activeItems = [activeItem];
|
||||
|
||||
let accepted = false;
|
||||
|
||||
qp.onDidAccept(() => {
|
||||
accepted = true;
|
||||
const picked = qp.activeItems[0];
|
||||
if (!picked) return;
|
||||
qp.hide();
|
||||
qp.dispose();
|
||||
resolve(picked);
|
||||
});
|
||||
|
||||
qp.onDidHide(() => {
|
||||
if (!accepted) {
|
||||
qp.dispose();
|
||||
resolve(undefined);
|
||||
}
|
||||
});
|
||||
|
||||
qp.show();
|
||||
});
|
||||
|
||||
if (!picked) return;
|
||||
|
||||
await applyFieldValue(document, EditableField.PRIORITY, picked.priority);
|
||||
@@ -180,6 +262,39 @@ export async function runSetAssigneeMe(uriArg?: string): Promise<void> {
|
||||
);
|
||||
}
|
||||
|
||||
export async function runOpenProjectConfig(
|
||||
deps: FrontmatterEditDeps,
|
||||
uriArg?: string,
|
||||
): Promise<void> {
|
||||
const document = await resolveDocument(uriArg);
|
||||
if (!document) {
|
||||
void vscode.window.showErrorMessage(
|
||||
`Open a ${TASK_FILE_EXTENSION} task file first`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const folder = await resolveTaskFolder(document, deps.config);
|
||||
if (!folder) {
|
||||
void vscode.window.showErrorMessage(
|
||||
"Task file is outside a configured task folder",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const configPath = getProjectConfigPath(folder);
|
||||
const uri = vscode.Uri.file(configPath);
|
||||
|
||||
try {
|
||||
const doc = await vscode.workspace.openTextDocument(uri);
|
||||
await vscode.window.showTextDocument(doc);
|
||||
} catch {
|
||||
void vscode.window.showErrorMessage(
|
||||
`Could not open config file: ${configPath}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveTaskFolder(
|
||||
document: vscode.TextDocument,
|
||||
config: IConfigProvider,
|
||||
|
||||
@@ -35,6 +35,12 @@ const FIELDS: FieldLensDef[] = [
|
||||
label: "Edit tags",
|
||||
command: "xboctukFlow.editTaskTags",
|
||||
},
|
||||
{
|
||||
field: EditableField.TAGS,
|
||||
icon: "$(gear)",
|
||||
label: "Configure project tags",
|
||||
command: "xboctukFlow.openProjectConfig",
|
||||
},
|
||||
{
|
||||
field: EditableField.ASSIGNEE,
|
||||
icon: "$(account)",
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
runEditTaskPriority,
|
||||
runEditTaskStatus,
|
||||
runEditTaskTags,
|
||||
runOpenProjectConfig,
|
||||
runSetAssigneeMe,
|
||||
} from "./editFrontmatterField";
|
||||
import { presentError } from "./presentError";
|
||||
@@ -267,6 +268,11 @@ export function registerTaskCommands(
|
||||
(uri?: string) =>
|
||||
runEditTaskTags({ fs: deps.fs, config: deps.config }, uri),
|
||||
),
|
||||
vscode.commands.registerCommand(
|
||||
"xboctukFlow.openProjectConfig",
|
||||
(uri?: string) =>
|
||||
runOpenProjectConfig({ fs: deps.fs, config: deps.config }, uri),
|
||||
),
|
||||
vscode.commands.registerCommand(
|
||||
"xboctukFlow.addTagToProject",
|
||||
(uri?: string, tag?: string) =>
|
||||
|
||||
Reference in New Issue
Block a user