diff --git a/package.json b/package.json index 0c5b0919..ff23b0ed 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "typescript": "4.3.5" }, "dependencies": { + "diff2html": "^3.4.13", "obsidian-community-lib": "^1.0.1", "simple-git": "^2.21.0", "supports-color": "^7.2.0" diff --git a/src/constants.ts b/src/constants.ts index bc1c383c..86132d43 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -18,8 +18,14 @@ export const DEFAULT_SETTINGS: ObsidianGitSettings = { mergeOnPull: true, }; -export const VIEW_CONFIG = { +export const GIT_VIEW_CONFIG = { type: 'git-view', name: 'Source Control', icon: 'feather-git-pull-request' -}; \ No newline at end of file +}; + +export const DIFF_VIEW_CONFIG = { + type: 'diff-view', + name: 'Diff View', + icon: 'feather-git-pull-request' +} \ No newline at end of file diff --git a/src/gitManager.ts b/src/gitManager.ts index 1531b4e3..95c5ecc2 100644 --- a/src/gitManager.ts +++ b/src/gitManager.ts @@ -61,6 +61,8 @@ export abstract class GitManager { abstract updateGitPath(gitPath: string): void; + abstract getDiffString(filePath: string): string; + async formatCommitMessage(message?: string): Promise { let template = message ?? this.plugin.settings.commitMessage; let status: Status | undefined; diff --git a/src/main.ts b/src/main.ts index f1ff8d03..10d28031 100644 --- a/src/main.ts +++ b/src/main.ts @@ -5,11 +5,12 @@ import { ObsidianGitSettingsTab } from "src/settings"; import { StatusBar } from "src/statusBar"; import { ChangedFilesModal } from "src/ui/modals/changedFilesModal"; import { CustomMessageModal } from "src/ui/modals/customMessageModal"; -import { DEFAULT_SETTINGS, VIEW_CONFIG } from "./constants"; +import { DEFAULT_SETTINGS, DIFF_VIEW_CONFIG, GIT_VIEW_CONFIG } from "./constants"; import { GitManager } from "./gitManager"; import openInGitHub from "./openInGitHub"; import { SimpleGit } from "./simpleGit"; import { ObsidianGitSettings, PluginState } from "./types"; +import DiffView from "./ui/diff/diffView"; import addIcons from "./ui/icons"; import { GeneralModal } from "./ui/modals/generalModal"; import GitView from "./ui/sidebar/sidebarView"; @@ -36,13 +37,18 @@ export default class ObsidianGit extends Plugin { async onload() { console.log('loading ' + this.manifest.name + " plugin"); await this.loadSettings(); + addIcons(); - this.registerView(VIEW_CONFIG.type, (leaf) => { + this.registerView(GIT_VIEW_CONFIG.type, (leaf) => { return new GitView(leaf, this); }); - (this.app.workspace as any).registerHoverLinkSource(VIEW_CONFIG.type, { + this.registerView(DIFF_VIEW_CONFIG.type, (leaf) => { + return new DiffView(leaf, this); + }); + + (this.app.workspace as any).registerHoverLinkSource(GIT_VIEW_CONFIG.type, { display: 'Git View', defaultMod: true, }); @@ -53,19 +59,28 @@ export default class ObsidianGit extends Plugin { id: 'open-git-view', name: 'Open Source Control View', callback: async () => { - if (this.app.workspace.getLeavesOfType(VIEW_CONFIG.type).length === 0) { + if (this.app.workspace.getLeavesOfType(GIT_VIEW_CONFIG.type).length === 0) { await this.app.workspace.getRightLeaf(false).setViewState({ - type: VIEW_CONFIG.type, + type: GIT_VIEW_CONFIG.type, }); } - this.app.workspace.revealLeaf(this.app.workspace.getLeavesOfType(VIEW_CONFIG.type).first()); + this.app.workspace.revealLeaf(this.app.workspace.getLeavesOfType(GIT_VIEW_CONFIG.type).first()); + }, + }); + + this.addCommand({ + id: 'open-diff-view', + name: 'Open Diff View', + editorCallback: async (editor, view) => { + this.app.workspace.createLeafBySplit(view.leaf).setViewState({ type: DIFF_VIEW_CONFIG.type }); + dispatchEvent(new CustomEvent('diff-update', { detail: { path: view.file.path } })); }, }); this.addCommand({ id: 'view-file-in-github', name: 'Open File in GitHub', - editorCallback: (editor, {file}) => openInGitHub(editor, file, this.gitManager), + editorCallback: (editor, { file }) => openInGitHub(editor, file, this.gitManager), }); this.addCommand({ @@ -151,8 +166,7 @@ export default class ObsidianGit extends Plugin { } async onunload() { - (this.app.workspace as any).unregisterHoverLinkSource(VIEW_CONFIG.type); - this.app.workspace.detachLeavesOfType(VIEW_CONFIG.type); + (this.app.workspace as any).unregisterHoverLinkSource(GIT_VIEW_CONFIG.type); this.clearAutoPull(); this.clearAutoBackup(); console.log('unloading ' + this.manifest.name + " plugin"); diff --git a/src/simpleGit.ts b/src/simpleGit.ts index b822e2e9..1c81e473 100644 --- a/src/simpleGit.ts +++ b/src/simpleGit.ts @@ -1,4 +1,4 @@ -import { spawnSync } from "child_process"; +import { spawn, spawnSync } from "child_process"; import { FileSystemAdapter } from "obsidian"; import * as path from "path"; import simpleGit, * as simple from "simple-git"; @@ -299,6 +299,15 @@ export class SimpleGit extends GitManager { this.setGitInstance(); } + getDiffString(filePath: string): string { + const command = spawnSync(this.plugin.settings.gitPath || 'git', ['diff', filePath], { + //@ts-ignore + cwd: this.plugin.app.vault.adapter.basePath + }); + this.git.diffSummary() + return command.output.toString(); + } + private isGitInstalled(): boolean { // https://github.com/steveukx/git-js/issues/402 const command = spawnSync(this.plugin.settings.gitPath || 'git', ['--version'], { diff --git a/src/ui/diff/diffView.ts b/src/ui/diff/diffView.ts new file mode 100644 index 00000000..33c514b9 --- /dev/null +++ b/src/ui/diff/diffView.ts @@ -0,0 +1,76 @@ +import { html } from "diff2html"; +import { HoverParent, HoverPopover, ItemView, MarkdownView, WorkspaceLeaf } from "obsidian"; +import { DIFF_VIEW_CONFIG } from "src/constants"; +import ObsidianGit from "src/main"; + + +export default class DiffView extends ItemView { + + plugin: ObsidianGit; + filePath: string; + parser: DOMParser; + + constructor(leaf: WorkspaceLeaf, plugin: ObsidianGit) { + super(leaf); + this.plugin = plugin; + this.parser = new DOMParser(); + + this.registerEvent(this.app.workspace.on('active-leaf-change', (leaf) => { + if (leaf.view instanceof MarkdownView) { + this.filePath = leaf.view.file.path; + } else { + this.filePath = null; + } + this.refresh(); + })); + this.firstOpen = this.firstOpen.bind(this); + addEventListener('diff-update', this.firstOpen); + this.registerInterval(window.setInterval(() => this.refresh(), 10000)); + } + + firstOpen(event: CustomEvent) { + this.filePath = event.detail.path; + this.refresh(); + } + + getViewType(): string { + return DIFF_VIEW_CONFIG.type; + } + + getDisplayText(): string { + return DIFF_VIEW_CONFIG.name; + } + + getIcon(): string { + return DIFF_VIEW_CONFIG.icon; + } + + onClose(): Promise { + removeEventListener('diff-update', this.firstOpen) + return super.onClose(); + } + + onOpen(): Promise { + this.refresh(); + return super.onOpen(); + } + + refresh(): void { + if (this.filePath) { + this.contentEl.empty(); + const diff = this.parser.parseFromString( + html(this.plugin.gitManager.getDiffString(this.filePath)), + 'text/html') + .querySelector('.d2h-file-diff'); + if (diff) { + this.contentEl.append(diff); + } else { + const div = this.contentEl.createDiv({ cls: 'diff-err' }); + div.createSpan({ text: '⚠️', cls: 'diff-err-sign' }); + div.createEl('br'); + div.createSpan({ text: 'No changes to this file.' }); + } + } + } + +} diff --git a/src/ui/icons.ts b/src/ui/icons.ts index 5b4f3ad9..a8bb86dc 100644 --- a/src/ui/icons.ts +++ b/src/ui/icons.ts @@ -14,4 +14,5 @@ export default function addIcons(): void { addFeatherIcon('alert-circle'); addFeatherIcon('alert-triangle'); addFeatherIcon('git-commit'); + addFeatherIcon('edit') } \ No newline at end of file diff --git a/src/ui/sidebar/components/fileComponent.svelte b/src/ui/sidebar/components/fileComponent.svelte index 81e15d70..71547cc1 100644 --- a/src/ui/sidebar/components/fileComponent.svelte +++ b/src/ui/sidebar/components/fileComponent.svelte @@ -1,6 +1,7 @@