feat: change ui from decorations to codelens
This commit is contained in:
+5
-5
@@ -1,18 +1,18 @@
|
|||||||
import * as path from "path";
|
|
||||||
import * as vscode from "vscode";
|
|
||||||
import { resolveTaskLocations } from "@/commands/resolveTaskLocations";
|
import { resolveTaskLocations } from "@/commands/resolveTaskLocations";
|
||||||
import { TASK_FILE_EXTENSION } from "@/model/taskFile";
|
import { TASK_FILE_EXTENSION } from "@/model/taskFile";
|
||||||
|
import { IConfigProvider } from "@/ports";
|
||||||
import { FsTaskRepository } from "@/storage/FsTaskRepository";
|
import { FsTaskRepository } from "@/storage/FsTaskRepository";
|
||||||
import { NodeFileSystem } from "@/storage/NodeFileSystem";
|
import { NodeFileSystem } from "@/storage/NodeFileSystem";
|
||||||
import { VscodeConfigProvider } from "@/storage/VscodeConfigProvider";
|
import { VscodeConfigProvider } from "@/storage/VscodeConfigProvider";
|
||||||
import { IConfigProvider } from "@/ports";
|
|
||||||
import { registerTaskCodeActions } from "@/ui/taskCodeActions";
|
import { registerTaskCodeActions } from "@/ui/taskCodeActions";
|
||||||
|
import { registerTaskCodeLenses } from "@/ui/taskCodeLenses";
|
||||||
import { registerTaskCommands } from "@/ui/taskCommands";
|
import { registerTaskCommands } from "@/ui/taskCommands";
|
||||||
import { registerTaskDecorations } from "@/ui/taskDecorations";
|
|
||||||
import { registerTaskDiagnostics } from "@/ui/taskDiagnostics";
|
import { registerTaskDiagnostics } from "@/ui/taskDiagnostics";
|
||||||
import { TaskDashboardPanel } from "@/views/taskDashboardPanel";
|
import { TaskDashboardPanel } from "@/views/taskDashboardPanel";
|
||||||
import { TaskKanbanPanel } from "@/views/taskKanbanPanel";
|
import { TaskKanbanPanel } from "@/views/taskKanbanPanel";
|
||||||
import { TaskTreeProvider } from "@/views/taskTreeProvider";
|
import { TaskTreeProvider } from "@/views/taskTreeProvider";
|
||||||
|
import * as path from "path";
|
||||||
|
import * as vscode from "vscode";
|
||||||
|
|
||||||
function createTaskWatchers(
|
function createTaskWatchers(
|
||||||
config: IConfigProvider,
|
config: IConfigProvider,
|
||||||
@@ -74,7 +74,7 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
context.subscriptions.push(statusBarItem);
|
context.subscriptions.push(statusBarItem);
|
||||||
|
|
||||||
registerTaskDiagnostics(context, { fs: fileSystem, config });
|
registerTaskDiagnostics(context, { fs: fileSystem, config });
|
||||||
registerTaskDecorations(context);
|
registerTaskCodeLenses(context);
|
||||||
registerTaskCodeActions(context);
|
registerTaskCodeActions(context);
|
||||||
|
|
||||||
registerTaskCommands(context, {
|
registerTaskCommands(context, {
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
import { findFieldRange } from "@/frontmatter/frontmatterFields";
|
||||||
|
import {
|
||||||
|
isTaskFileName,
|
||||||
|
TASK_FILE_EXTENSION,
|
||||||
|
TASK_LANGUAGE_ID,
|
||||||
|
} from "@/model/taskFile";
|
||||||
|
import * as vscode from "vscode";
|
||||||
|
import { EditableField } from "./editFrontmatterField";
|
||||||
|
|
||||||
|
const NBSP = "\u00A0";
|
||||||
|
|
||||||
|
type FieldLensDef = {
|
||||||
|
field: EditableField;
|
||||||
|
icon: string;
|
||||||
|
label: string;
|
||||||
|
command: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const FIELDS: FieldLensDef[] = [
|
||||||
|
{
|
||||||
|
field: EditableField.STATUS,
|
||||||
|
icon: "$(symbol-misc)",
|
||||||
|
label: "Change status",
|
||||||
|
command: "xboctukFlow.editTaskStatus",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: EditableField.PRIORITY,
|
||||||
|
icon: "$(arrow-up)",
|
||||||
|
label: "Change priority",
|
||||||
|
command: "xboctukFlow.editTaskPriority",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: EditableField.TAGS,
|
||||||
|
icon: "$(tag)",
|
||||||
|
label: "Edit tags",
|
||||||
|
command: "xboctukFlow.editTaskTags",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: EditableField.ASSIGNEE,
|
||||||
|
icon: "$(account)",
|
||||||
|
label: "Assign me",
|
||||||
|
command: "xboctukFlow.setAssigneeMe",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function isTaskDocument(document: vscode.TextDocument): boolean {
|
||||||
|
if (document.languageId === TASK_LANGUAGE_ID) return true;
|
||||||
|
return isTaskFileName(document.fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
class TaskFieldCodeLensProvider implements vscode.CodeLensProvider {
|
||||||
|
provideCodeLenses(document: vscode.TextDocument): vscode.CodeLens[] {
|
||||||
|
if (!isTaskDocument(document)) return [];
|
||||||
|
|
||||||
|
const lenses: vscode.CodeLens[] = [];
|
||||||
|
const text = document.getText();
|
||||||
|
const uri = document.uri.toString();
|
||||||
|
|
||||||
|
for (const { field, icon, label, command } of FIELDS) {
|
||||||
|
const range = findFieldRange(text, field);
|
||||||
|
if (!range) continue;
|
||||||
|
|
||||||
|
lenses.push(
|
||||||
|
new vscode.CodeLens(
|
||||||
|
new vscode.Range(range.startLine, 0, range.startLine, 0),
|
||||||
|
{
|
||||||
|
title: `${icon}${NBSP}${label}`,
|
||||||
|
command,
|
||||||
|
arguments: [uri],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lenses;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerTaskCodeLenses(context: vscode.ExtensionContext): void {
|
||||||
|
const provider = new TaskFieldCodeLensProvider();
|
||||||
|
context.subscriptions.push(
|
||||||
|
vscode.languages.registerCodeLensProvider(
|
||||||
|
[
|
||||||
|
{ language: TASK_LANGUAGE_ID },
|
||||||
|
{ pattern: `**/*${TASK_FILE_EXTENSION}` },
|
||||||
|
],
|
||||||
|
provider,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
import * as vscode from "vscode";
|
|
||||||
import { findFieldRange } from "@/frontmatter/frontmatterFields";
|
|
||||||
import { isTaskFileName, TASK_LANGUAGE_ID } from "@/model/taskFile";
|
|
||||||
import { EditableField } from "./editFrontmatterField";
|
|
||||||
|
|
||||||
function isTaskEditor(editor: vscode.TextEditor): boolean {
|
|
||||||
const { document } = editor;
|
|
||||||
if (document.languageId === TASK_LANGUAGE_ID) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return isTaskFileName(document.fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
function commandUri(command: string, args: unknown[]): string {
|
|
||||||
return `command:${command}?${encodeURIComponent(JSON.stringify(args))}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function hoverEditField(
|
|
||||||
document: vscode.TextDocument,
|
|
||||||
field: typeof EditableField.STATUS | typeof EditableField.PRIORITY,
|
|
||||||
): vscode.MarkdownString {
|
|
||||||
const command =
|
|
||||||
field === EditableField.STATUS
|
|
||||||
? "xboctukFlow.editTaskStatus"
|
|
||||||
: "xboctukFlow.editTaskPriority";
|
|
||||||
const label =
|
|
||||||
field === EditableField.STATUS ? "Change status…" : "Change priority…";
|
|
||||||
const md = new vscode.MarkdownString(
|
|
||||||
`[$(edit) ${label}](${commandUri(command, [document.uri.toString()])})`,
|
|
||||||
);
|
|
||||||
md.isTrusted = true;
|
|
||||||
md.supportThemeIcons = true;
|
|
||||||
return md;
|
|
||||||
}
|
|
||||||
|
|
||||||
function hoverAssigneeMe(document: vscode.TextDocument): vscode.MarkdownString {
|
|
||||||
const md = new vscode.MarkdownString(
|
|
||||||
`[$(account) me](${commandUri("xboctukFlow.setAssigneeMe", [document.uri.toString()])}) — set assignee from \`git config user.name\``,
|
|
||||||
);
|
|
||||||
md.isTrusted = true;
|
|
||||||
md.supportThemeIcons = true;
|
|
||||||
return md;
|
|
||||||
}
|
|
||||||
|
|
||||||
function hoverTags(document: vscode.TextDocument): vscode.MarkdownString {
|
|
||||||
const uri = document.uri.toString();
|
|
||||||
const md = new vscode.MarkdownString(
|
|
||||||
`[$(edit) Edit tags…](${commandUri("xboctukFlow.editTaskTags", [uri])})`,
|
|
||||||
);
|
|
||||||
md.isTrusted = true;
|
|
||||||
md.supportThemeIcons = true;
|
|
||||||
return md;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function registerTaskDecorations(
|
|
||||||
context: vscode.ExtensionContext,
|
|
||||||
): void {
|
|
||||||
const decorationType = vscode.window.createTextEditorDecorationType({
|
|
||||||
after: {
|
|
||||||
margin: "0 0 0 1.5em",
|
|
||||||
color: new vscode.ThemeColor("editorCodeLens.foreground"),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
context.subscriptions.push(decorationType);
|
|
||||||
|
|
||||||
const refresh = (editor: vscode.TextEditor | undefined): void => {
|
|
||||||
if (!editor || !isTaskEditor(editor)) {
|
|
||||||
if (editor) {
|
|
||||||
editor.setDecorations(decorationType, []);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const text = editor.document.getText();
|
|
||||||
const options: vscode.DecorationOptions[] = [];
|
|
||||||
|
|
||||||
for (const field of [
|
|
||||||
EditableField.STATUS,
|
|
||||||
EditableField.PRIORITY,
|
|
||||||
] as const) {
|
|
||||||
const range = findFieldRange(text, field);
|
|
||||||
if (!range) continue;
|
|
||||||
|
|
||||||
options.push({
|
|
||||||
range: new vscode.Range(
|
|
||||||
range.startLine,
|
|
||||||
range.startCol,
|
|
||||||
range.endLine,
|
|
||||||
range.endCol,
|
|
||||||
),
|
|
||||||
hoverMessage: hoverEditField(editor.document, field),
|
|
||||||
renderOptions: {
|
|
||||||
after: {
|
|
||||||
contentText: " ✎",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const assigneeRange = findFieldRange(text, EditableField.ASSIGNEE);
|
|
||||||
if (assigneeRange) {
|
|
||||||
options.push({
|
|
||||||
range: new vscode.Range(
|
|
||||||
assigneeRange.startLine,
|
|
||||||
assigneeRange.startCol,
|
|
||||||
assigneeRange.endLine,
|
|
||||||
assigneeRange.endCol,
|
|
||||||
),
|
|
||||||
hoverMessage: hoverAssigneeMe(editor.document),
|
|
||||||
renderOptions: {
|
|
||||||
after: {
|
|
||||||
contentText: " me",
|
|
||||||
color: new vscode.ThemeColor("textLink.foreground"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const tagsRange = findFieldRange(text, EditableField.TAGS);
|
|
||||||
if (tagsRange) {
|
|
||||||
const lines = text.split(/\r?\n/);
|
|
||||||
const lineEnd = lines[tagsRange.startLine]?.length ?? 0;
|
|
||||||
options.push({
|
|
||||||
range: new vscode.Range(
|
|
||||||
tagsRange.startLine,
|
|
||||||
lineEnd,
|
|
||||||
tagsRange.startLine,
|
|
||||||
lineEnd,
|
|
||||||
),
|
|
||||||
hoverMessage: hoverTags(editor.document),
|
|
||||||
renderOptions: {
|
|
||||||
after: {
|
|
||||||
contentText: " ✎ tags",
|
|
||||||
color: new vscode.ThemeColor("textLink.foreground"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.setDecorations(decorationType, options);
|
|
||||||
};
|
|
||||||
|
|
||||||
const refreshVisible = (): void => {
|
|
||||||
for (const editor of vscode.window.visibleTextEditors) {
|
|
||||||
refresh(editor);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
refreshVisible();
|
|
||||||
|
|
||||||
context.subscriptions.push(
|
|
||||||
vscode.window.onDidChangeActiveTextEditor(refresh),
|
|
||||||
vscode.window.onDidChangeVisibleTextEditors(() => refreshVisible()),
|
|
||||||
vscode.workspace.onDidChangeTextDocument((event) => {
|
|
||||||
for (const editor of vscode.window.visibleTextEditors) {
|
|
||||||
if (editor.document === event.document) {
|
|
||||||
refresh(editor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user