Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion packages/foam-core/src/note-graph.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Graph, Edge } from 'graphlib';
import { Position, Point } from 'unist';
import GithubSlugger from 'github-slugger';
import { EventEmitter } from 'events';

type ID = string;

Expand Down Expand Up @@ -54,24 +55,32 @@ export class Note {
}
}

export type NoteGraphEventHandler = (e: { note: Note }) => void;

export class NoteGraph {
private graph: Graph;
private events: EventEmitter;

constructor() {
this.graph = new Graph();
this.events = new EventEmitter();
}

public setNote(note: Note) {
if (this.graph.hasNode(note.id)) {
const noteExists = this.graph.hasNode(note.id);
if (noteExists) {
(this.graph.outEdges(note.id) || []).forEach(edge => {
this.graph.removeEdge(edge);
});
}

this.graph.setNode(note.id, note);
note.links.forEach(link => {
const slugger = new GithubSlugger();
this.graph.setEdge(note.id, slugger.slug(link.to), link.text);
});

this.events.emit(noteExists ? 'update' : 'add', { note });
}

public getNotes(): Note[] {
Expand Down Expand Up @@ -102,6 +111,23 @@ export class NoteGraph {
convertEdgeToLink(edge, this.graph)
);
}

public unstable_onNoteAdded(callback: NoteGraphEventHandler) {
this.events.addListener('add', callback);
}

public unstable_onNoteUpdated(callback: NoteGraphEventHandler) {
this.events.addListener('update', callback);
}

public unstable_removeEventListener(callback: NoteGraphEventHandler) {
this.events.removeListener('add', callback);
this.events.removeListener('update', callback);
}

public dispose() {
this.events.removeAllListeners();
}
}

const convertEdgeToLink = (edge: Edge, graph: Graph): Link => ({
Expand Down
71 changes: 59 additions & 12 deletions packages/foam-vscode/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,79 @@
"use strict";

import * as fs from "fs";
import { workspace, ExtensionContext, window, EndOfLine } from "vscode";
import {
workspace,
ExtensionContext,
window,
EndOfLine,
Uri,
FileSystemWatcher
} from "vscode";

import {
createNoteFromMarkdown,
createFoam,
FoamConfig,
Foam
} from "foam-core";

import { createNoteFromMarkdown, createFoam, FoamConfig } from "foam-core";
import { features } from "./features";

let workspaceWatcher: FileSystemWatcher | null = null;

export function activate(context: ExtensionContext) {
const foamPromise = bootstrap(getConfig());
features.forEach(f => {
f.activate(context, foamPromise);
});
}

export function deactivate() {
workspaceWatcher?.dispose();
}

function isLocalMarkdownFile(uri: Uri) {
return uri.scheme === "file" && uri.path.match(/\.(md|mdx|markdown)/i);
}

async function registerFile(foam: Foam, localUri: Uri) {
// read file from disk (async)
const path = localUri.fsPath;
const data = await fs.promises.readFile(path);
const markdown = (data || "").toString();

// create note
const eol =
window.activeTextEditor?.document?.eol === EndOfLine.CRLF ? "\r\n" : "\n";
const note = createNoteFromMarkdown(path, markdown, eol);

// add to graph
foam.notes.setNote(note);
return note;
}

const bootstrap = async (config: FoamConfig) => {
const files = await workspace.findFiles("**/*");
const foam = createFoam(config);
await Promise.all(
files
.filter(f => f.scheme === "file" && f.path.match(/\.(md|mdx|markdown)/i))
.map(f => {
return fs.promises.readFile(f.fsPath).then(data => {
const markdown = (data || "").toString();
const eol = window.activeTextEditor?.document?.eol === EndOfLine.CRLF ? "\r\n" : "\n";
foam.notes.setNote(createNoteFromMarkdown(f.fsPath, markdown, eol));
});
})
const addFile = (uri: Uri) => registerFile(foam, uri);

await Promise.all(files.filter(isLocalMarkdownFile).map(addFile));

workspaceWatcher = workspace.createFileSystemWatcher(
"**/*",
false,
true,
true
);

workspaceWatcher.onDidCreate(uri => {
if (isLocalMarkdownFile(uri)) {
addFile(uri).then(() => {
console.log(`Added ${uri} to workspace`);
});
}
});

return foam;
};

Expand Down
29 changes: 22 additions & 7 deletions packages/foam-vscode/src/features/wikilink-reference-generation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,15 @@ import { FoamFeature } from "../types";
const feature: FoamFeature = {
activate: async (context: ExtensionContext, foamPromise: Promise<Foam>) => {
const foam = await foamPromise;

context.subscriptions.push(
commands.registerCommand("foam-vscode.update-wikilinks", () =>
updateReferenceList(foam.notes)
),

workspace.onWillSaveTextDocument(e => {
if (e.document.languageId === "markdown") {
foam.notes.setNote(
createNoteFromMarkdown(
e.document.fileName,
e.document.getText(),
docConfig.eol
)
);
updateDocumentInNoteGraph(foam, e.document);
e.waitUntil(updateReferenceList(foam.notes));
}
}),
Expand All @@ -56,14 +52,33 @@ const feature: FoamFeature = {
new WikilinkReferenceCodeLensProvider(foam.notes)
)
);

// when a file is created as a result of peekDefinition
// action on a wikilink, add definition update references
foam.notes.unstable_onNoteAdded(e => {
let editor = window.activeTextEditor;
if (!editor || !isMdEditor(editor)) {
return;
}

updateDocumentInNoteGraph(foam, editor.document);
updateReferenceList(foam.notes);
});
}
};

function updateDocumentInNoteGraph(foam: Foam, document: TextDocument) {
foam.notes.setNote(
createNoteFromMarkdown(document.fileName, document.getText(), docConfig.eol)
);
}

const REFERENCE_HEADER = `[//begin]: # "Autogenerated link references for markdown compatibility"`;
const REFERENCE_FOOTER = `[//end]: # "Autogenerated link references"`;

async function createReferenceList(foam: NoteGraph) {
let editor = window.activeTextEditor;

if (!editor || !isMdEditor(editor)) {
return;
}
Expand Down