From 1b4317c3b9302964ded8b70fd37142028604879e Mon Sep 17 00:00:00 2001 From: Nathan Smith Date: Wed, 26 Feb 2025 23:23:21 -0600 Subject: [PATCH 1/2] =?UTF-8?q?=E2=9C=A8=20feat:=20#155=20Show=20the=20ful?= =?UTF-8?q?l=20filename=20when=20linking=20directly=20to=20a=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes: #155 --- src/github/url-parse.ts | 4 ++++ src/icon.ts | 4 ++++ src/inline/inline.ts | 31 ++++++++++++++++++++++++++++--- src/settings/settings-tab.ts | 22 ++++++++++++++++++++++ src/settings/types.ts | 4 ++++ styles.css | 12 +++++++++++- 6 files changed, 73 insertions(+), 4 deletions(-) diff --git a/src/github/url-parse.ts b/src/github/url-parse.ts index f63e357..64bbe29 100644 --- a/src/github/url-parse.ts +++ b/src/github/url-parse.ts @@ -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": diff --git a/src/icon.ts b/src/icon.ts index b42bbc8..2fb8ec9 100644 --- a/src/icon.ts +++ b/src/icon.ts @@ -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}`]); diff --git a/src/inline/inline.ts b/src/inline/inline.ts index bfc16e2..206c138 100644 --- a/src/inline/inline.ts +++ b/src/inline/inline.ts @@ -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. */ diff --git a/src/settings/settings-tab.ts b/src/settings/settings-tab.ts index 1730a2f..82319b0 100644 --- a/src/settings/settings-tab.ts +++ b/src/settings/settings-tab.ts @@ -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) diff --git a/src/settings/types.ts b/src/settings/types.ts index c1f5f80..10af0f5 100644 --- a/src/settings/types.ts +++ b/src/settings/types.ts @@ -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, diff --git a/styles.css b/styles.css index 2123f71..8f70c7b 100644 --- a/styles.css +++ b/styles.css @@ -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); } From dd3bfaa15ff0ffa65239b3da5d08878217631321 Mon Sep 17 00:00:00 2001 From: Nathan Smith Date: Wed, 26 Feb 2025 23:25:55 -0600 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=9A=A8=20test:=20#155=20Add=20file=20?= =?UTF-8?q?sections=20to=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/inline/inline.spec.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/inline/inline.spec.ts b/src/inline/inline.spec.ts index 410cd53..742866e 100644 --- a/src/inline/inline.spec.ts +++ b/src/inline/inline.spec.ts @@ -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(); }); });