feat: add inline icons for edit

This commit is contained in:
2026-07-16 02:49:55 +05:00
parent bda6c6b2e0
commit 7c0584707a
35 changed files with 1280 additions and 94 deletions
+155
View File
@@ -0,0 +1,155 @@
import * as vscode from "vscode";
import {
findFieldRange,
replaceFrontmatterField,
} from "@/frontmatter/frontmatterFields";
import {
isTaskFileName,
TASK_FILE_EXTENSION,
TASK_LANGUAGE_ID,
} from "@/model/taskFile";
import { getGitUserName, yamlInlineScalar } from "./getGitUserName";
import {
TASK_PRIORITY_LABELS,
TASK_PRIORITY_ORDER,
TASK_STATUS_LABELS,
TASK_STATUS_ORDER,
} from "./taskLabels";
export const EditableField = {
STATUS: "status",
PRIORITY: "priority",
ASSIGNEE: "assignee",
} as const;
export type EditableField = (typeof EditableField)[keyof typeof EditableField];
function isTaskDocument(document: vscode.TextDocument): boolean {
if (document.languageId === TASK_LANGUAGE_ID) {
return true;
}
return isTaskFileName(document.fileName);
}
async function resolveDocument(
uriArg?: string,
): Promise<vscode.TextDocument | undefined> {
if (typeof uriArg === "string" && uriArg.length > 0) {
const uri = vscode.Uri.parse(uriArg);
return vscode.workspace.openTextDocument(uri);
}
const active = vscode.window.activeTextEditor?.document;
if (active && isTaskDocument(active)) {
return active;
}
return undefined;
}
function workspaceCwdFor(document: vscode.TextDocument): string | undefined {
const folder = vscode.workspace.getWorkspaceFolder(document.uri);
return folder?.uri.fsPath;
}
async function applyFieldValue(
document: vscode.TextDocument,
key: EditableField,
value: string,
): Promise<boolean> {
const text = document.getText();
const result = replaceFrontmatterField(text, key, value);
if (!result.ok) {
void vscode.window.showErrorMessage(
result.reason === "no-field"
? `Field "${key}" not found in frontmatter`
: "No YAML frontmatter in this file",
);
return false;
}
const valueRange = findFieldRange(text, key);
if (!valueRange) {
// Fallback: replace whole document
const full = new vscode.Range(
document.positionAt(0),
document.positionAt(text.length),
);
const edit = new vscode.WorkspaceEdit();
edit.replace(document.uri, full, result.content);
return vscode.workspace.applyEdit(edit);
}
const range = new vscode.Range(
valueRange.startLine,
valueRange.startCol,
valueRange.endLine,
valueRange.endCol,
);
const edit = new vscode.WorkspaceEdit();
edit.replace(document.uri, range, value);
return vscode.workspace.applyEdit(edit);
}
export async function runEditTaskStatus(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 picked = await vscode.window.showQuickPick(
TASK_STATUS_ORDER.map((status) => ({
label: TASK_STATUS_LABELS[status],
description: status,
status,
})),
{ placeHolder: "Status" },
);
if (!picked) return;
await applyFieldValue(document, EditableField.STATUS, picked.status);
}
export async function runEditTaskPriority(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 picked = await vscode.window.showQuickPick(
TASK_PRIORITY_ORDER.map((priority) => ({
label: TASK_PRIORITY_LABELS[priority],
description: priority,
priority,
})),
{ placeHolder: "Priority" },
);
if (!picked) return;
await applyFieldValue(document, EditableField.PRIORITY, picked.priority);
}
/** Set `assignee` from `git config user.name`. */
export async function runSetAssigneeMe(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 name = await getGitUserName(workspaceCwdFor(document));
if (!name) {
void vscode.window.showErrorMessage(
"Could not read git user.name (set git config user.name)",
);
return;
}
await applyFieldValue(document, EditableField.ASSIGNEE, yamlInlineScalar(name));
}