├── .eslintrc.json ├── .github └── FUNDING.yml ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── docs ├── codeactions.gif ├── codeactions.md ├── commands.md ├── complete.gif ├── entries.gif ├── entries.md ├── img │ ├── date.png │ ├── offset.png │ ├── shortcut.png │ ├── smartinput.png │ └── weekday.png ├── into.gif ├── intro.gif ├── known_issues.md ├── memo.md ├── memos.gif ├── momentjs_duration.gif ├── momentjs_duration2.gif ├── navigation.gif ├── notes.gif ├── notes.md ├── print.md ├── printCommands.gif ├── scopes.gif ├── scopes.md ├── set-base-directory.gif ├── settings.md ├── smartInput.md ├── tasks.gif └── tasks.md ├── img ├── logo.png ├── logo.svg └── time.gif ├── package-lock.json ├── package.json ├── res ├── colors │ ├── dark.json │ └── light.json ├── snippets │ └── markdown.json └── syntax │ └── journal-markdown.json ├── src ├── actions │ ├── index.ts │ ├── inject.d.ts │ ├── inject.ts │ ├── parser.d.ts │ ├── parser.ts │ ├── reader.d.ts │ ├── reader.ts │ ├── writer.d.ts │ └── writer.ts ├── ext │ ├── commands.d.ts │ ├── conf.d.ts │ ├── conf.ts │ ├── dialogues.ts │ ├── index.d.ts │ ├── index.ts │ ├── messages.json │ ├── startup.d.ts │ ├── startup.ts │ ├── translations.ts │ ├── vscode-codelens.ts │ └── vscode.d.ts ├── extension.d.ts ├── extension.ts ├── index.d.ts ├── index.ts ├── model │ ├── config.ts │ ├── files.ts │ ├── index.ts │ ├── inline.ts │ ├── input.d.ts │ ├── input.ts │ ├── templates.ts │ └── vscode.ts ├── provider │ ├── InputMatcher.d.ts │ ├── codeactions │ │ ├── for-completed-tasks.ts │ │ └── for-open-tasks.ts │ ├── codelens │ │ ├── migrate-tasks.ts │ │ └── shift-task.ts │ ├── commands │ │ ├── copy-task.ts │ │ ├── index.ts │ │ ├── insert-memo.ts │ │ ├── open-journal-workspace.ts │ │ ├── print-current-time.ts │ │ ├── print-duration-between-selected-times.ts │ │ ├── print-sum-of-selected-numbers.ts │ │ ├── show-entry-for-date.ts │ │ ├── show-entry-for-input.ts │ │ ├── show-entry-for-today.ts │ │ ├── show-entry-for-tomorrow.ts │ │ ├── show-entry-for-yesterday.ts │ │ └── show-note.ts │ ├── features │ │ ├── load-note.ts │ │ ├── match-input.ts │ │ ├── scan-entries.ts │ │ ├── show-pick-list.ts │ │ └── sync-note-links.ts │ ├── index.d.ts │ └── index.ts ├── test │ ├── Readme.md │ ├── TestLogger.d.ts │ ├── TestLogger.ts │ ├── direct │ │ ├── direct.d.ts │ │ ├── direct.ts │ │ ├── input_parser.d.ts │ │ ├── input_parser.ts │ │ ├── path-parse-with-date.ts │ │ ├── replace-variables-in-string.ts │ │ └── simple-run-command.ts │ ├── runTest.d.ts │ ├── runTest.ts │ └── suite │ │ ├── extension.test.d.ts │ │ ├── index.d.ts │ │ ├── index.ts │ │ ├── input.test.ts │ │ ├── notes_sync.test.ts │ │ ├── read_templates.test.ts │ │ └── week_input.test.ts └── util │ ├── controller.ts │ ├── dates.ts │ ├── index.d.ts │ ├── index.ts │ ├── logger.d.ts │ ├── logger.ts │ ├── paths.ts │ ├── startup.d.ts │ ├── strings.ts │ ├── util.d.ts │ └── util.ts ├── test ├── Readme.md ├── backup │ └── user-settings.cjson ├── ws_manual │ └── .gitignore └── ws_unittests │ └── .gitignore ├── 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 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: pajoma 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | dist 3 | node_modules 4 | *.vsix 5 | npm-debug* 6 | src/*.js 7 | *.js.map 8 | 9 | *.log 10 | *.old 11 | test/workspace 12 | test/test -------------------------------------------------------------------------------- /.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 | "eamodio.tsl-problem-matcher" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.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 | "${workspaceFolder}/test/ws_manual/" 15 | ], 16 | "outFiles": [ 17 | "${workspaceFolder}/dist/**/*.js" 18 | ], 19 | "preLaunchTask": "${defaultBuildTask}" 20 | }, 21 | { 22 | "name": "Run Extension without Workspace", 23 | "type": "extensionHost", 24 | "request": "launch", 25 | "args": [ 26 | "--extensionDevelopmentPath=${workspaceFolder}", 27 | "${workspaceFolder}/test/ws_empty/" 28 | ], 29 | "outFiles": [ 30 | "${workspaceFolder}/dist/**/*.js" 31 | ], 32 | "preLaunchTask": "${defaultBuildTask}" 33 | }, 34 | { 35 | "name": "Extension Tests", 36 | "type": "extensionHost", 37 | "request": "launch", 38 | "args": [ 39 | "--extensionDevelopmentPath=${workspaceFolder}", 40 | "--extensionTestsPath=${workspaceFolder}/out/test/suite/index", 41 | "${workspaceFolder}/test/ws_unittests/" 42 | ], 43 | "outFiles": [ 44 | "${workspaceFolder}/out/**/*.js", 45 | "${workspaceFolder}/dist/**/*.js" 46 | ], 47 | "preLaunchTask": "tasks: watch-tests" 48 | } 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /.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 | "dist": false, // set this to true to hide the "dist" folder with the compiled JS files 6 | "src/**/*.js": false, 7 | "**/*.d.ts": true, 8 | "**/*.js.map": true 9 | }, 10 | "search.exclude": { 11 | "out": true, // set this to false to include "out" folder in search results 12 | "dist": true // set this to false to include "dist" folder in search results 13 | }, 14 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 15 | "typescript.tsc.autoDetect": "off" 16 | } -------------------------------------------------------------------------------- /.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": [ 10 | "$ts-webpack-watch", 11 | "$tslint-webpack-watch" 12 | ], 13 | "isBackground": true, 14 | "presentation": { 15 | "reveal": "never", 16 | "group": "watchers" 17 | }, 18 | "group": { 19 | "kind": "build", 20 | "isDefault": true 21 | } 22 | }, 23 | { 24 | "type": "npm", 25 | "script": "watch-tests", 26 | "problemMatcher": "$tsc-watch", 27 | "isBackground": true, 28 | "presentation": { 29 | "reveal": "never", 30 | "group": "watchers" 31 | }, 32 | "group": "build" 33 | }, 34 | { 35 | "label": "tasks: watch-tests", 36 | "dependsOn": [ 37 | "npm: watch", 38 | "npm: watch-tests" 39 | ], 40 | "problemMatcher": [] 41 | }, 42 | { 43 | "type": "npm", 44 | "script": "compile-tests", 45 | "group": "build", 46 | "problemMatcher": [], 47 | "label": "npm: compile-tests", 48 | "detail": "tsc -p . --outDir out" 49 | } 50 | ] 51 | } -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/** 4 | node_modules/** 5 | src/** 6 | .gitignore 7 | .yarnrc 8 | webpack.config.js 9 | vsc-extension-quickstart.md 10 | **/tsconfig.json 11 | **/.eslintrc.json 12 | **/*.map 13 | **/*.ts 14 | test/** 15 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to VSCode-Journal 2 | Thank you so much for your interest in contributing! All types of contributions are encouraged and valued. 3 | 4 | ## About the Project 5 | I have been writing notes every day for over 10 years and still use this extension all the time. In the beginning only with Notepad and other text editors. When the first version of Visual Studio Code came out, I saw an opportunity to get to know Typescript better and started developing this extension. That's why I mainly focus on ideas and extensions that help me in my daily work. 6 | 7 | The source code reflects this journey. A bit bumpy at the beginning (and still today for sure, all this javascript stuff makes me doubt myself often enough), but it got a bit better with the years. 8 | 9 | ## How to contribute 10 | 11 | There are several ways to contribute. 12 | 13 | * If you find any issues, weird behaviour or plain error, don't hesitate to [open an issue](https://github.com/pajoma/vscode-journal/issues/new). I try to react timely, but don't count on it. 14 | * [Start a discussion](https://github.com/pajoma/vscode-journal/discussions/new) if you have question or feature requests. Or see if there are any other unanswered questions you might be able to answer. 15 | * Leave a review on the [marketplace](https://marketplace.visualstudio.com/items?itemName=pajoma.vscode-journal&ssr=false#review-details) and keep me motivated ;) 16 | * Let me buy a beer by [sponsoring](https://github.com/sponsors/pajoma) my work here 17 | 18 | If you plan to contribute with updates to the source, follow these steps 19 | 20 | * Outline your idea in the discussions. 21 | * Talk to me on [Gitter](https://gitter.im/dictyo) for further questions. 22 | * Create a fork, do your thing, and create a pull request. Please write tests if possible. 23 | 24 | ## Code of conduct 25 | 26 | Just be decent. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![GitHub](https://img.shields.io/github/license/pajoma/vscode-journal?style=flat-square) 2 | ![Visual Studio Marketplace Version](https://img.shields.io/visual-studio-marketplace/v/pajoma.vscode-journal?style=flat-square) 3 | ![Visual Studio Marketplace Installs](https://img.shields.io/visual-studio-marketplace/i/pajoma.vscode-journal?style=flat-square) 4 | ![Visual Studio Marketplace Rating](https://img.shields.io/visual-studio-marketplace/r/pajoma.vscode-journal?style=flat-square) 5 | ![Visual Studio Marketplace Last Updated](https://img.shields.io/visual-studio-marketplace/last-updated/pajoma.vscode-journal?style=flat-square) 6 | 7 | 8 | 9 | 12 | 18 | 19 |
10 | drawing 11 | 13 |

14 | vscode-journal 15 |

16 | Lightweight Extension for Visual Studio Code to take track of your daily notes. 17 |
20 | 21 | 22 | 23 | 24 | ## What is this extension about? 25 | This extension is useful for people like me, who use simple text files for notes, task lists, and everything else which has to be remembered and searched for. Text (or Markdown) in files is easy to backup, sync and can be opened anywhere by everything. This extension has the following functions: 26 | 27 | * Open or create a journal entries for a specific day ([details and videos](./docs/entries.md)) 28 | * Add detailed notes for today ([details and videos](./docs/notes.md)) 29 | * Add a memo to today's journal page ([details and videos](./docs/memo.md)) 30 | * Manage your tasks ([details and videos](./docs/tasks.md)) 31 | * Print commands and snippets to support various tasks ([details and videos](./docs/print.md)) 32 | * Configure scopes to manage notes of different projects ([details](./docs/scopes.md)) 33 | * Use code actions to work on your task lists ([details](./docs/codeactions.md)) 34 | 35 | ## Features 36 | Press `Ctrl+Shift+J` to open the journal's smart input and start typing right away. Press `F1` or `Ctrl+Shift+P` to access one of the commands. All supported commands are described [here](./docs/commands.md). 37 | 38 | The notes are stored in a folder on your desktop using the following structure (taking ZIM Desktop wiki as inspiration: `year/month/day.md`, the notes files for October 22th would be `../2016/10/22.md`. Detailed notes (e.g. meeting notes) are placed in the subfolder `../2016/10/22/some-meeting-notes.md`. 39 | 40 | ## Contributing 41 | I am always looking for feedback, new ideas and your help. Check the [contribution guidelines](./CONTRIBUTING.md) 42 | 43 | ## Suggested extensions 44 | vscode-journal is mainly responsible for organizing your notes and journal entries, it does not come with any user interface (besides the smart input). If you prefer tree like views for your notes and tasks, have a look at the following extensions by Gruntfuggly and Kortina 45 | 46 | * [vscode-journal-view](https://marketplace.visualstudio.com/items?itemName=Gruntfuggly.vscode-journal-view) to easly navigate to your entries and notes by date 47 | * [todo-tree](https://marketplace.visualstudio.com/items?itemName=Gruntfuggly.todo-tree) to quickly access and manage your tasks 48 | * [markdown notes](https://marketplace.visualstudio.com/items?itemName=kortina.vscode-markdown-notes) for enhanced navigation capabilities between your notes 49 | 50 | 51 | ## Settings 52 | Settings are described in detail [here](./docs/settings.md) 53 | 54 | You have to set the base folder for notes folder structure before you start. Open your settings, search for 'journal' and copy the journal.base line into your personal settings. Adjust the value, for example: ` "journal.base": "C:/Users/FooBar/Documents/Journal"` (use forward slash!) 55 | 56 | The default file format is Markdown (using `md` as extension), which is natively supported by Visual Studio Code. I use Asciidoc for my notes (with `.adoc` as extension), in this case you should also install an Asciidoc Syntax extension. 57 | 58 | Syntax highlighting is configured in your workspace settings. The options are stored automatically in the settings, you can then manually change them. Make sure to restart to apply the changes. The settings try to recognize if a dark or light theme is used. If you switch in between, you either have to delete the settings (to reload them from the extension) or adapt them for yourself. 59 | 60 | 61 | ## Demo 62 | 63 | ![Screen Capture](./docs/complete.gif) -------------------------------------------------------------------------------- /docs/codeactions.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pajoma/vscode-journal/34ca1290d759c7c32caca9858d78235aae0ea423/docs/codeactions.gif -------------------------------------------------------------------------------- /docs/codeactions.md: -------------------------------------------------------------------------------- 1 | # Code Actions 2 | 3 | Code actions are a Visual Studio Code feature (and basically every serious other IDE) to quickly run a commands from within the text. For now, the journal extension has code extension active for tasks. When you place your mouse cursor on a line with a task (any line starting with `- []` or `-[x]`), a yellow balloon will appear. You can trigger the selection of code actions by selecting the balloon or using the shortcut `CTRL+.` 4 | 5 | The following actions are supported for now 6 | * Complete a task (marks the task and adds the completion time) 7 | * Reopen a task 8 | * Migrate a task to today (when current entry is from another day). It will modify the task to `[>]` (inspired by bullet journaling) and append the new date 9 | * Migrate a task to tomorrow 10 | * Migrate a task to the next working day (if tomorrow is in the weekend) 11 | 12 | 13 | ![Screen Capture](./codeactions.gif) 14 | -------------------------------------------------------------------------------- /docs/commands.md: -------------------------------------------------------------------------------- 1 | # Visual Studio Code Commands of vscode-journal 2 | Press 'F1' or Ctrl+Shift+P to access one of the commands. 3 | 4 | You can access all functionality (besides opening the journal) from the smart input. 5 | 6 | ## Journal Pages 7 | 8 | * `journal:day` (keybindings: `ctrl+shift+j` or `cmd+shift+j` on mac) opens the smart input, see 9 | * `journal:today` for opening today's entry 10 | * `journal:tomorrow` 11 | 12 | ## Notes & Memos 13 | `journal:note` opens a dialog to enter the title of a new page for notes. 14 | 15 | ## Open the journal 16 | `journal:open` starts a new instance of vscode with the base directory of your journal as root -------------------------------------------------------------------------------- /docs/complete.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pajoma/vscode-journal/34ca1290d759c7c32caca9858d78235aae0ea423/docs/complete.gif -------------------------------------------------------------------------------- /docs/entries.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pajoma/vscode-journal/34ca1290d759c7c32caca9858d78235aae0ea423/docs/entries.gif -------------------------------------------------------------------------------- /docs/entries.md: -------------------------------------------------------------------------------- 1 | # Navigating to journal entries 2 | 3 | A journal entry is created for every day. It collects the day's tasks, memos and any other content which you don't want to have in separate notes. I use it personally mostly for time tracking. 4 | 5 | A wide range of different inputs is supported, see [this page](./smartInput.md) for more details. 6 | 7 | ![Screen Capture](./navigation.gif) 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/img/date.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pajoma/vscode-journal/34ca1290d759c7c32caca9858d78235aae0ea423/docs/img/date.png -------------------------------------------------------------------------------- /docs/img/offset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pajoma/vscode-journal/34ca1290d759c7c32caca9858d78235aae0ea423/docs/img/offset.png -------------------------------------------------------------------------------- /docs/img/shortcut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pajoma/vscode-journal/34ca1290d759c7c32caca9858d78235aae0ea423/docs/img/shortcut.png -------------------------------------------------------------------------------- /docs/img/smartinput.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pajoma/vscode-journal/34ca1290d759c7c32caca9858d78235aae0ea423/docs/img/smartinput.png -------------------------------------------------------------------------------- /docs/img/weekday.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pajoma/vscode-journal/34ca1290d759c7c32caca9858d78235aae0ea423/docs/img/weekday.png -------------------------------------------------------------------------------- /docs/into.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pajoma/vscode-journal/34ca1290d759c7c32caca9858d78235aae0ea423/docs/into.gif -------------------------------------------------------------------------------- /docs/intro.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pajoma/vscode-journal/34ca1290d759c7c32caca9858d78235aae0ea423/docs/intro.gif -------------------------------------------------------------------------------- /docs/known_issues.md: -------------------------------------------------------------------------------- 1 | # Known Issues 2 | 3 | The following list consists of open issues which I have identified during testing, but decided to ignore them (for now). These issues are either just not worth the effort or just happening w/in rather weird conditions. 4 | 5 | ## Linebreaks for injected links when last line is empty 6 | Sometimes linebreaks are missing between the links 7 | 8 | ## '\n' in template patterns are required 9 | Since the settings need to be within one line, you have to add linebreaks manually. Alternative would be to store the patterns in separate files, which breaks the user story in vscode to edit settings. 10 | 11 | ## Compute Duration always below 12h 12 | Order doesn't matter, we always substract the smaller from the larger. No order was on purpose, I think its better to just add a "+1" to add day breaks (you can then also add +2) 13 | 14 | -------------------------------------------------------------------------------- /docs/memo.md: -------------------------------------------------------------------------------- 1 | # Working with memos 2 | 3 | ![Screen Capture](./memos.gif) 4 | 5 | Memos are simple reminders within your journal entries. You add memos through the smart input (Ctrl+Shift+J). Just enter any text, it will be added to your today's entry. By adding date modifiers, you can control in which journal entry the memo is inserted. 6 | 7 | * `+1 I have to remember this` adds a memo to tomorrow's journal entry 8 | * `Check out Tom's presentation` adds a memo to today's journal entry 9 | * `Next wednesday call my bank` adds the memo "Call my bank" to next wednesday's journal entry 10 | 11 | -------------------------------------------------------------------------------- /docs/memos.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pajoma/vscode-journal/34ca1290d759c7c32caca9858d78235aae0ea423/docs/memos.gif -------------------------------------------------------------------------------- /docs/momentjs_duration.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pajoma/vscode-journal/34ca1290d759c7c32caca9858d78235aae0ea423/docs/momentjs_duration.gif -------------------------------------------------------------------------------- /docs/momentjs_duration2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pajoma/vscode-journal/34ca1290d759c7c32caca9858d78235aae0ea423/docs/momentjs_duration2.gif -------------------------------------------------------------------------------- /docs/navigation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pajoma/vscode-journal/34ca1290d759c7c32caca9858d78235aae0ea423/docs/navigation.gif -------------------------------------------------------------------------------- /docs/notes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pajoma/vscode-journal/34ca1290d759c7c32caca9858d78235aae0ea423/docs/notes.gif -------------------------------------------------------------------------------- /docs/notes.md: -------------------------------------------------------------------------------- 1 | # Working with notes 2 | 3 | ![Screen Capture](./notes.gif) 4 | 5 | Notes are single markdown files linked to the today's journal entry. `journal:note` (in the command palette using the shortcut `Ctrl+Shift+P`) opens a dialog to enter the title of a new page for notes. The title is also the filename (stored typically as subfolder in the journal structure, e.g. folder ´25´ in folder ´10´ if today is 10/25). Local links are automatically added to the current day's journal entry. 6 | 7 | (soon) You can also use the journal's smart input to create a note. Press `Ctrl+Shift+J` and prefix your text with the flag `note`. It will then create a new page using the remaining text in the input as title. 8 | 9 | Notes are automatically linked in the according journal entry (of the same day, when the note has been created). 10 | 11 | ## Support for Scopes 12 | You can add free tags the beginning the 13 | 14 | 15 | If you enter something like "#projectA Workshop Minutes" as title, the new document will be stored not within in the base directory configured for this scope. 16 | -------------------------------------------------------------------------------- /docs/print.md: -------------------------------------------------------------------------------- 1 | # Working with print commands and snippets 2 | 3 | ![Screen Capture](./printCommands.gif) 4 | 5 | I use the journal daily to track my time spent on various projects. You can use the following commands (and shortcuts) to compute the duration between two timestamps or to print the sum of various numbers. 6 | 7 | * `Print Time` to enter timestamps at cursor position. 8 | * `Print elapased hours`, which computes the duration between two seleced timestamps (same format as what "Insert time" prints). Shortcut: `Ctrl+J Ctrl+D` 9 | * `Print sum of selected numbers`, which summarizes all selected numbers (I use it to compute the total time). Shortcut: `Ctrl+J Ctrl+S`v -------------------------------------------------------------------------------- /docs/printCommands.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pajoma/vscode-journal/34ca1290d759c7c32caca9858d78235aae0ea423/docs/printCommands.gif -------------------------------------------------------------------------------- /docs/scopes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pajoma/vscode-journal/34ca1290d759c7c32caca9858d78235aae0ea423/docs/scopes.gif -------------------------------------------------------------------------------- /docs/scopes.md: -------------------------------------------------------------------------------- 1 | # Scopes 2 | 3 | Scopes help you to manage notes within your different projects by declaring individual configurations. Scopes are specific tags such as "#clientA" or "#private". When creating a new note and using that tag in the input box, you tell the extension to use the scope's configuration (and not the global configuration of the journal). 4 | 5 | ## Setting up a new scope 6 | Let's stick to the example of the new scope "#clientA", which you plan to use to store all notes in a new project coming up with a Client. 7 | 8 | ### Configure Settings 9 | Open your settings and adapt ```journal.scopes``` directly in settings.json. 10 | 11 | Visual Studio Code will generate the following snippet for you: 12 | 13 | ```json 14 | "journal.scopes": { 15 | } 16 | 17 | ``` 18 | You have to replace the curly brackets with ```[]```, since we expect an array here (a list of scopes). 19 | 20 | Within a scopes definition, you can reconfigure the base path and the file patterns (only notes so far, see below). The following example sets up new scopes for "clientA" and "private". The Notes-Folder for ClientA points to a Git Repository shared with my Team, while the private Folder is part of my normal base path (but notes are not stored under the journal entries). 21 | 22 | 23 | ```json 24 | "journal.scopes": [ 25 | 26 | { 27 | "name": "clientA", 28 | "patterns": { 29 | "base": "D:/Repositories/ClientA/SharedNotes", 30 | "notes": { 31 | "path": "${base}/userX", 32 | "file": "${d: YYYYY-DD-MM}-${input}.${ext}" 33 | }, 34 | } 35 | }, 36 | { 37 | "name": "private", 38 | "patterns": { 39 | "notes": { 40 | "path": "${base}/scopes/private", 41 | "file": "${localDate}-${input}.${ext}" 42 | }, 43 | } 44 | } 45 | ] 46 | ``` 47 | 48 | 49 | ### Scoped Journal Entries 50 | Supporting scopes for journal entries messes with the input box and would require some serious rewriting of the pattern matching code. Please open an issue if you want this feature. 51 | 52 | 53 | ## Using scopes 54 | 55 | Whenever you want to create a new note, just prefix the note's title with the scope, written as tag. 56 | 57 | 1. Press ```Ctrl+Shift+J``` 58 | 2. Pick "Select or create note" 59 | 3. Enter the scope and some text, for example ```#clientA Sprint Daily Notes``` 60 | 61 | The details for the highlighted item in picklist should tell you, if the scope has been detected and should look like 62 | ```Create new note in scope #clientA and tags #clientA```. Any other tags you enter here will be pasted in the new file. 63 | 64 | ![Screen Capture](./scopes.gif) 65 | -------------------------------------------------------------------------------- /docs/set-base-directory.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pajoma/vscode-journal/34ca1290d759c7c32caca9858d78235aae0ea423/docs/set-base-directory.gif -------------------------------------------------------------------------------- /docs/smartInput.md: -------------------------------------------------------------------------------- 1 | # Examples how to use the smart input 2 | 3 | Using the shortcut ```CTRL+SHIFT+J``` to opens the following dialog. 4 | 5 | ![Screen Capture](./img/smartinput.png) 6 | 7 | From there you can either start typing away (see below for options) or select one of the following options: 8 | 9 | * *Today* selects or creates the journal entry of the today 10 | * *Tomorrow* selects or creates the journal entry of the tomorrow 11 | * *Select entry* opens a list of existing journal entries 12 | * *Select/Create a note* opens a list of existing notes for selection. You are still able to create a new note by simply typing the note title. 13 | 14 | This guide including its examples assume english as your display language. If you changed the setting **Display Language** in Visual Studio Code, you probably see other messages. Supported languages for this extension are: 15 | 16 | * English (en) 17 | * German (de) 18 | * French (fr) 19 | * Spanish (es) 20 | * Italian (it) 21 | * Portuguese (pt) 22 | * Dutch (nl) 23 | * Russian (ru) 24 | * Chinese (Pinyin) (zh) 25 | * Japanese (Romaji) (ja) 26 | * Arabic (ar) 27 | 28 | 29 | ## Supported input options 30 | The help text in the dialog will tell you, what action will be performed after analyzing the input. 31 | 32 | The following actions are possible: 33 | * Pick a specific journal entry by entering a shortcut, offset, weekday or specific date 34 | * Open a week's entry (last, this, and next, or a numbered calendar week) 35 | * Add a memo to today's page or to a page of a specific date 36 | * Add a task to today's page or to a page of a specific date 37 | 38 | 39 | 40 | ### Pick journal entry by shortcut 41 | ![Screen Capture](./img/shortcut.png) 42 | 43 | Supported shortcuts are 44 | 45 | * `today, tod` for today's journal entry 46 | * `tomorrow, tom` for tomorrow's journal entry 47 | * `yesterday, yes` for yesterday's journal entry 48 | 49 | Each entry will also create the entry if it doesn't exist yet. 50 | 51 | 52 | ### Pick journal entry by offset 53 | ![Screen Capture](./img/offset.png) 54 | 55 | * ``` 0 ``` to select today's entry. Or simply press enter. 56 | * ``` -1 ``` to select yesterday's entry 57 | * ``` +1 ``` to select tomorrows entry 58 | * ``` -2423 ``` to select a day far in the past 59 | 60 | ### Pick journal entry by day of week 61 | ![Screen Capture](./img/weekday.png) 62 | 63 | Supported values are `monday, mon, tuesday, tue, wednesday, wed, thursday, thu, friday, fri, saturday, sat, sunday, sun` 64 | 65 | You can use the modifiers `last` and `next` to go either into the past or future. The last is the default, if you simply enter `mon`, the journal page for next monday will be opened. 66 | 67 | * `next wednesday` for journal entry of next wednesday 68 | * `last wednesday` for journal entry of next wednesday 69 | 70 | ### Pick weekly entry 71 | Supported values are `week w13` 72 | 73 | You can use the modifiers `last` and `next` to go either into the past or future. The last is the default, if you simply enter `w` or `week`, the weekly page for the current week is opened 74 | 75 | * `next week` for weekly entry of next calendar week 76 | * `last week` for weekly entry of last calendar week 77 | * `w1` for weekly entry of the first week of this year 78 | * `w99` works as well (well, why not) 79 | 80 | 81 | 82 | ### Pick journal entry by date 83 | ![Screen Capture](./img/date.png) 84 | 85 | You can jump to a specific date using the following options 86 | 87 | * `25`: By only enterying a number, the journal entry for the day of the current month is selected 88 | * `10-25` selects Oct 25 of the current year 89 | * `2015-25-10` for Oct 25 in 2015 90 | 91 | The syntax follows the ISO Standard `YYYY-MM--DD` 92 | 93 | 94 | ### Add a memo to today's page 95 | `This is an important thing to remember` 96 | 97 | Just enter any text. If no modifiers (a date modifier for picking a journal entry) or the flag "task" are included, the new text will added as memo to the journal entry of the current day. 98 | 99 | 100 | 101 | ### Add a memo to any journal entry 102 | `+1 I have to remember this` 103 | 104 | Enter any of the modifiers (offset, date or weekday) _before_ entering any text. The new memo will then be added to the journal page of the selected date. 105 | 106 | ### Add a task to today's page 107 | `todo Order christmas presents` 108 | 109 | If you add flags like `todo` or `task` _before_ any text, it will be added as task to page of the journal entry of the current day. 110 | 111 | Other examples 112 | 113 | * `task fri Submit proposal` if you have a deadline this friday 114 | * `task 10-30 Buy myself a present for my birthday` .. since why not 115 | * `task next wed Remember the milk` .. because the one in the fridge is spoiled by then 116 | 117 | -------------------------------------------------------------------------------- /docs/tasks.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pajoma/vscode-journal/34ca1290d759c7c32caca9858d78235aae0ea423/docs/tasks.gif -------------------------------------------------------------------------------- /docs/tasks.md: -------------------------------------------------------------------------------- 1 | # Working with tasks 2 | 3 | ![Screen Capture](./tasks.gif) 4 | 5 | Tasks are modelled as checkpoints in your journal entries, e.g. 6 | 7 | ```markdown 8 | # Sunday, 2018/06/03 9 | 10 | - [ ] Task: Fill out my taxes 11 | - [x] Task: Finish the exercise 12 | ``` 13 | 14 | You can enter tasks either manually or by the journal smart input (using `Ctrl+Shift+J`). The following examples work for the smart input: 15 | 16 | * `task Fill out my taxes` adds a task to today's entry 17 | * `task 06-24 Call my mom` adds a task for entry of June 24th 18 | * `task +1 Call my mom` adds a task for entry for tomorrow 19 | * `task tom Call my mom` adds a task for entry for tomorrow 20 | * `task next friday Call my mom` adds a task for entry for next friday 21 | 22 | 23 | -------------------------------------------------------------------------------- /img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pajoma/vscode-journal/34ca1290d759c7c32caca9858d78235aae0ea423/img/logo.png -------------------------------------------------------------------------------- /img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 24 | 42 | 44 | 45 | 47 | image/svg+xml 48 | 50 | 51 | 52 | 53 | 54 | 58 | 66 | 74 | 82 | 90 | Today 105 | 106 | 107 | -------------------------------------------------------------------------------- /img/time.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pajoma/vscode-journal/34ca1290d759c7c32caca9858d78235aae0ea423/img/time.gif -------------------------------------------------------------------------------- /res/colors/dark.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "scope": "text.html.markdown.journal.task.open.bullet", 4 | "settings": { 5 | "foreground": "#FFFF00" 6 | } 7 | }, 8 | { 9 | "scope": "text.html.markdown.journal.task.open.marker", 10 | "settings": { 11 | "foreground": "#FFFF00" 12 | } 13 | }, 14 | { 15 | "scope": "text.html.markdown.journal.task.open.keyword", 16 | "settings": { 17 | "fontStyle": "italic" 18 | } 19 | }, 20 | { 21 | "scope": "text.html.markdown.journal.task.open.text", 22 | "settings": {} 23 | }, 24 | { 25 | "scope": "text.html.markdown.journal.task.completed.keyword", 26 | "settings": { 27 | "fontStyle": "italic" 28 | } 29 | }, 30 | { 31 | "scope": "text.html.markdown.journal.task.completed.marker", 32 | "settings": { 33 | "foreground": "#AAAAAA" 34 | } 35 | }, 36 | { 37 | "scope": "text.html.markdown.journal.task.completed.text", 38 | "settings": { 39 | "foreground": "#AAAAAA" 40 | } 41 | }, 42 | { 43 | "scope": "text.html.markdown.journal.task.completed.bullet", 44 | "settings": { 45 | "foreground": "#FFFF00" 46 | } 47 | }, 48 | { 49 | "scope": "text.html.markdown.journal.memo.keyword", 50 | "settings": { 51 | "fontStyle": "italic" 52 | } 53 | }, 54 | { 55 | "scope": "text.html.markdown.journal.memo.bullet", 56 | "settings": { 57 | "foreground": "#FFFF00" 58 | } 59 | }, 60 | { 61 | "scope": "text.html.markdown.journal.scope", 62 | "settings": { 63 | "foreground": "#FFFF00" 64 | } 65 | }, 66 | { 67 | "scope": "text.html.markdown.journal.link.keyword", 68 | "settings": { 69 | "fontStyle": "italic" 70 | } 71 | }, 72 | { 73 | "scope": "text.html.markdown.journal.link.bullet", 74 | "settings": { 75 | "foreground": "#FFFF00" 76 | } 77 | } 78 | ] -------------------------------------------------------------------------------- /res/colors/light.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "scope": "text.html.markdown.journal.task.open.bullet", 4 | "settings": { 5 | "foreground": "#CC3300" 6 | } 7 | }, 8 | { 9 | "scope": "text.html.markdown.journal.task.open.marker", 10 | "settings": { 11 | "foreground": "#CC3300" 12 | } 13 | }, 14 | { 15 | "scope": "text.html.markdown.journal.task.open.keyword", 16 | "settings": { 17 | "fontStyle": "italic" 18 | } 19 | }, 20 | { 21 | "scope": "text.html.markdown.journal.task.open.text", 22 | "settings": {} 23 | }, 24 | { 25 | "scope": "text.html.markdown.journal.task.completed.keyword", 26 | "settings": { 27 | "fontStyle": "italic" 28 | } 29 | }, 30 | { 31 | "scope": "text.html.markdown.journal.task.completed.marker", 32 | "settings": { 33 | "foreground": "#AAAAAA" 34 | } 35 | }, 36 | { 37 | "scope": "text.html.markdown.journal.task.completed.text", 38 | "settings": { 39 | "foreground": "#AAAAAA" 40 | } 41 | }, 42 | { 43 | "scope": "text.html.markdown.journal.task.completed.bullet", 44 | "settings": { 45 | "foreground": "#CC3300" 46 | } 47 | }, 48 | { 49 | "scope": "text.html.markdown.journal.memo.keyword", 50 | "settings": { 51 | "fontStyle": "italic" 52 | } 53 | }, 54 | { 55 | "scope": "text.html.markdown.journal.memo.bullet", 56 | "settings": { 57 | "foreground": "#CC3300" 58 | } 59 | }, 60 | { 61 | "scope": "text.html.markdown.journal.scope", 62 | "settings": { 63 | "foreground": "#CC3300" 64 | } 65 | }, 66 | { 67 | "scope": "text.html.markdown.journal.link.keyword", 68 | "settings": { 69 | "fontStyle": "italic" 70 | } 71 | }, 72 | { 73 | "scope": "text.html.markdown.journal.link.bullet", 74 | "settings": { 75 | "foreground": "#CC3300" 76 | } 77 | } 78 | ] -------------------------------------------------------------------------------- /res/snippets/markdown.json: -------------------------------------------------------------------------------- 1 | { 2 | "Create Task": { 3 | "prefix": "task", 4 | "body": [ 5 | "- [ ] ${1:description}" 6 | ], 7 | "description": "Snippet for adding a new task" 8 | }, 9 | "Track time": { 10 | "prefix": "track", 11 | 12 | 13 | "body": [ 14 | "${1:start}-${2:end} | | ${3:tags} | ${4:description}" 15 | ], 16 | "description": "Snippet for tracking time" 17 | } 18 | } -------------------------------------------------------------------------------- /res/syntax/journal-markdown.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", 3 | "fileTypes": [], 4 | "injectionSelector": "L:text.html.markdown", 5 | "patterns": [ 6 | { 7 | "include": "#checklist" 8 | }, 9 | { 10 | "include": "#scope" 11 | }, 12 | { 13 | "include": "#keywords" 14 | } 15 | ], 16 | "repository": { 17 | "scope": { 18 | "patterns": [ 19 | { 20 | "match": "(?:\\s|^)(#\\w+)", 21 | "captures": { 22 | "1": { 23 | "name": "text.html.markdown.journal.scope" 24 | } 25 | } 26 | } 27 | ] 28 | }, 29 | "checklist": { 30 | "patterns": [ 31 | { 32 | "match": "(-|\\*)\\s+(\\[\\s?\\])\\s*(?i:((?:task|todo):?))?\\s(.*)", 33 | "name": "text.html.markdown.journal.task.open", 34 | "captures": { 35 | "1": { 36 | "name": "text.html.markdown.journal.task.open.bullet" 37 | }, 38 | "2": { 39 | "name": "text.html.markdown.journal.task.open.marker" 40 | }, 41 | "3": { 42 | "name": "text.html.markdown.journal.task.open.keyword" 43 | }, 44 | "4": { 45 | "name": "text.html.markdown.journal.task.open.text" 46 | } 47 | } 48 | }, 49 | { 50 | "match": "(-|\\*)\\s+(\\[[(?:x|X)\\s?]\\])\\s*(?i:((?:task|todo):?))?\\s(.*)", 51 | "name": "text.html.markdown.journal.task.completed", 52 | "captures": { 53 | "1": { 54 | "name": "text.html.markdown.journal.task.completed.bullet" 55 | }, 56 | "2": { 57 | "name": "text.html.markdown.journal.task.completed.marker" 58 | }, 59 | "3": { 60 | "name": "text.html.markdown.journal.task.completed.keyword" 61 | }, 62 | "4": { 63 | "name": "text.html.markdown.journal.task.completed.text" 64 | } 65 | } 66 | }, 67 | { 68 | "match": "(-|\\*)\\s+(\\[[<|>]\\])\\s*(?i:((?:task|todo):?))?\\s(.*)", 69 | "name": "text.html.markdown.journal.task.shifted", 70 | "captures": { 71 | "1": { 72 | "name": "text.html.markdown.journal.task.shifted.bullet" 73 | }, 74 | "2": { 75 | "name": "text.html.markdown.journal.task.shifted.marker" 76 | }, 77 | "3": { 78 | "name": "text.html.markdown.journal.task.shifted.keyword" 79 | }, 80 | "4": { 81 | "name": "text.html.markdown.journal.task.shifted.text" 82 | } 83 | } 84 | }, 85 | { 86 | "match": "^(-|\\*)\\s+(?i:((?:memo|link|note):?))", 87 | "name": "text.html.markdown.journal.memo", 88 | "captures": { 89 | "1": { 90 | "name": "text.html.markdown.journal.memo.bullet" 91 | }, 92 | "2": { 93 | "name": "text.html.markdown.journal.memo.keyword" 94 | } 95 | } 96 | } 97 | ] 98 | } 99 | }, 100 | "scopeName": "text.markdown.journal" 101 | } -------------------------------------------------------------------------------- /src/actions/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 19 | 20 | 21 | export { Parser } from './parser'; 22 | export { Writer } from './writer'; 23 | export { Reader } from './reader'; 24 | export { Inject } from './inject'; 25 | -------------------------------------------------------------------------------- /src/actions/inject.d.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as J from '../.'; 3 | interface InlineString { 4 | position: vscode.Position; 5 | value: string; 6 | document: vscode.TextDocument; 7 | } 8 | export declare class Inject { 9 | ctrl: J.Util.Ctrl; 10 | constructor(ctrl: J.Util.Ctrl); 11 | /** 12 | * Adds a new memo or task to today's page. A memo/task is a one liner (entered in input box), 13 | * which can be used to quickly write down ToDos without leaving your current 14 | * document. 15 | * 16 | * @param {vscode.TextDocument} doc 17 | * @param {J.Model.Input} input 18 | * @returns {Q.Promise} 19 | * @memberof Inject 20 | */ 21 | injectInput(doc: vscode.TextDocument, input: J.Model.Input): Promise; 22 | /** 23 | * Writes content at the location configured in the Inline Template (the "after"-flag). If no after is present, 24 | * content will be injected after the header 25 | * 26 | * @param {vscode.TextDocument} doc 27 | * @param {J.Extension.InlineTemplate} tpl 28 | * @param {...string[][]} values 29 | * @param {number} multiple number of edits which are to be expected (with the same template) to collect and avoid concurrent edits 30 | * @returns {Q.Promise} 31 | * @memberof Inject 32 | * 33 | * Updates: Fix for #55, always make sure there is a linebreak between the header and the injected text to stay markdown compliant 34 | */ 35 | private buildInlineString; 36 | /** 37 | * Injects a string into the given position within the given document. 38 | * 39 | * @param doc the vscode document 40 | * @param content the string which is to be injected 41 | * @param position the position where we inject the string 42 | */ 43 | injectString(doc: vscode.TextDocument, content: string, position: vscode.Position): Promise; 44 | /** 45 | * Injects the string at the given position. 46 | * 47 | * @param content the @see InlineString to be injected 48 | * @param other additional InlineStrings 49 | * 50 | */ 51 | injectInlineString(content: InlineString, ...other: InlineString[]): Promise; 52 | private formatContent; 53 | /** 54 | * Injects the given string as header (first line of file) 55 | * 56 | * @param {vscode.TextDocument} doc the input file 57 | * @param {string} content the string to be injected as header 58 | * @returns {Q.Promise} the updated document 59 | * @memberOf Inject 60 | */ 61 | injectHeader(doc: vscode.TextDocument, content: string): Promise; 62 | /** 63 | * Builds the content of newly created notes file using the (scoped) configuration and the user input. 64 | *1 65 | * @param {J.Model.Input} input what the user has entered 66 | * @returns {Q.Promise} the built content 67 | * @memberof Inject 68 | */ 69 | formatNote(input: J.Model.Input): Promise; 70 | /** 71 | * Injects a reference to a file associated with the given document. The reference location can be configured in the template (after-flag) 72 | * @param doc the document which we will inject into 73 | * @param file the referenced path 74 | */ 75 | private buildReference; 76 | /** 77 | * Checks for the given text document if it contains references to notes (and if there are notes in the associated folders) 78 | * It compares the two lists and creates (or deletes) any missing links 79 | * 80 | * @param doc 81 | */ 82 | injectAttachementLinks(doc: vscode.TextDocument, date: Date): Promise; 83 | } 84 | export {}; 85 | -------------------------------------------------------------------------------- /src/actions/parser.d.ts: -------------------------------------------------------------------------------- 1 | import * as J from '../.'; 2 | /** 3 | * Helper Methods to interpret the input strings 4 | */ 5 | export declare class Parser { 6 | ctrl: J.Util.Ctrl; 7 | today: Date; 8 | private expr; 9 | private scopeExpression; 10 | constructor(ctrl: J.Util.Ctrl); 11 | /** 12 | * Returns the file path for a given input. If the input includes a scope classifier ("#scope"), the path will be altered 13 | * accordingly (depending on the configuration of the scope). 14 | * 15 | * @param {string} input the input entered by the user 16 | * @returns {Q.Promise} the path to the new file 17 | * @memberof JournalCommands 18 | * 19 | */ 20 | resolveNotePathForInput(input: J.Model.Input, scopeId?: string): Promise; 21 | /** 22 | * Takes a string and separates the flag, date and text 23 | * 24 | * @param {string} inputString the value to be parsed 25 | * @returns {Promise} the resolved input object 26 | * @memberof Parser 27 | */ 28 | parseInput(inputString: string): Promise; 29 | } 30 | -------------------------------------------------------------------------------- /src/actions/parser.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 'use strict'; 19 | 20 | import * as J from '../.'; 21 | import * as Path from 'path'; 22 | 23 | import { SCOPE_DEFAULT } from '../ext'; 24 | 25 | /** 26 | * Helper Methods to interpret the input strings 27 | */ 28 | export class Parser { 29 | public today: Date; 30 | 31 | constructor(public ctrl: J.Util.Ctrl) { 32 | this.today = new Date(); 33 | } 34 | 35 | /** 36 | * Returns the file path for a given input. If the input includes a scope classifier ("#scope"), the path will be altered 37 | * accordingly (depending on the configuration of the scope). 38 | * 39 | * @param {string} input the input entered by the user 40 | * @returns {Q.Promise} the path to the new file 41 | * @memberof JournalCommands 42 | * 43 | */ 44 | public async resolveNotePathForInput(input: J.Model.Input, scopeId?: string): Promise { 45 | 46 | 47 | 48 | return new Promise((resolve, reject) => { 49 | this.ctrl.logger.trace("Entering resolveNotePathForInput() in actions/parser.ts"); 50 | 51 | // Unscoped Notes are always created in today's folder 52 | let date = new Date(); 53 | let path: string = ""; 54 | input.scope = SCOPE_DEFAULT; 55 | 56 | // purge all tags from filename 57 | 58 | // all tags are filtered out. tags representing scopes are recognized here for resolving the note path. 59 | input.text.match(/#\w+\s/g)?.forEach(tag => { 60 | if(J.Util.isNullOrUndefined(tag) || tag!.length === 0) {return;} 61 | 62 | this.ctrl.logger.trace("Tags in input string: "+tag); 63 | 64 | // remove from value 65 | input.tags.push(tag.trim().substring(0, tag.length-1)); 66 | input.text = input.text.replace(tag, " "); 67 | 68 | // identify scope, input is #tag 69 | this.ctrl.logger.trace("Scopes defined in configuration: "+this.ctrl.config.getScopes()); 70 | let scope: string | undefined = this.ctrl.config.getScopes().filter((name: string) => name === tag.trim().substring(1, tag.length)).pop(); 71 | 72 | 73 | if(J.Util.isNotNullOrUndefined(scope) && scope!.length > 0) { 74 | input.scope = scope!; 75 | } 76 | 77 | this.ctrl.logger.trace("Identified scope in input: "+input.scope); 78 | 79 | }); 80 | 81 | 82 | let inputForFileName: string = J.Util.normalizeFilename(input.text); 83 | 84 | Promise.all([ 85 | this.ctrl.config.getNotesFilePattern(date, inputForFileName, input.scope), 86 | this.ctrl.config.getResolvedNotesPath(date, input.scope), 87 | ]) 88 | 89 | .then(([fileTemplate, pathTemplate]) => { 90 | path = Path.join(pathTemplate.value!, fileTemplate.value!.trim()); 91 | this.ctrl.logger.trace("Resolved path for note is", path); 92 | resolve(path); 93 | }) 94 | .catch(error => { 95 | this.ctrl.logger.error(error); 96 | reject(error); 97 | 98 | }); 99 | 100 | }); 101 | 102 | } 103 | 104 | 105 | 106 | /** 107 | * Takes a string and separates the flag, date and text 108 | * 109 | * @param {string} inputString the value to be parsed 110 | * @returns {Promise} the resolved input object 111 | * @memberof Parser 112 | */ 113 | public async parseInput(inputString: string): Promise { 114 | let inputMatcher = new J.Provider.MatchInput(this.ctrl.logger, this.ctrl.config.getLocale()); 115 | return inputMatcher.parseInput(inputString); 116 | 117 | } 118 | 119 | 120 | 121 | 122 | } 123 | 124 | -------------------------------------------------------------------------------- /src/actions/reader.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import * as Path from 'path'; 3 | import * as vscode from 'vscode'; 4 | import * as J from '../'; 5 | import { JournalPageType } from '../ext/conf'; 6 | export interface FileEntry { 7 | path: string; 8 | name: string; 9 | scope: string; 10 | updateAt: number; 11 | createdAt: number; 12 | type: JournalPageType; 13 | } 14 | export interface BaseDirectory { 15 | path: string; 16 | scope: string; 17 | } 18 | /** 19 | * Anything which scans the files in the background goes here 20 | * 21 | */ 22 | export declare class Reader { 23 | ctrl: J.Util.Ctrl; 24 | constructor(ctrl: J.Util.Ctrl); 25 | /** 26 | * Loads previous entries. This method is async and is called in combination with the sync method (which uses the threshold) 27 | * 28 | * Update: ignore threshold 29 | * 30 | * @returns {Q.Promise<[string]>} 31 | * @memberof Reader 32 | */ 33 | getPreviouslyAccessedFiles(thresholdInMs: number, callback: Function, picker: any, type: JournalPageType, directories: BaseDirectory[]): Promise; 34 | getPreviouslyAccessedFilesSync(thresholdInMs: number, directories: BaseDirectory[]): Promise; 35 | /** 36 | * Tries to infer the file type from the path by matching against the configured patterns 37 | * @param entry 38 | */ 39 | inferType(entry: Path.ParsedPath): JournalPageType; 40 | /** 41 | * Scans journal directory and scans for notes 42 | * 43 | * Update: Removed age threshold, take everything 44 | * Update: switched to async with readdir 45 | * 46 | * See https://medium.com/@allenhwkim/nodejs-walk-directory-f30a2d8f038f 47 | * @param dir 48 | * @param callback 49 | */ 50 | private walkDir; 51 | private walkDirSync; 52 | checkDirectory(d: Date, entries: string[]): Promise; 53 | /** 54 | * Returns a list of all local files referenced in the given document. 55 | * 56 | * @param {vscode.TextDocument} doc the current journal entry 57 | * @returns {Q.Promise} an array with all references in the current journal page 58 | * @memberof Reader 59 | */ 60 | getReferencedFiles(doc: vscode.TextDocument): Promise; 61 | getFilesInNotesFolderAllScopes(doc: vscode.TextDocument, date: Date): Promise; 62 | /** 63 | * Returns a list of files sitting in the notes folder for the current document (has to be a journal page) 64 | * 65 | * By making the notes folder configurable, we cannot differentiate anymore by path. We always find (and inject all notes). We therefore also check the last modification date of the file itself 66 | * 67 | * @param {vscode.TextDocument} doc the current journal entry 68 | * @returns {Q.Promise} an array with all files sitting in the directory associated with the current journal page 69 | * @memberof Reader 70 | */ 71 | getFilesInNotesFolder(doc: vscode.TextDocument, date: Date, scope: string): Promise; 72 | /** 73 | * Creates or loads a note 74 | * 75 | * @param {string} path 76 | * @param {string} content 77 | * @returns {Promise} 78 | * @memberof Writer 79 | */ 80 | loadNote(path: string, content: string): Promise; 81 | /** 82 | * Returns the page for a day with the given input. If the page doesn't exist yet, 83 | * it will be created (with the current date as header) 84 | * 85 | * @param {input} input with offset 0 is today, -1 is yesterday 86 | * @returns {Q.Promise} the document 87 | * @memberof Reader 88 | */ 89 | loadEntryForInput(input: J.Model.Input): Promise; 90 | /** 91 | * Converts given path and filename into a full path. 92 | * @param pathname 93 | * @param filename 94 | */ 95 | private resolvePath; 96 | /** 97 | * Loads the journal entry for the given date. If no entry exists, promise is rejected with the invalid path 98 | * 99 | * @param {Date} date the date for the entry 100 | * @returns {Q.Promise} the document 101 | * @throws {string} error message 102 | * @memberof Reader 103 | */ 104 | loadEntryForDate(date: Date): Promise; 105 | } 106 | -------------------------------------------------------------------------------- /src/actions/reader.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 19 | 'use strict'; 20 | 21 | import * as vscode from 'vscode'; 22 | import * as J from '..'; 23 | 24 | export class Reader { 25 | constructor(public ctrl: J.Util.Ctrl) { 26 | } 27 | 28 | 29 | /** 30 | * Returns the page for a day with the given input. If the page doesn't exist yet, 31 | * it will be created (with the current date as header) 32 | * 33 | * @param {input} input with offset 0 is today, -1 is yesterday 34 | * @returns {Q.Promise} the document 35 | * @memberof Reader 36 | */ 37 | public async loadEntryForInput(input: J.Model.Input): Promise { 38 | 39 | if (input.hasOffset()) { 40 | return this.loadEntryForDay(input.generateDate()); 41 | } 42 | if (input.hasWeek()) { 43 | return this.loadEntryForWeek(input.week); 44 | } 45 | throw Error("Neither offset nor week are defined in input, we abort."); 46 | 47 | } 48 | 49 | 50 | /** 51 | * Loads the weekly page for the given week number (of the year) 52 | * @param week the week of the current year 53 | */ 54 | public async loadEntryForWeek(week: Number): Promise { 55 | return new Promise((resolve, reject) => { 56 | this.ctrl.logger.trace("Entering loadEntryForWeek() in actions/reader.ts for week " + week); 57 | 58 | let path: string = ""; 59 | 60 | Promise.all([ 61 | this.ctrl.config.getWeekPathPattern(week), 62 | this.ctrl.config.getWeekFilePattern(week) 63 | 64 | ]).then(([pathname, filename]) => { 65 | path = J.Util.resolvePath(pathname.value!, filename.value!); 66 | return this.ctrl.ui.openDocument(path); 67 | 68 | }).catch((reason: any) => { 69 | if (reason instanceof Error) { 70 | if (!reason.message.startsWith("cannot open file:")) { 71 | this.ctrl.logger.printError(reason); 72 | reject(reason); 73 | } 74 | } 75 | return this.ctrl.writer.createWeeklyForPath(path, week); 76 | 77 | }).then((_doc: vscode.TextDocument) => { 78 | this.ctrl.logger.debug("loadEntryForWeek() - Loaded file in:", _doc.uri.toString()); 79 | resolve(_doc); 80 | 81 | }).catch((error: Error) => { 82 | this.ctrl.logger.printError(error); 83 | reject("Failed to load entry for week: " + week); 84 | }); 85 | }); 86 | } 87 | 88 | 89 | /** 90 | * Loads the journal entry for the given date. If no entry exists, promise is rejected with the invalid path 91 | * 92 | * @param {Date} date the date for the entry 93 | * @returns {Q.Promise} the document 94 | * @throws {string} error message 95 | * @memberof Reader 96 | */ 97 | public async loadEntryForDay(date: Date): Promise { 98 | 99 | return new Promise((resolve, reject) => { 100 | if (J.Util.isNullOrUndefined(date) || date!.toString().includes("Invalid")) { 101 | reject("Invalid date"); 102 | return; 103 | } 104 | 105 | this.ctrl.logger.trace("Entering loadEntryforDate() in actions/reader.ts for date " + date.toISOString()); 106 | 107 | let path: string = ""; 108 | 109 | Promise.all([ 110 | this.ctrl.config.getResolvedEntryPath(date), 111 | this.ctrl.config.getEntryFilePattern(date) 112 | 113 | ]).then(([pathname, filename]) => { 114 | path = J.Util.resolvePath(pathname.value!, filename.value!); 115 | return this.ctrl.ui.openDocument(path); 116 | 117 | 118 | }).catch((reason: any) => { 119 | if (reason instanceof Error) { 120 | if (!reason.message.startsWith("cannot open file:") && !reason.message.startsWith("cannot open vscode-remote:")) { 121 | this.ctrl.logger.printError(reason); 122 | reject(reason); 123 | } 124 | } 125 | return this.ctrl.writer.createEntryForPath(path, date); 126 | 127 | }).then((_doc: vscode.TextDocument) => { 128 | this.ctrl.logger.debug("loadEntryForDate() - Loaded file in:", _doc.uri.toString()); 129 | new J.Provider.SyncNoteLinks(this.ctrl).injectAttachementLinks(_doc, date) 130 | .finally(() => 131 | // do nothing 132 | this.ctrl.logger.trace("Scanning notes completed") 133 | ); 134 | resolve(_doc); 135 | 136 | }).catch((error: Error) => { 137 | this.ctrl.logger.printError(error); 138 | reject("Failed to load entry for date: " + date.toDateString()); 139 | 140 | }); 141 | 142 | }); 143 | } 144 | 145 | } 146 | 147 | 148 | -------------------------------------------------------------------------------- /src/actions/writer.d.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as J from '../.'; 3 | /** 4 | * Anything which modifies the text documents goes here. 5 | * 6 | */ 7 | export declare class Writer { 8 | ctrl: J.Util.Ctrl; 9 | constructor(ctrl: J.Util.Ctrl); 10 | saveDocument(doc: vscode.TextDocument): Promise; 11 | /** 12 | * Adds the given content at the start of text document 13 | */ 14 | writeHeader(doc: vscode.TextDocument, content: string): Promise; 15 | /** 16 | * Creates and saves a new file (with configured content) for a journal entry and returns the associated TextDocument 17 | * 18 | * @param {string} path 19 | * @param {Date} date 20 | * @returns {Promise} 21 | * @memberof Writer 22 | */ 23 | createEntryForPath(path: string, date: Date): Promise; 24 | /** 25 | * Creates a new file, adds the given content, saves it and opens it. 26 | * 27 | * @param {string} path The path in of the new file 28 | * @param {string} content The preconfigured content of the new file 29 | * @returns {Promise} The new document associated with the file 30 | */ 31 | createSaveLoadTextDocument(path: string, content: string): Promise; 32 | } 33 | -------------------------------------------------------------------------------- /src/actions/writer.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 19 | 'use strict'; 20 | 21 | import * as vscode from 'vscode'; 22 | import * as J from '../.'; 23 | 24 | /** 25 | * Anything which modifies the text documents goes here. 26 | * 27 | */ 28 | export class Writer { 29 | 30 | 31 | constructor(public ctrl: J.Util.Ctrl) { 32 | } 33 | 34 | public async saveDocument(doc: vscode.TextDocument): Promise { 35 | return new Promise((resolve, reject) => { 36 | doc.save() 37 | .then( 38 | _success => resolve(doc), 39 | _error => reject(_error) 40 | ); 41 | }); 42 | } 43 | 44 | 45 | /** 46 | * Adds the given content at the start of text document 47 | */ 48 | public async writeHeader(doc: vscode.TextDocument, content: string): Promise { 49 | return this.ctrl.inject.injectString(doc, content, new vscode.Position(0, 0)); 50 | } 51 | 52 | 53 | 54 | 55 | /** 56 | * Creates and saves a new file (with configured content) for a journal entry and returns the associated TextDocument 57 | * 58 | * @param {string} path 59 | * @param {Date} date 60 | * @returns {Promise} 61 | * @memberof Writer 62 | */ 63 | public async createEntryForPath(path: string, date: Date): Promise { 64 | 65 | 66 | return new Promise((resolve, reject) => { 67 | this.ctrl.logger.trace("Entering createEntryForPath() in ext/writer.ts for path: ", path); 68 | 69 | this.ctrl.config.getEntryTemplate(date) 70 | .then((tpl: J.Model.HeaderTemplate) => { 71 | 72 | // TODO: make this configurable (for now we keep the format hardcorded) 73 | // return J.Util.formatDate(date, tpl.template, this.ctrl.config.getLocale()); 74 | return tpl.value || ""; 75 | }) 76 | .then((content) => { 77 | return this.ctrl.writer.createSaveLoadTextDocument(path, content); 78 | }) 79 | .then((doc: vscode.TextDocument) => resolve(doc)) 80 | .catch(() => reject(path)); 81 | }); 82 | } 83 | 84 | /** 85 | * Creates and saves a new file (with configured content) for a weekly entry and returns the associated TextDocument 86 | * 87 | * @param {string} path 88 | * @param {Number} week 89 | * @returns {Promise} 90 | * @memberof Writer 91 | */ 92 | public async createWeeklyForPath(path: string, week: Number): Promise { 93 | 94 | 95 | return new Promise((resolve, reject) => { 96 | this.ctrl.logger.trace("Entering createWeeklyForPath() in ext/writer.ts for path: ", path); 97 | 98 | this.ctrl.config.getWeeklyTemplate(week) 99 | .then((tpl: J.Model.HeaderTemplate) => { 100 | return tpl.value || ""; 101 | }) 102 | .then((content) => { 103 | return this.ctrl.writer.createSaveLoadTextDocument(path, content); 104 | }) 105 | .then((doc: vscode.TextDocument) => resolve(doc)) 106 | .catch(() => reject(path)); 107 | }); 108 | } 109 | 110 | /** 111 | * Creates a new file, adds the given content, saves it and opens it. 112 | * 113 | * @param {string} path The path in of the new file 114 | * @param {string} content The preconfigured content of the new file 115 | * @returns {Promise} The new document associated with the file 116 | */ 117 | public async createSaveLoadTextDocument(path: string, content: string): Promise { 118 | 119 | return new Promise((resolve, reject) => { 120 | this.ctrl.logger.trace("Entering createSaveLoadTextDocument() in ext/writer.ts for path: ", path); 121 | let uri: vscode.Uri = vscode.Uri.parse('untitled:' + path); 122 | 123 | this.ctrl.ui.openDocument(uri) 124 | .then((doc: vscode.TextDocument) => this.ctrl.inject.injectHeader(doc, content)) 125 | .then((doc: vscode.TextDocument) => this.ctrl.ui.saveDocument(doc)) 126 | .then((doc: vscode.TextDocument) => { 127 | if (doc.isUntitled) { 128 | // open it again, this time not as untitled (since it has been saved) 129 | vscode.workspace.openTextDocument(vscode.Uri.file(path)) 130 | .then(doc => { 131 | this.ctrl.logger.debug("Opened new file with name: ", doc.fileName); 132 | resolve(doc); 133 | }, onRejected => reject(onRejected)); 134 | 135 | 136 | } else { 137 | resolve(doc); 138 | } 139 | }, 140 | failed=> { 141 | this.ctrl.logger.error("Failed to create file: ", uri.toString(), " with reason: ", failed); 142 | reject(failed); 143 | } 144 | ) 145 | .catch(onRejected => { 146 | reject(onRejected); 147 | }); 148 | }); 149 | 150 | 151 | 152 | 153 | } 154 | 155 | } -------------------------------------------------------------------------------- /src/ext/commands.d.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as J from '../.'; 3 | export interface Commands { 4 | processInput(): Promise; 5 | showNote(): Promise; 6 | showEntry(offset: number): Promise; 7 | loadJournalWorkspace(): Promise; 8 | printSum(): Promise; 9 | printDuration(): Promise; 10 | printTime(): Promise; 11 | runTestFeature(): Promise; 12 | } 13 | export declare class JournalCommands implements Commands { 14 | ctrl: J.Util.Ctrl; 15 | /** 16 | * 17 | */ 18 | constructor(ctrl: J.Util.Ctrl); 19 | /** 20 | * Opens the editor for a specific day. Supported values are explicit dates (in ISO format), 21 | * offsets (+ or - as prefix and 0) and weekdays (next wednesday) 22 | * 23 | * Update: supports much more now 24 | */ 25 | processInput(): Promise; 26 | /** 27 | * Called by command 'Journal:open'. Opens a new windows with the Journal base directory as root. 28 | * 29 | * @returns {Q.Promise} 30 | * @memberof JournalCommands 31 | */ 32 | loadJournalWorkspace(): Promise; 33 | /** 34 | * Prints the sum of the selected numbers in the current editor at the selection location 35 | */ 36 | printSum(): Promise; 37 | /** 38 | * Prints the current time at the cursor postion 39 | * 40 | * @returns {Q.Promise} 41 | * @memberof JournalCommands 42 | */ 43 | printTime(): Promise; 44 | /** 45 | * Called by command 'Journal:printDuration'. Requires three selections (three active cursors) 46 | * in current document. It identifies which of the selections are times (in the format hh:mm 47 | * or glued like "1223") and where to print the duration (in decimal form). 48 | * For now the duration is always printing hours. 49 | * 50 | * @returns {Q.Promise} 51 | * @memberof JournalCommands 52 | */ 53 | printDuration(): Promise; 54 | /** 55 | * Implements commands "yesterday", "today", "yesterday", where the input is predefined (no input box appears) 56 | * @param offset 57 | */ 58 | showEntry(offset: number): Promise; 59 | /** 60 | * Creates a new file in a subdirectory with the current day of the month as name. 61 | * Shows the file to let the user start adding notes right away. 62 | * 63 | * @returns {Q.Promise} 64 | * @memberof JournalCommands 65 | */ 66 | showNote(): Promise; 67 | runTestFeature(): Promise; 68 | showError(error: string | Promise | Error): void; 69 | private showErrorInternal; 70 | /** 71 | * Expects any user input from the magic input and either opens the file or creates it. 72 | * @param input 73 | */ 74 | private loadPageForInput; 75 | } 76 | -------------------------------------------------------------------------------- /src/ext/index.d.ts: -------------------------------------------------------------------------------- 1 | export { VSCode } from './vscode'; 2 | export { JournalCodeLensProvider } from './vscode-codelens'; 3 | export { Commands, JournalCommands } from './commands'; 4 | export { Startup } from './startup'; 5 | export { Configuration, InlineTemplate, ScopedTemplate, HeaderTemplate, SCOPE_DEFAULT } from './conf'; 6 | -------------------------------------------------------------------------------- /src/ext/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 19 | // export { JournalCompletionProvider, JournalActionsProvider } from './provider'; 20 | 21 | export { 22 | getInputDetailsStringForEntry, 23 | getInputDetailsStringForMemo, 24 | getInputDetailsStringForTask, 25 | getInputDetailsStringForTaskInWeek, 26 | getInputDetailsStringForWeekly, 27 | getInputDetailsTranslation, 28 | getInputLabelTranslation, 29 | getPickDetailsTranslation 30 | 31 | } from './translations'; 32 | export { Dialogues } from './dialogues'; 33 | export { JournalCodeLensProvider } from './vscode-codelens'; 34 | export { Startup } from './startup'; 35 | export { 36 | Configuration, 37 | SCOPE_DEFAULT, 38 | } from './conf'; 39 | -------------------------------------------------------------------------------- /src/ext/startup.d.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as J from '..'; 3 | export declare class Startup { 4 | context: vscode.ExtensionContext; 5 | config: vscode.WorkspaceConfiguration; 6 | /** 7 | * 8 | */ 9 | constructor(context: vscode.ExtensionContext, config: vscode.WorkspaceConfiguration); 10 | initialize(): Promise; 11 | registerLoggingChannel(ctrl: J.Util.Ctrl, context: vscode.ExtensionContext): Promise; 12 | registerCodeLens(ctrl: J.Util.Ctrl, context: vscode.ExtensionContext): Promise; 13 | registerCommands(ctrl: J.Util.Ctrl, context: vscode.ExtensionContext): Promise; 14 | /** 15 | * Sets default syntax highlighting settings on startup, we try to differentiate between dark and light themes 16 | * 17 | * @param {J.Util.Ctrl} ctrl 18 | * @param {vscode.ExtensionContext} context 19 | * @returns {Q.Promise} 20 | * @memberof Startup 21 | */ 22 | registerSyntaxHighlighting(ctrl: J.Util.Ctrl): Promise; 23 | } 24 | -------------------------------------------------------------------------------- /src/ext/translations.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as J from '..'; 3 | 4 | import messages from './messages.json'; 5 | 6 | type Messages = { 7 | [locale: string]: { 8 | [category: string]: { 9 | [code: string]: string; 10 | }; 11 | }; 12 | }; 13 | 14 | 15 | 16 | /** 17 | * Fetches a translation for the given category and code. 18 | * Falls back to English if the translation does not exist in the user's locale. 19 | * 20 | * @param category - The translation category (e.g., "descriptions", "labels", "picks", "details"). 21 | * @param code - The code that identifies the specific translation string. 22 | * @returns The translated string, or the English fallback if not found. 23 | */ 24 | function getTranslation(category: string, code: string): string { 25 | const translations: Messages = messages; 26 | const locale = vscode.env.language.substring(0, 2); // Get the first two letters of the locale (e.g., "en", "de") 27 | 28 | // Attempt to retrieve the translation from the user's locale 29 | const translationCategory = translations[locale]?.[category] || translations['en']?.[category]; // Default to English if locale not found 30 | 31 | // Return the specific translation code from the user's locale or fallback to the English version 32 | return translationCategory[code] || translations['en'][category][code]; 33 | } 34 | 35 | /** 36 | * Returns the description based on the code provided. 37 | * Always returns a fallback in English if no translation is found for the user's locale. 38 | * 39 | * @param code - The code of the description string. 40 | * @returns The description string. 41 | */ 42 | export function getInputDetailsTranslation(code: number): string { 43 | return getTranslation('descriptions', code.toString()); 44 | } 45 | 46 | /** 47 | * Returns the label based on the code provided. 48 | * Always returns a fallback in English if no translation is found for the user's locale. 49 | * 50 | * @param code - The code of the label string. 51 | * @returns The label string. 52 | */ 53 | export function getInputLabelTranslation(code: number): string { 54 | return getTranslation('labels', code.toString()); 55 | } 56 | 57 | /** 58 | * Returns the pick details based on the code provided. 59 | * Always returns a fallback in English if no translation is found for the user's locale. 60 | * 61 | * @param code - The code of the pick details string. 62 | * @returns The pick details string. 63 | */ 64 | export function getPickDetailsTranslation(code: number): string { 65 | return getTranslation('picks', code.toString()); 66 | } 67 | 68 | /** 69 | * Returns the task details string for a specific day. 70 | * Always returns a fallback in English if no translation is found for the user's locale. 71 | * 72 | * @param dayAsString - The day string (e.g., "Monday", "Tuesday"). 73 | * @returns The task details string. 74 | */ 75 | export function getInputDetailsStringForTask(dayAsString: string): string { 76 | const translation = getTranslation('details', 'task'); 77 | return translation.replace("{day}", dayAsString); 78 | } 79 | 80 | /** 81 | * Returns the task details string for a specific week. 82 | * Always returns a fallback in English if no translation is found for the user's locale. 83 | * 84 | * @param weekAsNumber - The week number (e.g., "32"). 85 | * @returns The task details string for the specified week. 86 | */ 87 | export function getInputDetailsStringForTaskInWeek(weekAsNumber: number): string { 88 | const translation = getTranslation('details', 'taskInWeek'); 89 | return translation.replace("{week}", weekAsNumber.toString()); 90 | } 91 | 92 | /** 93 | * Returns the weekly details string for a specific week. 94 | * Always returns a fallback in English if no translation is found for the user's locale. 95 | * 96 | * @param week - The week number. 97 | * @returns The weekly details string. 98 | */ 99 | export function getInputDetailsStringForWeekly(week: number): string { 100 | const translation = getTranslation('details', 'weekly'); 101 | return translation.replace("{week}", week.toString()); 102 | } 103 | 104 | /** 105 | * Returns the entry details string for a specific day. 106 | * Always returns a fallback in English if no translation is found for the user's locale. 107 | * 108 | * @param dayAsString - The day string (e.g., "Monday", "Tuesday"). 109 | * @returns The entry details string. 110 | */ 111 | export function getInputDetailsStringForEntry(dayAsString: string): string { 112 | const translation = getTranslation('details', 'entry'); 113 | return translation.replace("{day}", dayAsString); 114 | } 115 | 116 | /** 117 | * Returns the memo details string for a specific day. 118 | * Always returns a fallback in English if no translation is found for the user's locale. 119 | * 120 | * @param dayAsString - The day string (e.g., "Monday", "Tuesday"). 121 | * @returns The memo details string. 122 | */ 123 | export function getInputDetailsStringForMemo(dayAsString: string): string { 124 | const translation = getTranslation('details', 'memo'); 125 | return translation.replace("{day}", dayAsString); 126 | } 127 | -------------------------------------------------------------------------------- /src/ext/vscode-codelens.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 'use strict'; 19 | 20 | import * as vscode from 'vscode'; 21 | import * as J from '../.'; 22 | 23 | export class JournalCodeLensProvider implements vscode.CodeLensProvider { 24 | private codeLenses: vscode.CodeLens[] = []; 25 | private ctrl: J.Util.Ctrl; 26 | 27 | private _onDidChangeCodeLenses: vscode.EventEmitter = new vscode.EventEmitter(); 28 | 29 | 30 | 31 | constructor(ctrl: J.Util.Ctrl) { 32 | this.ctrl = ctrl; 33 | 34 | vscode.workspace.onDidChangeConfiguration((_) => { 35 | this._onDidChangeCodeLenses.fire(); 36 | }); 37 | } 38 | 39 | async getRegex() : Promise { 40 | let template = await this.ctrl.config.getTaskInlineTemplate(); 41 | return new RegExp(template.after); 42 | } 43 | 44 | public async provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { 45 | this.codeLenses = []; 46 | const regex = await this.getRegex(); 47 | const text = document.getText(); 48 | const matches = regex.exec(text); 49 | 50 | if(matches !== null) { 51 | const line = document.lineAt(document.positionAt(matches.index).line); 52 | const indexOf = line.text.indexOf(matches[0]); 53 | const position = new vscode.Position(line.lineNumber, indexOf); 54 | const range = document.getWordRangeAtPosition(position, regex); 55 | if (range) { 56 | this.codeLenses.push(new vscode.CodeLens(range)); 57 | } 58 | } 59 | 60 | return this.codeLenses; 61 | } 62 | 63 | public resolveCodeLens?(codeLens: vscode.CodeLens, token: vscode.CancellationToken): 64 | vscode.CodeLens | Thenable { 65 | 66 | 67 | codeLens.command = { 68 | title: "Collect all tasks", 69 | tooltip: "Collect and modify tasks from this and previous journal entries", 70 | command: "codelens-sample.codelensAction", 71 | arguments: ["Argument 1", false] 72 | }; 73 | return codeLens; 74 | } 75 | } -------------------------------------------------------------------------------- /src/ext/vscode.d.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as J from './..'; 3 | import { JournalPageType } from './conf'; 4 | import { FileEntry } from '../actions/reader'; 5 | interface DecoratedQuickPickItem extends vscode.QuickPickItem { 6 | parsedInput?: J.Model.Input; 7 | replace?: boolean; 8 | path: string; 9 | pickItem?: JournalPageType; 10 | fileEntry?: FileEntry; 11 | } 12 | /** 13 | * Anything which extends Visual Studio Code goes here 14 | * 15 | */ 16 | export declare class VSCode { 17 | ctrl: J.Util.Ctrl; 18 | constructor(ctrl: J.Util.Ctrl); 19 | /** 20 | * 21 | */ 22 | getUserInputWithValidation(): Promise; 23 | private generateDescription; 24 | private generateDetail; 25 | /** 26 | * Callback function for filewalker to add an item to our quickpick list 27 | * 28 | * @param fe 29 | */ 30 | addItem(fe: FileEntry, input: vscode.QuickPick, type: JournalPageType): void; 31 | /** 32 | * 33 | * @param type 34 | */ 35 | pickItem(type: JournalPageType): Promise; 36 | /** 37 | * Simple method to have Q Promise for vscode API call to get user input 38 | */ 39 | getUserInput(tip: string): Promise; 40 | saveDocument(textDocument: vscode.TextDocument): Promise; 41 | openDocument(path: string | vscode.Uri): Promise; 42 | /** 43 | * Shows the given document in Visual Studio Code 44 | * 45 | * @param {vscode.TextDocument} textDocument the document to show 46 | * @returns {vscode.TextEditor} the associated text editor 47 | * @memberOf VsCode 48 | */ 49 | showDocument(textDocument: vscode.TextDocument): Promise; 50 | } 51 | export {}; 52 | -------------------------------------------------------------------------------- /src/extension.d.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as J from './'; 3 | export declare var journalStartup: J.Extension.Startup; 4 | export declare var journalConfiguration: J.Extension.Configuration; 5 | export declare function activate(context: vscode.ExtensionContext): { 6 | getJournalConfiguration(): J.Extension.Configuration; 7 | }; 8 | export declare function deactivate(): void; 9 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 19 | 'use strict'; 20 | 21 | 22 | import * as vscode from 'vscode'; 23 | import * as J from './'; 24 | 25 | export var journalStartup: J.Extension.Startup; 26 | export var journalConfiguration: J.Extension.Configuration; 27 | 28 | export function activate(context: vscode.ExtensionContext) { 29 | 30 | try { 31 | console.time("startup"); 32 | 33 | let config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("journal"); 34 | journalStartup = new J.Extension.Startup(config); 35 | journalStartup.run(context); 36 | 37 | // return public API of this extension 38 | return { 39 | getJournalConfiguration() { 40 | return journalStartup.getJournalController().config; 41 | } 42 | }; 43 | } catch (error) { 44 | console.error("Failed to start journal extension with reason: ", error); 45 | throw error; 46 | } 47 | 48 | } 49 | 50 | 51 | // this method is called when your extension is deactivated 52 | export function deactivate() { 53 | } 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as Extension from './ext'; 2 | import * as Model from './model'; 3 | import * as Actions from './actions'; 4 | import * as Util from './util'; 5 | import * as Features from './features'; 6 | export { Model, Extension, Actions, Util, Features }; 7 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 19 | 20 | import * as Extension from './ext'; 21 | import * as Model from './model'; 22 | import * as Actions from './actions'; 23 | import * as Util from './util'; 24 | import * as Provider from './provider'; 25 | 26 | // export { Commons, Extension, Model, Actions, Journal, JournalMain }; 27 | export {Model, Extension, Actions, Util, Provider}; 28 | -------------------------------------------------------------------------------- /src/model/config.ts: -------------------------------------------------------------------------------- 1 | export enum JournalPageType { 2 | note, 3 | entry, 4 | attachement 5 | } 6 | 7 | export interface ScopedTemplate { 8 | name?: string; 9 | scope?: string; 10 | template: string; 11 | value?: string; 12 | } 13 | 14 | 15 | 16 | export interface FilePattern extends ScopedTemplate { 17 | type: JournalPageType; 18 | } 19 | 20 | export interface PathTemplate extends ScopedTemplate { 21 | type: JournalPageType; 22 | } 23 | 24 | export interface HeaderTemplate extends ScopedTemplate { 25 | } 26 | 27 | export interface InlineTemplate extends ScopedTemplate { 28 | after: string; 29 | } -------------------------------------------------------------------------------- /src/model/files.ts: -------------------------------------------------------------------------------- 1 | 2 | import * as J from './..'; 3 | 4 | export interface FileEntry { 5 | path: string; 6 | name: string; 7 | scope?: string; 8 | updateAt: number; 9 | createdAt: number; 10 | accessedAt: number; 11 | type?: J.Model.JournalPageType; 12 | } -------------------------------------------------------------------------------- /src/model/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 19 | 20 | export { HeaderTemplate, InlineTemplate, JournalPageType, ScopedTemplate } from './config'; 21 | export { InlineString } from './inline'; 22 | export { Input, SelectedInput, NoteInput } from './input'; 23 | export { TemplateInfo, ScopeDirectory } from './templates'; 24 | export { FileEntry } from "./files"; 25 | export { DecoratedQuickPickItem, TimedQuickPick } from "./vscode"; 26 | 27 | -------------------------------------------------------------------------------- /src/model/inline.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | export interface InlineString { 4 | position: vscode.Position; 5 | value: string; 6 | document: vscode.TextDocument; 7 | 8 | } -------------------------------------------------------------------------------- /src/model/input.d.ts: -------------------------------------------------------------------------------- 1 | export declare class Input { 2 | private _offset; 3 | private _flags; 4 | private _text; 5 | private _scope; 6 | private _tags; 7 | constructor(offset?: number); 8 | /** 9 | * Getter offset 10 | * @return {number } 11 | */ 12 | get offset(): number; 13 | get tags(): string[]; 14 | /** 15 | * Getter flags 16 | * @return {string } 17 | */ 18 | get flags(): string; 19 | /** 20 | * Getter text 21 | * @return {string } 22 | */ 23 | get text(): string; 24 | /** 25 | * Getter scope 26 | * @return {string } 27 | */ 28 | get scope(): string; 29 | /** 30 | * Setter offset 31 | * @param {number } value 32 | */ 33 | set offset(value: number); 34 | /** 35 | * Setter flags 36 | * @param {string } value 37 | */ 38 | set flags(value: string); 39 | /** 40 | * Setter text 41 | * @param {string } value 42 | */ 43 | set text(value: string); 44 | set tags(values: string[]); 45 | /** 46 | * Setter scope 47 | * @param {string } value 48 | */ 49 | set scope(value: string); 50 | hasMemo(): boolean; 51 | hasFlags(): boolean; 52 | hasOffset(): boolean; 53 | hasTask(): boolean; 54 | generateDate(): Date; 55 | } 56 | export declare class NoteInput extends Input { 57 | private _path; 58 | constructor(); 59 | get path(): string; 60 | set path(path: string); 61 | } 62 | export declare class SelectedInput extends Input { 63 | private _selected; 64 | private _path; 65 | get selected(): boolean; 66 | get path(): string; 67 | constructor(path: string); 68 | } 69 | -------------------------------------------------------------------------------- /src/model/input.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 19 | 20 | 'use strict'; 21 | 22 | import { isNullOrUndefined } from "../util/util"; 23 | 24 | export class Input { 25 | 26 | 27 | 28 | private _offset: number; 29 | private _flags: string = ""; 30 | private _text: string = ""; 31 | private _scope: string = ""; 32 | private _week: number; 33 | 34 | private _tags: string[] = []; 35 | 36 | 37 | 38 | constructor(offset?: number) { 39 | this._offset = (isNullOrUndefined(offset)) ? 0 : offset!; 40 | this._week = -1; 41 | } 42 | 43 | 44 | /** 45 | * Getter offset 46 | * @return {number } 47 | */ 48 | public get offset(): number { 49 | return this._offset; 50 | } 51 | 52 | public get tags(): string[] { 53 | return this._tags; 54 | } 55 | 56 | /** 57 | * Getter flags 58 | * @return {string } 59 | */ 60 | public get flags(): string { 61 | return this._flags; 62 | } 63 | 64 | /** 65 | * Getter text 66 | * @return {string } 67 | */ 68 | public get text(): string { 69 | return this._text; 70 | } 71 | 72 | /** 73 | * Getter scope 74 | * @return {string } 75 | */ 76 | public get scope(): string { 77 | return this._scope; 78 | } 79 | 80 | /** 81 | * Setter offset 82 | * @param {number } value 83 | */ 84 | public set offset(value: number ) { 85 | this._offset = value; 86 | } 87 | 88 | /** 89 | * Setter flags 90 | * @param {string } value 91 | */ 92 | public set flags(value: string ) { 93 | this._flags = value; 94 | } 95 | 96 | /** 97 | * Setter text 98 | * @param {string } value 99 | */ 100 | public set text(value: string ) { 101 | this._text = value; 102 | } 103 | 104 | public set tags(values: string[]) { 105 | this._tags = values; 106 | } 107 | 108 | /** 109 | * Setter scope 110 | * @param {string } value 111 | */ 112 | public set scope(value: string ) { 113 | this._scope = value; 114 | } 115 | 116 | /** 117 | * Return the week of year 118 | */ 119 | public get week(): number { 120 | return this._week; 121 | } 122 | public set week(value: number) { 123 | this._week = value; 124 | } 125 | 126 | 127 | public hasMemo(): boolean { 128 | return (this._text !== undefined) && this._text.length > 0; 129 | } 130 | 131 | public hasWeek() : boolean { 132 | return (this._week >= 0); 133 | } 134 | 135 | public hasFlags(): boolean { 136 | let res = (this._flags !== undefined) && (this._flags.length > 0); 137 | return res; 138 | } 139 | 140 | public hasOffset(): boolean { 141 | return !isNaN(this.offset) && this._week === -1; 142 | } 143 | 144 | public hasTask(): boolean { 145 | let matches: RegExpMatchArray | null = this.flags.match("task|todo"); 146 | return (matches !== null && matches.length > 0); 147 | } 148 | 149 | public hasText() { 150 | return this._text.length > 0; 151 | } 152 | 153 | public generateDate(): Date { 154 | let date = new Date(); 155 | date.setDate(date.getDate() + this.offset); 156 | return date; 157 | 158 | } 159 | 160 | 161 | 162 | } 163 | 164 | export class NoteInput extends Input { 165 | 166 | private _path: string = ""; 167 | 168 | constructor() { 169 | super(0); 170 | } 171 | 172 | public get path() {return this._path;} 173 | public set path( path: string ) {this._path = path;} 174 | 175 | } 176 | 177 | export class SelectedInput extends Input { 178 | 179 | private _selected: boolean = false; // if selected from quickpick 180 | private _path: string = ""; 181 | 182 | 183 | public get selected() {return this._selected;} 184 | public get path() {return this._path;} 185 | 186 | constructor(path: string) { 187 | super(0); 188 | this._selected = true; 189 | this._path = path; 190 | } 191 | 192 | 193 | 194 | 195 | } 196 | -------------------------------------------------------------------------------- /src/model/templates.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 19 | 20 | export interface TemplateInfo { 21 | template: string; 22 | after: string; 23 | } 24 | 25 | 26 | 27 | export interface ScopeDirectory { 28 | path: string; 29 | scope: string; 30 | 31 | 32 | } -------------------------------------------------------------------------------- /src/model/vscode.ts: -------------------------------------------------------------------------------- 1 | import { AnyARecord } from 'dns'; 2 | import * as vscode from 'vscode'; 3 | import * as J from '..'; 4 | 5 | export interface TimedQuickPick extends vscode.QuickPick { 6 | start?: number; 7 | } 8 | 9 | 10 | export interface DecoratedQuickPickItem extends vscode.QuickPickItem { 11 | parsedInput?: J.Model.Input; 12 | replace?: boolean; 13 | path: string; 14 | pickItem?: J.Model.JournalPageType; 15 | fileEntry?: J.Model.FileEntry; 16 | } 17 | 18 | export interface TextMateRule { 19 | scope: string; 20 | settings: any; 21 | } -------------------------------------------------------------------------------- /src/provider/InputMatcher.d.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from "../util/logger"; 2 | import { Input } from "../model/input"; 3 | export declare class InputMatcher { 4 | logger: Logger; 5 | today: Date; 6 | private scopeExpression; 7 | private expr; 8 | constructor(logger: Logger); 9 | /** 10 | * Takes a string and separates the flag, date and text 11 | * 12 | * @param {string} inputString the value to be parsed 13 | * @param {boolean} replaceSpecialCharacters if special characters like the # have to be normalized (e.g. for file names) 14 | * @returns {Q.Promise} the resolved input object 15 | * @memberof Parser 16 | */ 17 | parseInput(inputString: string): Promise; 18 | /** 19 | * If tags are present in the input string, extract them if these are configured scopes 20 | * 21 | * @private 22 | * @param {string[]} values 23 | * @returns {string} 24 | * @memberof Parser 25 | */ 26 | private extractTags; 27 | private extractText; 28 | private extractFlags; 29 | private extractOffset; 30 | private resolveOffsetString; 31 | private resolveShortcutString; 32 | /** 33 | * Resolves an ISO String and returns the offset to the current day 34 | * 35 | * @param inputString a date formatted as iso string, e.g. 06-22 36 | * @returns the offset to the current day 37 | */ 38 | private resolveISOString; 39 | /** 40 | * Resolves the weekday for a given string. Allowed strings are monday to friday. If a modifier is present 41 | * ("next" or "last"), it will return the according weekdey of last or next week. 42 | * 43 | * @param weekday the weekday as a string 44 | * @param mod next or last 45 | * @returns the offset to the current day as number 46 | */ 47 | private resolveWeekday; 48 | /** 49 | * Takes any given string as input and tries to compute the offset from today's date. 50 | * It translates something like "next wednesday" into "4" (if next wednesday is in four days). 51 | * 52 | * @param {string} value the string to be processed 53 | * @returns {Q.Promise} the resolved offeset 54 | * @memberof Parser 55 | */ 56 | private getExpression; 57 | } 58 | -------------------------------------------------------------------------------- /src/provider/codeactions/for-completed-tasks.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 'use strict'; 19 | 20 | import * as vscode from 'vscode'; 21 | import * as J from '../..'; 22 | 23 | 24 | /** 25 | * The complete task codelens is active for open tasks, e.g. '-[ ] some text' 26 | * 27 | * Once activated, it will 28 | * - close the task: '-[ ] some text' -> '-[x] some text' 29 | * - annotate the task with completion date: '-[x] some text (completed on 2021-05-12 at 12:12)' 30 | */ 31 | export class CompletedTaskActions implements vscode.CodeActionProvider { 32 | private ctrl: J.Util.Ctrl; 33 | private regex = new RegExp(/-\s{0,1}\[\s{0,2}x|X\s{0,2}\].*/g); 34 | 35 | 36 | public static readonly providedCodeActionKinds = [ 37 | vscode.CodeActionKind.QuickFix 38 | ]; 39 | 40 | 41 | constructor(ctrl: J.Util.Ctrl) { 42 | this.ctrl = ctrl; 43 | } 44 | 45 | public provideCodeActions(document: vscode.TextDocument, range: vscode.Range | vscode.Selection, context: vscode.CodeActionContext, token: vscode.CancellationToken): vscode.ProviderResult<(vscode.CodeAction | vscode.Command)[]> { 46 | try { 47 | if(! this.matchesExpression(document, range)) { 48 | return; 49 | } 50 | return Promise.all([ 51 | this.createReinstateTask(document, range) 52 | ] 53 | ); 54 | } catch (error: any) { 55 | throw new Error(error); 56 | } 57 | } 58 | 59 | 60 | private createReinstateTask(document: vscode.TextDocument, range: vscode.Range | vscode.Selection): vscode.CodeAction { 61 | try { 62 | const fix = new vscode.CodeAction(`Open this task again`, vscode.CodeActionKind.QuickFix); 63 | 64 | fix.edit = new vscode.WorkspaceEdit(); 65 | fix.edit.replace(document.uri, this.getTaskBoxRange(document, range) , "[ ]"); 66 | fix.edit.delete(document.uri, this.getAnnotationRange(document, range)); 67 | return fix; 68 | 69 | } catch (error) { 70 | throw error; 71 | } 72 | } 73 | 74 | private getAnnotationRange(document: vscode.TextDocument, range: vscode.Range | vscode.Selection): vscode.Range { 75 | const line: vscode.TextLine = document.lineAt(range.start.line); 76 | const start: number = line.text.indexOf(" (done: "); 77 | const end: number = line.text.substr(start).indexOf(")")+start+1; 78 | return new vscode.Range(range.start.line, start, range.end.line, end); 79 | 80 | } 81 | private getTaskBoxRange(document: vscode.TextDocument, range: vscode.Range | vscode.Selection ): vscode.Range { 82 | const line: vscode.TextLine = document.lineAt(range.start.line); 83 | return new vscode.Range(range.start.line, line.text.indexOf("["), range.end.line,line.text.indexOf("]")+1); 84 | } 85 | 86 | 87 | 88 | private matchesExpression(document: vscode.TextDocument, range: vscode.Range): boolean { 89 | return document.lineAt(range.start.line).text.match(this.regex) !== null; 90 | } 91 | 92 | 93 | 94 | } -------------------------------------------------------------------------------- /src/provider/codeactions/for-open-tasks.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 'use strict'; 19 | 20 | import moment = require('moment'); 21 | import * as vscode from 'vscode'; 22 | import * as J from '../..'; 23 | import { ShiftTarget } from '../commands/copy-task'; 24 | 25 | 26 | /** 27 | * The complete task codelens is active for open tasks, e.g. '-[ ] some text' 28 | * 29 | * Once activated, it will 30 | * - close the task: '-[ ] some text' -> '-[x] some text' 31 | * - annotate the task with completion date: '-[x] some text (completed on 2021-05-12 at 12:12)' 32 | */ 33 | export class OpenTaskActions implements vscode.CodeActionProvider { 34 | private ctrl: J.Util.Ctrl; 35 | private regex = new RegExp(/-\s{0,1}\[\s{0,2}\].*/g); 36 | 37 | 38 | public static readonly providedCodeActionKinds = [ 39 | vscode.CodeActionKind.QuickFix 40 | ]; 41 | 42 | 43 | constructor(ctrl: J.Util.Ctrl) { 44 | this.ctrl = ctrl; 45 | } 46 | 47 | 48 | provideCodeActions(document: vscode.TextDocument, range: vscode.Range | vscode.Selection, context: vscode.CodeActionContext, token: vscode.CancellationToken): vscode.ProviderResult<(vscode.CodeAction)[]> { 49 | 50 | try { 51 | if (!this.isOpenTask(document, range)) { return; } 52 | return Promise.all([ 53 | this.createCompleteTaskAction(`Complete this task`, document, range), 54 | this.createShiftTaskAction(`Plan for the next working day`, document, range, ShiftTarget.nextWorkingDay), 55 | this.createShiftTaskAction(`Plan for tomorrow`, document, range, ShiftTarget.tomorrow), 56 | this.createShiftTaskAction(`Plan for today`, document, range, ShiftTarget.today) 57 | ] 58 | ); 59 | } catch (error: any) { 60 | throw new Error(error); 61 | } 62 | } 63 | 64 | private async createCompleteTaskAction(description: string, document: vscode.TextDocument, range: vscode.Range | vscode.Selection): Promise { 65 | try { 66 | const fix = new vscode.CodeAction(description, vscode.CodeActionKind.QuickFix); 67 | 68 | fix.edit = new vscode.WorkspaceEdit(); 69 | fix.edit.replace(document.uri, this.getTaskBoxRange(document, range), "[x]"); 70 | 71 | const time = moment().format("YYYY-MM-DD HH:mm"); 72 | 73 | // FIXME: if current document is not current day, we need to insert also the current date (not only time) 74 | 75 | fix.edit.insert(document.uri, document.lineAt(range.start.line).range.end, " (done: " + time + ")"); 76 | 77 | return fix; 78 | 79 | } catch (error) { 80 | throw error; 81 | } 82 | } 83 | 84 | private async createShiftTaskAction(description: string, document: vscode.TextDocument, range: vscode.Range | vscode.Selection, target: ShiftTarget): Promise { 85 | try { 86 | const fix = new vscode.CodeAction(description, vscode.CodeActionKind.QuickFix); 87 | 88 | fix.edit = new vscode.WorkspaceEdit(); 89 | fix.edit.replace(document.uri, this.getTaskBoxRange(document, range), "[>]"); 90 | fix.edit.insert(document.uri, document.lineAt(range.end).range.end, this.getCopyText(target)); 91 | fix.command = { command: "journal.commands.copy-task", title: 'Copy a task', tooltip: 'Copy a task to another entry.', arguments: [document, await this.getTaskText(document, range), target] }; 92 | return fix; 93 | 94 | } catch (error) { 95 | throw error; 96 | } 97 | } 98 | 99 | 100 | private async getTaskText(document: vscode.TextDocument, range: vscode.Range | vscode.Selection): Promise { 101 | const line: vscode.TextLine = document.lineAt(range.start.line); 102 | let text = line.text.trim(); 103 | const tpl: J.Model.InlineTemplate = await this.ctrl.config.getTaskInlineTemplate(); 104 | 105 | // line: - [ ] Task: this is some text blabla 106 | // template: - [ ] Task: {$input} blabla 107 | 108 | const start = tpl.template.indexOf("${input}"); 109 | const end = tpl.template.indexOf("${input}")+8; 110 | 111 | const prefix = tpl.template.substring(0, start); 112 | const postfix = tpl.template.substring(end, tpl.template.length); 113 | 114 | text = text.replace(prefix, ""); 115 | text = text.replace(postfix, ""); 116 | 117 | // a task might have been created manually (it doesn't match the template). In this case, we still need to remove the any variation of "- [ ]" 118 | text = text.replace(/-\s?\[\s?\]/, ""); 119 | text = text.trim(); 120 | 121 | return text; 122 | } 123 | 124 | private getTaskBoxRange(document: vscode.TextDocument, range: vscode.Range | vscode.Selection): vscode.Range { 125 | const line: vscode.TextLine = document.lineAt(range.start.line); 126 | return new vscode.Range(range.start.line, line.text.indexOf("["), range.end.line, line.text.indexOf("]") + 1); 127 | } 128 | 129 | 130 | 131 | 132 | private isOpenTask(document: vscode.TextDocument, range: vscode.Range): boolean { 133 | return document.lineAt(range.start.line).text.match(this.regex) !== null; 134 | } 135 | 136 | private getCopyText(target: ShiftTarget): string { 137 | 138 | switch (target) { 139 | case ShiftTarget.nextWorkingDay: return " (moved: next work day)"; 140 | case ShiftTarget.today: return (" (moved: "+moment().format("YYYY-MM-DD")+")"); 141 | case ShiftTarget.tomorrow: return (" (moved: "+moment().add(1, "d").format("YYYY-MM-DD")+")"); 142 | case ShiftTarget.successorDay: return (" (moved: somewhere else"); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/provider/codelens/migrate-tasks.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 'use strict'; 19 | 20 | import * as vscode from 'vscode'; 21 | import * as J from '../..'; 22 | 23 | 24 | /** 25 | * The migrate task codelens is only active for the tasks header (as configured in settings, default is '## Tasks') for today's journal entry. 26 | * 27 | * Once activated, it scans the previous entries (last 20 entries) and copies any open tasks to today's entry. 28 | * For each identified open task, it will trigger the according shift action for the given range. 29 | * 30 | * 31 | */ 32 | export class MigrateTasksCodeLens implements vscode.CodeLensProvider { 33 | private codeLenses: vscode.CodeLens[] = []; 34 | private ctrl: J.Util.Ctrl; 35 | 36 | private _onDidChangeCodeLenses: vscode.EventEmitter = new vscode.EventEmitter(); 37 | 38 | 39 | 40 | constructor(ctrl: J.Util.Ctrl) { 41 | this.ctrl = ctrl; 42 | 43 | vscode.workspace.onDidChangeConfiguration((_) => { 44 | this._onDidChangeCodeLenses.fire(); 45 | }); 46 | } 47 | 48 | async getRegex() : Promise { 49 | let template = await this.ctrl.config.getTaskInlineTemplate(); 50 | return new RegExp(template.after); 51 | } 52 | 53 | public async provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { 54 | this.codeLenses = []; 55 | const regex = await this.getRegex(); 56 | const text = document.getText(); 57 | const matches = regex.exec(text); 58 | 59 | if(matches !== null) { 60 | const line = document.lineAt(document.positionAt(matches.index).line); 61 | const indexOf = line.text.indexOf(matches[0]); 62 | const position = new vscode.Position(line.lineNumber, indexOf); 63 | const range = document.getWordRangeAtPosition(position, regex); 64 | if (range) { 65 | this.codeLenses.push(new vscode.CodeLens(range)); 66 | } 67 | } 68 | 69 | return this.codeLenses; 70 | } 71 | 72 | public resolveCodeLens?(codeLens: vscode.CodeLens, token: vscode.CancellationToken): 73 | vscode.CodeLens | Thenable { 74 | 75 | 76 | codeLens.command = { 77 | title: "Collect all tasks", 78 | tooltip: "Collect and modify tasks from this and previous journal entries", 79 | command: "codelens-sample.codelensAction", 80 | arguments: ["Argument 1", false] 81 | }; 82 | return codeLens; 83 | } 84 | } -------------------------------------------------------------------------------- /src/provider/codelens/shift-task.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 'use strict'; 19 | 20 | import * as vscode from 'vscode'; 21 | import * as J from '../..'; 22 | 23 | 24 | export class ShiftTaskCodeLens implements vscode.CodeLensProvider { 25 | private codeLenses: vscode.CodeLens[] = []; 26 | private ctrl: J.Util.Ctrl; 27 | 28 | private _onDidChangeCodeLenses: vscode.EventEmitter = new vscode.EventEmitter(); 29 | 30 | 31 | 32 | constructor(ctrl: J.Util.Ctrl) { 33 | this.ctrl = ctrl; 34 | 35 | vscode.workspace.onDidChangeConfiguration((_) => { 36 | this._onDidChangeCodeLenses.fire(); 37 | }); 38 | } 39 | 40 | async getRegex() : Promise { 41 | let template = await this.ctrl.config.getTaskInlineTemplate(); 42 | return new RegExp(template.after); 43 | } 44 | 45 | public async provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { 46 | this.codeLenses = []; 47 | const regex = await this.getRegex(); 48 | const text = document.getText(); 49 | const matches = regex.exec(text); 50 | 51 | if(matches !== null) { 52 | const line = document.lineAt(document.positionAt(matches.index).line); 53 | const indexOf = line.text.indexOf(matches[0]); 54 | const position = new vscode.Position(line.lineNumber, indexOf); 55 | const range = document.getWordRangeAtPosition(position, regex); 56 | if (range) { 57 | this.codeLenses.push(new vscode.CodeLens(range)); 58 | } 59 | } 60 | 61 | return this.codeLenses; 62 | } 63 | 64 | public resolveCodeLens?(codeLens: vscode.CodeLens, token: vscode.CancellationToken): 65 | vscode.CodeLens | Thenable { 66 | 67 | 68 | codeLens.command = { 69 | title: "Collect all tasks", 70 | tooltip: "Collect and modify tasks from this and previous journal entries", 71 | command: "codelens-sample.codelensAction", 72 | arguments: ["Argument 1", false] 73 | }; 74 | return codeLens; 75 | } 76 | } -------------------------------------------------------------------------------- /src/provider/commands/copy-task.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 'use strict'; 19 | 20 | import moment = require('moment'); 21 | import * as vscode from 'vscode'; 22 | import * as J from '../..'; 23 | 24 | export enum ShiftTarget { 25 | nextWorkingDay, 26 | tomorrow, 27 | today, 28 | successorDay // the day after the currently active entries date (independent from current date) 29 | } 30 | 31 | /** 32 | * The shift task command is active for open tasks, e.g. '-[ ] some text' and triggered by the codeaction in complete-task 33 | * 34 | * Once activated, it will 35 | * - shift the task to the next working day: '-[ ] some text' -> '-[>] some text' 36 | * - annotate the task with link to new entry: '-[>] some text (copied to [../13.md](2021-05-13))' 37 | * - insert the task to the entry of the new date: '-[ ] some text (copied from [../12.md](2021-05-12))' 38 | */ 39 | 40 | export class CopyTaskCommand implements vscode.Command { 41 | title: string = "Copy selected task"; 42 | command: string = "journal.commands.copy-task"; 43 | 44 | 45 | protected constructor(public ctrl: J.Util.Ctrl) { } 46 | 47 | public async dispose(): Promise { 48 | // do nothing 49 | } 50 | 51 | public static create(ctrl: J.Util.Ctrl): vscode.Disposable { 52 | const cmd = new this(ctrl); 53 | vscode.commands.registerCommand(cmd.command, (document, range, target) => cmd.execute(document, range, target)); 54 | return cmd; 55 | } 56 | 57 | public async execute(document: vscode.TextDocument, text: string, target: ShiftTarget) { 58 | this.ctrl.logger.trace("command called with ", document.uri, ", text ", text, " and target ", target); 59 | 60 | switch (target) { 61 | case ShiftTarget.nextWorkingDay: return this.insertTaskInNextWorkdDaysEntry(document, text); 62 | case ShiftTarget.today: return this.insertTaskToTodaysEntry(text); 63 | case ShiftTarget.tomorrow: return this.insertTaskToTomorrowsEntry(text); 64 | } 65 | } 66 | 67 | private async insertTaskInNextWorkdDaysEntry(document: vscode.TextDocument, taskString: string) { 68 | // const entryDate: Date = await J.Util.getDateFromURIAndConfig(document.uri.toString(), this.ctrl.config); 69 | const entryMoment = moment(); 70 | 71 | let dayIncrement = 1; 72 | 73 | if (entryMoment.day() === 5) { 74 | dayIncrement = 3; 75 | } else if (entryMoment.day() === 6) { 76 | dayIncrement = 2; 77 | } 78 | 79 | this.insertTaskToEntry(taskString, entryMoment.add(dayIncrement, "d").toDate()); 80 | } 81 | 82 | private async insertTaskToTomorrowsEntry(taskString: string) { 83 | this.insertTaskToEntry(taskString, moment().add(1, 'd').toDate()); 84 | } 85 | 86 | private async insertTaskToTodaysEntry(taskString: string) { 87 | this.insertTaskToEntry(taskString, moment().toDate()); 88 | } 89 | 90 | private async insertTaskToEntry(taskString: string, date: Date) { 91 | const doc : vscode.TextDocument = await this.ctrl.reader.loadEntryForDay(date); 92 | const tpl : J.Model.InlineTemplate = await this.ctrl.config.getTaskInlineTemplate(); 93 | const pos = this.ctrl.inject.computePositionForInput(doc, tpl); 94 | const inlineString: J.Model.InlineString = await this.ctrl.inject.buildInlineString(doc, tpl, ["${input}", taskString]); 95 | this.ctrl.inject.injectInlineString(inlineString); 96 | 97 | doc.save(); 98 | } 99 | 100 | 101 | 102 | } -------------------------------------------------------------------------------- /src/provider/commands/index.ts: -------------------------------------------------------------------------------- 1 | export { CopyTaskCommand as ShiftTaskCommand } from './copy-task'; 2 | export { OpenJournalWorkspaceCommand } from './open-journal-workspace'; 3 | export { PrintTimeCommand } from './print-current-time'; 4 | export { PrintDurationCommand } from './print-duration-between-selected-times'; 5 | export { PrintSumCommand } from './print-sum-of-selected-numbers'; 6 | export { ShowEntryForInputCommand } from './show-entry-for-input'; 7 | export { ShowEntryForTodayCommand } from './show-entry-for-today'; 8 | export { ShowEntryForTomorrowCommand } from './show-entry-for-tomorrow'; 9 | export { ShowEntryForYesterdayCommand } from './show-entry-for-yesterday'; 10 | export { ShowNoteCommand } from './show-note'; 11 | export { InsertMemoCommand } from './insert-memo'; -------------------------------------------------------------------------------- /src/provider/commands/insert-memo.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 'use strict'; 19 | 20 | import * as vscode from 'vscode'; 21 | import * as J from '../..'; 22 | import { AbstractLoadEntryForDateCommand } from './show-entry-for-date'; 23 | 24 | /** 25 | * Legacy command, same functionality as ShowEntryForInputCommand 26 | * @deprecated 27 | */ 28 | 29 | export class InsertMemoCommand extends AbstractLoadEntryForDateCommand { 30 | title: string = "Insert Memo"; 31 | command: string = "journal.memo"; 32 | 33 | public static create(ctrl: J.Util.Ctrl): vscode.Disposable { 34 | const cmd = new this(ctrl); 35 | vscode.commands.registerCommand(cmd.command, () => cmd.execute()); 36 | return cmd; 37 | } 38 | 39 | /** 40 | * Opens the editor for a specific day. Supported values are explicit dates (in ISO format), 41 | * offsets (+ or - as prefix and 0) and weekdays (next wednesday) 42 | * 43 | * Update: supports much more now 44 | */ 45 | public async execute(): Promise { 46 | this.ctrl.logger.trace("Executing command: ", this.command); 47 | 48 | const input: J.Model.Input = await this.ctrl.ui.getUserInputWithValidation(); 49 | super.execute(input); 50 | } 51 | 52 | 53 | 54 | 55 | } -------------------------------------------------------------------------------- /src/provider/commands/open-journal-workspace.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 'use strict'; 19 | 20 | import * as vscode from 'vscode'; 21 | import * as J from '../..'; 22 | 23 | 24 | export class OpenJournalWorkspaceCommand implements vscode.Command, vscode.Disposable { 25 | title: string = 'Open the journal workspace'; 26 | command: string = 'journal.open'; 27 | 28 | 29 | protected constructor(public ctrl: J.Util.Ctrl) {} 30 | 31 | public async dispose(): Promise { 32 | // do nothing 33 | } 34 | 35 | public static create(ctrl: J.Util.Ctrl): vscode.Disposable { 36 | const cmd = new this(ctrl); 37 | vscode.commands.registerCommand(cmd.command, () => cmd.openWorkspace()); 38 | return cmd; 39 | 40 | } 41 | 42 | /** 43 | * Called by command 'Journal:open'. Opens a new windows with the Journal base directory as root. 44 | * 45 | * @returns {Q.Promise} 46 | * @memberof JournalCommands 47 | */ 48 | public async openWorkspace(): Promise { 49 | this.ctrl.logger.trace("Executing command: ", this.command); 50 | 51 | let path = vscode.Uri.file(this.ctrl.config.getBasePath()); 52 | 53 | try { 54 | vscode.commands.executeCommand('vscode.openFolder', path, true); 55 | } catch (error) { 56 | this.ctrl.logger.error("Failed to execute command: ", this.command, "Reason: ", error); 57 | this.ctrl.ui.showError("Failed to open the journal workspace."); 58 | } 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /src/provider/commands/print-current-time.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 'use strict'; 19 | 20 | import * as vscode from 'vscode'; 21 | import * as J from '../..'; 22 | 23 | 24 | export class PrintTimeCommand implements vscode.Command, vscode.Disposable { 25 | title: string = "Print current time"; 26 | command: string = "journal.printTime"; 27 | 28 | protected constructor(public ctrl: J.Util.Ctrl) {} 29 | 30 | public async dispose(): Promise { 31 | // do nothing 32 | } 33 | 34 | public static create(ctrl: J.Util.Ctrl): vscode.Disposable { 35 | const cmd = new this(ctrl); 36 | vscode.commands.registerCommand(cmd.command, () => cmd.printTime()); 37 | return cmd; 38 | } 39 | 40 | /** 41 | * Prints the current time at the cursor postion 42 | */ 43 | public async printTime(): Promise { 44 | this.ctrl.logger.trace("Executing command: ", this.command); 45 | 46 | try { 47 | let editor: vscode.TextEditor = vscode.window.activeTextEditor; 48 | 49 | // Todo: identify scope of the active editot 50 | let template: J.Model.ScopedTemplate = await this.ctrl.config.getTimeStringTemplate(); 51 | 52 | let currentPosition: vscode.Position = editor.selection.active; 53 | 54 | this.ctrl.inject.injectString(editor.document, template.value!, currentPosition); 55 | 56 | } catch (error) { 57 | this.ctrl.logger.error("Failed to execute command: ", this.command, "Reason: ", error); 58 | this.ctrl.ui.showError("Failed to print current time."); 59 | } 60 | } 61 | 62 | 63 | 64 | } -------------------------------------------------------------------------------- /src/provider/commands/print-duration-between-selected-times.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 'use strict'; 19 | 20 | import moment = require('moment'); 21 | import * as vscode from 'vscode'; 22 | import * as J from '../..'; 23 | 24 | 25 | export class PrintDurationCommand implements vscode.Command, vscode.Disposable { 26 | title: string = "Print duration between two selected times"; 27 | command: string = "journal.printDuration"; 28 | 29 | protected constructor(public ctrl: J.Util.Ctrl) {} 30 | 31 | public async dispose(): Promise { 32 | // do nothing 33 | } 34 | 35 | public static create(ctrl: J.Util.Ctrl): vscode.Disposable { 36 | const cmd = new this(ctrl); 37 | vscode.commands.registerCommand(cmd.command, () => cmd.printDuration()); 38 | return cmd; 39 | } 40 | 41 | /** 42 | * Called by command 'Journal:printDuration'. Requires three selections (three active cursors) 43 | * in current document. It identifies which of the selections are times (in the format hh:mm 44 | * or glued like "1223") and where to print the duration (in decimal form). 45 | * For now the duration is always printing hours. 46 | */ 47 | public async printDuration() { 48 | this.ctrl.logger.trace("Executing command: ", this.command); 49 | 50 | 51 | try { 52 | let editor: vscode.TextEditor = vscode.window.activeTextEditor; 53 | let regExp: RegExp = /\d{1,2}:?\d{0,2}(?:\s?(?:am|AM|pm|PM))?|\s/; 54 | // let regExp: RegExp = /(\d{1,2}:?\d{2}\s)|(\d{1,4}\s?(?:am|pm)\s)|(\d{1,2}[,\.]\d{1,2}\s)|(\s)/; 55 | 56 | if (editor.selections.length !== 3) { 57 | throw new Error("To compute the duration, you have to select two times in your text as well as the location where to print it. "); 58 | } 59 | 60 | // 61 | let start: moment.Moment | undefined; 62 | let end: moment.Moment | undefined; 63 | let target: vscode.Position | undefined; 64 | 65 | 66 | editor.selections.forEach((selection: vscode.Selection) => { 67 | let range: vscode.Range | undefined = editor.document.getWordRangeAtPosition(selection.active, regExp); 68 | 69 | 70 | if (J.Util.isNullOrUndefined(range)) { 71 | target = selection.active; 72 | return; 73 | } 74 | 75 | let text = editor.document.getText(range); 76 | 77 | // check if empty string 78 | if (text.trim().length === 0) { 79 | target = selection.active; 80 | return; 81 | } 82 | 83 | // try to format into local date 84 | let time: moment.Moment = moment(text, "LT"); 85 | 86 | if (!time.isValid()) { 87 | // 123pm 88 | let mod: number = text.search(/am|pm/); 89 | if (mod > 0) { 90 | if (text.charAt(mod - 1) !== " ") { 91 | text = text.substr(0, mod - 1) + " " + text.substr(mod); 92 | } 93 | time = moment(text, "hmm a"); 94 | } 95 | } 96 | 97 | if (!time.isValid()) { 98 | // 123AM 99 | let mod: number = text.search(/AM|PM/); 100 | if (mod > 0) { 101 | if (text.charAt(mod - 1) !== " ") { 102 | text = text.substring(0, mod - 1) + " " + text.substr(mod); 103 | } 104 | time = moment(text, "hmm A"); 105 | } 106 | } 107 | 108 | if (!time.isValid()) { 109 | // 2330 110 | time = moment(text, "Hmm"); 111 | } 112 | 113 | // parsing glued hours 114 | if (J.Util.isNullOrUndefined(start)) { 115 | start = time; 116 | } else if (start!.isAfter(time)) { 117 | end = start; 118 | start = time; 119 | } else { 120 | end = time; 121 | } 122 | }); 123 | 124 | if (J.Util.isNullOrUndefined(start)) { throw new Error("No valid start time selected"); } // tslint:disable-line 125 | else if (J.Util.isNullOrUndefined(end)) { throw new Error("No valid end time selected"); } // tslint:disable-line 126 | else if (J.Util.isNullOrUndefined(target)) { throw new Error("No valid target selected for printing the duration."); } // tslint:disable-line 127 | else { 128 | let duration = moment.duration(start!.diff(end!)); 129 | let formattedDuration = Math.abs(duration.asHours()).toFixed(2); 130 | this.ctrl.inject.injectString(editor.document, formattedDuration, target!); 131 | } 132 | 133 | 134 | } catch (error) { 135 | this.ctrl.logger.error("Failed to execute command: ", this.command, "Reason: ", error); 136 | this.ctrl.ui.showError("Failed to print duration."); 137 | } 138 | 139 | } 140 | 141 | } -------------------------------------------------------------------------------- /src/provider/commands/print-sum-of-selected-numbers.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 'use strict'; 19 | 20 | import * as vscode from 'vscode'; 21 | import * as J from '../..'; 22 | 23 | 24 | export class PrintSumCommand implements vscode.Command, vscode.Disposable { 25 | title: string = "Print sum of two selected numbers"; 26 | command: string = "journal.printSum"; 27 | 28 | protected constructor(public ctrl: J.Util.Ctrl) {} 29 | 30 | public async dispose(): Promise { 31 | // do nothing 32 | } 33 | 34 | public static create(ctrl: J.Util.Ctrl): vscode.Disposable { 35 | const cmd = new this(ctrl); 36 | vscode.commands.registerCommand(cmd.command, () => cmd.execute()); 37 | return cmd; 38 | } 39 | /** 40 | * Prints the sum of the selected numbers in the current editor at the selection location 41 | */ 42 | public async execute(): Promise { 43 | this.ctrl.logger.trace("Executing command: ", this.command); 44 | 45 | 46 | try { 47 | let editor: vscode.TextEditor = vscode.window.activeTextEditor; 48 | let regExp: RegExp = /(\d+[,\.]?\d*\s?)|(\s)/; 49 | 50 | let target: vscode.Position; 51 | let numbers: number[] = []; 52 | 53 | editor.selections.forEach((selection: vscode.Selection) => { 54 | let range: vscode.Range | undefined = editor.document.getWordRangeAtPosition(selection.active, regExp); 55 | 56 | if (J.Util.isNullOrUndefined(range)) { 57 | target = selection.active; 58 | return; 59 | } 60 | 61 | let text = editor.document.getText(range); 62 | 63 | // check if empty string 64 | if (text.trim().length === 0) { 65 | target = selection.active; 66 | 67 | return; 68 | } 69 | // check if number 70 | let number: number = Number(text); 71 | if (number > 0) { 72 | numbers.push(number); 73 | return; 74 | } 75 | 76 | }); 77 | 78 | if (numbers.length < 2) { throw Error("You have to select at least two numbers"); } // tslint:disable-line 79 | else if (J.Util.isNullOrUndefined(target!)) { throw Error("No valid target selected for printing the sum."); } // tslint:disable-line 80 | else { 81 | let result: string = numbers.reduce((previous, current) => previous + current).toString(); 82 | this.ctrl.inject.injectString(editor.document, result + "", target!); 83 | } 84 | } catch (error) { 85 | this.ctrl.logger.error("Failed to execute command: ", this.command, "Reason: ", error); 86 | this.ctrl.ui.showError("Failed to print sum of selected numbers."); 87 | } 88 | 89 | } 90 | 91 | } -------------------------------------------------------------------------------- /src/provider/commands/show-entry-for-date.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 'use strict'; 19 | 20 | import * as vscode from 'vscode'; 21 | import * as J from '../..'; 22 | import { NoteInput, SelectedInput } from '../../model'; 23 | 24 | 25 | export class AbstractLoadEntryForDateCommand implements vscode.Disposable { 26 | 27 | protected constructor(public ctrl: J.Util.Ctrl) { } 28 | 29 | public async dispose(): Promise { 30 | // do nothing 31 | } 32 | 33 | /** 34 | * Implements commands "yesterday", "today", "yesterday", where the input is predefined (no input box appears) 35 | * @param offset 36 | */ 37 | public async execute(input: J.Model.Input ): Promise { 38 | 39 | try { 40 | const doc = await this.loadPageForInput(input); 41 | await this.ctrl.ui.showDocument(doc); 42 | } catch (error) { 43 | if (error !== 'cancel') { 44 | this.ctrl.logger.error("Failed to load entry for input: ", input.text, "Reason: ", error); 45 | this.ctrl.ui.showError("Failed to open entry."); 46 | } else {return;} 47 | } 48 | } 49 | 50 | 51 | 52 | /** 53 | * Expects any user input from the magic input and either opens the file or creates it. 54 | * @param input 55 | */ 56 | protected async loadPageForInput(input: J.Model.Input): Promise { 57 | 58 | if (input instanceof SelectedInput) { 59 | // we just load the path 60 | return this.ctrl.ui.openDocument((input).path); 61 | } if (input instanceof NoteInput) { 62 | // we create or load the notes 63 | return new J.Provider.LoadNotes(input, this.ctrl).loadWithPath(input.path); 64 | 65 | } else { 66 | return this.ctrl.reader.loadEntryForInput(input) 67 | .then((doc: vscode.TextDocument) => this.ctrl.inject.injectInput(doc, input)); 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /src/provider/commands/show-entry-for-input.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 'use strict'; 19 | 20 | import * as vscode from 'vscode'; 21 | import * as J from '../..'; 22 | import { AbstractLoadEntryForDateCommand } from './show-entry-for-date'; 23 | 24 | 25 | export class ShowEntryForInputCommand extends AbstractLoadEntryForDateCommand { 26 | title: string = "Show journal entry for given user input"; 27 | command: string = "journal.day"; 28 | 29 | public static create(ctrl: J.Util.Ctrl): vscode.Disposable { 30 | const cmd = new this(ctrl); 31 | vscode.commands.registerCommand(cmd.command, () => cmd.execute()); 32 | return cmd; 33 | } 34 | 35 | /** 36 | * Opens the editor for a specific day. Supported values are explicit dates (in ISO format), 37 | * offsets (+ or - as prefix and 0) and weekdays (next wednesday) 38 | * 39 | * Update: supports much more now 40 | */ 41 | public async execute(): Promise { 42 | this.ctrl.logger.trace("Executing command: ", this.command); 43 | 44 | const input: J.Model.Input = await this.ctrl.ui.getUserInputWithValidation(); 45 | super.execute(input); 46 | } 47 | 48 | 49 | 50 | 51 | } -------------------------------------------------------------------------------- /src/provider/commands/show-entry-for-today.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 'use strict'; 19 | 20 | import * as vscode from 'vscode'; 21 | import * as J from '../..'; 22 | import { AbstractLoadEntryForDateCommand } from './show-entry-for-date'; 23 | 24 | 25 | export class ShowEntryForTodayCommand extends AbstractLoadEntryForDateCommand { 26 | title: string = "Show journal entry for today"; 27 | command: string = "journal.today"; 28 | 29 | 30 | public static create(ctrl: J.Util.Ctrl): vscode.Disposable { 31 | const cmd = new this(ctrl); 32 | 33 | let input = new J.Model.Input(); 34 | input.offset = 0; 35 | 36 | vscode.commands.registerCommand(cmd.command, () => cmd.execute(input)); 37 | return cmd; 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /src/provider/commands/show-entry-for-tomorrow.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 'use strict'; 19 | 20 | import * as vscode from 'vscode'; 21 | import * as J from '../..'; 22 | import { AbstractLoadEntryForDateCommand } from './show-entry-for-date'; 23 | 24 | 25 | export class ShowEntryForTomorrowCommand extends AbstractLoadEntryForDateCommand { 26 | title: string = "Show journal entry for tomorrow"; 27 | command: string = "journal.tomorrow"; 28 | 29 | 30 | public static create(ctrl: J.Util.Ctrl): vscode.Disposable { 31 | const cmd = new this(ctrl); 32 | 33 | let input = new J.Model.Input(); 34 | input.offset = 1; 35 | 36 | vscode.commands.registerCommand(cmd.command, () => cmd.execute(input)); 37 | return cmd; 38 | } 39 | } -------------------------------------------------------------------------------- /src/provider/commands/show-entry-for-yesterday.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 'use strict'; 19 | 20 | import * as vscode from 'vscode'; 21 | import * as J from '../..'; 22 | import { AbstractLoadEntryForDateCommand } from './show-entry-for-date'; 23 | 24 | 25 | export class ShowEntryForYesterdayCommand extends AbstractLoadEntryForDateCommand{ 26 | title: string = "Show journal entry for yesterday"; 27 | command: string = "journal.yesterday"; 28 | 29 | 30 | public static create(ctrl: J.Util.Ctrl): vscode.Disposable { 31 | const cmd = new this(ctrl); 32 | 33 | let input = new J.Model.Input(); 34 | input.offset = -1; 35 | 36 | vscode.commands.registerCommand(cmd.command, () => cmd.execute(input)); 37 | return cmd; 38 | } 39 | } -------------------------------------------------------------------------------- /src/provider/commands/show-note.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 'use strict'; 19 | 20 | import * as vscode from 'vscode'; 21 | import * as J from '../..'; 22 | 23 | 24 | export class ShowNoteCommand implements vscode.Command, vscode.Disposable { 25 | title: string = 'Loads and shows editor for new note'; 26 | command: string = 'journal.note'; 27 | 28 | 29 | protected constructor(public ctrl: J.Util.Ctrl) { } 30 | 31 | public async dispose(): Promise { 32 | // do nothing 33 | } 34 | 35 | public static create(ctrl: J.Util.Ctrl): vscode.Disposable { 36 | const cmd = new this(ctrl); 37 | vscode.commands.registerCommand(cmd.command, () => cmd.execute()); 38 | return cmd; 39 | } 40 | 41 | /** 42 | * Creates a new file for a note following the configured pattern 43 | * Shows the file to let the user start adding notes right away. 44 | */ 45 | public async execute(): Promise { 46 | 47 | this.ctrl.logger.trace("Executing command: ", this.command); 48 | 49 | try { 50 | const userInput: string = await this.ctrl.ui.getUserInput("Enter title for new note"); 51 | let parsedInput: J.Model.Input = await this.ctrl.parser.parseInput(userInput); 52 | 53 | const doc : vscode.TextDocument = await new J.Provider.LoadNotes(parsedInput, this.ctrl).load(); 54 | await this.ctrl.ui.showDocument(doc); 55 | 56 | 57 | } catch (error) { 58 | this.ctrl.logger.error("Failed to execute command: ", this.command, "Reason: ", error); 59 | this.ctrl.ui.showError("Failed to load note."); 60 | } 61 | } 62 | 63 | 64 | } -------------------------------------------------------------------------------- /src/provider/features/load-note.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as J from '../..'; 3 | 4 | /** 5 | * Feature responsible for creating (if needed) and loading notes given a user input as title. 6 | * (extracted as feature to enable direct unit tests) 7 | */ 8 | 9 | export class LoadNotes { 10 | 11 | constructor(public input: J.Model.Input, public ctrl: J.Util.Ctrl) { 12 | 13 | } 14 | 15 | public async load(): Promise { 16 | let path = await this.ctrl.parser.resolveNotePathForInput(this.input); 17 | return this.loadWithPath(path); 18 | } 19 | 20 | 21 | public async loadWithPath(path: string): Promise { 22 | 23 | let content: string = await this.ctrl.inject.formatNote(this.input); 24 | 25 | let document : vscode.TextDocument = await this.loadNote(path, content); 26 | 27 | // inject reference to new note in today's journal page 28 | await this.ctrl.reader.loadEntryForInput(new J.Model.Input(0)) // triggered automatically by loading today's page (we don't show it though) 29 | .catch(reason => this.ctrl.logger.error("Failed to load today's page for injecting link to note.", reason)); 30 | 31 | return document; 32 | } 33 | 34 | /** 35 | * Creates or loads a note 36 | * 37 | * @param {string} path 38 | * @param {string} content 39 | * @returns {Promise} 40 | * @memberof Writer 41 | */ 42 | public async loadNote(path: string, content: string): Promise { 43 | this.ctrl.logger.trace("Entering loadNote() in features/load-note.ts for path: ", path); 44 | 45 | return new Promise((resolve, reject) => { 46 | // check if file exists already 47 | 48 | this.ctrl.ui.openDocument(path) 49 | .then((doc: vscode.TextDocument) => resolve(doc)) 50 | .catch(error => { 51 | this.ctrl.writer.createSaveLoadTextDocument(path, content) 52 | .then((doc: vscode.TextDocument) => resolve(doc)) 53 | .catch(error => { 54 | this.ctrl.logger.error(error); 55 | reject("Failed to load note."); 56 | }); 57 | }); 58 | 59 | }); 60 | } 61 | 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/provider/features/show-pick-list.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as J from '../..'; 3 | 4 | /** 5 | * Feature responsible for scanning the journal and notes folders and collecting the items displayed in the picklist 6 | */ 7 | 8 | export class ShowPickLists { 9 | } -------------------------------------------------------------------------------- /src/provider/index.d.ts: -------------------------------------------------------------------------------- 1 | export { InputMatcher } from "./InputMatcher"; 2 | -------------------------------------------------------------------------------- /src/provider/index.ts: -------------------------------------------------------------------------------- 1 | export { MatchInput } from "./features/match-input"; 2 | export { SyncNoteLinks } from "./features/sync-note-links"; 3 | export { LoadNotes } from "./features/load-note"; 4 | export { ScanEntries, sortPickEntries } from "./features/scan-entries"; 5 | 6 | export * as Commands from './commands'; 7 | 8 | export { MigrateTasksCodeLens } from './codelens/migrate-tasks'; 9 | export { ShiftTaskCodeLens } from './codelens/shift-task'; 10 | 11 | export { CompletedTaskActions } from './codeactions/for-completed-tasks'; 12 | export { OpenTaskActions } from './codeactions/for-open-tasks'; 13 | 14 | -------------------------------------------------------------------------------- /src/test/Readme.md: -------------------------------------------------------------------------------- 1 | # How to run the direct tests 2 | 3 | npx ts-node ./test/direct.ts -------------------------------------------------------------------------------- /src/test/TestLogger.d.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from "../util/logger"; 2 | export declare class TestLogger implements Logger { 3 | error(message: string, ...optionalParams: any[]): void; 4 | printError(error: Error): void; 5 | showChannel(): void; 6 | debug(message: string, ...optionalParams: any[]): void; 7 | trace(message: string, ...optionalParams: any[]): void; 8 | } 9 | -------------------------------------------------------------------------------- /src/test/TestLogger.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from "../util/logger"; 2 | 3 | export class TestLogger implements Logger { 4 | constructor(public tracing: boolean) { 5 | 6 | } 7 | 8 | error(message: string, ...optionalParams: any[]): void { 9 | console.error("ERROR", message, ...optionalParams); 10 | } 11 | printError(error: Error): void { 12 | throw new Error("Method not implemented."); 13 | } 14 | showChannel(): void { 15 | throw new Error("Method not implemented."); 16 | } 17 | debug(message: string, ...optionalParams: any[]): void { 18 | console.debug("DEBUG", message, ...optionalParams); 19 | } 20 | trace(message: string, ...optionalParams: any[]): void { 21 | if(this.tracing) { 22 | console.trace(message, ...optionalParams); 23 | } 24 | // do nothing 25 | 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /src/test/direct/direct.d.ts: -------------------------------------------------------------------------------- 1 | import { ScopedTemplate } from "../../ext/index"; 2 | export declare function replaceDateFormats(st: ScopedTemplate, date: Date): void; 3 | -------------------------------------------------------------------------------- /src/test/direct/direct.ts: -------------------------------------------------------------------------------- 1 | 2 | import moment = require("moment"); 3 | import { ScopedTemplate } from "../../model"; 4 | 5 | 6 | let regExpDateFormats: RegExp = new RegExp(/\$\{(?:(year|month|day|localTime|localDate)|(d:\w+))\}/g); 7 | export function replaceDateFormats(st: ScopedTemplate, date: Date): void { 8 | let matches: RegExpMatchArray | null = st.template.match(regExpDateFormats); 9 | if(matches === null) { 10 | return; 11 | } 12 | 13 | 14 | console.log(JSON.stringify(matches)); 15 | 16 | if (matches.length === 0) { return; } 17 | 18 | let mom: moment.Moment = moment(date); 19 | 20 | matches.forEach(match => { 21 | switch (match) { 22 | case "${year}": 23 | st.template = st.template.replace(match, mom.format("YYYY")); 24 | break; 25 | case "${month}": 26 | st.template = st.template.replace(match, mom.format("MM")); 27 | break; 28 | case "${day}": 29 | st.template = st.template.replace(match, mom.format("DD")); 30 | break; 31 | case "${localTime}": 32 | st.template = st.template.replace(match, mom.format("LL")); 33 | break; 34 | case "${localDate}": 35 | st.template = st.template.replace(match, mom.format("LD")); 36 | break; 37 | default: 38 | // check if custom format 39 | if(match.startsWith("${d:")) { 40 | 41 | let modifier = match.substring(match.indexOf("d:")+2, match.length-1); // includes } at the end 42 | st.template = st.template.replace(match, mom.format(modifier)); 43 | } 44 | break; 45 | } 46 | }); 47 | 48 | 49 | } 50 | 51 | 52 | 53 | let t1: ScopedTemplate = { 54 | scope: "default", 55 | template: "${year} and ${day} and some ${month}", 56 | }; 57 | 58 | replaceDateFormats(t1, new Date()); 59 | console.log(t1.template); 60 | 61 | let t2: ScopedTemplate = { 62 | scope: "default", 63 | template: "Local time ${localTime} and custom format ${d:YY} for year", 64 | }; 65 | 66 | replaceDateFormats(t2, new Date()); 67 | console.log(t2.template); -------------------------------------------------------------------------------- /src/test/direct/input_parser.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /src/test/direct/input_parser.ts: -------------------------------------------------------------------------------- 1 | import { MatchInput } from '../../provider'; 2 | import { TestLogger } from '../TestLogger'; 3 | 4 | let inputMatcher = new MatchInput(new TestLogger(false), "en-US"); 5 | 6 | testExpr1(); 7 | 8 | async function testExpr1() { 9 | let str = "next monday"; 10 | let input = await inputMatcher.parseInput(str); 11 | console.log(input.flags); 12 | 13 | 14 | } 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/test/direct/path-parse-with-date.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import moment = require('moment'); 3 | 4 | 5 | 6 | const regExpDateFormats: RegExp = new RegExp(/\$\{(?:(year|month|day|localTime|localDate|weekday)|(d:[\s\S]+?))\}/g); 7 | let base = "c:\\Users\\user\\Git\\vscode-journal\\test\\workspace\\journal"; 8 | let pathTpl = "${base}/${year}-${month}"; 9 | let fileTpl = "${year}${month}${day}.${ext}"; 10 | let uri = "file:/Users/user/Git/vscode-journal/test/workspace/journal/2020-07/20200709.md"; 11 | 12 | console.log("Test to acquire date from uri: ", uri); 13 | getDateFromURI(uri, pathTpl, fileTpl, base) 14 | .then(result => assertCorrectDate(result)); 15 | 16 | pathTpl = "${year}/${month}/${day}"; 17 | fileTpl = "journal.${ext}"; 18 | uri = "file:/Users/user/Git/vscode-journal/test/workspace/journal/2020/07/09/journal.md"; 19 | console.log("Test to acquire date from uri: ", uri); 20 | getDateFromURI(uri, pathTpl, fileTpl, base) 21 | .then(result => assertCorrectDate(result)); 22 | 23 | 24 | pathTpl = "${base}"; 25 | fileTpl = "${year}-${month}-${day}"; 26 | uri = "file:/Users/user/Git/vscode-journal/test/workspace/journal/2020-07-09.md"; 27 | console.log("Test to acquire date from uri: ", uri); 28 | getDateFromURI(uri, pathTpl, fileTpl, base) 29 | .then(result => assertCorrectDate(result)); 30 | 31 | pathTpl = "${base}"; 32 | fileTpl = "${d:YYYY-MM-DD}"; 33 | uri = "file:/Users/user/Git/vscode-journal/test/workspace/journal/2020-07-09.md"; 34 | console.log("Test to acquire date from uri: ", uri); 35 | getDateFromURI(uri, pathTpl, fileTpl, base) 36 | .then(result => assertCorrectDate(result)); 37 | 38 | export async function getDateFromURI(uri: string, pathTemplate: string, fileTemplate: string, basePath: string) { 39 | if(fileTemplate.indexOf(".") > 0) {fileTemplate = fileTemplate.substr(0, fileTemplate.lastIndexOf("."));} 40 | if(pathTemplate.startsWith("${base}/")) {pathTemplate = pathTemplate.substring("${base}/".length);} 41 | 42 | let year: number | undefined; 43 | let month: number | undefined; 44 | let day: number | undefined; 45 | 46 | // input template is something like 47 | // "path": "${base}/${year}/${month}", -> sdlkjfwejf/2021/12 48 | // "file": "${day}.${ext}" -> 21.md 49 | // but also file: ${year}-${month}-${day}.md 50 | 51 | 52 | // go through each element in path and assign it to a date part or skip it 53 | let pathParts = uri.split("/"); 54 | 55 | 56 | // check if part is in base path (if yes, we ignore) 57 | // for the rest: last part is file, everything else path pattern 58 | let pathElements: string[] = []; 59 | let pathStr: string = ""; 60 | let fileStr = ""; 61 | 62 | pathParts.forEach((element, index) => { 63 | if(element.trim().length === 0) {return;} 64 | else if(element.startsWith("file:")) {return;} 65 | else if(basePath.search(element) >= 0) {return;} 66 | else if(index+1 === pathParts.length) {fileStr = element.substr(0, element.lastIndexOf("."));} 67 | else { 68 | pathElements.concat(element); 69 | if(pathStr.length > 1) {pathStr += "/";} 70 | pathStr += element; 71 | } 72 | }); 73 | 74 | 75 | console.log(pathStr); 76 | console.log(fileStr); 77 | 78 | // ${base}/${year}/${month}-${day}/${LD}.${ext} 79 | // remove base and ext variables 80 | // tokenize in variables and loop through matches. 81 | // replace each match with momemnt format and call moment.parse 82 | 83 | // path: 2021-08 84 | // file: 2021-08-12.md 85 | 86 | // handle path segment (we just compare indicies) 87 | /* 88 | pathTpl.split("/").forEach((element, index) => { 89 | 90 | if(element.match("") 91 | })*/ 92 | let mom: moment.Moment = moment(fileStr, fileTemplate); 93 | 94 | const entryMomentTpl = replaceDateTemplatesWithMomentsFormats(fileTemplate); 95 | const pathMomentTpl = replaceDateTemplatesWithMomentsFormats(pathTemplate); 96 | 97 | // filestr: "20210809" 98 | // path str: "/202108" 99 | // path tpl: ${year}-${month}" 100 | 101 | let a = moment(fileStr, entryMomentTpl); 102 | let b = moment(pathStr, pathMomentTpl); 103 | 104 | 105 | console.log("Parsed file string: ", a.format()); 106 | console.log("Parsed path string: ", b.format()); 107 | 108 | let result = moment(); 109 | 110 | // consolidate the two 111 | if(fileTemplate.indexOf("${year}")>=0) {result = result.year(a.year());} 112 | else {result = result.year(b.year());} 113 | if(fileTemplate.indexOf("${month}")>=0) {result = result.month(a.month());} 114 | else {result = result.month(b.month());} 115 | if(fileTemplate.indexOf("${day}")>=0) {result = result.date(a.date());} 116 | else {result = result.date(b.date());} 117 | 118 | return result.toDate(); 119 | 120 | } 121 | 122 | 123 | 124 | export function replaceDateTemplatesWithMomentsFormats(template: string): string { 125 | let matches: RegExpMatchArray | null = template.match(regExpDateFormats); 126 | if(matches === null) { 127 | return template; 128 | } 129 | 130 | matches.forEach(match => { 131 | switch (match) { 132 | case "${year}": 133 | template = template.replace(match, "YYYY"); break; 134 | case "${month}": 135 | template = template.replace(match, "MM"); break; 136 | case "${day}": 137 | template = template.replace(match, "DD"); break; 138 | case "${localTime}": 139 | template = template.replace(match, "LT"); break; 140 | case "${localDate}": 141 | template = template.replace(match, "LL"); break; 142 | case "${weekday}": 143 | template = template.replace(match, "dddd"); break; 144 | default: 145 | // check if custom format 146 | if (match.startsWith("${d:")) { 147 | 148 | let modifier = match.substring(match.indexOf("d:") + 2, match.length - 1); // includes } at the end 149 | // st.template = st.template.replace(match, mom.format(modifier)); 150 | // fix for #51 151 | template = template.replace(match, modifier); 152 | break; 153 | } 154 | break; 155 | } 156 | }); 157 | return template; 158 | 159 | } 160 | 161 | function assertCorrectDate(date: Date): void { 162 | let iso = date.toISOString(); 163 | let result = iso.substring(0, iso.indexOf('T')); 164 | console.log("Parsed result is: ", result); 165 | assert.match(result, /2020-07-09/); 166 | } -------------------------------------------------------------------------------- /src/test/direct/replace-variables-in-string.ts: -------------------------------------------------------------------------------- 1 | import { replaceDateFormats } from "../../util/dates"; 2 | 3 | 4 | 5 | let result: string = "no result"; 6 | result = replaceDateFormats("${year} and ${day} and some ${month}", new Date()); 7 | console.log(result); 8 | 9 | result = replaceDateFormats("Local time ${localTime} and custom format ${d:YY} for year", new Date()); 10 | console.log(result); -------------------------------------------------------------------------------- /src/test/direct/simple-run-command.ts: -------------------------------------------------------------------------------- 1 | const msg: string = 'Hello World'; 2 | console.log(msg); -------------------------------------------------------------------------------- /src/test/runTest.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /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.join(__dirname, '../../'); 10 | 11 | // The path to test runner 12 | // Passed to --extensionTestsPath 13 | const extensionTestsPath = path.join(__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.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /src/test/suite/index.d.ts: -------------------------------------------------------------------------------- 1 | export declare function run(): Promise; 2 | -------------------------------------------------------------------------------- /src/test/suite/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import { glob } from 'glob'; 3 | import Mocha from 'mocha'; 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.join(__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.join(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 | -------------------------------------------------------------------------------- /src/test/suite/input.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | 3 | 4 | // You can import and use all API from the 'vscode' module 5 | // as well as import your extension to test it 6 | import * as vscode from 'vscode'; 7 | import * as J from '../..'; 8 | import { TestLogger } from '../TestLogger'; 9 | 10 | suite.skip('Open Journal Entries', () => { 11 | vscode.window.showInformationMessage('Start all tests.'); 12 | 13 | /* */ 14 | test('Simple', async () => { 15 | assert.strictEqual(-1, [1, 2, 3].indexOf(5)); 16 | assert.strictEqual(-1, [1, 2, 3].indexOf(0)); 17 | }) 18 | ; 19 | test("Input '+1'", async () => { 20 | let config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("journal"); 21 | let ctrl = new J.Util.Ctrl(config); 22 | ctrl.logger = new TestLogger(false); 23 | 24 | 25 | let parser = new J.Actions.Parser(ctrl); 26 | let input = await parser.parseInput("+1"); 27 | 28 | 29 | assert.strictEqual(1, input.offset); 30 | }) 31 | ; 32 | 33 | test("Input '2021-05-12'", async () => { 34 | let config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("journal"); 35 | let ctrl = new J.Util.Ctrl(config); 36 | ctrl.logger = new TestLogger(false); 37 | 38 | 39 | let parser = new J.Actions.Parser(ctrl); 40 | let input = await parser.parseInput("2021-05-12"); 41 | 42 | assert.strictEqual(input.offset > 0 || input.offset <= 0, true); 43 | }) 44 | ; 45 | 46 | test("Input '05-12'", async () => { 47 | let config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("journal"); 48 | let ctrl = new J.Util.Ctrl(config); 49 | ctrl.logger = new TestLogger(false); 50 | 51 | 52 | let parser = new J.Actions.Parser(ctrl); 53 | let input = await parser.parseInput("05-12"); 54 | 55 | assert.strictEqual(input.offset > 0 || input.offset <= 0, true); 56 | }) 57 | ; 58 | 59 | test("Input '12'", async () => { 60 | let config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("journal"); 61 | let ctrl = new J.Util.Ctrl(config); 62 | ctrl.logger = new TestLogger(false); 63 | 64 | 65 | let parser = new J.Actions.Parser(ctrl); 66 | let input = await parser.parseInput("12"); 67 | 68 | assert.strictEqual(input.offset > 0 || input.offset <= 0, true); 69 | }) 70 | ; 71 | 72 | test("Input 'next monday'", async () => { 73 | let config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("journal"); 74 | let ctrl = new J.Util.Ctrl(config); 75 | ctrl.logger = new TestLogger(false); 76 | 77 | 78 | let parser = new J.Actions.Parser(ctrl); 79 | let input = await parser.parseInput("next monday"); 80 | 81 | 82 | assert.ok(input.offset > 0, "Offset not > 0, is "+input.offset); 83 | }); 84 | 85 | test("Input 'next tue'", async () => { 86 | let config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("journal"); 87 | let ctrl = new J.Util.Ctrl(config); 88 | ctrl.logger = new TestLogger(false); 89 | 90 | 91 | let parser = new J.Actions.Parser(ctrl); 92 | let input = await parser.parseInput("next tue"); 93 | 94 | assert.ok(input.offset > 0, "Offset not > 0, is "+input.offset); 95 | }); 96 | 97 | test("Input 'last wed'", async () => { 98 | let config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("journal"); 99 | let ctrl = new J.Util.Ctrl(config); 100 | ctrl.logger = new TestLogger(false); 101 | 102 | 103 | let parser = new J.Actions.Parser(ctrl); 104 | let input = await parser.parseInput("last wed"); 105 | 106 | assert.ok(input.offset < 0, "Offset not < 0, is "+input.offset); 107 | }); 108 | 109 | 110 | test("Input 'task +1 do this'", async () => { 111 | let config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("journal"); 112 | let ctrl = new J.Util.Ctrl(config); 113 | ctrl.logger = new TestLogger(false); 114 | 115 | 116 | let parser = new J.Actions.Parser(ctrl); 117 | let input = await parser.parseInput("task +1 text"); 118 | 119 | assert.ok(input.offset > 0, "Offset not > 0, is " + input.offset); 120 | assert.ok(input.hasFlags(), "Input has no flags " + JSON.stringify(input)); 121 | assert.ok(input.hasTask(), "Input has no task flag " + JSON.stringify(input)); 122 | assert.ok(input.text.length > 0, "Input has no text " + JSON.stringify(input)); 123 | }); 124 | 125 | test("Input 'task next wed do this'", async () => { 126 | let config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("journal"); 127 | let ctrl = new J.Util.Ctrl(config); 128 | ctrl.logger = new TestLogger(false); 129 | 130 | 131 | let parser = new J.Actions.Parser(ctrl); 132 | let input = await parser.parseInput("task next wed text"); 133 | 134 | assert.ok(input.offset > 0, "Offset not > 0, is " + input.offset); 135 | assert.ok(input.hasFlags(), "Input has no flags " + JSON.stringify(input)); 136 | assert.ok(input.hasTask(), "Input has no task flag " + JSON.stringify(input)); 137 | assert.ok(input.text.length > 0, "Input has no text " + JSON.stringify(input)); 138 | }); 139 | 140 | 141 | 142 | }); 143 | -------------------------------------------------------------------------------- /src/test/suite/notes_sync.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | 3 | 4 | // You can import and use all API from the 'vscode' module 5 | // as well as import your extension to test it 6 | import * as vscode from 'vscode'; 7 | import * as J from '../..'; 8 | import { LoadNotes } from '../../provider'; 9 | import { ShowEntryForInputCommand, ShowEntryForTodayCommand } from '../../provider/commands'; 10 | import { TestLogger } from '../TestLogger'; 11 | 12 | suite.skip('Test Notes Syncing', () => { 13 | 14 | test('Sync notes', async () => { 15 | 16 | 17 | let config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("journal"); 18 | let ctrl = new J.Util.Ctrl(config); 19 | ctrl.logger = new TestLogger(false); 20 | 21 | // create a new entry.. remember length 22 | await vscode.commands.executeCommand("journal.today"); 23 | let editor = vscode.window.activeTextEditor; 24 | assert.ok(editor, "Failed to open today's journal"); 25 | 26 | let originalLength = editor.document.getText().length; 27 | assert.ok(originalLength > 0, "Nothing in document"); 28 | 29 | // create a new note 30 | let input = new J.Model.NoteInput(); 31 | input.text = "This is a test note"; 32 | let notesDoc : vscode.TextDocument = await new LoadNotes(input, ctrl).load(); 33 | let notesEditor = await ctrl.ui.showDocument(notesDoc); 34 | assert.ok(notesEditor, "Failed to open note"); 35 | 36 | await new Promise( resolve => setTimeout(resolve, 2000)); 37 | 38 | await vscode.commands.executeCommand("journal.today"); 39 | let editorAgain = vscode.window.activeTextEditor; 40 | assert.ok(editorAgain, "Failed to open today's journal"); 41 | 42 | let newLength = editorAgain.document.getText().length; 43 | 44 | assert.ok(newLength > originalLength, "Notes link wasn't injected"); 45 | 46 | // check length of new entry 47 | }).timeout(5000) 48 | ; 49 | }); -------------------------------------------------------------------------------- /src/test/suite/read_templates.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import moment = require('moment'); 3 | 4 | // You can import and use all API from the 'vscode' module 5 | // as well as import your extension to test it 6 | import * as vscode from 'vscode'; 7 | import * as J from '../..'; 8 | import { TestLogger } from '../TestLogger'; 9 | import { suite, before, test } from 'mocha'; 10 | import { Ctrl } from '../../util'; 11 | import { fstat } from 'fs'; 12 | import path = require('path'); 13 | 14 | suite('Read templates from configuration', () => { 15 | let ctrl: J.Util.Ctrl; 16 | 17 | before(() => { 18 | let config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("journal"); 19 | ctrl = new J.Util.Ctrl(config); 20 | ctrl.logger = new TestLogger(true); 21 | 22 | }); 23 | 24 | 25 | 26 | test.skip('Sync scopes', async () => { 27 | const scopes = ctrl.config.getScopes(); 28 | assert.strictEqual(scopes.length, 3, "Invalid scope number"); 29 | }); 30 | 31 | test.skip('Test resolving note paths', async () => { 32 | const inPriv = new J.Model.Input(0); 33 | inPriv.text = "#priv a note created in private scope"; 34 | const pathPriv = await ctrl.parser.resolveNotePathForInput(inPriv); 35 | const uriPriv = vscode.Uri.file(pathPriv); 36 | 37 | 38 | const inWork = new J.Model.Input(0); 39 | inWork.text = "#work a note created in work scope"; 40 | const pathWork = await ctrl.parser.resolveNotePathForInput(inWork); 41 | const uriWork = vscode.Uri.file(pathWork); 42 | 43 | 44 | assert.ok(uriWork.path.match(/.*\/work\/.*/), "Wrong path to work note: "+uriWork.path); 45 | assert.ok(uriPriv.path.match(/\/private\//), "Wrong path to private note: "+uriPriv.path); 46 | 47 | }); 48 | test('Create notes in different scopes', async () => { 49 | const scopes = ctrl.config.getScopes(); 50 | assert.strictEqual(scopes.length, 3, "Invalid scope number"); 51 | 52 | 53 | let a = ctrl.config.getScopes(); 54 | 55 | // create a new note 56 | const privInput = await ctrl.parser.parseInput("#priv a note created in private scop"); 57 | let privNotes = await new J.Provider.LoadNotes(privInput, ctrl); 58 | let privDoc: vscode.TextDocument = await privNotes.load(); 59 | privDoc = await ctrl.ui.saveDocument(privDoc); 60 | const privUri = privDoc.uri; 61 | 62 | 63 | 64 | const workInput = await ctrl.parser.parseInput("#work a note created in work scope"); 65 | let workDoc: vscode.TextDocument = await new J.Provider.LoadNotes(workInput, ctrl).load(); 66 | workDoc = await ctrl.ui.saveDocument(workDoc); 67 | const uriWork = workDoc.uri; 68 | 69 | 70 | assert.ok(uriWork.path.match(/.*\/work\/.*/), "Wrong path to work note: "+uriWork.path); 71 | assert.ok(privUri.path.match(/\/private\//), "Wrong path to private note: "+privUri.path); 72 | }).timeout(50000); 73 | 74 | }); -------------------------------------------------------------------------------- /src/test/suite/week_input.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import moment = require('moment'); 3 | 4 | // You can import and use all API from the 'vscode' module 5 | // as well as import your extension to test it 6 | import * as vscode from 'vscode'; 7 | import * as J from '../..'; 8 | import { TestLogger } from '../TestLogger'; 9 | import {suite, before,test} from 'mocha'; 10 | 11 | 12 | suite.skip('Open Week Entries', () => { 13 | vscode.window.showInformationMessage('Start all tests.'); 14 | let ctrl: J.Util.Ctrl; 15 | 16 | before(() => { 17 | let config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("journal"); 18 | ctrl = new J.Util.Ctrl(config); 19 | ctrl.logger = new TestLogger(false); 20 | 21 | }); 22 | 23 | test("Input 'w13'", async () => { 24 | 25 | let parser = new J.Actions.Parser(ctrl); 26 | let input = await parser.parseInput("w13"); 27 | 28 | assert.ok(! input.hasOffset(), "Offset is set, is " + input.offset); 29 | assert.ok(! input.hasFlags(), "Input has flags " + JSON.stringify(input)); 30 | assert.ok(! input.hasTask(), "Input has task flag " + JSON.stringify(input)); 31 | assert.ok(! input.hasText(), "Input has no text " + JSON.stringify(input)); 32 | assert.ok(input.hasWeek(), "Input has no week definition " + JSON.stringify(input)); 33 | }); 34 | 35 | 36 | test("Input 'w'", async () => { 37 | let config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("journal"); 38 | let ctrl = new J.Util.Ctrl(config); 39 | ctrl.logger = new TestLogger(false); 40 | 41 | let parser = new J.Actions.Parser(ctrl); 42 | let input = await parser.parseInput("w"); 43 | let currentWeek = moment().week(); 44 | 45 | assert.ok(!input.hasOffset(), "Offset is set, is " + input.offset); 46 | assert.ok(!input.hasFlags(), "Input has flags " + JSON.stringify(input)); 47 | assert.ok(!input.hasTask(), "Input has task flag " + JSON.stringify(input)); 48 | assert.ok(!input.hasText(), "Input has no text " + JSON.stringify(input)); 49 | assert.ok(input.hasWeek(), "Input has no week definition " + JSON.stringify(input)); 50 | 51 | assert.strictEqual(input.week, currentWeek, "weeks mismatch"); 52 | }); 53 | 54 | test("Input 'next week'", async () => { 55 | let config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("journal"); 56 | let ctrl = new J.Util.Ctrl(config); 57 | ctrl.logger = new TestLogger(false); 58 | 59 | let parser = new J.Actions.Parser(ctrl); 60 | let input = await parser.parseInput("next week"); 61 | 62 | let currentWeek = moment().week(); 63 | 64 | assert.ok(!input.hasOffset(), "Offset is set, is " + input.offset); 65 | assert.ok(!input.hasFlags(), "Input has flags " + JSON.stringify(input)); 66 | assert.ok(!input.hasTask(), "Input has task flag " + JSON.stringify(input)); 67 | assert.ok(!input.hasText(), "Input has no text " + JSON.stringify(input)); 68 | assert.ok(input.hasWeek(), "Input has no week definition " + JSON.stringify(input)); 69 | 70 | assert.strictEqual(input.week, currentWeek + 1, "weeks mismatch"); 71 | }); 72 | 73 | }); 74 | -------------------------------------------------------------------------------- /src/util/controller.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 19 | 20 | 'use strict'; 21 | 22 | 23 | import * as J from '../.'; 24 | import * as vscode from 'vscode'; 25 | 26 | export class Ctrl { 27 | 28 | 29 | private _config: J.Extension.Configuration; 30 | private _ui: J.Extension.Dialogues; 31 | private _parser: J.Actions.Parser; 32 | private _writer: J.Actions.Writer; 33 | private _reader: J.Actions.Reader; 34 | 35 | 36 | private _logger: J.Util.Logger | undefined; 37 | 38 | 39 | private _inject: J.Actions.Inject; 40 | 41 | 42 | constructor(vscodeConfig: vscode.WorkspaceConfiguration) { 43 | this._config = new J.Extension.Configuration(vscodeConfig); 44 | this._parser = new J.Actions.Parser(this); 45 | this._writer = new J.Actions.Writer(this); 46 | this._reader = new J.Actions.Reader(this); 47 | this._inject = new J.Actions.Inject(this); 48 | this._ui = new J.Extension.Dialogues(this); 49 | } 50 | 51 | 52 | 53 | /** 54 | * Getter $ui 55 | * @return {J.Extension.VSCode} 56 | */ 57 | public get ui(): J.Extension.Dialogues { 58 | return this._ui; 59 | } 60 | 61 | /** 62 | * Getter $writer 63 | * @return {J.Actions.Writer} 64 | */ 65 | public get writer(): J.Actions.Writer { 66 | return this._writer; 67 | } 68 | 69 | /** 70 | * Getter $reader 71 | * @return {J.Actions.Reader} 72 | */ 73 | public get reader(): J.Actions.Reader { 74 | return this._reader; 75 | } 76 | 77 | /** 78 | * Getter $parser 79 | * @return {J.Actions.Parser} 80 | */ 81 | public get parser(): J.Actions.Parser { 82 | return this._parser; 83 | } 84 | 85 | /** 86 | * Getter $config 87 | * @return {J.Extension.Configuration} 88 | */ 89 | public get config(): J.Extension.Configuration { 90 | return this._config; 91 | } 92 | 93 | /** 94 | * Getter inject 95 | * @return {J.Actions.Inject} 96 | */ 97 | public get inject(): J.Actions.Inject { 98 | return this._inject; 99 | } 100 | 101 | /** 102 | * Getter logger 103 | * @return {J.Util.Logger} 104 | */ 105 | public get logger(): J.Util.Logger { 106 | if(J.Util.isNullOrUndefined(this._logger)) { throw Error("Tried to access undefined logger in journal"); } 107 | return this._logger!; 108 | } 109 | 110 | /** 111 | * Setter logger 112 | * @param {J.Util.Logger} value 113 | */ 114 | public set logger(value: J.Util.Logger) { 115 | this._logger = value; 116 | } 117 | 118 | 119 | } -------------------------------------------------------------------------------- /src/util/index.d.ts: -------------------------------------------------------------------------------- 1 | export { Ctrl } from './controller'; 2 | export { ConsoleLogger, Logger } from './logger'; 3 | export { checkIfFileIsAccessible, denormalizeFilename, formatDate, getDayAsString, getDayOfWeekForString, getEntryPathForDate, getFileInURI, getFilePathInDateFolder, getNextLine, getPathOfMonth, normalizeFilename, prefixZero, getPathAsString, isNotNullOrUndefined, isNullOrUndefined, stringIsNotEmpty, isError, isString } from './util'; 4 | -------------------------------------------------------------------------------- /src/util/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 19 | 20 | export { Ctrl } from './controller'; 21 | 22 | export { ConsoleLogger, Logger } from './logger'; 23 | 24 | export { 25 | isNotNullOrUndefined, 26 | isNullOrUndefined, 27 | isError 28 | 29 | 30 | } from './util'; 31 | 32 | 33 | export { 34 | formatDate, 35 | getDayOfWeekForString, 36 | replaceDateFormats, 37 | replaceDateTemplatesWithMomentsFormats, 38 | getMonthForString 39 | } 40 | from './dates'; 41 | 42 | export { 43 | getNextLine, 44 | denormalizeFilename, 45 | getDayAsString, 46 | isString, 47 | normalizeFilename, 48 | prefixZero,replaceVariableValue,stringIsNotEmpty 49 | } 50 | from './strings'; 51 | 52 | export { 53 | checkIfFileIsAccessible, 54 | getDateFromURI, 55 | getDateFromURIAndConfig, 56 | getFileInURI, 57 | getFilePathInDateFolder, 58 | getPathAsString, 59 | getPathOfMonth, 60 | inferType, 61 | resolvePath 62 | } 63 | from './paths'; 64 | 65 | /* 66 | declare module Comm { 67 | export const Comfiguration = _Configuration; 68 | export const TemplateInfo = _TemplateInfo; 69 | export const Util = _Util; 70 | } 71 | 72 | export namespace Common { 73 | export const Comfiguration = _Configuration; 74 | export const TemplateInfo = _TemplateInfo; 75 | export const Util = _Util; 76 | } 77 | */ -------------------------------------------------------------------------------- /src/util/logger.d.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as J from '../.'; 3 | export interface Logger { 4 | trace(message: string, ...optionalParams: any[]): void; 5 | debug(message: string, ...optionalParams: any[]): void; 6 | error(message: string, ...optionalParams: any[]): void; 7 | printError(error: Error): void; 8 | showChannel(): void; 9 | } 10 | export declare class ConsoleLogger implements Logger { 11 | ctrl: J.Util.Ctrl; 12 | channel: vscode.OutputChannel; 13 | private devMode; 14 | constructor(ctrl: J.Util.Ctrl, channel: vscode.OutputChannel); 15 | showChannel(): void; 16 | traceLine(message: string, ...optionalParams: any[]): void; 17 | trace(message: string, ...optionalParams: any[]): void; 18 | debug(message: string, ...optionalParams: any[]): void; 19 | printError(error: Error): void; 20 | error(message: string, ...optionalParams: any[]): void; 21 | private appendCurrentTime; 22 | } 23 | -------------------------------------------------------------------------------- /src/util/logger.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 19 | 20 | import moment = require('moment'); 21 | import * as vscode from 'vscode'; 22 | import * as J from '../.'; 23 | 24 | export interface Logger { 25 | trace(message: string, ...optionalParams: any[]): void; 26 | debug(message: string, ...optionalParams: any[]): void; 27 | error(message: string, ...optionalParams: any[]): void; 28 | printError(error: Error): void; 29 | showChannel(): void; 30 | } 31 | 32 | 33 | 34 | export class ConsoleLogger implements Logger { 35 | private devMode = false; 36 | 37 | 38 | constructor(public ctrl: J.Util.Ctrl, public channel: vscode.OutputChannel) { 39 | this.devMode = ctrl.config.isDevelopmentModeEnabled(); 40 | } 41 | 42 | public showChannel(): void { 43 | this.channel.show(); 44 | } 45 | 46 | public traceLine(message: string, ...optionalParams: any[]): void { 47 | if (this.devMode === true) { 48 | this.appendCurrentTime(); 49 | this.channel.append(" [trace] "); 50 | 51 | this.channel.append(message); 52 | optionalParams.forEach(msg => this.channel.append(msg+"")); 53 | 54 | this.channel.appendLine(""); 55 | 56 | 57 | console.trace("[TRACE]", message, ...optionalParams); 58 | } 59 | } 60 | 61 | public trace(message: string, ...optionalParams: any[]): void { 62 | if (this.devMode === true) { 63 | this.appendCurrentTime(); 64 | this.channel.append(" [trace] "); 65 | 66 | this.channel.append(message); 67 | optionalParams.forEach(msg => this.channel.append(msg+"")); 68 | 69 | this.channel.appendLine(""); 70 | 71 | console.info("[TRACE]", message, ...optionalParams); 72 | } 73 | } 74 | 75 | 76 | public debug(message: string, ...optionalParams: any[]): void { 77 | if (this.devMode === true) { 78 | this.appendCurrentTime(); 79 | this.channel.append(" [debug] "); 80 | 81 | this.channel.append(message); 82 | optionalParams.forEach(msg => this.channel.append(msg+"")); 83 | 84 | this.channel.appendLine(""); 85 | 86 | console.log("[DEBUG]", message, ...optionalParams); 87 | } 88 | } 89 | 90 | public printError(error: Error): void { 91 | this.error(error.message, error); 92 | 93 | 94 | } 95 | 96 | public error(message: string, ...optionalParams: any[]): void { 97 | 98 | this.appendCurrentTime(); 99 | this.channel.append(" [ERROR] "); 100 | 101 | this.channel.append(message); 102 | 103 | if(optionalParams.length > 0) { 104 | this.channel.append(" "); 105 | } 106 | optionalParams.forEach(msg => { 107 | if(J.Util.isString(msg)) { 108 | this.channel.append(msg+""); 109 | } 110 | else if(J.Util.isError(msg)) { 111 | if(J.Util.isNotNullOrUndefined(msg.stack)) { 112 | let method: string | undefined = /at \w+\.(\w+)/.exec(msg.stack!.split('\n')[2])?.pop(); 113 | this.channel.append("("+method+")"); 114 | } 115 | 116 | this.channel.appendLine("See Exception below."); 117 | if(J.Util.isNotNullOrUndefined(msg.stack)) { 118 | this.channel.append(msg.stack); 119 | } 120 | 121 | } 122 | else { 123 | this.channel.appendLine(JSON.stringify(msg)); 124 | } 125 | }); 126 | this.channel.appendLine(""); 127 | 128 | console.error("[ERROR]", message, ...optionalParams); 129 | } 130 | 131 | 132 | 133 | private appendCurrentTime() : void { 134 | this.channel.append("["); 135 | this.channel.append(moment(new Date()).format('HH:mm:ss.SSS')); 136 | this.channel.append("]"); 137 | } 138 | } 139 | 140 | -------------------------------------------------------------------------------- /src/util/startup.d.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as J from '../.'; 3 | export declare class Startup { 4 | context: vscode.ExtensionContext; 5 | config: vscode.WorkspaceConfiguration; 6 | /** 7 | * 8 | */ 9 | constructor(context: vscode.ExtensionContext, config: vscode.WorkspaceConfiguration); 10 | initialize(): Promise; 11 | registerLoggingChannel(ctrl: J.Util.Ctrl, context: vscode.ExtensionContext): Promise; 12 | registerCodeLens(ctrl: J.Util.Ctrl, context: vscode.ExtensionContext): Promise; 13 | registerCommands(ctrl: J.Util.Ctrl, context: vscode.ExtensionContext): Promise; 14 | /** 15 | * Sets default syntax highlighting settings on startup, we try to differentiate between dark and light themes 16 | * 17 | * @param {J.Util.Ctrl} ctrl 18 | * @param {vscode.ExtensionContext} context 19 | * @returns {Q.Promise} 20 | * @memberof Startup 21 | */ 22 | registerSyntaxHighlighting(ctrl: J.Util.Ctrl): Promise; 23 | } 24 | -------------------------------------------------------------------------------- /src/util/strings.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 19 | 'use strict'; 20 | 21 | import { isNotNullOrUndefined } from '.'; 22 | 23 | 24 | export function getDayAsString(date: Date): string { 25 | return prefixZero(date.getDate()); 26 | } 27 | 28 | /** 29 | * Takes a number and a leading 0 if it is only one digit, e.g. 9 -> "09" 30 | */ 31 | export function prefixZero(nr: number): string { 32 | let current = nr.toString(); 33 | if (current.length === 1) { current = '0' + current; } 34 | return current; 35 | } 36 | 37 | /** 38 | * Returns a normalized filename for given string. Special characters will be replaced. 39 | * @param input 40 | */ 41 | export function normalizeFilename(input: string): string { 42 | let result = input.trim(); 43 | result = result.replace(/\s/g, '_'); 44 | result = result.replace(/\\|\/|\<|\>|\:|\n|\||\?|\*/g, '-'); 45 | return result; 46 | } 47 | 48 | /** 49 | * Converts a filename into its readable form (for file links) 50 | * 51 | * @param input the line to convert 52 | * @param ext the file extension used for notes and journal entries 53 | */ 54 | export function denormalizeFilename(input: string): string { 55 | 56 | input = input.substring(0, input.lastIndexOf(".")); 57 | input = input.replace(/_/g, " "); 58 | input = input.replace(/-/g, " "); 59 | 60 | return input; 61 | } 62 | 63 | export function stringIsNotEmpty(value: string | undefined | null) : boolean { 64 | return value !== null && value !== undefined && value.length > 0; 65 | } 66 | 67 | export function isString(object: any | string | undefined ): boolean { 68 | return isNotNullOrUndefined(object) && typeof object === 'string'; 69 | } 70 | 71 | 72 | 73 | export function replaceVariableValue(key: string, value: string, template: string): string { 74 | if (template.search("\\$\\{" + key + "\\}") >= 0) { 75 | return template.replace("${" + key + "}", value); 76 | } else { 77 | return template; 78 | } 79 | } 80 | 81 | 82 | export function getNextLine(content: string): string[] { 83 | 84 | let res: string[] = ["", ""]; 85 | 86 | let pos: number = content.indexOf('\n'); 87 | if (pos > 0) { 88 | res[0] = content.slice(0, pos); 89 | res[1] = content.slice(pos + 1, content.length); 90 | } else { 91 | res[0] = content; 92 | } 93 | return res; 94 | } -------------------------------------------------------------------------------- /src/util/util.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import * as Path from 'path'; 3 | /** 4 | * Utility Methods for the vscode-journal extension 5 | */ 6 | /** 7 | * Check if config dir exists, otherwise copy defaults from extension directory 8 | * We can't Q's nfcall, since those nodejs operations don't have (err,data) responses 9 | * 10 | * fs.exists does only return "true", see https://github.com/petkaantonov/bluebird/issues/418 11 | * @param path 12 | */ 13 | export declare function checkIfFileIsAccessible(path: string): Promise; 14 | /** 15 | * Return day of week for given string. 16 | */ 17 | export declare function getDayOfWeekForString(day: string): number; 18 | /** 19 | * Formats a given Date in long format (for Header in journal pages) 20 | */ 21 | export declare function formatDate(date: Date, template: string, locale: string): string; 22 | /** 23 | * Returns target for notes as string; 24 | */ 25 | export declare function getFilePathInDateFolder(date: Date, filename: string, base: string, ext: string): Promise; 26 | /** 27 | * Returns the path for a given date as string 28 | * @deprecated 29 | */ 30 | export declare function getEntryPathForDate(date: Date, base: string, ext: string): Promise; 31 | export declare function getPathAsString(path: Path.ParsedPath): string; 32 | /** 33 | * Returns the filename of a given URI. 34 | * Example: "21" of uri "file://some/path/to/21.md"" 35 | * @param uri 36 | */ 37 | export declare function getFileInURI(uri: string, withExtension?: boolean): string; 38 | export declare function getNextLine(content: string): string[]; 39 | /** 40 | * Returns path to month folder. 41 | */ 42 | export declare function getPathOfMonth(date: Date, base: string): string; 43 | export declare function getDayAsString(date: Date): string; 44 | /** 45 | * Takes a number and a leading 0 if it is only one digit, e.g. 9 -> "09" 46 | */ 47 | export declare function prefixZero(nr: number): string; 48 | /** 49 | * Returns a normalized filename for given string. Special characters will be replaced. 50 | * @param input 51 | */ 52 | export declare function normalizeFilename(input: string): string; 53 | /** 54 | * Converts a filename into its readable form (for file links) 55 | * 56 | * @param input the line to convert 57 | * @param ext the file extension used for notes and journal entries 58 | */ 59 | export declare function denormalizeFilename(input: string): string; 60 | export declare function isNullOrUndefined(value: any | undefined | null): boolean; 61 | export declare function isNotNullOrUndefined(value: any | undefined | null): boolean; 62 | export declare function stringIsNotEmpty(value: string | undefined | null): boolean; 63 | export declare function isString(object: any | string | undefined): boolean; 64 | export declare function isError(object: any | Error | undefined): boolean; 65 | -------------------------------------------------------------------------------- /src/util/util.ts: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Patrick Maué 2 | // 3 | // This file is part of vscode-journal. 4 | // 5 | // vscode-journal is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // vscode-journal is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with vscode-journal. If not, see . 17 | // 18 | 19 | 20 | 21 | 'use strict'; 22 | import { types } from 'util'; 23 | 24 | export function isNullOrUndefined(value: any | undefined | null): boolean { 25 | return value === null || value === undefined; 26 | } 27 | 28 | 29 | export function isNotNullOrUndefined(value: any | undefined | null): boolean { 30 | return value !== null && value !== undefined; 31 | } 32 | 33 | 34 | export function isError(object: any | Error | undefined ): boolean { 35 | return isNotNullOrUndefined(object) && types.isNativeError(object); 36 | } 37 | 38 | -------------------------------------------------------------------------------- /test/Readme.md: -------------------------------------------------------------------------------- 1 | # Test workspaces 2 | 3 | * ws_manual -> default workspace for extension tests 4 | * ws_unittests -> workspace for the unit tests (has to be cleaned before each test) 5 | 6 | Both are configured in launch.json -------------------------------------------------------------------------------- /test/backup/user-settings.cjson: -------------------------------------------------------------------------------- 1 | { 2 | "opa.path": "C:\\Users\\patrick.maue\\opa.exe", 3 | "git.ignoreMissingGitWarning": true, 4 | "workbench.colorTheme": "Visual Studio Dark", 5 | 6 | "git.autofetch": true, 7 | "terminal.integrated.defaultProfile.windows": "Git Bash", 8 | "vscode-kubernetes.helm-path": "C:\\Users\\patrick.maue\\AppData\\Local\\Microsoft\\WindowsApps\\oc.exe", 9 | "vs-kubernetes": { 10 | "vscode-kubernetes.kubectl-path.windows": "C:\\Users\\patrick.maue\\.vs-kubernetes\\tools\\kubectl\\kubectl.exe", 11 | "vscode-kubernetes.minikube-path.windows": "C:\\Users\\patrick.maue\\.vs-kubernetes\\tools\\minikube\\windows-amd64\\minikube.exe" 12 | }, 13 | "redhat.telemetry.enabled": false, 14 | "editor.tokenColorCustomizations": { 15 | "textMateRules": [ 16 | { 17 | "scope": "text.html.markdown.journal.task.open.bullet", 18 | "settings": { 19 | "foreground": "#FFFF00" 20 | } 21 | }, 22 | { 23 | "scope": "text.html.markdown.journal.task.open.marker", 24 | "settings": { 25 | "foreground": "#FFFF00" 26 | } 27 | }, 28 | { 29 | "scope": "text.html.markdown.journal.task.open.keyword", 30 | "settings": { 31 | "fontStyle": "italic" 32 | } 33 | }, 34 | { 35 | "scope": "text.html.markdown.journal.task.open.text", 36 | "settings": {} 37 | }, 38 | { 39 | "scope": "text.html.markdown.journal.task.completed.keyword", 40 | "settings": { 41 | "fontStyle": "italic" 42 | } 43 | }, 44 | { 45 | "scope": "text.html.markdown.journal.task.completed.marker", 46 | "settings": { 47 | "foreground": "#AAAAAA" 48 | } 49 | }, 50 | { 51 | "scope": "text.html.markdown.journal.task.completed.text", 52 | "settings": { 53 | "foreground": "#AAAAAA" 54 | } 55 | }, 56 | { 57 | "scope": "text.html.markdown.journal.task.completed.bullet", 58 | "settings": { 59 | "foreground": "#FFFF00" 60 | } 61 | }, 62 | { 63 | "scope": "text.html.markdown.journal.memo.keyword", 64 | "settings": { 65 | "fontStyle": "italic" 66 | } 67 | }, 68 | { 69 | "scope": "text.html.markdown.journal.memo.bullet", 70 | "settings": { 71 | "foreground": "#FFFF00" 72 | } 73 | }, 74 | { 75 | "scope": "text.html.markdown.journal.scope", 76 | "settings": { 77 | "foreground": "#FFFF00" 78 | } 79 | }, 80 | { 81 | "scope": "text.html.markdown.journal.link.keyword", 82 | "settings": { 83 | "fontStyle": "italic" 84 | } 85 | }, 86 | { 87 | "scope": "text.html.markdown.journal.link.bullet", 88 | "settings": { 89 | "foreground": "#FFFF00" 90 | } 91 | } 92 | ] 93 | }, 94 | "journal.locale": "de-DE", 95 | "journal.scopes": [ 96 | 97 | 98 | 99 | 100 | { 101 | "name": "work", 102 | "patterns": { 103 | "notes": { 104 | "path": "${base}/scopes/work", 105 | "file": "${month}-${day}-${input}.${ext}" 106 | }, 107 | } 108 | }, 109 | 110 | { 111 | "name": "plan", 112 | "patterns": { 113 | "notes": { 114 | "path": "${base}/scopes/plan", 115 | "file": "${year}-${input}.${ext}" 116 | }, 117 | } 118 | }, 119 | { 120 | "name": "priv", 121 | "patterns": { 122 | "notes": { 123 | "path": "${base}/scopes/private", 124 | "file": "${month}-${input}.${ext}" 125 | }, 126 | } 127 | }, 128 | { 129 | "name": "diga", 130 | "patterns": { 131 | "notes": { 132 | "path": "${base}/scopes/diga", 133 | "file": "${localDate}-${input}.${ext}" 134 | }, 135 | } 136 | } 137 | ], 138 | "journal.templates": [ 139 | 140 | { 141 | "name": "memo", 142 | "template": "- Memo: ${input} (created: ${localTime}) " 143 | }, 144 | { 145 | "name": "task", 146 | "template": "- [] Task: ${input} (created: ${localTime})", 147 | "after": "## Tasks" 148 | }, 149 | { 150 | "name": "event", 151 | "template": "- [!] Task: ${input} (created: ${localTime})" 152 | }, 153 | { 154 | "name": "entry", 155 | "template": "# ${d:dddd, MMMM DD YYYY}\n\n## Tasks\n\n## Notes\n\n" 156 | }, 157 | { 158 | "name": "time", 159 | "template": "${localTime}" 160 | }, 161 | { 162 | "name": "note", 163 | "template": "# ${input}\n\n${tags}\n" 164 | }, 165 | { 166 | "name": "files", 167 | "template": "- NOTE: [${title}](${link})", 168 | "after": "## Notes" 169 | 170 | } 171 | ], 172 | "journal.patterns": { 173 | 174 | 175 | 176 | 177 | "notes": { 178 | "path": "${base}/${year}/${month}/${day}", 179 | "file": "${input}.${ext}" 180 | }, 181 | "entries": { 182 | "path": "${base}/${year}/${month}", 183 | "file": "${day}.${ext}" 184 | }, 185 | "weeks": { 186 | "path": "${base}/${year}", 187 | "file": "week_${week}.${ext}" 188 | } 189 | }, 190 | "diffEditor.ignoreTrimWhitespace": false, 191 | "journal.dev": false, 192 | "journal.base": "C:\\Users\\pajom\\OneDrive\\Journal", 193 | "settingsSync.ignoredSettings": [ 194 | "journal.base" 195 | ], 196 | "[markdown]": { 197 | // quickSuggestions true will provide suggestions as you type. 198 | // If you turn this on and DO NOT want suggestions 199 | // for non-wiki-link, non-tag words, 200 | "editor.quickSuggestions": true, 201 | // This is poorly documented, but seems to offer suggestions 202 | // from any word in open document when turned on, which 203 | // can be a little distracting in markdown docs: 204 | "editor.wordBasedSuggestions": false, 205 | "editor.tabSize": 2, 206 | // Set this to false if you turn on quickSuggestions 207 | // but don't want suggestions for markdown related snippets 208 | // as you type: 209 | "editor.suggest.showSnippets": true, 210 | }, 211 | "editor.quickSuggestions": { 212 | 213 | "other": "on", 214 | "comments": "off", 215 | "strings": "off", 216 | 217 | }, 218 | "security.workspace.trust.untrustedFiles": "open" 219 | 220 | } -------------------------------------------------------------------------------- /test/ws_manual/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.vscode 3 | !.gitignore -------------------------------------------------------------------------------- /test/ws_unittests/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.vscode 3 | !.gitignore -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "skipLibCheck": true, /* otherwise lint is printing a lot of errors */ 4 | "module": "Node16", 5 | "target": "ES2022", 6 | "lib": [ 7 | "ES2022" 8 | ], 9 | "outDir": "out", 10 | "rootDirs": ["src"], 11 | "resolveJsonModule": true, /* required to import translations.json */ 12 | /* Strict Type-Checking Option */ 13 | "strict": true, /* enable all strict type-checking options */ 14 | /* Additional Checks */ 15 | // "noUnusedLocals": false /* Report errors on unused locals. */ 16 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 17 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 18 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 19 | // "esModuleInterop": true, 20 | // "allowSyntheticDefaultImports": true, 21 | // "experimentalDecorators": true, 22 | // "emitDecoratorMetadata": true, 23 | "sourceMap": true, 24 | "declaration": true, 25 | } 26 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | //@ts-check 2 | 3 | 'use strict'; 4 | 5 | const path = require('path'); 6 | 7 | //@ts-check 8 | /** @typedef {import('webpack').Configuration} WebpackConfig **/ 9 | 10 | /** @type WebpackConfig */ 11 | const extensionConfig = { 12 | target: 'node', // vscode extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/ 13 | mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production') 14 | 15 | entry: './src/extension.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ 16 | output: { 17 | // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/ 18 | path: path.resolve(__dirname, 'dist'), 19 | filename: 'extension.js', 20 | libraryTarget: 'commonjs2' 21 | }, 22 | externals: { 23 | vscode: 'commonjs vscode' // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/ 24 | // modules added here also need to be added in the .vscodeignore file 25 | }, 26 | resolve: { 27 | // support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader 28 | extensions: ['.ts', '.js'] 29 | }, 30 | module: { 31 | rules: [ 32 | { 33 | test: /\.ts$/, 34 | exclude: /node_modules/, 35 | use: [ 36 | { 37 | loader: 'ts-loader' 38 | } 39 | ] 40 | } 41 | ] 42 | }, 43 | devtool: 'nosources-source-map', 44 | infrastructureLogging: { 45 | level: "log", // enables logging required for problem matchers 46 | }, 47 | }; 48 | module.exports = [ extensionConfig ]; --------------------------------------------------------------------------------