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
66 changes: 66 additions & 0 deletions src/app/container.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright (C) 2018 Patrick Maué
//
// This file is part of vscode-journal.
//
// vscode-journal is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// vscode-journal is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with vscode-journal. If not, see <http://www.gnu.org/licenses/>.
//

'use strict';

import * as vscode from 'vscode';
import { IConfiguration, IFileSystem, ILogger, IWorkspaceConfigReader, JournalController } from '../model';
import { Configuration } from '../vscode/conf';
import { Parser } from '../journal/parser';
import { Writer } from '../journal/writer';
import { Reader } from '../journal/reader';
import { Inject } from '../journal/inject';
import { Dialogues } from '../vscode/dialogues';
import { VscodeFileSystem } from '../vscode/vscode-fs';

/**
* Builds the logger once the configuration is available. The logger needs the
* configuration (for trace/dev settings), so the container resolves the config
* first and then hands it to this factory — single-pass, no two-phase init.
*/
export type LoggerFactory = (config: IConfiguration) => ILogger;

/**
* Composition root. Constructs the full service graph in one pass and exposes
* it through the {@link JournalController} interface. Consumers depend on the
* interface, never on this concrete class — only the registration layer
* (`app/`) instantiates it.
*/
export class Container implements JournalController {

public readonly config: Configuration;
public readonly logger: ILogger;
public readonly fs: IFileSystem;
public readonly inject: Inject;
public readonly parser: Parser;
public readonly ui: Dialogues;
public readonly writer: Writer;
public readonly reader: Reader;

constructor(configSource: IWorkspaceConfigReader, loggerFactory: LoggerFactory) {
this.config = new Configuration(configSource);
this.logger = loggerFactory(this.config);
this.fs = new VscodeFileSystem();
this.inject = new Inject(this.config, this.logger);
this.parser = new Parser(this.config, this.logger);
this.ui = new Dialogues(this.config, this.logger, this.parser, this.fs);
this.writer = new Writer(this.config, this.logger, this.inject, this.fs,
async (path) => vscode.workspace.openTextDocument(vscode.Uri.file(path)));
this.reader = new Reader(this.config, this.logger, this.writer, this.ui, this.fs);
}
}
21 changes: 21 additions & 0 deletions src/app/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (C) 2018 Patrick Maué
//
// This file is part of vscode-journal.
//
// vscode-journal is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// vscode-journal is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with vscode-journal. If not, see <http://www.gnu.org/licenses/>.
//

export { Container, LoggerFactory } from './container';
export { Startup } from './startup';
export { registerCommands, registerCodeActions, registerCacheInvalidation } from './register';
81 changes: 81 additions & 0 deletions src/app/register.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (C) 2018 Patrick Maué
//
// This file is part of vscode-journal.
//
// vscode-journal is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// vscode-journal is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with vscode-journal. If not, see <http://www.gnu.org/licenses/>.
//

'use strict';

import * as vscode from 'vscode';
import { Container } from './container';
import {
OpenJournalWorkspaceCommand, OpenNextEntryCommand, OpenPreviousEntryCommand,
PrintDurationCommand, PrintSumCommand, PrintTimeCommand, ShiftTaskCommand,
ShowEntryForInputCommand, ShowEntryForTodayCommand, ShowEntryForTomorrowCommand,
ShowEntryForYesterdayCommand, ShowNoteCommand,
} from '../commands';
import { SyncDailyLinks, SyncNoteLinks, WeeklyEntryWatcher } from '../features';
import { CompletedTaskActions, OpenTaskActions } from '../ui';

const MARKDOWN_SELECTOR: vscode.DocumentSelector = { scheme: 'file', language: 'markdown' };

/** Registers all journal commands and the weekly/note sync wiring. */
export function registerCommands(ctrl: Container, context: vscode.ExtensionContext): void {
ctrl.logger.trace("Entering registerCommands() in app/register.ts");

ctrl.reader.onNotesInjected = (doc, date) => {
new SyncNoteLinks(ctrl).injectAttachmentLinks(doc, date)
.finally(() => ctrl.logger.trace("Scanning notes completed"));
};

const syncDailyLinks = new SyncDailyLinks(ctrl);
context.subscriptions.push(new WeeklyEntryWatcher(ctrl, syncDailyLinks));

context.subscriptions.push(
OpenJournalWorkspaceCommand.create(ctrl),
PrintTimeCommand.create(ctrl),
PrintSumCommand.create(ctrl),
PrintDurationCommand.create(ctrl),
ShowEntryForInputCommand.create(ctrl),
vscode.commands.registerCommand('journal.memo', () =>
new ShowEntryForInputCommand(ctrl).execute()),
ShowEntryForTodayCommand.create(ctrl),
ShowEntryForTomorrowCommand.create(ctrl),
ShowEntryForYesterdayCommand.create(ctrl),
ShowNoteCommand.create(ctrl),
ShiftTaskCommand.create(ctrl),
OpenPreviousEntryCommand.create(ctrl),
OpenNextEntryCommand.create(ctrl),
);
}

/** Registers the markdown code-action providers (task state transitions). */
export function registerCodeActions(ctrl: Container, context: vscode.ExtensionContext): void {
// TODO: add filters only for configured base directories
context.subscriptions.push(
vscode.languages.registerCodeActionsProvider(MARKDOWN_SELECTOR, new CompletedTaskActions(ctrl)),
vscode.languages.registerCodeActionsProvider(MARKDOWN_SELECTOR, new OpenTaskActions(ctrl)),
);
}

/** Wires the entry-scan cache invalidation listeners. */
export function registerCacheInvalidation(ctrl: Container, context: vscode.ExtensionContext): void {
try {
const scanner = ctrl.ui.getScanner();
context.subscriptions.push(...scanner.registerInvalidationListeners());
} catch (error) {
ctrl.logger.error("Failed to register cache invalidation listeners, reason: ", error);
}
}
126 changes: 126 additions & 0 deletions src/app/startup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Copyright (C) 2018 Patrick Maué
//
// This file is part of vscode-journal.
//
// vscode-journal is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// vscode-journal is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with vscode-journal. If not, see <http://www.gnu.org/licenses/>.
//

'use strict';

import * as vscode from 'vscode';
import * as Path from 'path';
import { isNullOrUndefined, ConsoleLogger } from '../util';
import { Configuration } from '../vscode/conf';
import { Container } from './container';
import { registerCacheInvalidation, registerCodeActions, registerCommands } from './register';

interface TextMateRule { scope: string; settings: any; }

/**
* Extension lifecycle entry point. Builds the {@link Container} (composition
* root) in a single pass, then registers commands, providers and optional
* syntax highlighting.
*/
export class Startup {

private ctrl!: Container;

constructor(public config: vscode.WorkspaceConfiguration) { }

public async run(context: vscode.ExtensionContext): Promise<void> {
try {
const channel = vscode.window.createOutputChannel("Journal");
context.subscriptions.push(channel);

this.ctrl = new Container(this.config, (cfg) => new ConsoleLogger(cfg, channel));
this.ctrl.logger.debug("VSCode Journal is starting");

if (this.ctrl.config.isDevelopmentModeEnabled()) {
console.log("Development Mode for Journal extension is enabled, Tracing in Console and Output is activated.");
}

registerCommands(this.ctrl, context);
registerCodeActions(this.ctrl, context);
await this.registerSyntaxHighlighting(this.ctrl);
registerCacheInvalidation(this.ctrl, context);

console.timeEnd("startup");
console.log("VSCode-Journal extension was successfully initialized.");
} catch (error) {
console.error(error);
throw error;
}
}

public getConfiguration(): Configuration {
return this.ctrl.config;
}

public getJournalController(): Container {
return this.ctrl;
}

public async registerSyntaxHighlighting(ctrl: Container): Promise<Container> {
if (this.ctrl.config.isSyntaxHighlightingEnabled()) {
return this.enableSyntaxHighlighting(ctrl);
} else {
return this.disableSyntaxHighlighting(ctrl);
}
}

public async disableSyntaxHighlighting(ctrl: Container): Promise<Container> {
const tokenColorCustomizations = vscode.workspace.getConfiguration('editor.tokenColorCustomizations');
if (!tokenColorCustomizations.has("textMateRules")) { return ctrl; }

const rules: TextMateRule[] = tokenColorCustomizations.get<TextMateRule[]>("textMateRules")!;
const result: TextMateRule[] = rules.filter(rule => !rule.scope.includes("journal"));
await vscode.workspace.getConfiguration().update("editor.tokenColorCustomizations", { "textMateRules": result }, vscode.ConfigurationTarget.Global);
return ctrl;
}

/**
* Sets default syntax highlighting settings on startup, we try to differentiate between dark and light themes
*/
public async enableSyntaxHighlighting(ctrl: Container): Promise<Container> {
const theme: string | undefined = vscode.workspace.getConfiguration().get<string>("workbench.colorTheme");
let style: string;
if (isNullOrUndefined(theme) || theme!.search('Light') > -1) { style = "light"; }
else if (theme!.search('High Contrast') > -1) { style = "high-contrast"; }
else { style = "dark"; }

const tokenColorCustomizations: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration('editor.tokenColorCustomizations');
const rules: TextMateRule[] | undefined = tokenColorCustomizations.get<TextMateRule[]>("textMateRules");

if (isNullOrUndefined(rules) || rules!.length > 0) {
return ctrl;
}

if (style.startsWith("high-contrast")) { return ctrl; }

const ext: vscode.Extension<any> | undefined = vscode.extensions.getExtension("pajoma.vscode-journal");
if (isNullOrUndefined(ext)) { throw Error("Failed to load this extension"); }

const colorConfigDir: string = Path.join(ext!.extensionPath, "res", "colors");
const rawData = await vscode.workspace.fs.readFile(vscode.Uri.file(Path.join(colorConfigDir, style + ".json")));
const data = Buffer.from(rawData).toString('utf-8');

// FIXME: this is a workaround, since we can't simply inject the textMateRules here (not registered configuration)
const existingConfig = vscode.workspace.getConfiguration('editor').get('tokenColorCustomizations');
const mutableExistingConfig = JSON.parse(JSON.stringify(existingConfig));
mutableExistingConfig.textMateRules = JSON.parse(data);
await vscode.workspace.getConfiguration("editor").update("tokenColorCustomizations", mutableExistingConfig, vscode.ConfigurationTarget.Global);

return ctrl;
}
}
7 changes: 3 additions & 4 deletions src/commands/copy-task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@

import moment = require('moment');
import * as vscode from 'vscode';
import { InlineString, InlineTemplate } from '../model';
import { Ctrl } from '../util';
import { JournalController, InlineString, InlineTemplate } from '../model';

export enum ShiftTarget {
nextWorkingDay,
Expand All @@ -43,13 +42,13 @@ export class CopyTaskCommand implements vscode.Command {
command: string = "journal.commands.copy-task";


protected constructor(public ctrl: Ctrl) { }
protected constructor(public ctrl: JournalController) { }

public async dispose(): Promise<void> {
// do nothing
}

public static create(ctrl: Ctrl): vscode.Disposable {
public static create(ctrl: JournalController): vscode.Disposable {
const cmd = new this(ctrl);
vscode.commands.registerCommand(cmd.command, (document, range, target) => cmd.execute(document, range, target));
return cmd;
Expand Down
8 changes: 4 additions & 4 deletions src/commands/open-journal-workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
'use strict';

import * as vscode from 'vscode';
import { Ctrl } from '../util';
import { JournalController } from '../model';
import { isRemoteSession, toLocalFileUri } from '../journal/paths';


Expand All @@ -27,13 +27,13 @@ export class OpenJournalWorkspaceCommand implements vscode.Command, vscode.Dispo
command: string = 'journal.open';


protected constructor(public ctrl: Ctrl) { }
protected constructor(public ctrl: JournalController) { }

public async dispose(): Promise<void> {
// do nothing
}

public static create(ctrl: Ctrl): vscode.Disposable {
public static create(ctrl: JournalController): vscode.Disposable {
const cmd = new this(ctrl);
vscode.commands.registerCommand(cmd.command, () => cmd.openWorkspace());
return cmd;
Expand Down Expand Up @@ -92,4 +92,4 @@ export class OpenJournalWorkspaceCommand implements vscode.Command, vscode.Dispo
}


}
}
5 changes: 2 additions & 3 deletions src/commands/open-next-entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,15 @@
'use strict';

import * as vscode from 'vscode';
import { Input } from '../model';
import { Ctrl } from '../util';
import { JournalController, Input } from '../model';
import { AbstractLoadEntryForDateCommand } from './show-entry-for-date';
import { daysBetween, findAdjacentEntry, getAdjacentWeekInput, resolveAnchor, Mode } from '../journal/navigation';

export class OpenNextEntryCommand extends AbstractLoadEntryForDateCommand {
title: string = "Open the next journal entry";
command: string = "journal.openNext";

public static create(ctrl: Ctrl): vscode.Disposable {
public static create(ctrl: JournalController): vscode.Disposable {
const cmd = new this(ctrl);
vscode.commands.registerCommand(cmd.command, () => cmd.run());
return cmd;
Expand Down
5 changes: 2 additions & 3 deletions src/commands/open-previous-entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,15 @@
'use strict';

import * as vscode from 'vscode';
import { Input } from '../model';
import { Ctrl } from '../util';
import { JournalController, Input } from '../model';
import { AbstractLoadEntryForDateCommand } from './show-entry-for-date';
import { daysBetween, findAdjacentEntry, getAdjacentWeekInput, resolveAnchor, Mode } from '../journal/navigation';

export class OpenPreviousEntryCommand extends AbstractLoadEntryForDateCommand {
title: string = "Open the previous journal entry";
command: string = "journal.openPrevious";

public static create(ctrl: Ctrl): vscode.Disposable {
public static create(ctrl: JournalController): vscode.Disposable {
const cmd = new this(ctrl);
vscode.commands.registerCommand(cmd.command, () => cmd.run());
return cmd;
Expand Down
Loading
Loading