├── .eslintrc.json ├── .gitattributes ├── .github └── workflows │ └── docs.yml ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── CNAME ├── LICENSE ├── README.md ├── assets ├── features │ ├── bujo-syntax-highlighting-colored-entries.png │ ├── bujo-syntax-highlighting-time-blocking.png │ └── bujo-syntax-highlighting-time-tracking.png └── icon │ ├── bujo_128.png │ └── bujo_circle_128.png ├── docs ├── .vuepress │ ├── config.ts │ ├── configs │ │ ├── head.ts │ │ ├── index.ts │ │ ├── navbar.ts │ │ └── sidebar.ts │ ├── public │ │ ├── favicon.ico │ │ └── images │ │ │ ├── features │ │ │ ├── bujo-syntax-highlighting-colored-entries.png │ │ │ ├── bujo-syntax-highlighting-entries-with-wiki-link.png │ │ │ ├── bujo-syntax-highlighting-gray-colored-grids.png │ │ │ ├── bujo-syntax-highlighting-modifier-placement.png │ │ │ ├── bujo-syntax-highlighting-modifiers.png │ │ │ ├── bujo-syntax-highlighting-multi-colored-grids.png │ │ │ ├── bujo-syntax-highlighting-multiple-entries-per-line.png │ │ │ ├── bujo-syntax-highlighting-notation-override.png │ │ │ ├── bujo-syntax-highlighting-time-blocking.png │ │ │ ├── bujo-syntax-highlighting-time-tracking.png │ │ │ └── bujo-syntax-highlighting-transparent-notation.png │ │ │ ├── icons │ │ │ ├── apple-touch-icon.jpg │ │ │ ├── favicon-16x16.png │ │ │ └── favicon-32x32.png │ │ │ ├── logos │ │ │ ├── bujo-logo-128x128.png │ │ │ ├── bujo-logo-square.jpg │ │ │ └── bujo-logo.png │ │ │ └── thumbs │ │ │ └── bujo-cover-1280x720.jpg │ └── styles │ │ └── index.scss ├── guide │ ├── index.md │ ├── symbol-updating.md │ ├── syntax-highlighting.md │ ├── time-blocking.md │ └── time-tracking.md ├── index.md └── reference │ ├── commands.md │ ├── index.md │ ├── keybindings.md │ ├── scopes.md │ ├── settings.md │ └── snippets.md ├── package-lock.json ├── package.json ├── snippets └── bujo.markdown.json ├── src ├── extension.ts ├── helpers │ ├── index.ts │ └── uuid.ts ├── models │ ├── Entry.ts │ ├── EntryLine.ts │ ├── Interval.ts │ ├── Pattern.ts │ ├── Sanitizer.ts │ ├── Scheduler.ts │ ├── Symbol.ts │ └── Tracker.ts ├── operations │ ├── index.ts │ ├── scheduleOperations.ts │ ├── symbolOperations.ts │ └── trackOperations.ts └── test │ ├── runTest.ts │ └── suite │ ├── extension.test.ts │ └── index.ts ├── syntaxes ├── bujo.entry.injection.json ├── bujo.grid.injection.json ├── bujo.timeblock.injection.json └── bujo.timetrack.injection.json ├── tsconfig.json └── webpack.config.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { 5 | "ecmaVersion": 6, 6 | "sourceType": "module" 7 | }, 8 | "plugins": [ 9 | "@typescript-eslint" 10 | ], 11 | "rules": { 12 | "@typescript-eslint/naming-convention": "warn", 13 | "@typescript-eslint/semi": "warn", 14 | "curly": "warn", 15 | "eqeqeq": "warn", 16 | "no-throw-literal": "warn", 17 | "semi": "off" 18 | }, 19 | "ignorePatterns": [ 20 | "out", 21 | "dist", 22 | "**/*.d.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set default behavior to automatically normalize line endings. 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | # Workflow name. 2 | name: docs 3 | 4 | # Events. 5 | on: 6 | # Trigger on push to branch. 7 | push: 8 | branches: [main] 9 | # Trigger manually via the GitHub UI. 10 | workflow_dispatch: 11 | 12 | # Jobs. 13 | jobs: 14 | docs: 15 | # Virtual machine type. 16 | runs-on: ubuntu-latest 17 | 18 | # Job steps. 19 | steps: 20 | # Fetch all commits to get all `.git` info logs. 21 | - name: Checkout repository 22 | uses: actions/checkout@v2 23 | with: 24 | fetch-depth: 0 25 | 26 | # Install `Node.js`. 27 | - name: Setup Node.js 28 | uses: actions/setup-node@v1 29 | with: 30 | node-version: '14' 31 | 32 | # Cache `node_modules`. 33 | - name: Cache dependencies 34 | uses: actions/cache@v2 35 | id: yarn-cache 36 | with: 37 | path: | 38 | **/node_modules 39 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 40 | restore-keys: | 41 | ${{ runner.os }}-yarn- 42 | 43 | # Install `Node.js` dependencies if the cache did not hit. 44 | - name: Install dependencies 45 | if: steps.yarn-cache.outputs.cache-hit != 'true' 46 | run: yarn --frozen-lockfile 47 | 48 | # Run build script. 49 | - name: Build VuePress site 50 | run: yarn docs:build 51 | 52 | # Deploy website. 53 | # https://github.com/crazy-max/ghaction-github-pages. 54 | - name: Deploy to GitHub Pages 55 | uses: crazy-max/ghaction-github-pages@v2 56 | with: 57 | # Deploy to `gh-pages` branch. 58 | target_branch: gh-pages 59 | # Deploy the default output dir of VuePress. 60 | build_dir: docs/.vuepress/dist 61 | env: 62 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.vsix 3 | debug 4 | out 5 | dist 6 | .vscode-test 7 | .DS_Store 8 | .temp 9 | .cache 10 | **/temp_* 11 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "dbaeumer.vscode-eslint" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "args": [ 13 | "--extensionDevelopmentPath=${workspaceFolder}" 14 | ], 15 | "outFiles": [ 16 | "${workspaceFolder}/out/**/*.js" 17 | ], 18 | "preLaunchTask": "${defaultBuildTask}" 19 | }, 20 | { 21 | "name": "Extension Tests", 22 | "type": "extensionHost", 23 | "request": "launch", 24 | "args": [ 25 | "--extensionDevelopmentPath=${workspaceFolder}", 26 | "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" 27 | ], 28 | "outFiles": [ 29 | "${workspaceFolder}/out/test/**/*.js" 30 | ], 31 | "preLaunchTask": "${defaultBuildTask}" 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off" 11 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | # Configuration files. 2 | .github/ 3 | .vscode/ 4 | .gitignore 5 | tsconfig.json 6 | .eslintrc.json 7 | package-json.lock 8 | webpack.config.js 9 | CNAME 10 | 11 | # Source files. 12 | node_modules/** 13 | src/ 14 | **/*.ts 15 | 16 | # Documentation files. 17 | docs/ 18 | 19 | # Debug files. 20 | debug/ 21 | 22 | # Development files. 23 | .vscode-test/ 24 | out/ 25 | **/*.map 26 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | bujo.mihaiconstantin.com -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2022] [Mihai Constantin](mihai@mihaiconstantin.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |

Bullet Journal Markdown Workflows

6 | 7 |
8 | GitHub release (latest by date) 9 | Repository Status 10 | GitHub issues 11 | Visual Studio Marketplace Downloads 12 | GitHub Repo stars 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 | BuJo Syntax Highlighting Feature 40 |
41 | 42 |
43 |

Time Blocking

44 | Time Blocking Feature 45 |
46 | 47 |
48 |

Time Tracking

49 | Time Tracking Feature 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 |

74 | Check out the guide at 75 | bujo.mihaiconstantin.com for 76 | an overview of what BuJo can do. 77 |

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 | -------------------------------------------------------------------------------- /assets/features/bujo-syntax-highlighting-colored-entries.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihaiconstantin/bujo/66a4f892f14faafd9e152f2c1b91223f485d6d56/assets/features/bujo-syntax-highlighting-colored-entries.png -------------------------------------------------------------------------------- /assets/features/bujo-syntax-highlighting-time-blocking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihaiconstantin/bujo/66a4f892f14faafd9e152f2c1b91223f485d6d56/assets/features/bujo-syntax-highlighting-time-blocking.png -------------------------------------------------------------------------------- /assets/features/bujo-syntax-highlighting-time-tracking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihaiconstantin/bujo/66a4f892f14faafd9e152f2c1b91223f485d6d56/assets/features/bujo-syntax-highlighting-time-tracking.png -------------------------------------------------------------------------------- /assets/icon/bujo_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihaiconstantin/bujo/66a4f892f14faafd9e152f2c1b91223f485d6d56/assets/icon/bujo_128.png -------------------------------------------------------------------------------- /assets/icon/bujo_circle_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihaiconstantin/bujo/66a4f892f14faafd9e152f2c1b91223f485d6d56/assets/icon/bujo_circle_128.png -------------------------------------------------------------------------------- /docs/.vuepress/config.ts: -------------------------------------------------------------------------------- 1 | import { defineUserConfig } from "vuepress" 2 | import { defaultTheme } from "vuepress"; 3 | import { sidebar, navbar, head } from "./configs"; 4 | 5 | /** 6 | * VuePress config. 7 | */ 8 | export default defineUserConfig({ 9 | base: "/", 10 | lang: "en-US", 11 | title: "BuJo", 12 | description: "Bullet Journal Markdown Workflows", 13 | head: head, 14 | theme: defaultTheme({ 15 | docsDir: "docs", 16 | docsBranch: "main", 17 | repoLabel: "GitHub", 18 | lastUpdated: true, 19 | contributors: true, 20 | navbar: navbar, 21 | sidebar: sidebar, 22 | sidebarDepth: 2, 23 | logo: "/images/logos/bujo-logo-128x128.png", 24 | repo: "https://github.com/mihaiconstantin/bujo", 25 | editLinkText: "Edit this page on GitHub" 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /docs/.vuepress/configs/head.ts: -------------------------------------------------------------------------------- 1 | import type { HeadConfig } from '@vuepress/core' 2 | 3 | 4 | /** 5 | * Header links. 6 | */ 7 | export const head: HeadConfig[] = [ 8 | [ 9 | 'link', 10 | { 11 | rel: 'icon', 12 | type: 'image/png', 13 | sizes: '16x16', 14 | href: `/images/icons/favicon-16x16.png`, 15 | }, 16 | ], 17 | [ 18 | 'link', 19 | { 20 | rel: 'icon', 21 | type: 'image/png', 22 | sizes: '32x32', 23 | href: `/images/icons/favicon-32x32.png`, 24 | }, 25 | ], 26 | ['meta', { name: 'application-name', content: 'BuJo' }], 27 | ['meta', { name: 'apple-mobile-web-app-title', content: 'BuJo' }], 28 | ['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black' }], 29 | [ 30 | 'link', 31 | { rel: 'apple-touch-icon', href: `/images/icons/apple-touch-icon.png` }, 32 | ], 33 | ] 34 | -------------------------------------------------------------------------------- /docs/.vuepress/configs/index.ts: -------------------------------------------------------------------------------- 1 | export * from './head'; 2 | export * from './sidebar'; 3 | export * from './navbar'; 4 | -------------------------------------------------------------------------------- /docs/.vuepress/configs/navbar.ts: -------------------------------------------------------------------------------- 1 | import type { NavbarConfig } from '@vuepress/theme-default'; 2 | 3 | 4 | /** 5 | * Navbar links. 6 | */ 7 | export const navbar: NavbarConfig = [ 8 | { 9 | text: "Guide", 10 | link: "/guide/" 11 | }, 12 | { 13 | text: "Reference", 14 | children: [ 15 | { 16 | text: "Commands", 17 | link: "/reference/commands.md" 18 | }, 19 | { 20 | text: "Settings", 21 | link: "/reference/settings.md" 22 | }, 23 | { 24 | text: "Keybindings", 25 | link: "/reference/keybindings.md" 26 | }, 27 | { 28 | text: "Snippets", 29 | link: "/reference/snippets.md" 30 | }, 31 | { 32 | text: "TextMate Scopes", 33 | link: "/reference/scopes.md" 34 | } 35 | ] 36 | }, 37 | { 38 | text: "Marketplace", 39 | link: "https://marketplace.visualstudio.com/items?itemName=mihaiconstantin.bujo" 40 | } 41 | ] 42 | -------------------------------------------------------------------------------- /docs/.vuepress/configs/sidebar.ts: -------------------------------------------------------------------------------- 1 | import type { SidebarConfig } from '@vuepress/theme-default'; 2 | 3 | 4 | /** 5 | * Sidebar links. 6 | */ 7 | export const sidebar: SidebarConfig = { 8 | "/guide/": [ 9 | "/guide/index.md", 10 | "/guide/syntax-highlighting.md", 11 | "/guide/symbol-updating.md", 12 | "/guide/time-tracking.md", 13 | "/guide/time-blocking.md" 14 | ], 15 | "/reference/": [ 16 | "/reference/index.md", 17 | "/reference/commands.md", 18 | "/reference/settings.md", 19 | "/reference/keybindings.md", 20 | "/reference/snippets.md", 21 | "/reference/scopes.md" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /docs/.vuepress/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihaiconstantin/bujo/66a4f892f14faafd9e152f2c1b91223f485d6d56/docs/.vuepress/public/favicon.ico -------------------------------------------------------------------------------- /docs/.vuepress/public/images/features/bujo-syntax-highlighting-colored-entries.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihaiconstantin/bujo/66a4f892f14faafd9e152f2c1b91223f485d6d56/docs/.vuepress/public/images/features/bujo-syntax-highlighting-colored-entries.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/features/bujo-syntax-highlighting-entries-with-wiki-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihaiconstantin/bujo/66a4f892f14faafd9e152f2c1b91223f485d6d56/docs/.vuepress/public/images/features/bujo-syntax-highlighting-entries-with-wiki-link.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/features/bujo-syntax-highlighting-gray-colored-grids.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihaiconstantin/bujo/66a4f892f14faafd9e152f2c1b91223f485d6d56/docs/.vuepress/public/images/features/bujo-syntax-highlighting-gray-colored-grids.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/features/bujo-syntax-highlighting-modifier-placement.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihaiconstantin/bujo/66a4f892f14faafd9e152f2c1b91223f485d6d56/docs/.vuepress/public/images/features/bujo-syntax-highlighting-modifier-placement.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/features/bujo-syntax-highlighting-modifiers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihaiconstantin/bujo/66a4f892f14faafd9e152f2c1b91223f485d6d56/docs/.vuepress/public/images/features/bujo-syntax-highlighting-modifiers.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/features/bujo-syntax-highlighting-multi-colored-grids.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihaiconstantin/bujo/66a4f892f14faafd9e152f2c1b91223f485d6d56/docs/.vuepress/public/images/features/bujo-syntax-highlighting-multi-colored-grids.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/features/bujo-syntax-highlighting-multiple-entries-per-line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihaiconstantin/bujo/66a4f892f14faafd9e152f2c1b91223f485d6d56/docs/.vuepress/public/images/features/bujo-syntax-highlighting-multiple-entries-per-line.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/features/bujo-syntax-highlighting-notation-override.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihaiconstantin/bujo/66a4f892f14faafd9e152f2c1b91223f485d6d56/docs/.vuepress/public/images/features/bujo-syntax-highlighting-notation-override.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/features/bujo-syntax-highlighting-time-blocking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihaiconstantin/bujo/66a4f892f14faafd9e152f2c1b91223f485d6d56/docs/.vuepress/public/images/features/bujo-syntax-highlighting-time-blocking.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/features/bujo-syntax-highlighting-time-tracking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihaiconstantin/bujo/66a4f892f14faafd9e152f2c1b91223f485d6d56/docs/.vuepress/public/images/features/bujo-syntax-highlighting-time-tracking.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/features/bujo-syntax-highlighting-transparent-notation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihaiconstantin/bujo/66a4f892f14faafd9e152f2c1b91223f485d6d56/docs/.vuepress/public/images/features/bujo-syntax-highlighting-transparent-notation.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/icons/apple-touch-icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihaiconstantin/bujo/66a4f892f14faafd9e152f2c1b91223f485d6d56/docs/.vuepress/public/images/icons/apple-touch-icon.jpg -------------------------------------------------------------------------------- /docs/.vuepress/public/images/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihaiconstantin/bujo/66a4f892f14faafd9e152f2c1b91223f485d6d56/docs/.vuepress/public/images/icons/favicon-16x16.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihaiconstantin/bujo/66a4f892f14faafd9e152f2c1b91223f485d6d56/docs/.vuepress/public/images/icons/favicon-32x32.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/logos/bujo-logo-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihaiconstantin/bujo/66a4f892f14faafd9e152f2c1b91223f485d6d56/docs/.vuepress/public/images/logos/bujo-logo-128x128.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/logos/bujo-logo-square.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihaiconstantin/bujo/66a4f892f14faafd9e152f2c1b91223f485d6d56/docs/.vuepress/public/images/logos/bujo-logo-square.jpg -------------------------------------------------------------------------------- /docs/.vuepress/public/images/logos/bujo-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihaiconstantin/bujo/66a4f892f14faafd9e152f2c1b91223f485d6d56/docs/.vuepress/public/images/logos/bujo-logo.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/thumbs/bujo-cover-1280x720.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihaiconstantin/bujo/66a4f892f14faafd9e152f2c1b91223f485d6d56/docs/.vuepress/public/images/thumbs/bujo-cover-1280x720.jpg -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docs/guide/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageClass: page-guide 3 | --- 4 | 5 | # Introduction 6 | 7 |
8 | GitHub release (latest by date) 9 | Repository Status 10 | GitHub issues 11 | Visual Studio Marketplace Downloads 12 | GitHub Repo stars 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | Default highlighting for Bullet Journal symbols 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 | Highlighting for Bullet Journal symbols with transparent notation 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 | Default highlighting for Bullet Journal modifiers 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 | Modifier placement 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 | Support for multiple entries per line 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 | Highlighting for Bullet Journal entries with wiki links 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 | Highlighting for table grids 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 | Highlighting for table grids 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 | Highlighting for the time tracking table 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 | Highlighting for the time blocking table 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 | Custom highlighting with pink notation for a completed task 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 | -------------------------------------------------------------------------------- /docs/guide/time-blocking.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageClass: page-guide 3 | title: Time Blocking 4 | --- 5 | 6 | # Time Blocking 7 | 8 | **BuJo** supports time blocking syntax highlighting, based on the methodology 9 | discussed in [Newport 10 | (2016)](https://www.goodreads.com/book/show/25744928-deep-work). An example of a 11 | time blocking table can be seen below: 12 | 13 |
14 | Highlighting for the time blocking table 15 |
16 | 17 | ::: tip 18 | Check out the [Snippets Reference](/reference/snippets.md) for handy snippets 19 | that can be used to generate time blocking tables, add revisions, add chunks and 20 | notes, and more. 21 | ::: 22 | -------------------------------------------------------------------------------- /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 | Highlighting for the Time Tracking Table 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 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Welcome 3 | home: true 4 | heroImage: /images/logos/bujo-logo.png 5 | heroAlt: Bullet Journal Markdown Workflows 6 | heroText: BuJo 7 | tagline: Bullet Journal Markdown Workflows 8 | actions: 9 | - text: Get Started 10 | link: /guide/ 11 | type: primary 12 | - text: Reference 13 | link: /reference/ 14 | type: secondary 15 | footer: Made with ❤️ by Mihai Constantin
MIT licensed
16 | footerHtml: true 17 | pageClass: page-home 18 | --- 19 | 20 |
21 |
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 | BuJo Syntax Highlighting Feature 41 |

42 |
43 | 44 |
45 |

Time Blocking

46 |

47 | Time Blocking Feature 48 |

49 |
50 | 51 |
52 |

Time Tracking

53 |

54 | Time Tracking Feature 55 |

56 |
57 |
58 | -------------------------------------------------------------------------------- /docs/reference/commands.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageClass: page-reference 3 | --- 4 | 5 | # Commands 6 | 7 | Below you can find the **BuJo** commands available via the command palette 8 | (i.e., `ctrl+shift+p`). 9 | 10 | | Name | Description | Shortcut | 11 | | :-------------------------------- | :-------------------------- | :-----------: | 12 | | `bujo.symbol.setMigratedForward` | BuJo: Set Migrated Forward | | 13 | | `bujo.symbol.setMigratedBackward` | BuJo: Set Migrated Backward | | 14 | | `bujo.symbol.setCompleted` | BuJo: Set Completed | | 15 | | `bujo.symbol.setOpened` | BuJo: Set Open | | 16 | | `bujo.symbol.setStarted` | BuJo: Set Started | | 17 | | `bujo.symbol.setDropped` | BuJo: Set Dropped | | 18 | | `bujo.scheduler.scheduleEntry` | BuJo: Schedule Entry | `shift+alt+p` | 19 | | `bujo.tracker.recordTime` | BuJo: Record Time | `shift+alt+t` | 20 | | `bujo.tracker.calculateEntryTime` | BuJo: Calculate Entry Time | `shift+alt+s` | 21 | 22 | Additionally, the command `bujo.symbol.setSymbol` for setting custom entry 23 | symbols is only available via keybindings (i.e., see the [Keybindings 24 | Reference](/reference/keybindings.md)). 25 | -------------------------------------------------------------------------------- /docs/reference/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageClass: page-reference 3 | --- 4 | 5 | # Overview 6 | 7 | The following reference is intended to provide an overview of the feature 8 | contributions included in **BuJo**. The remainder of this reference contains 9 | information about the following: 10 | 11 | 1. [Available Commands](/reference/commands.md) 12 | 2. [Extension Settings](/reference/settings.md) 13 | 3. [Default Keybindings](/reference/keybindings.md) 14 | 4. [Snippets](/reference/snippets.md) 15 | 5. [TextMate Scopes](/reference/scopes.md) 16 | -------------------------------------------------------------------------------- /docs/reference/keybindings.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageClass: page-reference 3 | --- 4 | 5 | # Keybindings 6 | 7 | Below you can find the default keybindings available via **BuJo**. 8 | 9 | | Shortcut | Command Invoked | Command Argument | 10 | | :------------ | :-------------------------------- | :--------------: | 11 | | `shift+alt+p` | `bujo.scheduler.scheduleEntry` | | 12 | | `shift+alt+t` | `bujo.tracker.recordTime` | | 13 | | `shift+alt+s` | `bujo.tracker.calculateEntryTime` | | 14 | | `alt+x` | `bujo.symbol.setSymbol` | `x` | 15 | | `alt+o` | `bujo.symbol.setSymbol` | ` ` | 16 | | `alt+-` | `bujo.symbol.setSymbol` | `-` | 17 | | `alt+/` | `bujo.symbol.setSymbol` | `/` | 18 | | `alt+,` | `bujo.symbol.setSymbol` | `<` | 19 | | `alt+.` | `bujo.symbol.setSymbol` | `>` | 20 | | `alt+p` | `bujo.symbol.setSymbol` | `o` | 21 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docs/reference/settings.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageClass: page-reference 3 | --- 4 | 5 | # Settings 6 | 7 | Below you can find the extension settings included with **BuJo**: 8 | 9 | #### `bujo.scheduler.plannerPrefix` 10 | - Specifies the prefix for the planner file (e.g., **`log`**`.2022.03.20`). 11 | - **default:** `log` 12 | 13 | #### `bujo.scheduler.symbolForScheduledEntry` 14 | - Specifies the symbol to set for a scheduled entry (e.g., `>`). 15 | - **default:** `>` 16 | 17 | #### `bujo.scheduler.taskName` 18 | - Specifies how to construct the task name for the time tracking table when 19 | scheduling a `BuJo` entry that contains a wiki link with an alias (e.g., `- [ 20 | ] [[An arbitrary task|file-for-arbitrary-task]]`). Possible options are 21 | `alias` (i.e., `An arbitrary task`) or `filename` (i.e., 22 | `file-for-arbitrary-task`). 23 | - **default:** `alias` 24 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as operations from "./operations"; 3 | 4 | 5 | // When extension gets activated. 6 | export function activate(context: vscode.ExtensionContext) { 7 | 8 | // Register commands. 9 | context.subscriptions.push( 10 | // Symbol operations. 11 | // Set custom symbol via keybindings. 12 | vscode.commands.registerCommand('bujo.symbol.setSymbol', operations.symbol.setSymbol), 13 | 14 | // Symbol default symbols. 15 | vscode.commands.registerCommand('bujo.symbol.setMigratedForward', operations.symbol.setSymbolMigratedForward), 16 | vscode.commands.registerCommand('bujo.symbol.setMigratedBackward', operations.symbol.setSymbolMigratedBackward), 17 | vscode.commands.registerCommand('bujo.symbol.setCompleted', operations.symbol.setSymbolCompleted), 18 | vscode.commands.registerCommand('bujo.symbol.setOpened', operations.symbol.setSymbolOpened), 19 | vscode.commands.registerCommand('bujo.symbol.setStarted', operations.symbol.setSymbolStarted), 20 | vscode.commands.registerCommand('bujo.symbol.setDropped', operations.symbol.setSymbolDropped), 21 | 22 | // Scheduler operations. 23 | // Schedule entry. 24 | vscode.commands.registerCommand('bujo.scheduler.scheduleEntry', operations.scheduler.scheduleEntry), 25 | 26 | // Tracker operations. 27 | // Add time records to the time tracking table. 28 | vscode.commands.registerCommand('bujo.tracker.recordTime', operations.tracker.recordTime), 29 | 30 | // Sum time records. 31 | vscode.commands.registerCommand('bujo.tracker.calculateEntryTime', operations.tracker.calculateEntryTime) 32 | ); 33 | } 34 | 35 | 36 | // Clean-up. 37 | export function deactivate() {} 38 | -------------------------------------------------------------------------------- /src/helpers/index.ts: -------------------------------------------------------------------------------- 1 | import * as uuid from './uuid'; 2 | 3 | 4 | /** 5 | * Create helpers module. 6 | */ 7 | const helpers = { 8 | ...uuid 9 | }; 10 | 11 | 12 | /** 13 | * Export the bundled helpers module. 14 | */ 15 | export = helpers; 16 | -------------------------------------------------------------------------------- /src/helpers/uuid.ts: -------------------------------------------------------------------------------- 1 | // Code from `https://github.com/dendronhq/dendron/blob/master/packages/common-all/src/uuid.ts`. 2 | 3 | import { customAlphabet as nanoid } from "nanoid"; 4 | import { customAlphabet as nanoidInsecure } from "nanoid/non-secure"; 5 | 6 | /** Using this length, according to [nanoid collision calculator](https://zelark.github.io/nano-id-cc/), 7 | * generating 1000 IDs per hour, it would take around 919 years to have 1 percent chance of a single collision. 8 | * This is okay for the "insecure" generator, which is used in limited cases where collisions are less likely. 9 | */ 10 | const SHORT_ID_LENGTH = 12; 11 | /** Default length for nanoids. */ 12 | const LONG_ID_LENGTH = 23; 13 | 14 | const alphanumericLowercase = "0123456789abcdefghijklmnopqrstuvwxyz"; 15 | 16 | /** 17 | * Generates a random identifier. 18 | * 19 | * Backward compatibility notes: 20 | * Previously this id has been generated differently including using 21 | * ------------------------------ 22 | * * uuidv4(); from "uuid/v4"; 23 | * * { v4 } from "uuid"; 24 | * * nanoid(); from "nanoid"; uses: [A-Za-z0-9_-] 25 | * ------------------------------ 26 | * Hence even though right now we only have alphanumeric ids, previously there 27 | * has been ids with `-` and `_` around, that still exist in our users notes. 28 | * 29 | * @returns A url-safe, random identifier. 30 | */ 31 | export const genUUID = nanoid(alphanumericLowercase, LONG_ID_LENGTH); 32 | 33 | /** Generates a shorter random identifier, faster but with potential cryptographic risks. 34 | * 35 | * Uses an insecure random generator for faster generation. 36 | * Also shortens the length of the generated IDs to 16 characters. 37 | * This increases the risk of collisions. 38 | * Only use this if performance is important and collisions are relatively unimportant. 39 | * 40 | * @returns A url-safe, random identifier. 41 | */ 42 | export const genUUIDInsecure = nanoidInsecure( 43 | alphanumericLowercase, 44 | SHORT_ID_LENGTH 45 | ); 46 | -------------------------------------------------------------------------------- /src/models/Entry.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Representation of a `BuJo` entry. 3 | */ 4 | export interface Entry { 5 | // Fields. 6 | notationOpen: string; 7 | notationClose: string; 8 | symbol: string; 9 | modifier: string; 10 | text: string; 11 | id: string; 12 | } 13 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/models/Sanitizer.ts: -------------------------------------------------------------------------------- 1 | import { Range, TextEditor, TextEditorEdit, TextLine } from "vscode"; 2 | 3 | 4 | export class Sanitizer { 5 | /** 6 | * Patterns for removing trailing whitespace. 7 | */ 8 | private patternWhitespace: RegExp = /(\s*)(?<=$)/; 9 | private patternLineEnd: RegExp = /$/; 10 | 11 | 12 | /** 13 | * Text editor. 14 | */ 15 | private editor: TextEditor; 16 | 17 | 18 | /** 19 | * Sanitizer constructor. 20 | */ 21 | public constructor(editor: TextEditor) { 22 | this.editor = editor; 23 | } 24 | 25 | 26 | /** 27 | * Sanitize the active line in the editor. 28 | */ 29 | public async sanitizeActiveLine(): Promise { 30 | // Get active line. 31 | const line: TextLine = this.editor.document.lineAt(this.editor.selection.active.line); 32 | 33 | // Find all white space at the end of the line. 34 | let indexStart: number = line.text.match(this.patternWhitespace)?.index!; 35 | 36 | // Get the index at the end of the line. 37 | let indexEnd: number = line.text.match(this.patternLineEnd)?.index!; 38 | 39 | // Create range for replacement. 40 | const range: Range = new Range(line.lineNumber, indexStart, line.lineNumber, indexEnd); 41 | 42 | // Remove white space. 43 | return await this.editor.edit((editBuilder: TextEditorEdit) => { 44 | editBuilder.replace(range, ''); 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/operations/index.ts: -------------------------------------------------------------------------------- 1 | import * as symbol from './symbolOperations'; 2 | import * as scheduler from './scheduleOperations'; 3 | import * as tracker from './trackOperations'; 4 | 5 | 6 | /** 7 | * Create operations module. 8 | * 9 | * Each operation has a wrapper that can directly use in a command exposed via 10 | * the command palette. 11 | */ 12 | const operations = { 13 | symbol, 14 | scheduler, 15 | tracker 16 | }; 17 | 18 | 19 | /** 20 | * Export the bundled operations module. 21 | */ 22 | export = operations; 23 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /src/test/runTest.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | 3 | import { runTests } from '@vscode/test-electron'; 4 | 5 | async function main() { 6 | try { 7 | // The folder containing the Extension Manifest package.json 8 | // Passed to `--extensionDevelopmentPath` 9 | const extensionDevelopmentPath = path.resolve(__dirname, '../../'); 10 | 11 | // The path to test runner 12 | // Passed to --extensionTestsPath 13 | const extensionTestsPath = path.resolve(__dirname, './suite/index'); 14 | 15 | // Download VS Code, unzip it and run the integration test 16 | await runTests({ extensionDevelopmentPath, extensionTestsPath }); 17 | } catch (err) { 18 | console.error('Failed to run tests'); 19 | process.exit(1); 20 | } 21 | } 22 | 23 | main(); 24 | -------------------------------------------------------------------------------- /src/test/suite/extension.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | 3 | // You can import and use all API from the 'vscode' module 4 | // as well as import your extension to test it 5 | import * as vscode from 'vscode'; 6 | // import * as myExtension from '../../extension'; 7 | 8 | suite('Extension Test Suite', () => { 9 | vscode.window.showInformationMessage('Start all tests.'); 10 | 11 | test('Sample test', () => { 12 | assert.strictEqual(-1, [1, 2, 3].indexOf(5)); 13 | assert.strictEqual(-1, [1, 2, 3].indexOf(0)); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/test/suite/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as Mocha from 'mocha'; 3 | import * as glob from 'glob'; 4 | 5 | export function run(): Promise { 6 | // Create the mocha test 7 | const mocha = new Mocha({ 8 | ui: 'tdd', 9 | color: true 10 | }); 11 | 12 | const testsRoot = path.resolve(__dirname, '..'); 13 | 14 | return new Promise((c, e) => { 15 | glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { 16 | if (err) { 17 | return e(err); 18 | } 19 | 20 | // Add files to the test suite 21 | files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); 22 | 23 | try { 24 | // Run the mocha test 25 | mocha.run(failures => { 26 | if (failures > 0) { 27 | e(new Error(`${failures} tests failed.`)); 28 | } else { 29 | c(); 30 | } 31 | }); 32 | } catch (err) { 33 | console.error(err); 34 | e(err); 35 | } 36 | }); 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /syntaxes/bujo.grid.injection.json: -------------------------------------------------------------------------------- 1 | { 2 | "scopeName": "bujo.grid.injection", 3 | "injectionSelector": "L:text.html.markdown", 4 | "patterns": [ 5 | { "include": "#bujo-grid" } 6 | ], 7 | "repository": { 8 | "bujo-grid": { 9 | "match": "(\\|)(?=\\s+.+\\|\\s*$|$)|((?<=\\|\\s):|:(?=\\s\\|))|(?<=\\|\\s|\\|\\s:)(-+)(?=:?\\s\\|)", 10 | "captures": { 11 | "1": { "name" : "bujo.grid.vertical" }, 12 | "2": { "name" : "bujo.grid.colon" }, 13 | "3": { "name" : "bujo.grid.horizontal" } 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /syntaxes/bujo.timeblock.injection.json: -------------------------------------------------------------------------------- 1 | { 2 | "scopeName": "bujo.timeblock.injection", 3 | "injectionSelector": "L:text.html.markdown", 4 | "patterns": [ 5 | { "include": "#bujo-timeblock" } 6 | ], 7 | "repository": { 8 | "bujo-timeblock": { 9 | "match": "(?<=\\|)(?:\\s+)(\\()([0-2][0-9])(:)([0-5][0-9])(\\))(?=\\s+\\|)|(?<=\\s\\|\\s)(\\(.+\\s\\#\\d+\\))(?=\\s+\\|)|(?<=\\|\\s[0-2][0-9]:[0-5][0-9]-[0-2][0-9]:[0-5][0-9]\\s\\|\\s)(?!\\[|\\s*\\|)(?:\\s*)?(.+?)(?=\\s+\\|)|(?<=\\s\\|\\s)(?!-{2})(-.+?)(?=\\s+\\|)", 10 | "captures": { 11 | "1": { "name" : "bujo.timeblock.revision.time.parenthesis.open" }, 12 | "2": { "name" : "bujo.timeblock.revision.time.hour" }, 13 | "3": { "name" : "bujo.timeblock.revision.time.colon" }, 14 | "4": { "name" : "bujo.timeblock.revision.time.minute" }, 15 | "5": { "name" : "bujo.timeblock.revision.time.parenthesis.close" }, 16 | "6": { "name" : "bujo.timeblock.revision.text" }, 17 | "7": { "name" : "bujo.timeblock.chunk.title" }, 18 | "8": { "name" : "bujo.timeblock.chunk.note" } 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /syntaxes/bujo.timetrack.injection.json: -------------------------------------------------------------------------------- 1 | { 2 | "scopeName": "bujo.timetrack.injection", 3 | "injectionSelector": "L:text.html.markdown", 4 | "patterns": [ 5 | { "include": "#bujo-timetrack" } 6 | ], 7 | "repository": { 8 | "bujo-timetrack": { 9 | "match": "(?<=\\|)(?:\\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+)(\\d+[m])(?=\\s+\\|)", 10 | "captures": { 11 | "1": { "name": "bujo.todo.start.hour" }, 12 | "2": { "name": "bujo.todo.start.colon" }, 13 | "3": { "name": "bujo.todo.start.minute" }, 14 | "4": { "name": "bujo.todo.separator" }, 15 | "5": { "name": "bujo.todo.end.hour" }, 16 | "6": { "name": "bujo.todo.end.colon" }, 17 | "7": { "name": "bujo.todo.end.minute" }, 18 | "8": { "name": "bujo.todo.total" } 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "ES2021", 5 | "outDir": "out", 6 | "lib": [ 7 | "ES2021", 8 | "dom" 9 | ], 10 | "sourceMap": true, 11 | "rootDir": "src", 12 | "strict": true, 13 | 14 | /* Additional Checks */ 15 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 16 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 17 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 18 | }, 19 | "exclude": [ 20 | "node_modules", 21 | ".vscode-test" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | //@ts-check 2 | 3 | 'use strict'; 4 | 5 | const path = require('path'); 6 | const webpack = require('webpack'); 7 | 8 | /**@type {import('webpack').Configuration}*/ 9 | const config = { 10 | target: 'webworker', 11 | entry: './src/extension.ts', 12 | output: { 13 | path: path.resolve(__dirname, 'dist'), 14 | filename: 'extension.js', 15 | libraryTarget: 'commonjs2', 16 | devtoolModuleFilenameTemplate: '../[resource-path]' 17 | }, 18 | devtool: 'source-map', 19 | externals: { 20 | vscode: 'commonjs vscode' 21 | }, 22 | resolve: { 23 | mainFields: ['browser', 'module', 'main'], 24 | extensions: ['.ts', '.js'], 25 | alias: {}, 26 | fallback: {} 27 | }, 28 | module: { 29 | rules: [ 30 | { 31 | test: /\.ts$/, 32 | exclude: /node_modules/, 33 | use: [ 34 | { 35 | loader: 'ts-loader' 36 | } 37 | ] 38 | } 39 | ] 40 | } 41 | }; 42 | 43 | module.exports = config; 44 | --------------------------------------------------------------------------------