22 |
23 | **BuJo** is an [VSCode](https://code.visualstudio.com/) extension that adds
24 | syntax highlighting for Bullet Journal entries and provides convenient commands,
25 | snippets, keybindings, and more for managing tasks and notes in Markdown. It
26 | works best in combination with [Dendron](https://github.com/dendronhq/dendron)
27 | or [Foam](https://github.com/foambubble/foam) for an excellent way to turn your
28 | text editor into a full-fledged personal knowledge management and productivity
29 | system.
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
BuJo Syntax
39 |
40 |
41 |
42 |
43 |
44 |
45 |
Time Blocking
46 |
47 |
48 |
49 |
50 |
51 |
52 |
Time Tracking
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/docs/.vuepress/styles/index.scss:
--------------------------------------------------------------------------------
1 | // Global.
2 | .medium-zoom-image {
3 | border-radius: 0.5rem;
4 | }
5 |
6 | .medium-zoom-image--opened {
7 | border-radius: 0.5rem;
8 | box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
9 | }
10 |
11 | // Home page.
12 | .theme-container.page-home {
13 | .feature img {
14 | box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);
15 | }
16 |
17 | .main-text {
18 | margin-top: 2.5rem;
19 | border-top: 1px solid var(--c-border);
20 | transition: border-color var(--t-color);
21 | padding-top: 1.5rem;
22 |
23 | .main-text-content {
24 | margin-left: 2.5rem;
25 | margin-right: 2.5rem;
26 | text-align: center;
27 | }
28 | }
29 |
30 | @media only screen and (max-width: 719px) {
31 | .main-text {
32 | .main-text-content {
33 | margin-left: 1rem;
34 | margin-right: 1rem;
35 | text-align: center;
36 | }
37 | }
38 | }
39 |
40 | .license {
41 | margin-top: 0.5rem;
42 | }
43 | }
44 |
45 | // Guide.
46 | .theme-container.page-guide {
47 | .showcase-image {
48 | text-align: center;
49 |
50 | img {
51 | box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
52 | }
53 | }
54 |
55 | .repo-badges {
56 | img {
57 | margin-right: 0.5rem;
58 | border-radius: 0rem;
59 | }
60 | }
61 |
62 | // See https://stackoverflow.com/a/54924505/5252007.
63 | .showcase-video {
64 | position: relative;
65 | padding-bottom: 56.25%; /* 16:9 */
66 | height: 0;
67 |
68 | iframe {
69 | position: absolute;
70 | top: 0;
71 | left: 0;
72 | width: 100%;
73 | height: 100%;
74 | border-radius: 0.5rem;
75 | box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/models/Symbol.ts:
--------------------------------------------------------------------------------
1 | import { Range, TextEditor, TextEditorEdit, TextLine } from "vscode";
2 | import { EntryLine } from "./EntryLine";
3 | import { Pattern } from "./Pattern";
4 |
5 |
6 | export class Symbol {
7 | /**
8 | * VS Code objects.
9 | */
10 | private editor: TextEditor;
11 |
12 |
13 | /**
14 | * Symbol constructor.
15 | */
16 | public constructor(editor: TextEditor) {
17 | this.editor = editor;
18 | }
19 |
20 |
21 | /**
22 | * Get line index for a given entry symbol.
23 | * @param line An instance of `TextLine` class.
24 | * @param symbol The symbol to use during parsing.
25 | */
26 | private getLineIndexAtSymbol(line: TextLine, symbol: string): number {
27 | // Get `regex` pattern given symbol.
28 | const pattern = Pattern.extractSymbol(symbol);
29 |
30 | // Match and return.
31 | return line.text.match(pattern)!.index!;
32 | };
33 |
34 |
35 | /**
36 | * Update the symbol corresponding to an entry.
37 | * @param newSymbol The new symbol for updating the entry.
38 | * @param entry An instance of `Entry` class.
39 | * @param toggle A logical value indicating whether to toggle between the current symbol and the open task symbol.
40 | */
41 | public async update(newSymbol: string, entry: EntryLine, toggle: boolean = true): Promise {
42 | // Get index for the current symbol.
43 | const index = this.getLineIndexAtSymbol(entry.line, entry.symbol);
44 |
45 | // Toggle symbol if the new symbol matches the current one.
46 | if (toggle && entry.symbol == newSymbol) {
47 | // Toggle between the current and the open symbol,
48 | entry.symbol = " ";
49 | } else {
50 | // Otherwise set the new symbol.
51 | entry.symbol = newSymbol;
52 | }
53 |
54 | // Create range for replacement.
55 | const range: Range = new Range(entry.line.lineNumber, index, entry.line.lineNumber, index + 1);
56 |
57 | // Replace character with symbol.
58 | const status = await this.editor.edit((editBuilder: TextEditorEdit) => {
59 | editBuilder.replace(range, entry.symbol);
60 | });
61 |
62 | return status;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/docs/guide/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | pageClass: page-guide
3 | ---
4 |
5 | # Introduction
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | **BuJo** is an extension that adds syntax highlighting for Bullet Journal
16 | entries (e.g., tasks and events) and provides convenient commands, snippets, and
17 | keybindings for managing tasks and notes in Markdown. It works best in
18 | combination with [Dendron](https://github.com/dendronhq/dendron) or
19 | [Foam](https://github.com/foambubble/foam) for an awesome way to turn VS Code
20 | into a full-fledged personal knowledge management and productivity system.
21 |
22 | ## Syntax Highlighting
23 |
24 | At the syntax highlighting level, **BuJo** parses the text written in Markdown
25 | files for specific patterns and allows arbitrary colors to be mapped to
26 | different parts of a **BuJo** entry. Out of the box, **BuJo** provides
27 | highlighting for the standard Bullet Journal entries proposed by [Carroll
28 | (2018)](https://bulletjournal.com/pages/book). It also provides a way to select
29 | and colorize markdown table grids, and tasks and time records within tables.
30 |
31 | ## Task Management
32 |
33 | **BuJo** goes beyond syntax highlighting and taps into time tracking and time
34 | blocking methodologies (e.g., [Newport,
35 | 2016](https://www.goodreads.com/book/show/25744928-deep-work)). To this end,
36 | **BuJo** proposes commands and keybindings to effortlessly update task statuses,
37 | plan working days, and track the time spent on tasks.
38 |
39 | ## Learn More
40 |
41 | The following guide is aimed at first-time users to provide an overview of the
42 | **BuJo** notation and its main features. The remainder of the guide is
43 | structured as follows:
44 |
45 | 1. [Syntax Highlighting](/guide/syntax-highlighting.md)
46 | 2. [Symbol Updating](/guide/symbol-updating.md)
47 | 3. [Time Tracking](/guide/time-tracking.md)
48 | 4. [Time Blocking](/guide/time-blocking.md)
49 |
--------------------------------------------------------------------------------
/src/operations/scheduleOperations.ts:
--------------------------------------------------------------------------------
1 | import { TextEditor, window, workspace } from "vscode";
2 | import { EntryLine } from "../models/EntryLine";
3 | import { Sanitizer } from "../models/Sanitizer";
4 | import { Scheduler } from "../models/Scheduler";
5 | import { Symbol } from "../models/Symbol";
6 |
7 |
8 | /*
9 | * Copy an entry to a time tracking table.
10 | */
11 | const scheduleEntryOperation = async (): Promise => {
12 | // Ensure an editor is open.
13 | const editor: TextEditor | undefined = window.activeTextEditor;
14 |
15 | // Ensure an editor is open.
16 | if (!editor) {
17 | throw new Error("No editors available.");
18 | }
19 |
20 | // Ensure an workspace is open.
21 | if (!workspace.workspaceFolders) {
22 | throw new Error('Workspace not opened.');
23 | }
24 |
25 | // Get configuration.
26 | const config = workspace.getConfiguration('bujo');
27 |
28 | // Create sanitizer.
29 | const sanitizer = new Sanitizer(editor);
30 |
31 | // Sanitize the line.
32 | await sanitizer.sanitizeActiveLine();
33 |
34 | // Make entry.
35 | const entry = new EntryLine(editor, editor.document.lineAt(editor.selection.active.line));
36 |
37 | // Parse and set the entry elements.
38 | await entry.parse();
39 |
40 | // Create scheduler.
41 | const scheduler = new Scheduler(editor, config);
42 |
43 | // Schedule entry.
44 | return scheduler.schedule(entry).then((success) => {
45 | // Update the entry symbol if the scheduling succeeded.
46 | if (success) {
47 | // Get the scheduling symbol from user settings.
48 | const newSymbol = config.get("scheduler.symbolForScheduledEntry");
49 |
50 | // Create symbol instance.
51 | const symbol: Symbol = new Symbol(editor);
52 |
53 | // Update symbol without toggling.
54 | return symbol.update(newSymbol as string, entry, false);
55 | }
56 |
57 | // Otherwise indicate scheduling failed.
58 | return false;
59 | });
60 | };
61 |
62 |
63 | /**
64 | * Wrapper for to the user command for the scheduling operation.
65 | */
66 | export const scheduleEntry = (): void => {
67 | scheduleEntryOperation().then(success => {
68 | if (success) {
69 | window.showInformationMessage('Entry scheduled.');
70 | } else {
71 | window.showErrorMessage('Failed to schedule entry.');
72 | }
73 | }).catch(error => {
74 | window.showErrorMessage(error.message);
75 | });
76 | }
77 |
--------------------------------------------------------------------------------
/src/models/Interval.ts:
--------------------------------------------------------------------------------
1 | import { TextLine } from "vscode";
2 | import { Pattern } from "./Pattern";
3 |
4 |
5 | export class Interval {
6 | /**
7 | * Time record separator.
8 | */
9 | public static separator: string = "-";
10 |
11 |
12 | /**
13 | * The line used to parse the interval elements.
14 | */
15 | public line: TextLine;
16 |
17 |
18 | /**
19 | * Interval elements for the start and stop time records.
20 | */
21 | public start: string = "";
22 | public stop: string = "";
23 |
24 |
25 | /**
26 | * Whether the interval comes from a line that contains a `BuJo` entry.
27 | */
28 | public root: boolean = false;
29 |
30 |
31 | /**
32 | * Parse a given line to get the time records.
33 | */
34 | constructor(line: TextLine, root: boolean) {
35 | // Set the line.
36 | this.line = line;
37 |
38 | // Indicate whether it is a task entry line.
39 | this.root = root;
40 |
41 | // Set time records.
42 | this.parseTimeRecords();
43 | }
44 |
45 |
46 | /**
47 | * Parse a line to extract time the time records.
48 | */
49 | private parseTimeRecords(): void {
50 | // Store the match.
51 | let match: RegExpMatchArray | null;
52 |
53 | // Check if the current line is the root (i.e., containing a task).
54 | if (this.root) {
55 | // Match.
56 | match = this.line.text.match(Pattern.extractRootTimeInterval);
57 | } else {
58 | // Match.
59 | match = this.line.text.match(Pattern.extractSubsequentTimeInterval);
60 | }
61 |
62 | // Update the time records.
63 | if (match) {
64 | // Update start record.
65 | if (match.groups!.startHour != undefined && match.groups!.startMinute != undefined) {
66 | this.start = `${match.groups!.startHour}:${match.groups!.startMinute}`
67 | }
68 |
69 | // Update stop record.
70 | if (match.groups!.stopHour != undefined && match.groups!.stopMinute != undefined) {
71 | this.stop = `${match.groups!.stopHour}:${match.groups!.stopMinute}`
72 | }
73 | }
74 | }
75 |
76 |
77 | /**
78 | * Return status for the time interval.
79 | */
80 | public getState(): { start: boolean; stop: boolean } {
81 | return {
82 | start: Pattern.checkTimeRecord.test(this.start),
83 | stop: Pattern.checkTimeRecord.test(this.stop)
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/docs/guide/symbol-updating.md:
--------------------------------------------------------------------------------
1 | ---
2 | pageClass: page-guide
3 | title: Symbol Updating
4 | ---
5 |
6 | # Symbol Updating
7 |
8 | **BuJo** provides two ways to update entry symbols: (1) via user commands and
9 | (2) via keybindings.
10 |
11 | ## Via Commands
12 |
13 | **Bujo** provides several commands via the command palette (i.e., `ctrl/cmd +
14 | shift + p`) to update the symbol for the first entry on the line where the
15 | cursor is placed (i.e., including in markdown tables). The following commands
16 | are available:
17 |
18 | - `BuJo: Set Migrated Forward` to set the entry symbol to `[>]`
19 | - `BuJo: Set Migrated Backward` to set the entry symbol to `[<]`
20 | - `BuJo: Set Completed` to set the entry symbol to `[x]`
21 | - `BuJo: Set Open` to set the entry symbol to `[ ]`
22 | - `BuJo: Set In Progress` to set the entry symbol to `[/]`
23 | - `BuJo: Set Dropped` to set the entry symbol to `[-]`
24 |
25 | The following video demonstrates the commands in action:
26 |
27 |
28 |
29 |
30 |
31 | ## Via Keybindings
32 |
33 | **BuJo** also provides functionality to update entry symbols via arbitrary
34 | keybindings that pass the symbol to be set as an argument. For instance, when
35 | triggered, the following keybinding will update the task status to `[x]`, and
36 | toggle between `[x]` and `[ ]` on subsequent triggers:
37 |
38 | ```jsonc
39 | [
40 | // ...
41 | {
42 | "key": "alt+x",
43 | "command": "bujo.setSymbol",
44 | "args": {
45 | "symbol": "x"
46 | },
47 | "when": "editorTextFocus && editorLangId == markdown"
48 | }
49 | // ...
50 | ]
51 | ```
52 |
53 | Several default keybindings are provided for changing entry symbols, albeit they
54 | can be changed as needed:
55 |
56 | - `alt+x` to toggle between `[x]` and `[ ]`
57 | - `alt+o` to set `[ ]`
58 | - `alt+-` to toggle between `[-]` and `[ ]`
59 | - `alt+/` to toggle between `[/]` and `[ ]`
60 | - `alt+,` to toggle between `[<]` and `[ ]`
61 | - `alt+.` to toggle between `[>]` and `[ ]`
62 | - `alt+p` to toggle between `[o]` and `[ ]`
63 |
64 | The video below demonstrates the keybindings in action:
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/src/models/Pattern.ts:
--------------------------------------------------------------------------------
1 | export class Pattern {
2 | /*
3 | * Pattern to check if a line contains a valid entry.
4 | * Demo: https://regex101.com/r/kQeMBp/1
5 | */
6 | public static checkEntry: RegExp = /(?<=\s)\[.\](?=\s*[?!*]?\s*)(?!\s*[?!*]?\s*\||\s*[?!*]?\s*$)/;
7 |
8 |
9 | /*
10 | * Pattern to check if the text of an entry is a wiki link with alias.
11 | * Demo: https://regex101.com/r/tRfPvi/1
12 | */
13 | public static checkAlias: RegExp = /^\[\[.*?\|.*?\]\]$/;
14 |
15 |
16 | /*
17 | * Pattern to parse a line and extract the components of an entry.
18 | * Demo: https://regex101.com/r/aRIlGX/3
19 | */
20 | public static extractEntry: RegExp = /(?<=\s)(?\[)(?.)(?\])(?=\s*[?!*]?\s*)(?!\s*[?!*]?\s*\||\s*[?!*]?\s*$)\s*(?[!?*]?)\s*(?.*?)(?=\s+\^[a-z0-9]{6,}|\s+\||\s*$)/;
21 |
22 |
23 | /*
24 | * Pattern to extract the block quote ID from an entry line.
25 | * Demo: https://regex101.com/r/hQV3mo/2
26 | */
27 | public static extractId: RegExp = /(?<=\s)\[.\](?=\s*[?!*]?\s*)(?!\s*[?!*]?\s*\||\s*[?!*]?\s*$).*?(?<=\^)(?[a-z0-9]{6,})(?=\s*$|\s+\|)/;
28 |
29 |
30 | /*
31 | * Pattern to parse an entry text that contains a wiki link with an alias.
32 | * Demo: https://regex101.com/r/FgtTGc/2
33 | */
34 | public static extractWikiLink: RegExp = /^\[\[(?.*?)\|(?.*?)\]\]$/;
35 |
36 | /*
37 | * Pattern to match the head of the time tracking table.
38 | * Demo: https://regex101.com/r/yvw2uQ/6
39 | */
40 | public static checkTimeTrackingTable: RegExp = /\s+\|\s+Task\s+\|\s+/;
41 |
42 |
43 | /*
44 | * Pattern to extract the symbol of an entry.
45 | * Demo: https://regex101.com/r/ABVEf2/3
46 | */
47 | public static extractSymbol(symbol: string) {
48 | return new RegExp("(?<=\\s\\[)(" + symbol + ")(?=\\]\\s)");
49 | }
50 |
51 |
52 | /*
53 | * Pattern to match a time record.
54 | */
55 | public static checkTimeRecord: RegExp = /[0-2][0-9]:[0-5][0-9]/;
56 |
57 |
58 | /*
59 | * Pattern to extract a time record on the same line with the task name.
60 | */
61 | public static extractRootTimeInterval = /(?<=\|)(?:\s+[^(]?)(?[0-2][0-9])(?:\s*)(:)\s*(?[0-5][0-9])(?:\s*)(-)?(?:(?:\s*)(?[0-2][0-9])(?:\s*)(:)(?:\s*)(?[0-5][0-9]))?(?=[^\)]?\s+\|\s+\[.\])/;
62 |
63 |
64 | /*
65 | * Pattern to extract a subsequent time record (i.e., on a line below the
66 | * line containing the task name).
67 | */
68 | public static extractSubsequentTimeInterval = /(?<=\|)(?:\s+[^(]?)(?[0-2][0-9])(?:\s*)(:)\s*(?[0-5][0-9])(?:\s*)(-)?(?:(?:\s*)(?[0-2][0-9])(?:\s*)(:)(?:\s*)(?[0-5][0-9]))?(?=[^\)]?\s+\|)(?!\s+\|\s+\[.\])/;
69 | }
70 |
--------------------------------------------------------------------------------
/src/operations/trackOperations.ts:
--------------------------------------------------------------------------------
1 | import { commands, TextEditor, window } from "vscode";
2 | import { EntryLine } from "../models/EntryLine";
3 | import { Tracker } from "../models/Tracker";
4 | import * as _ from "lodash";
5 |
6 |
7 | /**
8 | * Add a time record to the time tracking table.
9 | */
10 | const recordTimeOperation = async (): Promise => {
11 | // Ensure an editor is open.
12 | const editor: TextEditor | undefined = window.activeTextEditor;
13 |
14 | // Ensure an editor is open.
15 | if (!editor) {
16 | throw new Error("No editors open.");
17 | }
18 |
19 | // Make entry.
20 | const entry = new EntryLine(editor, editor.document.lineAt(editor.selection.active.line));
21 |
22 | // Parse and set the entry elements.
23 | await entry.parse(false);
24 |
25 | // Make tracker.
26 | const tracker = new Tracker(editor);
27 |
28 | // Add time record.
29 | return tracker.addTimeRecord(entry).then((success) => {
30 | if (success) {
31 | // Format the document to align the table.
32 | commands.executeCommand('editor.action.formatDocument');
33 |
34 | // Indicate everything went okay.
35 | return true;
36 | }
37 |
38 | // Otherwise indicate time tracking failed.
39 | return false;
40 | });
41 | }
42 |
43 |
44 | /**
45 | * Wrapper for the user command for the record adding operation.
46 | */
47 | export const recordTime = (): void => {
48 | recordTimeOperation().then(success => {
49 | if (success) {
50 | window.showInformationMessage("Time record added successfully.");
51 | } else {
52 | window.showErrorMessage("Failed to add time record.");
53 | }
54 | }).catch(error => {
55 | window.showErrorMessage(error.message);
56 | });
57 | }
58 |
59 |
60 | /**
61 | * Calculate the total time for a valid entry in the time tracking table.
62 | */
63 | const calculateEntryTimeOperation = async (): Promise => {
64 | // Ensure an editor is open.
65 | const editor: TextEditor | undefined = window.activeTextEditor;
66 |
67 | // Ensure an editor is open.
68 | if (!editor) {
69 | throw new Error("No editors open.");
70 | }
71 |
72 | // Make entry.
73 | const entry = new EntryLine(editor, editor.document.lineAt(editor.selection.active.line));
74 |
75 | // Parse and set the entry elements.
76 | await entry.parse(false);
77 |
78 | // Make tracker.
79 | const tracker = new Tracker(editor);
80 |
81 | // Sum time intervals.
82 | return tracker.sumTimeIntervals(entry);
83 | }
84 |
85 |
86 | /**
87 | * Wrapper for the user command for entry time calculation operation.
88 | */
89 | export const calculateEntryTime = (): void => {
90 | calculateEntryTimeOperation().then(total => {
91 | window.showInformationMessage(`Time spent: ${total} minutes (${_.round(total / 60, 2)} hours).`);
92 | }).catch(error => {
93 | window.showErrorMessage(error.message);
94 | });
95 | }
96 |
--------------------------------------------------------------------------------
/docs/reference/snippets.md:
--------------------------------------------------------------------------------
1 | ---
2 | pageClass: page-reference
3 | ---
4 |
5 | # Snippets
6 |
7 | **BuJo** provides various snippets for everyday actions. Below you can find a
8 | short description and example output for the snippets available:
9 |
10 | - `task` to enter a task
11 |
12 | ```markdown
13 | - [ ]
14 | ```
15 |
16 | - `taskclip` to enter a task from clipboard
17 |
18 | ```markdown
19 | - [ ]
20 | ```
21 |
22 | - `scratch` to scratch a text selection
23 |
24 | ```markdown
25 | ~Some text~
26 | ```
27 |
28 | - `time` to enter the current time
29 |
30 | ```markdown
31 | 10:38
32 | ```
33 |
34 | - `date` to enter the current date
35 |
36 | ```markdown
37 | 2022.04.24
38 | ```
39 |
40 | - `datetime` to enter the current date and time
41 |
42 | ```markdown
43 | 2022.04.24 10:39
44 | ```
45 |
46 | - `timetracktable` to enter a time tracking table
47 |
48 | ```markdown
49 | | Tracker | Task | Backlog |
50 | | ----------: | :--------------- | :------- |
51 | | 00:00-00:00 | [ ] Example task | [[link]] |
52 | | | | |
53 | ```
54 |
55 | - `timetrackrow` to add an empty row to the time tracking table
56 |
57 | ```markdown
58 | | | | |
59 | ```
60 |
61 | - `timetracktask` to enter a task in the time tracking table
62 |
63 | ```markdown
64 | | | [ ] | |
65 | ```
66 |
67 | - `timetracktaskclip` to enter a task from clipboard in the time tracking table
68 |
69 | ```markdown
70 | | | [ ] | |
71 | ```
72 |
73 | - `timeblocktable` to enter a time blocking table
74 |
75 | ```markdown
76 | | Time | Block |
77 | | ----------: | :------------ |
78 | | (00:00) | (Revision #1) |
79 | | | |
80 | | 00:00-00:00 | Chunk (#1) |
81 | | | - Chunk note |
82 | | | |
83 | ```
84 |
85 | - `timeblockrow` to add an empty row to the time blocking table
86 |
87 | ```markdown
88 | | | |
89 | ```
90 |
91 | - `timeblockrev` to enter a revision row in the time blocking table
92 |
93 | ```markdown
94 | | (10:53) | (Revision #1) |
95 | ```
96 |
97 | - `timeblockchunk` to enter a chunk row in the time blocking table
98 |
99 | ```markdown
100 | | 00:00-00:00 | |
101 | ```
102 |
103 | - `timeblocknote` to enter a note row in the time blocking table
104 |
105 | ```markdown
106 | | | - |
107 | ```
108 |
109 | :::tip
110 | The *Markdown All in One* extension provides a table auto-formatting via the
111 | `alt+shift+f` keybinding. This makes it easy to work with and align markdown
112 | tables with a simple keyboard shortcut. This functionality will soon be brought
113 | to **BuJo**.
114 | :::
115 |
--------------------------------------------------------------------------------
/docs/guide/time-tracking.md:
--------------------------------------------------------------------------------
1 | ---
2 | pageClass: page-guide
3 | title: Time Tracking
4 | ---
5 |
6 | # Time Tracking
7 |
8 | **BuJo** provides several commands via the command palette to schedule entries
9 | (i.e., copy them to the **time tracking table** as tasks) and track the time
10 | spent. An example of a time tracking table can be seen below:
11 |
12 |
13 |
14 |
15 |
16 | ## Scheduling Entries
17 |
18 | To schedule tasks, one can use the `BuJo: Schedule Entry`. When executed, this
19 | command will:
20 |
21 | - Extract the **BuJo** entry text (i.e., alias if the entry is a wiki-link) or
22 | wiki-link depending on the value of the setting `bujo.scheduler.taskName`.
23 | - Generate a unique identifier (e.g., `^bf4uuibsangd`) for the **BuJo** entry
24 | and append it at the end of the line.
25 | - Prompt the user to type the name of a markdown file that contains a time
26 | tracking table.
27 | - Copy the entry text to the time tracking table as an open task and include a
28 | reference to the original **BuJo** entry based on the unique identifier.
29 | - Update the symbol of the original **BuJo** entry to indicate that the task has
30 | been migrated to a time tracking table.
31 |
32 | The following video demonstrates the scheduling command in action:
33 |
34 |
35 |
36 |
37 |
38 | Alternatively, the command `BuJo: Schedule Entry` can also be invoked via the
39 | default keybinding `alt+shift+p`.
40 |
41 | ### Settings
42 |
43 | The behavior of the scheduling command can be further customized through the
44 | following user settings:
45 |
46 | #### `bujo.scheduler.plannerPrefix`
47 |
48 | Can be used to specify the prefix to use when selecting the daily planner file
49 | via the input box (e.g., `*prefix*.2022.03.20`).
50 |
51 | #### `bujo.scheduler.symbolForScheduledEntry`
52 |
53 | Can be used to specify the symbol to set for a `BuJo` entry scheduled to the
54 | time track table (i.e., by default, the symbol is updated from `[ ]` to `[>]`).
55 |
56 | #### `bujo.scheduler.taskName`
57 |
58 | Can be used to specify what to use as task name for the time tracking table when
59 | scheduling a `BuJo` entry that contains a wiki link with an alias (e.g., `[[A
60 | random task|project.example.a-random-task]]`:
61 |
62 | - `alias` sets the name of the task in the table to wiki link alias (e.g., `A
63 | random task`)
64 | - `filename` sets the name of the task to the actual wiki link (e.g.,
65 | `[[project.example.a-random-task]]`)
66 |
67 | ## Tracking Time
68 |
69 | **BuJo** also introduces commands to track the time spent on tasks in a time
70 | tracking table:
71 |
72 | - `BuJo: Record Time` to add a time record for a task
73 | - `BuJo: Time Spent` to calculate the total time spent on a task
74 |
75 | The following video demonstrates these commands in action:
76 |
77 |
78 |
79 |
80 |
81 | Additionally, the two commands above can also be invoked via the default
82 | keybindings:
83 |
84 | - `alt+shift+t` to run command `BuJo: Record Time`
85 | - `alt+shift+s` to run command `BuJo: Time Spent`
86 |
87 | ::: tip
88 | Check out the [Snippets Reference](/reference/snippets.md) for handy snippets
89 | that can be used to generate time tracking tables, add tasks from the clipboard,
90 | and more.
91 | :::
92 |
--------------------------------------------------------------------------------
/snippets/bujo.markdown.json:
--------------------------------------------------------------------------------
1 | {
2 | "task": {
3 | "prefix": "task",
4 | "scope": "markdown",
5 | "body": "- [ ] $1",
6 | "description": "Enter a task"
7 | },
8 | "task from clipboard": {
9 | "prefix": "taskclip",
10 | "scope": "markdown",
11 | "body": "- [ ] $CLIPBOARD$0",
12 | "description": "Enter a task from clipboard"
13 | },
14 |
15 | "scratch text": {
16 | "prefix": "scratch",
17 | "scope": "markdown",
18 | "body": [
19 | "~${TM_SELECTED_TEXT}~"
20 | ],
21 | "description": "Scratch a text selection"
22 | },
23 |
24 | "time": {
25 | "prefix": "time",
26 | "scope": "markdown",
27 | "body": "$CURRENT_HOUR:$CURRENT_MINUTE",
28 | "description": "Enter the current time"
29 | },
30 | "date": {
31 | "prefix": "date",
32 | "scope": "markdown",
33 | "body": "$CURRENT_YEAR.$CURRENT_MONTH.$CURRENT_DATE",
34 | "description": "Enter the current date"
35 | },
36 | "datetime": {
37 | "prefix": "datetime",
38 | "scope": "markdown",
39 | "body": "$CURRENT_YEAR.$CURRENT_MONTH.$CURRENT_DATE $CURRENT_HOUR:$CURRENT_MINUTE",
40 | "description": "Enter the current date and time"
41 | },
42 |
43 | "time track table": {
44 | "prefix": "timetracktable",
45 | "scope": "markdown",
46 | "body": [
47 | "| Tracker | Task | Backlog |",
48 | "| ----------: | :--------------- | :------- |",
49 | "| 00:00-00:00 | [ ] Example task | [[link]] |",
50 | "| | | |"
51 |
52 | ],
53 | "description": "Enter a time tracking table"
54 | },
55 | "time track row": {
56 | "prefix": "timetrackrow",
57 | "scope": "markdown",
58 | "body": [
59 | "| | | |"
60 | ],
61 | "description": "Add an empty row to the time tracking table"
62 | },
63 | "time track task": {
64 | "prefix": "timetracktask",
65 | "scope": "markdown",
66 | "body": [
67 | "| | [ ] ${1} | |"
68 | ],
69 | "description": "Enter a task in the time tracking table"
70 | },
71 | "time track clipboard": {
72 | "prefix": "timetracktaskclip",
73 | "scope": "markdown",
74 | "body": [
75 | "| | [ ] ${CLIPBOARD/(-\\s\\[[\\sx\\/<>]\\]\\s|\\^[a-z1-9].*|\\[\\[|\\|.*\\]\\])//g} | ${1} |"
76 | ],
77 | "description": "Enter a task from clipboard in the time tracking table"
78 | },
79 |
80 | "time block table": {
81 | "prefix": "timeblocktable",
82 | "scope": "markdown",
83 | "body": [
84 | "| Time | Block |",
85 | "| ----------: | :------------ |",
86 | "| (00:00) | (Revision #1) |",
87 | "| | |",
88 | "| 00:00-00:00 | Chunk (#1) |",
89 | "| | - Chunk note |",
90 | "| | |"
91 | ],
92 | "description": "Enter a time blocking table"
93 | },
94 | "time block row": {
95 | "prefix": "timeblockrow",
96 | "scope": "markdown",
97 | "body": [
98 | "| | |"
99 | ],
100 | "description": "Add an empty row to the time blocking table"
101 | },
102 | "time block revision": {
103 | "prefix": "timeblockrev",
104 | "scope": "markdown",
105 | "body": [
106 | "| ($CURRENT_HOUR:$CURRENT_MINUTE) | (Revision #${1}) |"
107 | ],
108 | "description": "Enter a revision row in the time blocking table"
109 | },
110 | "time block chunk": {
111 | "prefix": "timeblockchunk",
112 | "scope": "markdown",
113 | "body": [
114 | "| 00:00-00:00 | ${1} |"
115 | ],
116 | "description": "Enter a chunk row in the time blocking table"
117 | },
118 | "time block note": {
119 | "prefix": "timeblocknote",
120 | "scope": "markdown",
121 | "body": [
122 | "| | - ${1} |"
123 | ],
124 | "description": "Enter a note row in the time blocking table"
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/operations/symbolOperations.ts:
--------------------------------------------------------------------------------
1 | import { TextEditor, window } from "vscode";
2 | import { EntryLine } from "../models/EntryLine";
3 | import { Symbol } from "../models/Symbol";
4 |
5 |
6 | /**
7 | * Set an entry's symbol.
8 | * @param newSymbol The new symbol for updating the entry.
9 | */
10 | async function setSymbolOperation(newSymbol: string): Promise {
11 | // Ensure an editor is open.
12 | const editor: TextEditor | undefined = window.activeTextEditor;
13 |
14 | // Ensure an editor is open.
15 | if (!editor) {
16 | throw new Error("No editors open.");
17 | }
18 |
19 | // Make entry.
20 | const entry = new EntryLine(editor, editor.document.lineAt(editor.selection.active.line));
21 |
22 | // Parse and set the entry elements.
23 | await entry.parse(false);
24 |
25 | // Make symbol.
26 | const symbol = new Symbol(editor);
27 |
28 | // Update entry symbol.
29 | return await symbol.update(newSymbol, entry);
30 | };
31 |
32 |
33 | /**
34 | * Wrapper for to the user command for the symbol setting operation.
35 | */
36 | export const setSymbol = (args: any): void => {
37 | // Ensure symbol is provided.
38 | if (!args.symbol) {
39 | window.showErrorMessage('Symbol not provided via keybinding.');
40 | return
41 | }
42 |
43 | // Update the task status.
44 | setSymbolOperation(args.symbol).then(success => {
45 | if (success) {
46 | window.showInformationMessage('Updated entry symbol.');
47 | } else {
48 | window.showErrorMessage("Failed to update task symbol");
49 | }
50 | }).catch(error => {
51 | window.showErrorMessage(error.message);
52 | });
53 | }
54 |
55 |
56 | /**
57 | * Migrated forward.
58 | */
59 | export const setSymbolMigratedForward = (): void => {
60 | setSymbolOperation('>').then(success => {
61 | if (success) {
62 | window.showInformationMessage('Migrated forward.');
63 | } else {
64 | window.showErrorMessage("Failed to update task symbol");
65 | }
66 | }).catch(error => {
67 | window.showErrorMessage(error.message);
68 | });
69 | };
70 |
71 |
72 | /**
73 | * Migrated backward.
74 | */
75 | export const setSymbolMigratedBackward = (): void => {
76 | setSymbolOperation('<').then(success => {
77 | if (success) {
78 | window.showInformationMessage('Migrated backward.');
79 | } else {
80 | window.showErrorMessage("Failed to update task symbol");
81 | }
82 | }).catch(error => {
83 | window.showErrorMessage(error.message);
84 | });
85 | };
86 |
87 |
88 | /**
89 | * Completed.
90 | */
91 | export const setSymbolCompleted = (): void => {
92 | setSymbolOperation('x').then(success => {
93 | if (success) {
94 | window.showInformationMessage('Completed.');
95 | } else {
96 | window.showErrorMessage("Failed to update task symbol");
97 | }
98 | }).catch(error => {
99 | window.showErrorMessage(error.message);
100 | });
101 | };
102 |
103 |
104 | /**
105 | * Opened.
106 | */
107 | export const setSymbolOpened = (): void => {
108 | setSymbolOperation(' ').then(success => {
109 | if (success) {
110 | window.showInformationMessage('Opened.');
111 | } else {
112 | window.showErrorMessage("Failed to update task symbol");
113 | }
114 | }).catch(error => {
115 | window.showErrorMessage(error.message);
116 | });
117 | };
118 |
119 |
120 | /**
121 | * Started (i.e., in progress).
122 | */
123 | export const setSymbolStarted = (): void => {
124 | setSymbolOperation('/').then(success => {
125 | if (success) {
126 | window.showInformationMessage('Started.');
127 | } else {
128 | window.showErrorMessage("Failed to update task symbol");
129 | }
130 | }).catch(error => {
131 | window.showErrorMessage(error.message);
132 | });
133 | };
134 |
135 |
136 | /**
137 | * Dropped.
138 | */
139 | export const setSymbolDropped = (): void => {
140 | setSymbolOperation('-').then(success => {
141 | if (success) {
142 | window.showInformationMessage('Dropped.');
143 | } else {
144 | window.showErrorMessage("Failed to update task symbol");
145 | }
146 | }).catch(error => {
147 | window.showErrorMessage(error.message);
148 | });
149 | };
150 |
--------------------------------------------------------------------------------
/src/models/EntryLine.ts:
--------------------------------------------------------------------------------
1 | import { TextEditor, TextEditorEdit, TextLine } from "vscode";
2 | import { Entry } from "./Entry";
3 | import { Pattern } from "./Pattern";
4 | import { genUUIDInsecure } from '../helpers';
5 |
6 | export class EntryLine implements Entry {
7 | /**
8 | * BuJo skeleton implementation.
9 | */
10 | public notationOpen: string = "";
11 | public notationClose: string = "";
12 | public symbol: string = "";
13 | public modifier: string = "";
14 | public text: string = "";
15 | public id: string = "";
16 |
17 |
18 | /**
19 | * If the entry text is a full wiki link with an alias.
20 | */
21 | public wikiLink: { alias: string; filename: string } = { alias: "", filename: "" };
22 |
23 |
24 | /**
25 | * The editor and text line corresponding to the entry.
26 | */
27 | public line: TextLine;
28 | private editor: TextEditor;
29 |
30 |
31 | /**
32 | * Entry constructor.
33 | */
34 | public constructor(editor: TextEditor, line: TextLine) {
35 | // Set the editor.
36 | this.editor = editor;
37 |
38 | // Set the line.
39 | this.line = line;
40 | }
41 |
42 |
43 | /**
44 | * Write a given entry id to the corresponding line in the editor.
45 | */
46 | private async writeEntryId(id: string): Promise {
47 | // Write the id.
48 | return await this.editor.edit((editBuilder: TextEditorEdit) => {
49 | editBuilder.insert(this.line.range.end, ` ^${id}`);
50 | });
51 | }
52 |
53 |
54 | /**
55 | * Set entry elements from text.
56 | */
57 | private parseEntryComponents(text: string) {
58 | // Match the entry elements text.
59 | const match = text.match(Pattern.extractEntry);
60 |
61 | // Set the entry elements.
62 | this.notationOpen = match!.groups!.open;
63 | this.notationClose = match!.groups!.close;
64 | this.symbol = match!.groups!.symbol;
65 | this.modifier = match!.groups!.modifier;
66 | this.text = match!.groups!.text;
67 | }
68 |
69 |
70 | /**
71 | * Set entry id from text.
72 | */
73 | private async parseEntryId(text: string): Promise {
74 | // Match the entry id.
75 | const match = text.match(Pattern.extractId);
76 |
77 | // If there is match.
78 | if (match) {
79 | // Set the entry ID.
80 | this.id = match.groups!.id;
81 | } else {
82 | // Generate an entry ID.
83 | this.id = genUUIDInsecure();
84 |
85 | // Write the ID on the line.
86 | await this.writeEntryId(this.id);
87 | }
88 | }
89 |
90 |
91 | /**
92 | * Set wiki link alias and filename.
93 | */
94 | private parseWikiLink(): void {
95 | // Match the entry id.
96 | const match = this.text.match(Pattern.extractWikiLink);
97 |
98 | // Throw if not a match.
99 | if (!match) {
100 | throw new Error("The entry text is not a wiki link with an alias.");
101 | }
102 |
103 | // Store the matches.
104 | this.wikiLink.alias = match.groups!.alias;
105 | this.wikiLink.filename = match.groups!.filename;
106 | }
107 |
108 |
109 | /**
110 | * Create an entry from a given editor line or fail.
111 | * @param {boolean} [parseId = true] - A boolean indicating whether to parse the entry ID or not.
112 | */
113 | public async parse(parseId: boolean = true): Promise {
114 | // Check if the line has a valid entry.
115 | if (!Pattern.checkEntry.test(this.line.text)) {
116 | throw new Error("The line does not contain a valid BuJo entry.");
117 | }
118 |
119 | // Set entry components.
120 | this.parseEntryComponents(this.line.text);
121 |
122 | // If the ID is relevant.
123 | if (parseId) {
124 | // Set entry ID.
125 | await this.parseEntryId(this.line.text);
126 | }
127 |
128 | // Parse the entry text if it is a valid wiki link with an alias.
129 | if (this.isWikiLinkWithALias()) {
130 | this.parseWikiLink();
131 | }
132 | }
133 |
134 |
135 | /**
136 | * Check if the entry **text** is a wiki link with an alias.
137 | */
138 | public isWikiLinkWithALias(): boolean {
139 | // Perform the check.
140 | return Pattern.checkAlias.test(this.text!);
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Bullet Journal Markdown Workflows
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | [**BuJo**](https://bujo.mihaiconstantin.com) is a [VS
18 | Code](https://code.visualstudio.com/) extension that adds syntax highlighting
19 | for Bullet Journal entries and provides convenient commands, snippets,
20 | keybindings, and more for managing tasks and notes in Markdown. It works best in
21 | combination with [Dendron](https://github.com/dendronhq/dendron) or
22 | [Foam](https://github.com/foambubble/foam) for an awesome way to turn your text
23 | editor into a full-fledged personal knowledge management and productivity
24 | system.
25 |
26 | ## Features
27 |
28 | ### Syntax Highlighting
29 |
30 | At the highlighting syntax level, **BuJo** parses the text written in Markdown
31 | files for specific patterns and allows arbitrary colors to be mapped to
32 | different parts of a **BuJo** entry. Out of the box, **BuJo** provides
33 | highlighting for the standard Bullet Journal entries proposed by [Carroll
34 | (2018)](https://bulletjournal.com/pages/book). It also provides a way to select
35 | and colorize markdown table grids, and tasks and time records within tables.
36 |
37 |
38 |
BuJo Entries
39 |
40 |
41 |
42 |
43 |
Time Blocking
44 |
45 |
46 |
47 |
48 |
Time Tracking
49 |
50 |
51 |
52 | ### Task Management
53 |
54 | **BuJo** goes beyond syntax highlighting and taps into time tracking and time
55 | blocking methodologies (e.g., [Newport,
56 | 2016](https://www.goodreads.com/book/show/25744928-deep-work)). To this end,
57 | **BuJo** proposes commands and keybindings to effortlessly update task statuses,
58 | plan working days, and track the time spent on tasks.
59 |
60 | | Name | Description | Context |
61 | | :-------------------------------- | :-------------------------- | :-----------: |
62 | | `bujo.symbol.setMigratedForward` | BuJo: Set Migrated Forward | entry |
63 | | `bujo.symbol.setMigratedBackward` | BuJo: Set Migrated Backward | entry |
64 | | `bujo.symbol.setCompleted` | BuJo: Set Completed | entry |
65 | | `bujo.symbol.setOpened` | BuJo: Set Open | entry |
66 | | `bujo.symbol.setStarted` | BuJo: Set Started | entry |
67 | | `bujo.symbol.setDropped` | BuJo: Set Dropped | entry |
68 | | `bujo.scheduler.scheduleEntry` | BuJo: Schedule Entry | time tracking |
69 | | `bujo.tracker.recordTime` | BuJo: Record Time | time tracking |
70 | | `bujo.tracker.calculateEntryTime` | BuJo: Calculate Entry Time | time tracking |
71 |
72 |
73 |
78 |
79 |
80 | ## Release Notes
81 |
82 | See the [CHANGELOG](CHANGELOG.md) file.
83 |
84 | ## Contributing
85 |
86 | Any contributions, suggestions, or bug reports are welcome and greatly
87 | appreciated.
88 |
89 | ## Roadmap
90 | For planned features, please visit our [project
91 | page](https://github.com/users/mihaiconstantin/projects/1). Any ideas and
92 | discussions are welcome!
93 |
94 | ## License
95 | `BuJo` is licensed under the [MIT license](LICENSE).
96 |
97 | ## References
98 | - Carroll, R. (2018). *The bullet journal method: Track the past, order the
99 | present, design the future.* Penguin.
100 | - Newport, C. (2016). *Deep Work: Rules for Focused Success in a Distracted
101 | World.* Hachette UK.
102 |
--------------------------------------------------------------------------------
/syntaxes/bujo.entry.injection.json:
--------------------------------------------------------------------------------
1 | {
2 | "scopeName": "bujo.entry.injection",
3 | "injectionSelector": "L:text.html.markdown",
4 | "patterns": [
5 | { "include": "#bujo-task-open" },
6 | { "include": "#bujo-task-in-progress" },
7 | { "include": "#bujo-task-completed" },
8 | { "include": "#bujo-task-migrated-forward" },
9 | { "include": "#bujo-task-migrated-backward" },
10 | { "include": "#bujo-task-dropped" },
11 | { "include": "#bujo-event" }
12 | ],
13 | "repository": {
14 | "bujo-task-open": {
15 | "match": "(?<=\\s)(?\\[)(?\\s)(?\\])(?=\\s*[?!*]?\\s*)(?!\\s*[?!*]?\\s*\\||\\s*[?!*]?\\s*$)(?:\\s*(?[!?*])\\s*(?.*?)|\\s*(?![!?*])\\s*(?.*?))(?=\\s+\\^[a-z0-9]{6,}|\\s+\\||\\s*$)",
16 | "captures": {
17 | "1": { "name" : "bujo.task.open.notation" },
18 | "2": { "name" : "bujo.task.open.symbol" },
19 | "3": { "name" : "bujo.task.open.notation" },
20 | "4": { "name" : "bujo.task.open.modifier" },
21 | "6": { "name" : "bujo.task.open.text" },
22 | "5": { "name" : "bujo.task.open.text.modifier" }
23 | }
24 | },
25 |
26 | "bujo-task-in-progress": {
27 | "match": "(?<=\\s)(?\\[)(?\\/)(?\\])(?=\\s*[?!*]?\\s*)(?!\\s*[?!*]?\\s*\\||\\s*[?!*]?\\s*$)(?:\\s*(?[!?*])\\s*(?.*?)|\\s*(?![!?*])\\s*(?.*?))(?=\\s+\\^[a-z0-9]{6,}|\\s+\\||\\s*$)",
28 | "captures": {
29 | "1": { "name" : "bujo.task.in.progress.notation" },
30 | "2": { "name" : "bujo.task.in.progress.symbol" },
31 | "3": { "name" : "bujo.task.in.progress.notation" },
32 | "4": { "name" : "bujo.task.in.progress.modifier" },
33 | "6": { "name" : "bujo.task.in.progress.text" },
34 | "5": { "name" : "bujo.task.in.progress.text.modifier" }
35 | }
36 | },
37 |
38 | "bujo-task-completed": {
39 | "match": "(?<=\\s)(?\\[)(?x)(?\\])(?=\\s*[?!*]?\\s*)(?!\\s*[?!*]?\\s*\\||\\s*[?!*]?\\s*$)(?:\\s*(?[!?*])\\s*(?.*?)|\\s*(?![!?*])\\s*(?.*?))(?=\\s+\\^[a-z0-9]{6,}|\\s+\\||\\s*$)",
40 | "captures": {
41 | "1": { "name" : "bujo.task.completed.notation" },
42 | "2": { "name" : "bujo.task.completed.symbol" },
43 | "3": { "name" : "bujo.task.completed.notation" },
44 | "4": { "name" : "bujo.task.completed.modifier" },
45 | "6": { "name" : "bujo.task.completed.text" },
46 | "5": { "name" : "bujo.task.completed.text.modifier" }
47 | }
48 | },
49 |
50 | "bujo-task-migrated-forward": {
51 | "match": "(?<=\\s)(?\\[)(?>)(?\\])(?=\\s*[?!*]?\\s*)(?!\\s*[?!*]?\\s*\\||\\s*[?!*]?\\s*$)(?:\\s*(?[!?*])\\s*(?.*?)|\\s*(?![!?*])\\s*(?.*?))(?=\\s+\\^[a-z0-9]{6,}|\\s+\\||\\s*$)",
52 | "captures": {
53 | "1": { "name" : "bujo.task.migrated.forward.notation" },
54 | "2": { "name" : "bujo.task.migrated.forward.symbol" },
55 | "3": { "name" : "bujo.task.migrated.forward.notation" },
56 | "4": { "name" : "bujo.task.migrated.forward.modifier" },
57 | "6": { "name" : "bujo.task.migrated.forward.text" },
58 | "5": { "name" : "bujo.task.migrated.forward.text.modifier" }
59 | }
60 | },
61 |
62 | "bujo-task-migrated-backward": {
63 | "match": "(?<=\\s)(?\\[)(?<)(?\\])(?=\\s*[?!*]?\\s*)(?!\\s*[?!*]?\\s*\\||\\s*[?!*]?\\s*$)(?:\\s*(?[!?*])\\s*(?.*?)|\\s*(?![!?*])\\s*(?.*?))(?=\\s+\\^[a-z0-9]{6,}|\\s+\\||\\s*$)",
64 | "captures": {
65 | "1": { "name" : "bujo.task.migrated.backward.notation" },
66 | "2": { "name" : "bujo.task.migrated.backward.symbol" },
67 | "3": { "name" : "bujo.task.migrated.backward.notation" },
68 | "4": { "name" : "bujo.task.migrated.backward.modifier" },
69 | "6": { "name" : "bujo.task.migrated.backward.text" },
70 | "5": { "name" : "bujo.task.migrated.backward.text.modifier" }
71 | }
72 | },
73 |
74 | "bujo-task-dropped": {
75 | "match": "(?<=\\s)(?\\[)(?-)(?\\])(?=\\s*[?!*]?\\s*)(?!\\s*[?!*]?\\s*\\||\\s*[?!*]?\\s*$)(?:\\s*(?[!?*])\\s*(?.*?)|\\s*(?![!?*])\\s*(?.*?))(?=\\s+\\^[a-z0-9]{6,}|\\s+\\||\\s*$)",
76 | "captures": {
77 | "1": { "name" : "bujo.task.dropped.notation" },
78 | "2": { "name" : "bujo.task.dropped.symbol" },
79 | "3": { "name" : "bujo.task.dropped.notation" },
80 | "4": { "name" : "bujo.task.dropped.modifier" },
81 | "6": { "name" : "bujo.task.dropped.text" },
82 | "5": { "name" : "bujo.task.dropped.text.modifier" }
83 | }
84 | },
85 |
86 | "bujo-event": {
87 | "match": "(?<=\\s)(?\\[)(?o)(?\\])(?=\\s*[?!*]?\\s*)(?!\\s*[?!*]?\\s*\\||\\s*[?!*]?\\s*$)(?:\\s*(?[!?*])\\s*(?.*?)|\\s*(?![!?*])\\s*(?.*?))(?=\\s+\\^[a-z0-9]{6,}|\\s+\\||\\s*$)",
88 | "captures": {
89 | "1": { "name" : "bujo.event.notation" },
90 | "2": { "name" : "bujo.event.symbol" },
91 | "3": { "name" : "bujo.event.notation" },
92 | "4": { "name" : "bujo.event.modifier" },
93 | "6": { "name" : "bujo.event.text" },
94 | "5": { "name" : "bujo.event.text.modifier" }
95 | }
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/models/Scheduler.ts:
--------------------------------------------------------------------------------
1 | import { TextEditor, WorkspaceConfiguration, window, Uri, workspace, TextDocument, Position, WorkspaceEdit, commands } from "vscode";
2 | import { EntryLine } from "./EntryLine";
3 | import { Pattern } from "./Pattern";
4 |
5 |
6 | export class Scheduler {
7 | /**
8 | * VS Code objects.
9 | */
10 | private editor: TextEditor;
11 | private config: WorkspaceConfiguration;
12 |
13 |
14 | /**
15 | * Scheduler constructor.
16 | */
17 | public constructor(editor: TextEditor, config: WorkspaceConfiguration) {
18 | // Set the editor.
19 | this.editor = editor;
20 |
21 | // Get user settings.
22 | this.config = config;
23 | }
24 |
25 |
26 | /**
27 | * Get the origin file name (i.e., the backlog).
28 | */
29 | private getOriginFile(): string {
30 | // Get the file name.
31 | let fileName: string = this.editor.document.fileName.match(/[^\\\/]+?(?=$)/)![0];
32 |
33 | // Remove the file extension.
34 | fileName = fileName.split(/.md$/)[0]
35 |
36 | return fileName;
37 | }
38 |
39 |
40 | /**
41 | * Get the destination file name (i.e., the daily planner).
42 | */
43 | private async getDestinationFile(): Promise {
44 | // Get prefix from user configuration.
45 | const prefix = this.config.get("scheduler.plannerPrefix");
46 |
47 | // Get today's date.
48 | const [month, day, year]: string[] = new Date().toLocaleString("en-US",
49 | {
50 | year: 'numeric',
51 | month: '2-digit',
52 | day: '2-digit'
53 | }
54 | ).split("/");
55 |
56 | // Ask user what daily note to use.
57 | const dailyPlanner = await window.showInputBox({
58 | title: 'Enter the name of the planner file',
59 | value: [prefix, year, month, day].join('.')
60 | });
61 |
62 | // If no input is provided cancel the planning.
63 | if (!dailyPlanner) {
64 | throw new Error('Planner file not provided.');
65 | }
66 |
67 | return dailyPlanner;
68 | }
69 |
70 |
71 | /**
72 | * Prepare a table row for the time tracking table.
73 | * @returns A table row with a task for the time tracking table.
74 | */
75 | private makeTableRow(entry: EntryLine): string {
76 | // Get the origin file (i.e., the backlog).
77 | const backlog = this.getOriginFile();
78 |
79 | // Get default text for the task name.
80 | let task: string = entry.text;
81 |
82 | // Customize task name if entry text is valid wiki link with alias.
83 | if (entry.isWikiLinkWithALias()) {
84 | // Get the user configuration.
85 | const name = this.config.get("scheduler.taskName");
86 |
87 | // Decide whether to use the alias or the filename.
88 | if (name == "alias") {
89 | task = entry.wikiLink.alias;
90 | } else {
91 | task = `[[${entry.wikiLink.filename}]]`;
92 | }
93 | }
94 |
95 | // Append modifier if present.
96 | if (entry.modifier != '') {
97 | task = `${entry.modifier} ${task}`
98 | }
99 |
100 | return `| | [ ] ${task} | [[${backlog}#^${entry.id}]] |\n`;
101 | }
102 |
103 |
104 | /**
105 | * Get the destination document where to schedule the task.
106 | * @returns An instance of `TextDocument` corresponding to the location
107 | * where to schedule the task.
108 | */
109 | private async getDestinationDocument(): Promise {
110 | // Get the destination file (i.e., the daily log or planner).
111 | const destinationFile = await this.getDestinationFile();
112 |
113 | // Find the destination file in the workspace.
114 | const destinationSearch: Uri[] = await workspace.findFiles(`**/${destinationFile}.md`);
115 |
116 | // Throw if the daily planner does not exist.
117 | if (destinationSearch.length == 0) {
118 | throw new Error(`Planner '${destinationFile}.md' not found.`);
119 | }
120 |
121 | // Extract the daily note URI.
122 | const destinationUri: Uri = destinationSearch[0];
123 |
124 | // Open the daily note.
125 | return await workspace.openTextDocument(destinationUri);
126 | }
127 |
128 |
129 | /**
130 | * Write the task to the time tracking table in the given document.
131 | * @param document The destination document where write the task.
132 | * @param row The row as a string to add to the time tracking table.
133 | */
134 | private async writeTask(document: TextDocument, row: string): Promise {
135 | // Match the time tracking table header.
136 | const tableHeader = document.getText().match(Pattern.checkTimeTrackingTable);
137 |
138 | // Ensure the time tracking table exists.
139 | if (!tableHeader) {
140 | throw new Error('Planner is missing the time tracking table.');
141 | }
142 |
143 | // Get the position instance.
144 | const tablePosition: Position = document.positionAt(tableHeader.index!);
145 |
146 | // Find first available line in the table.
147 | let lineNumber: number = tablePosition.line + 1;
148 |
149 | // Update the candidate line number until a suitable line is found.
150 | while (!document.lineAt(lineNumber).isEmptyOrWhitespace) {
151 | // Increment the line.
152 | lineNumber++;
153 | }
154 |
155 | // Start a workspace edit.
156 | const edit: WorkspaceEdit = new WorkspaceEdit();
157 |
158 | // Construct the edit.
159 | edit.insert(document.uri, new Position(lineNumber, 0), row);
160 |
161 | // Apply the edit.
162 | return await workspace.applyEdit(edit);
163 | }
164 |
165 |
166 | /**
167 | * Schedule a single entry.
168 | * @param entry An instance of class `Entry`.
169 | */
170 | public async schedule(entry: EntryLine): Promise {
171 | // Prepare row for the time tracking table.
172 | const row: string = this.makeTableRow(entry);
173 |
174 | // Get destination document (i.e., the daily planner).
175 | const destDocument = await this.getDestinationDocument();
176 |
177 | // Write the task to the table.
178 | const status = await this.writeTask(destDocument, row);
179 |
180 | // If the writing action succeeded.
181 | if (status) {
182 | // Show the destination document.
183 | await window.showTextDocument(destDocument);
184 |
185 | // Format the destination document to realign the table.
186 | await commands.executeCommand('editor.action.formatDocument');
187 |
188 | // Focus back on the origin document.
189 | await window.showTextDocument(this.editor.document);
190 | }
191 |
192 | return status;
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/docs/reference/scopes.md:
--------------------------------------------------------------------------------
1 | ---
2 | pageClass: page-reference
3 | ---
4 |
5 | # TextMate Scopes
6 |
7 | Below are the **TextMate Scopes** that can be targeted via the VS Code setting
8 | `editor.tokenColorCustomizations"` for color customizations.
9 |
10 | ## BuJo Entries
11 |
12 | ### Open Tasks
13 |
14 | - `bujo.task.open.notation`: targets the left `[` and the right `]` brackets only when they contain a space in-between
15 | - `bujo.task.open.symbol`: targets the space between the notation brackets `[` and `]`
16 | - `bujo.task.open.modifier`: targets any supported modifier that follows after `[ ]`
17 | - `bujo.task.open.text`: targets the **text** that follows `[ ]` **without a modifier**, e.g., `[ ]` ___`This is targeted.`___
18 | - `bujo.task.open.text.modifier`: targets the **text** that follows `[ ]` **with a modifier**, e.g., `[ ] !` ___`This is targeted.`___
19 |
20 | ### Completed Tasks
21 |
22 | - `bujo.task.completed.notation`: targets the left `[` and the right `]` brackets only when they contain `x` in-between
23 | - `bujo.task.completed.symbol`: targets the symbol `x` between the notation brackets `[` and `]`
24 | - `bujo.task.completed.modifier`: targets any supported modifier that follows after `[x]`
25 | - `bujo.task.completed.text`: targets the **text** that follows `[x]` **without a modifier**, e.g., `[x]` ___`This is targeted.`___
26 | - `bujo.task.completed.text.modifier`: targets the **text** that follows `[x]` **with a modifier**, e.g., `[x] !` ___`This is targeted.`___
27 |
28 | ### Tasks In Progress
29 |
30 | - `bujo.task.in.progress.notation`: targets the left `[` and the right `]` brackets only when they contain `/` in-between
31 | - `bujo.task.in.progress.symbol`: targets the symbol `/` between the notation brackets `[` and `]`
32 | - `bujo.task.in.progress.modifier`: targets any supported modifier that follows after `[/]`
33 | - `bujo.task.in.progress.text`: targets the **text** that follows `[/]` **without a modifier**, e.g., `[/]` ___`This is targeted.`___
34 | - `bujo.task.in.progress.text.modifier`: targets the **text** that follows `[/]` **with a modifier**, e.g., `[/] !` ___`This is targeted.`___
35 |
36 | ### Tasks Migrated Forward
37 |
38 | - `bujo.task.migrated.forward.notation`: targets the left `[` and the right `]` brackets only when they contain `>` in-between
39 | - `bujo.task.migrated.forward.symbol`: targets the symbol `>` between the notation brackets `[` and `]`
40 | - `bujo.task.migrated.forward.modifier`: targets any supported modifier that follows after `[>]`
41 | - `bujo.task.migrated.forward.text`: targets the **text** that follows `[>]` **without a modifier**, e.g., `[>]` ___`This is targeted.`___
42 | - `bujo.task.migrated.forward.text.modifier`: targets the **text** that follows `[>]` **with a modifier**, e.g., `[>] !` ___`This is targeted.`___
43 |
44 | ### Tasks Migrated Backward
45 |
46 | - `bujo.task.migrated.forward.notation`: targets the left `[` and the right `]` brackets only when they contain `<` in-between
47 | - `bujo.task.migrated.forward.symbol`: targets the symbol `<` between the notation brackets `[` and `]`
48 | - `bujo.task.migrated.forward.modifier`: targets any supported modifier that follows after `[<]`
49 | - `bujo.task.migrated.forward.text`: targets the **text** that follows `[<]` **without a modifier**, e.g., `[<]` ___`This is targeted.`___
50 | - `bujo.task.migrated.forward.text.modifier`: targets the **text** that follows `[<]` **with a modifier**, e.g., `[<] !` ___`This is targeted.`___
51 |
52 | ### Dropped Tasks
53 |
54 | - `bujo.task.dropped.notation`: targets the left `[` and the right `]` brackets only when they contain `-` in-between
55 | - `bujo.task.dropped.symbol`: targets the symbol `-` between the notation brackets `[` and `]`
56 | - `bujo.task.dropped.modifier`: targets any supported modifier that follows after `[-]`
57 | - `bujo.task.dropped.text`: targets the **text** that follows `[-]` **without a modifier**, e.g., `[-]` ___`This is targeted.`___
58 | - `bujo.task.dropped.text.modifier`: targets the **text** that follows `[-]` **with a modifier**, e.g., `[-] !` ___`This is targeted.`___
59 |
60 | ### Events
61 |
62 | - `bujo.task.event.notation`: targets the left `[` and the right `]` brackets only when they contain `o` in-between
63 | - `bujo.task.event.symbol`: targets the symbol `o` between the notation brackets `[` and `]`
64 | - `bujo.task.event.modifier`: targets any supported modifier that follows after `[o]`
65 | - `bujo.task.event.text`: targets the **text** that follows `[o]` **without a modifier**, e.g., `[o]` ___`This is targeted.`___
66 | - `bujo.task.event.text.modifier`: targets the **text** that follows `[o]` **with a modifier**, e.g., `[o] !` ___`This is targeted.`___
67 |
68 | ## Markdown Tables
69 |
70 | - `bujo.grid.horizontal`: targets the `:---:`, `:---`, or `---:` horizontal grids in tables
71 | - `bujo.grid.colon`: targets the `:` in horizontal grids
72 | - `bujo.grid.vertical`: target the `|` vertical grids in tables
73 |
74 | ## Time Tracking Table
75 |
76 | - `bujo.todo.start.hour`: targets, e.g., `08` in `08:10-09:20` inside a table row
77 | - `bujo.todo.start.colon`: targets, e.g., the `:` after `08` in `08:10-09:20` inside a table row
78 | - `bujo.todo.start.minute`: targets, e.g., `10` in `08:10-09:20` inside a table row
79 | - `bujo.todo.separator`: targets, e.g., `-` in `08:10-09:20` inside a table row
80 | - `bujo.todo.end.hour`: targets, e.g., `09` in `08:10-09:20` inside a table row
81 | - `bujo.todo.end.colon`: targets, e.g., the `:` after `09` in `08:10-09:20` inside a table row
82 | - `bujo.todo.end.minute`: targets, e.g., `20` in `08:10-09:20` inside a table row
83 | - `bujo.todo.total`: targets the total time spent, e.g., `98m` in a table row
84 |
85 | :::warning
86 | The tokens `bujo.todo.*` are deprecated and will be replaced by
87 | `bujo.timetrack.*` in the next major release.
88 | :::
89 |
90 | ## Time Blocking Table
91 |
92 | - `bujo.timeblock.revision.time.parenthesis.open`: targets, e.g., `(` in `| (07:40) | (Revision #1) |` inside a table row
93 | - `bujo.timeblock.revision.time.hour`: targets, e.g., `07` in `| (07:40) | (Revision #1) |` inside a table row
94 | - `bujo.timeblock.revision.time.colon`: targets, e.g., `:` in `| (07:40) | (Revision #1) |` inside a table row
95 | - `bujo.timeblock.revision.time.minute`: targets, e.g., `40` in `| (07:40) | (Revision #1) |` inside a table row
96 | - `bujo.timeblock.revision.time.parenthesis.close`: targets, e.g., `)` in `| (07:40) | (Revision #1) |` inside a table row
97 | - `bujo.timeblock.revision.text`: targets, e.g., `(Revision #1)` in `| (07:40) | (Revision #1) |` inside a table row
98 | - `bujo.timeblock.chunk.title`: targets, e.g., `Deep work (#1)` in `| 08:00-10:00 | Deep work (#1) |` in a table row
99 | - `bujo.timeblock.chunk.note`: targets, e.g., `- Random meeting` in `| | - Random meeting |` in a table row
100 |
101 | ## Regular Expressions
102 |
103 | If you discover edge cases where the tokens are not appropriately highlighted,
104 | please open an issue on
105 | [GitHub](https://github.com/mihaiconstantin/bujo/issues). The regular
106 | expressions used for capturing the **TextMate Scopes** above can be consulted
107 | at:
108 |
109 | - [for `BuJo` entries](https://regex101.com/r/LVVrrS/26)
110 | - [for markdown tables](https://regex101.com/r/91IC8c/1)
111 | - [for the time tracking table](https://regex101.com/r/36951B/6)
112 | - [for the time blocking table](https://regex101.com/r/npln0p/5)
113 |
--------------------------------------------------------------------------------
/src/models/Tracker.ts:
--------------------------------------------------------------------------------
1 | import { Position, TextEditor, TextEditorEdit, TextLine } from "vscode";
2 | import { EntryLine } from "./EntryLine";
3 | import { Interval } from "./Interval";
4 |
5 |
6 | export class Tracker {
7 | /**
8 | * Patterns for finding indices for adding time records.
9 | */
10 | private patternStartRecord: RegExp = /(?<=^\|\s)/;
11 | private patternStopRecord: RegExp = /(?<=[0-2][0-9]:[0-5][0-9]-)/;
12 |
13 |
14 | /**
15 | * VS Code objects.
16 | */
17 | private editor: TextEditor;
18 |
19 |
20 | /**
21 | * Tracker constructor.
22 | */
23 | public constructor(editor: TextEditor) {
24 | this.editor = editor;
25 | }
26 |
27 |
28 | /**
29 | * Get index on line where to insert a given time record.
30 | */
31 | private getIndex(line: TextLine, pattern: RegExp): number {
32 | // Get index.
33 | return line.text.match(pattern)!.index!;
34 | }
35 |
36 |
37 | /**
38 | * Write a time record to the time tracking table.
39 | */
40 | private async writeTimeRecord(line: TextLine, state: { start: boolean; stop: boolean }, index: number): Promise {
41 | // Create time.
42 | const date = new Date();
43 |
44 | // Extract hour and minute with two digits.
45 | // See https://stackoverflow.com/questions/18889548/javascript-change-gethours-to-2-digit.
46 | const hour = ("0" + date.getHours()).slice(-2);
47 | const minute = ("0" + date.getMinutes()).slice(-2);
48 |
49 | // Formatted time.
50 | let timeRecord = `${hour}:${minute}`
51 |
52 | // If the line is missing a start record.
53 | if (!state.start) {
54 | // Add time record separator.
55 | timeRecord = `${timeRecord}-`
56 | }
57 |
58 | // Create position for replacement.
59 | const position = new Position(line.lineNumber, index)
60 |
61 | // Write time record.
62 | const status = await this.editor.edit((editBuilder: TextEditorEdit) => {
63 | editBuilder.insert(position, timeRecord);
64 | });
65 |
66 | return status;
67 | }
68 |
69 |
70 | /**
71 | * Compute the time difference for the interval.
72 | * @param interval The time interval to compute the difference for.
73 | */
74 | private getTimeIntervalDifference(interval: Interval): number {
75 | // Create dates.
76 | let start = new Date(Date.parse(`2000-01-01 ${interval.start}:00`));
77 | let stop = new Date(Date.parse(`2000-01-01 ${interval.stop}:00`));
78 |
79 | // Check if a day has passed.
80 | if (stop.getHours() < start.getHours()) {
81 | // Add a day.
82 | stop.setDate(stop.getDate() + 1);
83 | }
84 |
85 | // Compute difference.
86 | return +(stop) - (+start);
87 | }
88 |
89 |
90 | /**
91 | * Add time record to the time tracking table.
92 | */
93 | public async addTimeRecord(entry: EntryLine): Promise {
94 | // Create time interval from entry line.
95 | let interval = new Interval(entry.line, true);
96 |
97 | // Get interval state.
98 | let state = interval.getState();
99 |
100 | // If the entry line is missing a start record.
101 | if (!state.start) {
102 | // Get index for adding a start time record.
103 | let index = this.getIndex(entry.line, this.patternStartRecord);
104 |
105 | // Write time record.
106 | return await this.writeTimeRecord(entry.line, state, index);
107 | }
108 |
109 | // If the entry line is missing a stop record.
110 | if (!(state.start && state.stop)) {
111 | // Get index for adding a start time record.
112 | let index = this.getIndex(entry.line, this.patternStopRecord);
113 |
114 | // Write time record.
115 | return await this.writeTimeRecord(entry.line, state, index);
116 | }
117 |
118 | // Line number.
119 | let lineNumber = entry.line.lineNumber;
120 |
121 | // If the entry line contains a complete time interval.
122 | if (state.start && state.stop) {
123 | while (true) {
124 | // Increment line number.
125 | lineNumber++;
126 |
127 | // Get the new line.
128 | let line = this.editor.document.lineAt(lineNumber);
129 |
130 | // Recreate time interval.
131 | interval = new Interval(line, false);
132 |
133 | // Check state.
134 | state = interval.getState();
135 |
136 | // If both the start and the stop time records are missing.
137 | if (!state.start && !state.stop) {
138 | // Insert a new row.
139 | await this.editor.edit((editBuilder: TextEditorEdit) => {
140 | editBuilder.insert(new Position(lineNumber, 0), '| |||\n')
141 | });
142 |
143 | // Get the update line.
144 | line = this.editor.document.lineAt(lineNumber);
145 |
146 | // Get index for adding a start time record.
147 | let index = this.getIndex(line, this.patternStartRecord);
148 |
149 | // Write time record.
150 | return await this.writeTimeRecord(line, state, index);
151 | }
152 |
153 | // If the record that follows has only a start time record.
154 | if (!(state.start && state.stop)) {
155 | // Get index for adding a stop time record.
156 | let index = this.getIndex(line, this.patternStopRecord);
157 |
158 | // Write time record.
159 | return await this.writeTimeRecord(line, state, index);
160 | }
161 | }
162 | }
163 |
164 | // If nothing happened.
165 | return false;
166 | }
167 |
168 |
169 | /**
170 | * Calculate the total time spent on a task.
171 | */
172 | public sumTimeIntervals(entry: EntryLine): number {
173 | // Starting total time.
174 | let total = 0;
175 |
176 | // Create interval from entry line.
177 | let interval = new Interval(entry.line, true)
178 |
179 | // Get the state for entry line.
180 | let state = interval.getState();
181 |
182 | // If the interval is complete.
183 | if (state.start && state.stop) {
184 | total += this.getTimeIntervalDifference(interval);
185 | }
186 |
187 | // Get the line number.
188 | let lineNumber = entry.line.lineNumber;
189 |
190 | // Compute time difference for any subsequent intervals.
191 | while (true) {
192 | // Increment line number.
193 | lineNumber++;
194 |
195 | // Get new line.
196 | let line = this.editor.document.lineAt(lineNumber);
197 |
198 | // Create new interval.
199 | interval = new Interval(line, false);
200 |
201 | // Get interval state.
202 | state = interval.getState();
203 |
204 | // If the interval is not complete break.
205 | if (!(state.start && state.stop)) {
206 | break;
207 | }
208 |
209 | // If the stop time record is missing continue.
210 | if (state.start && !state.stop) {
211 | continue;
212 | }
213 |
214 | // Otherwise calculate time difference.
215 | total += this.getTimeIntervalDifference(interval);
216 | }
217 |
218 | return total / 1000 / 60;
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/docs/guide/syntax-highlighting.md:
--------------------------------------------------------------------------------
1 | ---
2 | pageClass: page-guide
3 | title: Syntax Highlighting
4 | ---
5 |
6 | # Syntax Highlighting
7 |
8 | When enabled, **BuJo** parses the text written in Markdown files for specific
9 | `regex` patterns. These patterns are exposed as
10 | [TextMate](https://macromates.com/manual/en/language_grammars) scopes to form a
11 | language grammar that can be used for syntax highlighting. At its core, **BuJo**
12 | uses the VS Code API for injecting a language grammar (i.e., see [VS Code
13 | documentation](https://code.visualstudio.com/api/language-extensions/syntax-highlight-guide)
14 | for more details). This section of the guide provides information about the
15 | anatomy of a **BuJo** entry, as well as other components that are supported for
16 | syntax highlighting.
17 |
18 | ## BuJo Entries
19 |
20 | For each Bullet Journal entry, you can highlight four different tokens. Take,
21 | for example, the Bullet Journal entry below that constitutes a completed task:
22 |
23 | ```markdown
24 | - [x] Write BuJo readme file.
25 | ```
26 |
27 | ### Notation
28 |
29 | **BuJo** uses the **_notation_** `[` `]` to indicate that the text that follows
30 | is a Bullet Journal entry. The `x` in `[x]` represents the **_symbol_** that
31 | describes the type of Bullet Journal entry. The **_text_** that follows (i.e.,
32 | `Write BuJo readme file.`) represents the entry's content.
33 |
34 | Aside from the *notation*, *symbol*, and *text*, you may also use a
35 | **_modifier_**. For example, you can use the `!` modifier after `[x]` to
36 | indicate a sense of priority:
37 |
38 | ```markdown
39 | [x] ! Write BuJo readme file.
40 | ```
41 |
42 | ### Symbols
43 |
44 | Below is a list with the supported Bullet Journal symbols (i.e., more can be
45 | added upon request):
46 |
47 | ```markdown
48 | - [ ] Represents a task.
49 | - [x] Represents a completed task.
50 | - [>] Represents a task migrated forward.
51 | - [<] Represents a task migrated backward.
52 | - [/] Represents a task in progress.
53 | - [-] Represents a dropped task.
54 | - [o] Represents an event.
55 | - Represents a note. Nothing special about it.
56 | ```
57 |
58 | With the default colors and the **Default Dark+** theme, the entries above are
59 | highlighted as follows:
60 |
61 |
62 |
63 |
64 |
65 | The notation brackets `[` and `]` can be colored such that they are not visible,
66 | but will still remain present in the editor (e.g., `[x]`). This can be useful if
67 | one wants to make the notation brackets transparent to keep the entry clean and
68 | emphasize the content. For example:
69 |
70 |
71 |
72 |
73 |
74 | ### Modifiers
75 |
76 | **BuJo** also supports three Bullet Journal modifiers:
77 |
78 | - `!` - may indicate priority, inspiration, etc.
79 | - `?` - may indicate waiting for someone or something, unclear, etc.
80 | - `*` - may indicate something special about the entry, etc.
81 |
82 | These modifiers can be combined with any of the supported Bullet Journal
83 | symbols. For example:
84 |
85 |
86 |
87 |
88 |
89 | **BuJo** can easily be extended upon request to support an arbitrary number of
90 | characters (i.e., including combinations of characters) as modifiers. **BuJo**
91 | provides flexibility for where a supported modifier can be placed. For example,
92 | all of the following are correctly identified and parsed as valid entries, as
93 | can be seen in the image below:
94 |
95 | ```markdown
96 | - [ ] ! Represents a task
97 | - [ ]! Represents a task
98 | - [ ] !Represents a task
99 | - [ ]!Represents a task
100 | ```
101 |
102 |
103 |
104 |
105 |
106 |
107 | ### Complex Entries
108 |
109 | **BuJo** also provides syntax highlighting support for multiple entries on the
110 | same line separated by a pipe (i.e., `|`) character:
111 |
112 | ```text
113 | - [ ] Task one | [ ] ! Task two | [x] Task three
114 | - [<] Task one | [-] ! Task two | [>] Task three
115 | ```
116 |
117 |
118 |
119 |
120 |
121 | ::: warning Experimental
122 | The support for multiple **BuJo** entries on the line is *experimental* and may
123 | change in the future.
124 | :::
125 |
126 |
127 | ### Metadata
128 |
129 | **BuJo** entries may also contain inline *metadata* stored after the `|`
130 | character. For example, entries can contain wiki links or blockquote IDs (e.g.,
131 | as used by [Dendron](https://github.com/dendronhq/dendron) and
132 | [Foam](https://github.com/foambubble/foam)):
133 |
134 | ```markdown
135 | - [ ] Represents a task | [[wiki.link]]
136 | - [ ] Represents a task ^dzywxd9fvg
137 | - [ ] Represents a task | [[wiki.link]] ^dzywxd9fvg
138 | ```
139 |
140 | The lines above will be parsed in such a way that the wiki link and the block
141 | quote IDs at the end of the line are omitted.
142 |
143 |
144 |
145 |
146 |
147 | ## Markdown Tables
148 |
149 | **BuJo** also exposes scopes for targeting and highlighting grids in markdown
150 | tables (i.e., the `:---:`, `:---`, or `---:` for horizontal grids, and the `|`
151 | for vertical grids). A separate scope is also provided for highlighting the `:`
152 | in in horizontal grids. The following screenshot shows the tokens that can
153 | highlighted:
154 |
155 |
156 |
157 |
158 |
159 | With the default colors (i.e., and the `Default Dark+` theme) the table grid can
160 | be faded way to be less obtrusive:
161 |
162 |
163 |
164 |
165 |
166 | ## Time Tracking
167 |
168 | **BuJo** also provides support for highlighting tasks in markdown tables, as
169 | well as well as associated time records (i.e., `hh:mm-hh:mm`):
170 |
171 |
172 |
173 |
174 |
175 | _Note. See section [Time Tracking](/guide/time-tracking.md) for how to easily
176 | add entries to the time tracking table and track the time spent on tasks._
177 |
178 | ## Time Blocking
179 |
180 | Similarly, **BuJo** it also supports time blocking syntax highlighting, based on
181 | the methodology discussed in [Newport
182 | (2016)](https://www.goodreads.com/book/show/25744928-deep-work).
183 |
184 |
185 |
186 |
187 |
188 | ## Custom Colors
189 |
190 | **BuJo** comes with default colors and styles for the
191 | [TextMate](https://macromates.com/manual/en/language_grammars) scopes it
192 | exposes. These colors and styles are chosen to work well with the **Default
193 | Dark+** theme. However, they can be customized via the
194 | `editor.tokenColorCustomizations` setting in [VS
195 | Code](https://code.visualstudio.com).
196 |
197 | Upon typing `"editor.tokenColorCustomizations"` you can trigger VS Code's
198 | intellisense which will automatically pre-fill the `textMateRules` array with
199 | the default colors provided by **BuJo**.
200 |
201 | ```json
202 | {
203 | // Other VS Code settings.
204 |
205 | "editor.tokenColorCustomizations": {
206 | // Override only for the `Default Dark+` theme.
207 | "[Default Dark+]": {
208 | "textMateRules": [
209 | // The scopes for which we want to provide custom colors.
210 | ]
211 | }
212 | }
213 | }
214 | ```
215 |
216 | For example, to colorize the notation brackets `[` and `]` for a task `[x]`, you
217 | can use:
218 |
219 | ```json
220 | {
221 | // Other VS Code settings.
222 |
223 | "editor.tokenColorCustomizations": {
224 | // Override only for the `Default Dark+` theme.
225 | "[*Dark*]": {
226 | "textMateRules": [
227 | {
228 | "scope": "bujo.task.completed.notation",
229 | "settings": {
230 | "foreground": "#FFB6C1",
231 | "fontStyle": "bold underline"
232 | }
233 | }
234 | ]
235 | }
236 | }
237 | }
238 | ```
239 |
240 | When the theme `Default Dark+` is used, the above override will result in a
241 | completed task with bolded, underlined, and pink notation brackets:
242 |
243 |
244 |
245 |
246 |
247 | ::: tip
248 | Check out the [TextMate Scopes Reference](/reference/scopes.md) for the complete
249 | list of tokens that can be targeted and colorized.
250 | :::
251 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [2.4.1] - 2022.07.08
4 | ### Changed
5 | - Update extension homepage from
6 | [github.com/mihaiconstantin/bujo](https://github.com/mihaiconstantin/bujo) to
7 | [bujo.mihaiconstantin.com](https://bujo.mihaiconstantin.com).
8 |
9 | ### Fixed
10 | - Add correct links to snippets reference in the guide pages.
11 |
12 | ## [2.4.0] - 2022.07.07
13 | ### Added
14 | - Add `.github` workflow for building documentation website.
15 | - Add extension documentation via `VuePress` at
16 | [bujo.mihaiconstantin.com](https://bujo.mihaiconstantin.com). Closes
17 | [#14](https://github.com/mihaiconstantin/bujo/issues/14).
18 | - Add functionality to **schedule `BuJo` entries** to time tracking tables via
19 | the `BuJo: Schedule Entry` command. Closes
20 | [#8](https://github.com/mihaiconstantin/bujo/issues/8).
21 | - Add functionality for **time tracking** for `BuJo` entries via the `BuJo:
22 | Record Time` command. Closes
23 | [#6](https://github.com/mihaiconstantin/bujo/issues/6).
24 | - Add functionality to *calculate the total time spent on a task* for `BuJo`
25 | entries scheduled to the time tracking table via the command `BuJo: Calculate
26 | Entry Time`. Closes [#7](https://github.com/mihaiconstantin/bujo/issues/7).
27 | - Add default keybindings scheduling and time tracking commands:
28 | - `alt+shift+p` to run command `BuJo: Schedule Entry`
29 | - `alt+shift+t` to run command `BuJo: Record Time`
30 | - `alt+shift+s` to run command `BuJo: Calculate Entry Time`
31 | - Add user settings for customizing the scheduling and time tracking behavior:
32 | - `bujo.scheduler.plannerPrefix` to specify the prefix to use when
33 | selecting the daily planner file via the input box (e.g.,
34 | **`prefix`**`.2022.03.20`)
35 | - `bujo.scheduler.taskName` to specify what to use as task name for the
36 | time tracking table when scheduling a `BuJo` entry that contains a wiki link
37 | with an alias (e.g., `[[A random task|project.example.a-random-task]]`:
38 | - `alias` sets the name of the task in the table to wiki link alias (e.g.,
39 | `A random task`)
40 | - `filename` sets the name of the task to the actual wiki link (e.g.,
41 | `[[project.example.a-random-task]]`)
42 | - `bujo.scheduler.symbolForScheduledEntry` to specify the symbol to set for a
43 | `BuJo` entry scheduled to the time track table (i.e., by default, the symbol
44 | is updated from `[ ]` to `[>]`)
45 | - Add `genUUID` and `genUUIDInsecure` helper functions to generate
46 | Dendron-compatible blockquote IDs for scheduling `BuJo` entries to the time
47 | tracking table.
48 | - Add essential documentation in `README.md` for new changes. Closes
49 | [#12](https://github.com/mihaiconstantin/bujo/issues/12).
50 |
51 | ### Changed
52 | - Switched from `esbuild` to `webpack` for bundling extension source files.
53 | - Simplify `README.md` file to point to the new documentation. Closes
54 | [#12](https://github.com/mihaiconstantin/bujo/issues/12).
55 | - Refactor `Entry` class into multiple classes, each corresponding to a type of
56 | functionality:
57 | - `Scheduler` class for handling scheduling operation
58 | - `Symbol` class for handling entry symbol updates
59 | - `Tracker` and `Interval` classes for handling time tracking and time totals
60 | - Move most of the regular expressions to the `Pattern` class and add
61 | demonstration links to `regex101.com` to improve debugging of `regex`.
62 | - Create `operations` module that contains functions and wrappers to use in the
63 | context of the command palette, e.g.:
64 |
65 | ```typescript
66 | // Import the module.
67 | import * as operations from "./operations";
68 |
69 | // The module currently contains `symbol`, `scheduler` and `tracker` commands.
70 | // ...
71 | vscode.commands.registerCommand('bujo.scheduler.scheduleEntry', operations.scheduler.scheduleEntry)
72 | // ...
73 | ```
74 |
75 | - Create `helpers` module for small functions used in various places.
76 | - Update the functions within the operations module to use new classes.
77 | - **Rename several commands to maintain consistency with the `operations`
78 | module:**
79 | - from `bujo.setMigratedForward` to `bujo.symbol.setMigratedForward`
80 | - from `bujo.setMigratedBackward` to `bujo.symbol.setMigratedBackward`
81 | - from `bujo.setCompleted` to `bujo.symbol.setCompleted`
82 | - from `bujo.setOpen` to `bujo.symbol.setOpened`
83 | - from `bujo.setInProgress` to `bujo.symbol.setStarted`
84 | - from `bujo.setDropped` to `bujo.symbol.setDropped`
85 | - from `bujo.setSymbol` to `bujo.symbol.setSymbol`
86 | - from `bujo.scheduleToTimeTrackingTable` to `bujo.scheduler.scheduleEntry`
87 | - from `bujo.recordTime` to `bujo.tracker.recordTime`
88 | - from `bujo.calculateTime` to `bujo.tracker.calculateEntryTime`
89 |
90 | ## [2.1.0] - 2022.04.24
91 | ### Added
92 | - Add syntax highlighting support for multiple entries on the same line
93 | separated by a `|` character:
94 | - e.g., `[ ] Task one | [ ] ! Task two | [x] Task three`
95 | - Add greater flexibility with respect to where a supported modifier can be
96 | placed. All of the following are correctly identified and parsed as valid
97 | entries:
98 | - `[ ] ! Some task`
99 | - `[ ]! Some task`
100 | - `[ ] !Some task`
101 | - `[ ]!Some task`
102 | - Add commands to the command palette (i.e., `ctrl/cmd + shift + p`) to change
103 | the symbol for the first entry on the line where the cursor is placed. The
104 | following commands are available:
105 | - `BuJo: Set Migrated Forward` to set `[>]`
106 | - `BuJo: Set Migrated Backward` to set `[<]`
107 | - `BuJo: Set Completed` to set `[x]`
108 | - `BuJo: Set Open` to set `[ ]`
109 | - `BuJo: Set In Progress` to set `[/]`
110 | - `BuJo: Set Dropped` to set `[-]`
111 | - Add functionality to update entry symbols via arbitrary keybindings that can
112 | also pass the symbol to be set as argument. For instance, the following
113 | keybinding when triggered will update the task status to `[x]`, and toggle
114 | between `[x]` and `[ ]` on subsequent triggers:
115 |
116 | ```jsonc
117 | [
118 | // ...
119 | {
120 | "key": "alt+x",
121 | "command": "bujo.setSymbol",
122 | "args": {
123 | "symbol": "x"
124 | },
125 | "when": "editorTextFocus && editorLangId == markdown"
126 | }
127 | // ...
128 | ]
129 | ```
130 |
131 | - Add default keybindings for changing entry symbols:
132 | - `alt+x` to toggle between `[x]` and `[ ]`
133 | - `alt+o` to set `[ ]`
134 | - `alt+-` to toggle between `[-]` and `[ ]`
135 | - `alt+/` to toggle between `[/]` and `[ ]`
136 | - `alt+,` to toggle between `[<]` and `[ ]`
137 | - `alt+.` to toggle between `[>]` and `[ ]`
138 | - `alt+p` to toggle between `[o]` and `[ ]`
139 | - Add various snippets for **BuJo** elements:
140 | - `task` to enter a task
141 |
142 | ```markdown
143 | - [ ]
144 | ```
145 |
146 | - `taskclip` to enter a task from clipboard
147 |
148 | ```markdown
149 | - [ ]
150 | ```
151 |
152 | - `scratch` to scratch a text selection
153 |
154 | ```markdown
155 | ~Some text~
156 | ```
157 |
158 | - `time` to enter the current time
159 |
160 | ```markdown
161 | 10:38
162 | ```
163 |
164 | - `date` to enter the current date
165 |
166 | ```markdown
167 | 2022.04.24
168 | ```
169 |
170 | - `datetime` to enter the current date and time
171 |
172 | ```markdown
173 | 2022.04.24 10:39
174 | ```
175 |
176 | - `timetracktable` to enter a time tracking table
177 |
178 | ```markdown
179 | | Tracker | Task | Backlog |
180 | | ----------: | :--------------- | :------- |
181 | | 00:00-00:00 | [ ] Example task | [[link]] |
182 | | | | |
183 | ```
184 |
185 | - `timetrackrow` to add an empty row to the time tracking table
186 |
187 | ```markdown
188 | | | | |
189 | ```
190 |
191 | - `timetracktask` to enter a task in the time tracking table
192 |
193 | ```markdown
194 | | | [ ] | |
195 | ```
196 |
197 | - `timetracktaskclip` to enter a task from clipboard in the time tracking table
198 |
199 | ```markdown
200 | | | [ ] | |
201 | ```
202 |
203 | - `timeblocktable` to enter a time blocking table
204 |
205 | ```markdown
206 | | Time | Block |
207 | | ----------: | :------------- |
208 | | (00:00) | (Revision \#1) |
209 | | | |
210 | | 00:00-00:00 | Chunk (#1) |
211 | | | - Chunk note |
212 | | | |
213 | ```
214 |
215 | - `timeblockrow` to add an empty row to the time blocking table
216 |
217 | ```markdown
218 | | | |
219 | ```
220 |
221 | - `timeblockrev` to enter a revision row in the time blocking table
222 |
223 | ```markdown
224 | | (10:53) | (Revision \#1) |
225 | ```
226 |
227 | - `timeblockchunk` to enter a chunk row in the time blocking table
228 |
229 | ```markdown
230 | | 00:00-00:00 | |
231 | ```
232 |
233 | - `timeblocknote` to enter a note row in the time blocking table
234 |
235 | ```markdown
236 | | | - |
237 | ```
238 |
239 | - Add way to highlight the time separator (i.e., `-`) in a time tracking table
240 | also only the start time of a task is recorded (i.e., `-` will get matched
241 | also in `00:00-`, and not only `00:00-00:00`; see
242 | https://regex101.com/r/36951B/6).
243 |
244 | ### Changed
245 | - Improve `regex` for matching Bullet Journal entries (i.e., moved from
246 | https://regex101.com/r/ByIG8W/20 to https://regex101.com/r/LVVrrS/26).
247 | - Improve matching performance for Bullet Journal entries.
248 | - Update `README.md` to reflect the new features.
249 |
250 | ### Fixed
251 | - Update `regex` for markdown table grids to select `|` only when they are part of
252 | a markdown table grid (i.e., https://regex101.com/r/91IC8c/5).
253 | - Fix headings in `CHANGELOG.md` for previous releases.
254 |
255 | ### Deprecated
256 | - Time tracking scopes (i.e. `bujo.todo.*` will be renamed to `bujo.timetrack.*`
257 | in the future. For now, no changes need to be made. Below you can see the
258 | entire list of scopes that will be affected:
259 | - to be renamed from `bujo.todo.start.hour` to `bujo.timetrack.start.hour`
260 | - to be renamed from `bujo.todo.start.colon` to `bujo.timetrack.start.colon`
261 | - to be renamed from `bujo.todo.start.minute` to `bujo.timetrack.start.minute`
262 | - to be renamed from `bujo.todo.separator` to `bujo.timetrack.separator`
263 | - to be renamed from `bujo.todo.end.hour` to `bujo.timetrack.end.hour`
264 | - to be renamed from `bujo.todo.end.colon` to `bujo.timetrack.end.colon`
265 | - to be renamed from `bujo.todo.end.minute` to `bujo.timetrack.end.minute`
266 | - to be renamed from `bujo.todo.total` to `bujo.timetrack.total`
267 |
268 | ## [2.0.2] - 2022.04.20
269 | ### Changed
270 | - Improved `README.md` text and images presentation.
271 | - Update extension name and description.
272 |
273 | ## [2.0.1] - 2022.04.17
274 | ### Changed
275 | - Remove language grammar identifiers in `package.json`.
276 |
277 | ## [2.0.0] - 2022.04.17
278 | ### Added
279 | - Add scope for `*` modifier.
280 | - Add scope for `:` in horizontal table grids.
281 | - Add scopes for time blocking syntax.
282 | - Add scopes for time tracking syntax.
283 | - Add default colors via `contributes.configurationDefaults` point in
284 | `package.json`.
285 |
286 | ### Changed
287 | - Update regular expressions for more precise matching.
288 | - Simplify `TextMate` scopes for Bullet Journal items.
289 | - Improve `README.md` documentation.
290 |
291 | ### Removed
292 | - Drop scopes for each modifier. Currently, all supported modifiers fall under
293 | the same scope.
294 |
295 | ## [1.1.0] - 2021.11.29
296 | - Add two new tokens `bujo.horizontal.grid` and `bujo.vertical.grid` for
297 | selecting grids in markdown tables (i.e., the `:---:`, `:---`, or `---:` for
298 | horizontal grid, and the `|` for vertical grid).
299 |
300 | ## [1.0.0] - 2021.05.25
301 | ### Added
302 | - Added `TextMate` scopes for standard Bullet Journal symbols
303 | - `[ ]` task
304 | - `[x]` completed task
305 | - `[>]` migrated forward task
306 | - `[<]` migrated backward task
307 | - `[o]` event
308 | - Added `TextMate` scopes for two modifiers `!` and `?`
309 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bujo",
3 | "displayName": "Bullet Journal Markdown Workflows",
4 | "description": "Bullet Journal markdown workflows for managing tasks and notes.",
5 | "publisher": "mihaiconstantin",
6 | "icon": "assets/icon/bujo_circle_128.png",
7 | "private": true,
8 | "version": "2.4.1",
9 | "license": "MIT",
10 | "engines": {
11 | "vscode": "^1.65.0"
12 | },
13 | "homepage": "https://bujo.mihaiconstantin.com",
14 | "repository": {
15 | "url": "https://github.com/mihaiconstantin/bujo",
16 | "type": "git"
17 | },
18 | "categories": [
19 | "Other"
20 | ],
21 | "main": "./dist/extension.js",
22 | "activationEvents": [
23 | "onCommand:bujo.symbol.setMigratedForward",
24 | "onCommand:bujo.symbol.setMigratedBackward",
25 | "onCommand:bujo.symbol.setCompleted",
26 | "onCommand:bujo.symbol.setOpened",
27 | "onCommand:bujo.symbol.setStarted",
28 | "onCommand:bujo.symbol.setDropped",
29 | "onCommand:bujo.symbol.setSymbol",
30 | "onCommand:bujo.scheduler.scheduleEntry",
31 | "onCommand:bujo.tracker.recordTime",
32 | "onCommand:bujo.tracker.calculateEntryTime"
33 | ],
34 | "contributes": {
35 | "commands": [
36 | {
37 | "command": "bujo.symbol.setMigratedForward",
38 | "title": "BuJo: Set Migrated Forward"
39 | },
40 | {
41 | "command": "bujo.symbol.setMigratedBackward",
42 | "title": "BuJo: Set Migrated Backward"
43 | },
44 | {
45 | "command": "bujo.symbol.setCompleted",
46 | "title": "BuJo: Set Completed"
47 | },
48 | {
49 | "command": "bujo.symbol.setOpened",
50 | "title": "BuJo: Set Open"
51 | },
52 | {
53 | "command": "bujo.symbol.setStarted",
54 | "title": "BuJo: Set Started"
55 | },
56 | {
57 | "command": "bujo.symbol.setDropped",
58 | "title": "BuJo: Set Dropped"
59 | },
60 | {
61 | "command": "bujo.scheduler.scheduleEntry",
62 | "title": "BuJo: Schedule Entry"
63 | },
64 | {
65 | "command": "bujo.tracker.recordTime",
66 | "title": "BuJo: Record Time"
67 | },
68 | {
69 | "command": "bujo.tracker.calculateEntryTime",
70 | "title": "BuJo: Calculate Entry Time"
71 | }
72 | ],
73 | "configuration": {
74 | "title": "BuJo",
75 | "properties": {
76 | "bujo.scheduler.plannerPrefix": {
77 | "type": "string",
78 | "default": "log",
79 | "markdownDescription": "Specifies the prefix for the planner file (e.g., **`log`**`.2022.03.20`)."
80 | },
81 | "bujo.scheduler.taskName": {
82 | "type": "string",
83 | "default": "alias",
84 | "enum": [
85 | "alias",
86 | "filename"
87 | ],
88 | "markdownDescription": "Specifies how to construct the task name for the time tracking table when scheduling a `BuJo` entry that contains a wiki link with an alias (e.g., `- [ ] [[An arbitrary task|file-for-arbitrary-task]]`).",
89 | "markdownEnumDescriptions": [
90 | "Use the wiki link **alias** as task name (e.g., `An arbitrary task`).",
91 | "Use the wiki link **filename** as task name (e.g., `[[file-for-arbitrary-task]]`)."
92 | ]
93 | },
94 | "bujo.scheduler.symbolForScheduledEntry": {
95 | "type": "string",
96 | "default": ">",
97 | "markdownDescription": "Specifies the symbol to set for a scheduled entry (e.g., `>`)."
98 | }
99 | }
100 | },
101 | "grammars": [
102 | {
103 | "path": "./syntaxes/bujo.entry.injection.json",
104 | "scopeName": "bujo.entry.injection",
105 | "injectTo": [
106 | "text.html.markdown"
107 | ]
108 | },
109 | {
110 | "path": "./syntaxes/bujo.grid.injection.json",
111 | "scopeName": "bujo.grid.injection",
112 | "injectTo": [
113 | "text.html.markdown"
114 | ]
115 | },
116 | {
117 | "path": "./syntaxes/bujo.timeblock.injection.json",
118 | "scopeName": "bujo.timeblock.injection",
119 | "injectTo": [
120 | "text.html.markdown"
121 | ]
122 | },
123 | {
124 | "path": "./syntaxes/bujo.timetrack.injection.json",
125 | "scopeName": "bujo.timetrack.injection",
126 | "injectTo": [
127 | "text.html.markdown"
128 | ]
129 | }
130 | ],
131 | "snippets": [
132 | {
133 | "language": "markdown",
134 | "path": "./snippets/bujo.markdown.json"
135 | }
136 | ],
137 | "keybindings": [
138 | {
139 | "key": "alt+x",
140 | "command": "bujo.symbol.setSymbol",
141 | "args": {
142 | "symbol": "x"
143 | },
144 | "when": "editorTextFocus && editorLangId == markdown"
145 | },
146 | {
147 | "key": "alt+o",
148 | "command": "bujo.symbol.setSymbol",
149 | "args": {
150 | "symbol": " "
151 | },
152 | "when": "editorTextFocus && editorLangId == markdown"
153 | },
154 | {
155 | "key": "alt+-",
156 | "command": "bujo.symbol.setSymbol",
157 | "args": {
158 | "symbol": "-"
159 | },
160 | "when": "editorTextFocus && editorLangId == markdown"
161 | },
162 | {
163 | "key": "alt+/",
164 | "command": "bujo.symbol.setSymbol",
165 | "args": {
166 | "symbol": "/"
167 | },
168 | "when": "editorTextFocus && editorLangId == markdown"
169 | },
170 | {
171 | "key": "alt+,",
172 | "command": "bujo.symbol.setSymbol",
173 | "args": {
174 | "symbol": "<"
175 | },
176 | "when": "editorTextFocus && editorLangId == markdown"
177 | },
178 | {
179 | "key": "alt+.",
180 | "command": "bujo.symbol.setSymbol",
181 | "args": {
182 | "symbol": ">"
183 | },
184 | "when": "editorTextFocus && editorLangId == markdown"
185 | },
186 | {
187 | "key": "alt+p",
188 | "command": "bujo.symbol.setSymbol",
189 | "args": {
190 | "symbol": "o"
191 | },
192 | "when": "editorTextFocus && editorLangId == markdown"
193 | },
194 | {
195 | "key": "alt+shift+p",
196 | "command": "bujo.scheduler.scheduleEntry",
197 | "when": "editorTextFocus && editorLangId == markdown"
198 | },
199 | {
200 | "key": "alt+shift+t",
201 | "command": "bujo.tracker.recordTime",
202 | "when": "editorTextFocus && editorLangId == markdown"
203 | },
204 | {
205 | "key": "alt+shift+s",
206 | "command": "bujo.tracker.calculateEntryTime",
207 | "when": "editorTextFocus && editorLangId == markdown"
208 | }
209 | ],
210 | "configurationDefaults": {
211 | "editor.tokenColorCustomizations": {
212 | "textMateRules": [
213 | {
214 | "scope": "bujo.task.open.notation",
215 | "settings": {
216 | "foreground": "#7599C3"
217 | }
218 | },
219 | {
220 | "scope": "bujo.task.open.symbol",
221 | "settings": {
222 | "foreground": "#00000000"
223 | }
224 | },
225 | {
226 | "scope": "bujo.task.open.modifier",
227 | "settings": {
228 | "foreground": "#FF6E64",
229 | "fontStyle": "bold"
230 | }
231 | },
232 | {
233 | "scope": "bujo.task.open.text",
234 | "settings": {
235 | "foreground": "#7599C3"
236 | }
237 | },
238 | {
239 | "scope": "bujo.task.open.text.modifier",
240 | "settings": {
241 | "foreground": "#7599C3",
242 | "fontStyle": "bold"
243 | }
244 | },
245 | {
246 | "scope": "bujo.task.in.progress.notation",
247 | "settings": {
248 | "foreground": "#77BED9"
249 | }
250 | },
251 | {
252 | "scope": "bujo.task.in.progress.symbol",
253 | "settings": {
254 | "foreground": "#77BED9"
255 | }
256 | },
257 | {
258 | "scope": "bujo.task.in.progress.modifier",
259 | "settings": {
260 | "foreground": "#FF6E64",
261 | "fontStyle": "bold"
262 | }
263 | },
264 | {
265 | "scope": "bujo.task.in.progress.text",
266 | "settings": {
267 | "foreground": "#77BED9",
268 | "fontStyle": "italic bold"
269 | }
270 | },
271 | {
272 | "scope": "bujo.task.in.progress.text.modifier",
273 | "settings": {
274 | "foreground": "#77BED9",
275 | "fontStyle": "bold italic underline"
276 | }
277 | },
278 | {
279 | "scope": "bujo.task.completed.notation",
280 | "settings": {
281 | "foreground": "#6D6D6D"
282 | }
283 | },
284 | {
285 | "scope": "bujo.task.completed.symbol",
286 | "settings": {
287 | "foreground": "#6D6D6D"
288 | }
289 | },
290 | {
291 | "scope": "bujo.task.completed.modifier",
292 | "settings": {
293 | "foreground": "#6D6D6D",
294 | "fontStyle": "bold"
295 | }
296 | },
297 | {
298 | "scope": "bujo.task.completed.text",
299 | "settings": {
300 | "foreground": "#6D6D6D"
301 | }
302 | },
303 | {
304 | "scope": "bujo.task.completed.text.modifier",
305 | "settings": {
306 | "foreground": "#6D6D6D",
307 | "fontStyle": "bold"
308 | }
309 | },
310 | {
311 | "scope": "bujo.task.migrated.forward.notation",
312 | "settings": {
313 | "foreground": "#4D6C7D"
314 | }
315 | },
316 | {
317 | "scope": "bujo.task.migrated.forward.symbol",
318 | "settings": {
319 | "foreground": "#4D6C7D"
320 | }
321 | },
322 | {
323 | "scope": "bujo.task.migrated.forward.modifier",
324 | "settings": {
325 | "foreground": "#4D6C7D",
326 | "fontStyle": "bold"
327 | }
328 | },
329 | {
330 | "scope": "bujo.task.migrated.forward.text",
331 | "settings": {
332 | "foreground": "#4D6C7D",
333 | "fontStyle": "italic"
334 | }
335 | },
336 | {
337 | "scope": "bujo.task.migrated.forward.text.modifier",
338 | "settings": {
339 | "foreground": "#4D6C7D",
340 | "fontStyle": "italic bold"
341 | }
342 | },
343 | {
344 | "scope": "bujo.task.migrated.backward.notation",
345 | "settings": {
346 | "foreground": "#4D6C7D"
347 | }
348 | },
349 | {
350 | "scope": "bujo.task.migrated.backward.symbol",
351 | "settings": {
352 | "foreground": "#4D6C7D"
353 | }
354 | },
355 | {
356 | "scope": "bujo.task.migrated.backward.modifier",
357 | "settings": {
358 | "foreground": "#4D6C7D",
359 | "fontStyle": "bold"
360 | }
361 | },
362 | {
363 | "scope": "bujo.task.migrated.backward.text",
364 | "settings": {
365 | "foreground": "#4D6C7D",
366 | "fontStyle": "italic"
367 | }
368 | },
369 | {
370 | "scope": "bujo.task.migrated.backward.text.modifier",
371 | "settings": {
372 | "foreground": "#4D6C7D",
373 | "fontStyle": "italic bold"
374 | }
375 | },
376 | {
377 | "scope": "bujo.task.dropped.notation",
378 | "settings": {
379 | "foreground": "#6D6D6D",
380 | "fontStyle": "strikethrough"
381 | }
382 | },
383 | {
384 | "scope": "bujo.task.dropped.symbol",
385 | "settings": {
386 | "foreground": "#6D6D6D",
387 | "fontStyle": "strikethrough"
388 | }
389 | },
390 | {
391 | "scope": "bujo.task.dropped.modifier",
392 | "settings": {
393 | "foreground": "#6D6D6D",
394 | "fontStyle": "strikethrough"
395 | }
396 | },
397 | {
398 | "scope": "bujo.task.dropped.text",
399 | "settings": {
400 | "foreground": "#6D6D6D",
401 | "fontStyle": "strikethrough"
402 | }
403 | },
404 | {
405 | "scope": "bujo.task.dropped.text.modifier",
406 | "settings": {
407 | "foreground": "#6D6D6D",
408 | "fontStyle": "strikethrough"
409 | }
410 | },
411 | {
412 | "scope": "bujo.event.notation",
413 | "settings": {
414 | "foreground": "#D6A418"
415 | }
416 | },
417 | {
418 | "scope": "bujo.event.symbol",
419 | "settings": {
420 | "foreground": "#D6A418"
421 | }
422 | },
423 | {
424 | "scope": "bujo.event.modifier",
425 | "settings": {
426 | "foreground": "#FF6E64"
427 | }
428 | },
429 | {
430 | "scope": "bujo.event.text",
431 | "settings": {
432 | "foreground": "#D6A418"
433 | }
434 | },
435 | {
436 | "scope": "bujo.event.text.modifier",
437 | "settings": {
438 | "foreground": "#D6A418"
439 | }
440 | },
441 | {
442 | "scope": "bujo.todo.start.hour",
443 | "settings": {
444 | "foreground": "#b5954b"
445 | }
446 | },
447 | {
448 | "scope": "bujo.todo.start.colon",
449 | "settings": {
450 | "foreground": "#919191"
451 | }
452 | },
453 | {
454 | "scope": "bujo.todo.start.minute",
455 | "settings": {
456 | "foreground": "#b5954b"
457 | }
458 | },
459 | {
460 | "scope": "bujo.todo.separator",
461 | "settings": {
462 | "foreground": "#919191"
463 | }
464 | },
465 | {
466 | "scope": "bujo.todo.end.hour",
467 | "settings": {
468 | "foreground": "#b5954b"
469 | }
470 | },
471 | {
472 | "scope": "bujo.todo.end.colon",
473 | "settings": {
474 | "foreground": "#919191"
475 | }
476 | },
477 | {
478 | "scope": "bujo.todo.end.minute",
479 | "settings": {
480 | "foreground": "#b5954b"
481 | }
482 | },
483 | {
484 | "scope": "bujo.todo.total",
485 | "settings": {
486 | "foreground": "#e7b541",
487 | "fontStyle": "bold"
488 | }
489 | },
490 | {
491 | "scope": "bujo.timeblock.revision.time.parenthesis.open",
492 | "settings": {
493 | "foreground": "#6D6D6D"
494 | }
495 | },
496 | {
497 | "scope": "bujo.timeblock.revision.time.hour",
498 | "settings": {
499 | "foreground": "#6D6D6D"
500 | }
501 | },
502 | {
503 | "scope": "bujo.timeblock.revision.time.colon",
504 | "settings": {
505 | "foreground": "#6D6D6D"
506 | }
507 | },
508 | {
509 | "scope": "bujo.timeblock.revision.time.minute",
510 | "settings": {
511 | "foreground": "#6D6D6D"
512 | }
513 | },
514 | {
515 | "scope": "bujo.timeblock.revision.time.parenthesis.close",
516 | "settings": {
517 | "foreground": "#6D6D6D"
518 | }
519 | },
520 | {
521 | "scope": "bujo.timeblock.revision.text",
522 | "settings": {
523 | "foreground": "#c85f5f",
524 | "fontStyle": "bold underline"
525 | }
526 | },
527 | {
528 | "scope": "bujo.timeblock.chunk.title",
529 | "settings": {
530 | "foreground": "#7599C3"
531 | }
532 | },
533 | {
534 | "scope": "bujo.timeblock.chunk.note",
535 | "settings": {
536 | "foreground": "#6D6D6D"
537 | }
538 | },
539 | {
540 | "scope": "bujo.grid.horizontal",
541 | "settings": {
542 | "foreground": "#6d6d6d92"
543 | }
544 | },
545 | {
546 | "scope": "bujo.grid.colon",
547 | "settings": {
548 | "foreground": "#6d6d6d92"
549 | }
550 | },
551 | {
552 | "scope": "bujo.grid.vertical",
553 | "settings": {
554 | "foreground": "#6d6d6d92"
555 | }
556 | }
557 | ]
558 | }
559 | }
560 | },
561 | "scripts": {
562 | "compile": "tsc -p ./",
563 | "watch": "tsc -watch -p ./",
564 | "pretest": "npm run compile && npm run lint",
565 | "lint": "eslint src --ext ts",
566 | "test": "node ./out/test/runTest.js",
567 | "vscode:prepublish": "npm run package",
568 | "webpack": "webpack --mode development",
569 | "webpack-dev": "webpack --mode development --watch",
570 | "package": "webpack --mode production --devtool hidden-source-map",
571 | "test-compile": "tsc -p ./",
572 | "docs:dev": "vuepress dev docs",
573 | "docs:build": "vuepress build docs"
574 | },
575 | "devDependencies": {
576 | "@types/glob": "^7.2.0",
577 | "@types/lodash": "^4.14.180",
578 | "@types/mocha": "^9.1.0",
579 | "@types/node": "14.x",
580 | "@types/vscode": "^1.65.0",
581 | "@typescript-eslint/eslint-plugin": "^5.16.0",
582 | "@typescript-eslint/parser": "^5.16.0",
583 | "@vscode/test-electron": "^2.1.3",
584 | "@vuepress/plugin-search": "^2.0.0-beta.46",
585 | "eslint": "^8.11.0",
586 | "glob": "^7.2.0",
587 | "mocha": "^9.2.2",
588 | "ts-loader": "^9.3.1",
589 | "typescript": "^4.5.5",
590 | "vuepress": "^2.0.0-beta.46",
591 | "webpack": "^5.73.0",
592 | "webpack-cli": "^4.10.0"
593 | },
594 | "dependencies": {
595 | "lodash": "^4.17.21",
596 | "nanoid": "^3.3.1"
597 | }
598 | }
599 |
--------------------------------------------------------------------------------