feat: add inline icons for edit
This commit is contained in:
@@ -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));
|
||||
}
|
||||
Reference in New Issue
Block a user