Merge pull request #167 from nathonius/feat/155/file-tag

 feat: #155 Show the full filename when linking directly to a file
This commit is contained in:
Nathan 2025-02-26 23:30:45 -06:00 committed by GitHub
commit ceab053dca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 76 additions and 6 deletions

View file

@ -10,6 +10,8 @@ export interface ParsedUrl {
code?: {
branch?: string;
path?: string;
filename?: string;
line?: string;
};
commit?: string;
}
@ -60,6 +62,8 @@ export function parseUrl(urlString: string): ParsedUrl | null {
if (urlParts[5]) {
const pathParts = urlParts.slice(5);
parsedUrl.code.path = pathParts.join("/");
parsedUrl.code.filename = pathParts.last();
parsedUrl.code.line = url.hash.slice(1);
}
break;
case "commit":

View file

@ -56,6 +56,10 @@ export function setIssueIcon(icon: HTMLElement, status: IssueStatus): void {
icon.dataset.status = status;
}
export function setFileIcon(icon: HTMLElement): void {
setIcon(icon, "file");
}
export function setPRMergeableIcon(icon: HTMLElement, mergeable: boolean): void {
if (PluginSettings.tagTooltips) {
icon.setAttribute("aria-label", PRMergeableText[`${mergeable}`]);

View file

@ -7,6 +7,7 @@ const selectors = {
repo: ".github-link-inline-repo",
issueTitle: ".github-link-inline-issue-title",
prTitle: ".github-link-inline-pr-title",
file: ".github-link-inline-file",
};
describe("createTag", () => {
@ -23,7 +24,7 @@ describe("createTag", () => {
expect(tag).toBeTruthy();
const org = tag?.querySelector(selectors.org) as HTMLSpanElement;
expect(org.innerText).toEqual(user);
[selectors.repo, selectors.issueTitle, selectors.prTitle].forEach((s) => {
[selectors.repo, selectors.issueTitle, selectors.prTitle, selectors.file].forEach((s) => {
expect(tag?.querySelector(s)).toBeFalsy();
});
});
@ -38,7 +39,7 @@ describe("createTag", () => {
const repoEl = tag?.querySelector(selectors.repo) as HTMLSpanElement;
expect(org.innerText).toEqual(user);
expect(repoEl.innerText).toEqual(repo);
[selectors.issueTitle, selectors.prTitle].forEach((s) => {
[selectors.issueTitle, selectors.prTitle, selectors.file].forEach((s) => {
expect(tag?.querySelector(s)).toBeFalsy();
});
});

View file

@ -1,6 +1,6 @@
import { setIcon } from "obsidian";
import { IssueStatus, getIssueStatus, getPRStatus } from "../github/response";
import { setIssueIcon, setPRIcon, setPRMergeableIcon } from "../icon";
import { setFileIcon, setIssueIcon, setPRIcon, setPRMergeableIcon } from "../icon";
import { PluginSettings } from "../plugin";
import type { PullResponse } from "../github/response";
@ -20,6 +20,7 @@ export function createTag(href: string): HTMLAnchorElement | null {
if (!parsedUrl) {
return null;
}
const container = createEl("a", { cls: "github-link-inline", href, attr: { target: "_blank" } });
const config: TagConfig = {
icon: createSpan({ cls: ["github-link-status-icon", "github-link-inline-icon"] }),
@ -30,8 +31,8 @@ export function createTag(href: string): HTMLAnchorElement | null {
createOrgSection(config, parsedUrl);
createRepoSection(config, parsedUrl);
// Add issue OR pr
if (parsedUrl.issue !== undefined || parsedUrl.pr !== undefined) {
// Add issue OR pr OR file
if (parsedUrl.issue !== undefined || parsedUrl.pr !== undefined || parsedUrl.code !== undefined) {
// Remove org
const orgIndex = config.sections.findIndex((section) => section.classList.contains("github-link-inline-org"));
if (orgIndex !== -1) {
@ -42,6 +43,8 @@ export function createTag(href: string): HTMLAnchorElement | null {
createIssueSection(config, parsedUrl, container);
} else if (parsedUrl.pr !== undefined) {
createPullRequestSection(config, parsedUrl, container);
} else if (parsedUrl.code !== undefined) {
createFileSection(config, parsedUrl, container);
}
}
@ -140,6 +143,28 @@ function createPullRequestSection(config: TagConfig, parsedUrl: ParsedUrl, conta
}
}
function createFileSection(config: TagConfig, parsedUrl: ParsedUrl, _container: HTMLAnchorElement) {
if (parsedUrl.code === undefined) {
return;
}
const fileContainer = createSpan({ cls: "github-link-inline-file" });
setFileIcon(config.icon);
config.sections.push(fileContainer);
if (parsedUrl.code.filename) {
fileContainer.setText(parsedUrl.code.filename);
} else if (parsedUrl.code.path) {
fileContainer.setText(parsedUrl.code.path);
}
if (PluginSettings.tagShowFileLineNumber && parsedUrl.code.line) {
fileContainer.appendChild(createSpan({ cls: "github-link-inline-file-line-number", text: parsedUrl.code.line }));
}
if (PluginSettings.tagShowFileBranchName && parsedUrl.code.branch) {
fileContainer.appendChild(
createSpan({ cls: "github-link-inline-file-branch", text: `(${parsedUrl.code.branch})` }),
);
}
}
/**
* Note that this function is called AFTER the tag has been built, so it adds itself to the dom.
*/

View file

@ -157,6 +157,28 @@ export class GithubLinkPluginSettingsTab extends PluginSettingTab {
});
});
new Setting(containerEl)
.setName("File branch name")
.setDesc("Append the branch name to links to an individual file inside a repo")
.addToggle((toggle) => {
toggle.setValue(PluginSettings.tagShowFileBranchName);
toggle.onChange((value) => {
PluginSettings.tagShowFileBranchName = value;
void this.saveSettings();
});
});
new Setting(containerEl)
.setName("File line number")
.setDesc("Append the line number (if provided) to links to an individual file inside a repo")
.addToggle((toggle) => {
toggle.setValue(PluginSettings.tagShowFileLineNumber);
toggle.onChange((value) => {
PluginSettings.tagShowFileLineNumber = value;
void this.saveSettings();
});
});
containerEl.createEl("h3", { text: "Cache settings" });
new Setting(containerEl)

View file

@ -27,6 +27,8 @@ export interface GithubLinkPluginSettings {
logLevel: LogLevel;
tagTooltips: boolean;
tagShowPRMergeable: boolean;
tagShowFileBranchName: boolean;
tagShowFileLineNumber: boolean;
cacheIntervalSeconds: number;
maxCacheAgeHours: number;
minRequestSeconds: number;
@ -41,6 +43,8 @@ export const DEFAULT_SETTINGS: GithubLinkPluginSettings = {
logLevel: LogLevel.Error,
tagTooltips: false,
tagShowPRMergeable: false,
tagShowFileBranchName: true,
tagShowFileLineNumber: true,
cacheIntervalSeconds: 60,
maxCacheAgeHours: 120,
minRequestSeconds: 60,

View file

@ -277,13 +277,23 @@ body.theme-dark {
}
.github-link-inline-pr-title,
.github-link-inline-issue-title {
.github-link-inline-issue-title,
.github-link-inline-file {
overflow: hidden;
text-overflow: ellipsis;
text-wrap: nowrap;
line-height: var(--line-height-normal);
}
.github-link-inline-file {
display: flex;
gap: 2px;
}
.github-link-inline-file > .github-link-inline-file-line-number,
.github-link-inline-file > .github-link-inline-file-branch {
color: var(--gh-color-fg-muted);
}
.github-link-table-wrapper {
margin-block-end: var(--p-spacing);
}