This commit is contained in:
medjo 2026-03-23 11:32:41 +01:00 committed by GitHub
commit bdae4ded9b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 98 additions and 16 deletions

View file

@ -124,6 +124,22 @@ describe("File Metadata Inheritance", () => {
// tags应该被继承但不会覆盖非继承字段
});
test("should use filename when inherited frontmatter project is true", () => {
const content = "- [ ] Task without explicit project";
const fileMetadata = {
project: true,
};
const tasks = parser.parseLegacy(
content,
"Projects/My Awesome Project.md",
fileMetadata
);
expect(tasks).toHaveLength(1);
expect(tasks[0].metadata.project).toBe("My Awesome Project");
});
test("should work independently of project configuration", () => {
// 项目配置为null验证不会崩溃
mockPlugin.settings.projectConfig = null;
@ -515,4 +531,4 @@ describe("File Metadata Inheritance", () => {
});
});
});
});
});

View file

@ -199,6 +199,29 @@ describe("FileMetadataTaskParser", () => {
expect(dueDateTask?.status).toBe(" "); // Due dates are typically incomplete
});
it("should use filename as project name when frontmatter project is true", () => {
const filePath = "Projects/My Awesome Project.md";
const fileContent = "# Test File";
const fileCache = {
frontmatter: {
title: "Test Task",
todo: true,
project: true,
},
tags: [],
};
const result = parser.parseFileForTasks(
filePath,
fileContent,
fileCache as any
);
expect(result.errors).toHaveLength(0);
expect(result.tasks).toHaveLength(1);
expect(result.tasks[0].metadata.project).toBe("My Awesome Project");
});
it("should not create tasks when parsing is disabled", () => {
const disabledConfig: FileParsingConfiguration = {
enableFileMetadataParsing: false,

View file

@ -331,6 +331,21 @@ describe('FileSource', () => {
expect(task!.metadata.project).toBe('Test Project');
});
it('should use filename as project name when frontmatter project is true', async () => {
const fileCache = {
frontmatter: {
dueDate: '2024-01-01',
project: true
}
};
mockApp.metadataCache.getFileCache.mockReturnValue(fileCache);
const task = await fileSource.createFileTask('Projects/My Awesome Project.md');
expect(task).toBeTruthy();
expect(task!.metadata.project).toBe('My Awesome Project');
});
it('should use default status when not specified', async () => {
const fileCache = {
frontmatter: {
@ -436,4 +451,4 @@ describe('FileSource', () => {
await expect(fileSource.refresh()).resolves.not.toThrow();
});
});
});
});

View file

@ -143,6 +143,7 @@ export class MarkdownTaskParser {
const inheritedMetadata = this.inheritFileMetadata(
metadata,
isSubtask,
filePath,
);
// Extract time components from task content using enhanced time parsing
@ -1736,6 +1737,7 @@ export class MarkdownTaskParser {
private inheritFileMetadata(
taskMetadata: Record<string, string>,
isSubtask: boolean = false,
filePath: string = "",
): Record<string, string> {
// Helper function to convert priority values to numbers
const convertPriorityValue = (value: any): string => {
@ -1861,22 +1863,34 @@ export class MarkdownTaskParser {
try {
const configuredProjectKey =
this.config.projectConfig?.metadataConfig?.metadataKey;
const configuredProjectValue =
configuredProjectKey !== undefined
? this.fileMetadata[configuredProjectKey]
: undefined;
if (
configuredProjectKey &&
this.fileMetadata[configuredProjectKey] !== undefined &&
this.fileMetadata[configuredProjectKey] !== null &&
String(
this.fileMetadata[configuredProjectKey],
).trim() !== ""
configuredProjectValue !== undefined &&
configuredProjectValue !== null
) {
if (
inherited.project === undefined ||
inherited.project === null ||
inherited.project === ""
) {
inherited.project = String(
this.fileMetadata[configuredProjectKey],
).trim();
if (configuredProjectValue === true) {
const fileName =
filePath.split("/").pop() || filePath;
inherited.project = fileName.replace(
/\.md$/i,
"",
);
} else if (
typeof configuredProjectValue === "string" &&
configuredProjectValue.trim() !== ""
) {
inherited.project =
configuredProjectValue.trim();
}
}
}
} catch {}
@ -1939,6 +1953,10 @@ export class MarkdownTaskParser {
// Convert priority values to numbers before inheritance
if (key === "priority") {
inherited[key] = convertPriorityValue(value);
} else if (key === "project" && value === true) {
const fileName =
filePath.split("/").pop() || filePath;
inherited[key] = fileName.replace(/\.md$/i, "");
} else {
inherited[key] = String(value);
}

View file

@ -907,10 +907,13 @@ export class FileSource {
// 2. Metadata mapping (e.g., custom_project mapped to project via metadataMappings)
// 3. Tag extraction (#project/xxx) - lowest priority, only if frontmatter has nothing
const projectVal = resolvedFrontmatter.project ?? projectFromTags;
const projectValue: string | undefined =
projectVal !== undefined && projectVal !== null
? String(projectVal)
: undefined;
let projectValue: string | undefined;
if (projectVal === true) {
const fileName = filePath.split("/").pop() || filePath;
projectValue = fileName.replace(/\.md$/i, "");
} else if (typeof projectVal === "string" && projectVal.trim()) {
projectValue = projectVal.trim();
}
const shouldStripProjectTags =
!resolvedFrontmatter.project &&

View file

@ -336,9 +336,16 @@ export class FileMetadataTaskParser {
const detectedProject = this.detectProjectFromFile(filePath, frontmatter, fileCache);
if (detectedProject) {
metadata.project = detectedProject;
} else if (frontmatter.project) {
} else if (frontmatter.project === true) {
// Legacy boolean semantics: `project: true` means use filename
const fileName = filePath.split("/").pop() || filePath;
metadata.project = fileName.replace(/\.md$/i, "");
} else if (
typeof frontmatter.project === "string" &&
frontmatter.project.trim()
) {
// Fallback to legacy project field
metadata.project = String(frontmatter.project);
metadata.project = frontmatter.project.trim();
}
if (frontmatter.context) {
metadata.context = String(frontmatter.context);