Skip to content
Closed
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
53d2d8e
Markdown lint fixes
sytone May 3, 2022
bea862f
Merge branch 'main' of https://github.com/schemar/obsidian-tasks
sytone May 4, 2022
f2104c9
feat: disregard the global tag
sytone May 4, 2022
1bb8f9f
feat: allow tag query to be hashless
sytone May 5, 2022
d8a3168
Merge branch 'main' of https://github.com/schemar/obsidian-tasks
sytone May 5, 2022
75eba71
test: refactor tag tests out and make them data driven
sytone May 5, 2022
d8a95b0
feat: update query language, add tests, make hash optional and query …
sytone May 5, 2022
547f776
test: add tests to validate substring
sytone May 5, 2022
9716007
test: chect to see if global tag is not found in query
sytone May 5, 2022
a3964f7
docs: update filter documentation on the tag query
sytone May 5, 2022
9c64527
feat: add ability to sort by tag instance/index
sytone May 7, 2022
f90abc4
Merge branch 'main' of https://github.com/schemar/obsidian-tasks
sytone May 7, 2022
4b88b4a
fix: resolved issues raised in PR.
sytone May 15, 2022
f02f2d2
build: add markdown lint to list command
sytone May 15, 2022
67e97c6
fix: resolve remaining comments and revert markdown lint changes
sytone May 15, 2022
b2ace95
docs: update grammar based issue and sorting description for tags
sytone May 15, 2022
3848523
Merge branch 'main' of https://github.com/schemar/obsidian-tasks
sytone May 17, 2022
788e25e
feat: Allow additional tasks states and filtering by states
sytone May 19, 2022
de6cb0f
feat: add cancelled and in progress along with minimal supported task…
sytone May 20, 2022
010f439
feat: add query capabilities for status
sytone May 20, 2022
bd31de1
build: enable minify main.js 604.9kb -> 283.8kb
sytone May 20, 2022
b08bdc8
build: move script to symlinks
sytone May 20, 2022
f58186a
fix: Update li rendering to match obsidian
sytone May 20, 2022
9d27b5d
feat: add feature flags to plugin
sytone May 21, 2022
149eb34
update flags for next menu feature only
sytone May 21, 2022
d5976ba
feat: add git attributes to force LF on windows
sytone May 22, 2022
292f684
Merge commit 'ff1216477242401139fee2031b28ebec9422334c' into sytone/i…
sytone May 22, 2022
69b8517
Merge from upstream main
sytone May 29, 2022
2b92499
Merge branch 'main' of https://github.com/schemar/obsidian-tasks into…
sytone May 29, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.js text eol=lf
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does changing line endings relate to "feat: Allow additional tasks states and filtering by states"?

Could this be a separate PR, with a statement about motivation?

What impact will this have on anyone who already has a clone of this repo on Windows? In the past, when these attributes have changed, I think I have struggled to get a clean repo, because it sees any local CRLF as differences. I seem to recall being confused, in any case.

From my own experience, I feel that mixing (seemingly) unrelated changes inside one PR generally makes it harder for reviews. The more focussed a PR is, the quicker it can be reviewed, and the quicker separate PRs for later features can be started.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reduces noise in windows, if you have CRLF and then add them in the file will just go away as git is smart enough to know that they are the same on commit/push. However in a IDE they will be different as CRLF <> LF and you will get reg conflicts.

Adding this in just lets the IDE/git know they should always be LF on all operating systems and to ignore the git config settings for LF/CRLF for these files.

I can add this to a very small PR if needed, typically we would not make a separate PR for something like this and place it in a change going in as it is small and easily identified.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made #682, so when it goes in an I merge with main this file will go away from this PR.

*.ts text eol=lf
101 changes: 52 additions & 49 deletions esbuild.config.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import esbuild from "esbuild";
import process from "process";
import builtins from 'builtin-modules'
import esbuildSvelte from "esbuild-svelte";
import sveltePreprocess from "svelte-preprocess";

const banner =
`/*
import process from 'process';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file has been reformatted, which makes it impossible to see if or where the content has changed.

And also, not possible to know whether any change was related to the topic of this PR, or something independent that could/should have been in a separate PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ill reset the structure, ironically this is now formatted using the same settings as lint 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see #683

import esbuild from 'esbuild';
import builtins from 'builtin-modules';
import esbuildSvelte from 'esbuild-svelte';
import sveltePreprocess from 'svelte-preprocess';

const banner = `/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source visit the plugins github repository
*/
Expand Down Expand Up @@ -130,44 +129,48 @@ THE SOFTWARE.
*/
`;

const prod = (process.argv[2] === 'production');

esbuild.build({
banner: {
js: banner,
},
bundle: true,
entryPoints: ['main.ts'],
external: [
'obsidian',
'electron',
'codemirror',
'@codemirror/autocomplete',
'@codemirror/closebrackets',
'@codemirror/commands',
'@codemirror/fold',
'@codemirror/gutter',
'@codemirror/history',
'@codemirror/language',
'@codemirror/rangeset',
'@codemirror/rectangular-selection',
'@codemirror/search',
'@codemirror/state',
'@codemirror/stream-parser',
'@codemirror/text',
'@codemirror/view',
...builtins
],
format: 'cjs',
logLevel: "info",
outfile: 'main.js',
plugins: [
esbuildSvelte({
preprocess: sveltePreprocess()
}),
],
sourcemap: prod ? false : 'inline',
target: 'es2016',
treeShaking: true,
watch: !prod,
}).catch(() => process.exit(1));
const prod = process.argv[2] === 'production';
const dev = process.argv[2] === 'development';

esbuild
.build({
banner: {
js: banner,
},
bundle: true,
entryPoints: ['main.ts'],
external: [
'obsidian',
'electron',
'codemirror',
'@codemirror/autocomplete',
'@codemirror/closebrackets',
'@codemirror/commands',
'@codemirror/fold',
'@codemirror/gutter',
'@codemirror/history',
'@codemirror/language',
'@codemirror/rangeset',
'@codemirror/rectangular-selection',
'@codemirror/search',
'@codemirror/state',
'@codemirror/stream-parser',
'@codemirror/text',
'@codemirror/view',
...builtins,
],
format: 'cjs',
logLevel: 'info',
minify: prod ? true : false,
outfile: 'main.js',
plugins: [
esbuildSvelte({
preprocess: sveltePreprocess(),
}),
],
sourcemap: prod ? false : 'inline',
target: 'es2016',
treeShaking: true,
watch: !prod && !dev,
})
.catch(() => process.exit(1));
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
"scripts": {
"dev": "node esbuild.config.mjs",
"build": "node esbuild.config.mjs production",
"build:dev": "node esbuild.config.mjs development",
"lint": "eslint ./src --fix && eslint ./tests --fix && tsc --noEmit --pretty && svelte-check",
"test": "jest --ci",
"test:dev": "jest --watch"
"test:dev": "jest --watch",
"test:obsidian": "pwsh -ExecutionPolicy Unrestricted -NoProfile -File ./scripts/Test-TasksInLocalObsidian.ps1"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ooh, using PowerShell to drive Obsidian. This is very intriguing!

Will it work on Linux and Mac? (I know that PowerShell has been released on those platforms, but I don't know whether it can drive Obsidian on those platforms.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Later: it doesn't drive obsidian, it copies a distribution in to a vault.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it works across platforms. I use it on my linux machine and in WSL on windows. I am using it as bash sends me around the twist these days with string manipulation. Being able to work with objects in the shell is much better.

I am slowly playing with a PowerShell module that will work with obsidian and the shell plugin also supports running PowerShell scripts. This will setup synlinks to the vault you specify in a environment variable or via the command line and then start the build watch process. Makes the dev cycle faster.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see #683

},
"keywords": [
"obsidian",
Expand Down
53 changes: 53 additions & 0 deletions scripts/Test-TasksInLocalObsidian.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
[CmdletBinding()]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, based on the file name, I misunderstood the purpose of this file. I though it was actually testing Obsidian, but it is copying the built plugin in to an Obsidian vault.

That's still very useful, but also a candidate for being in a separate PR please (alongside the associated change to package.json

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am using it to help me work on this PR. So there will be a chicken egg scenario. Ill make a separate PR and if it goes in first then this will disappear from this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #683

param (
[Parameter(HelpMessage = 'The path to the plugins folder uner the .obsidian directory.')]
[String]
$ObsidianPluginRoot = $env:OBSIDIAN_PLUGIN_ROOT,
[Parameter(HelpMessage = 'The folder name of the plugin to copy the files to.')]
[String]
$PluginFolderName = 'obsidian-tasks-plugin'
)

$repoRoot = (Resolve-Path -Path $(git rev-parse --show-toplevel)).Path

if (-not (Test-Path $ObsidianPluginRoot)) {
Write-Error "Obsidian plugin root not found: $ObsidianPluginRoot"
return
} else {
Write-Host "Obsidian plugin root found: $ObsidianPluginRoot"
}

Push-Location $repoRoot
Write-Host "Repo root: $repoRoot"

yarn run build:dev

if ($?) {
Write-Output 'Build successful'

$filesToLink = @('main.js', 'styles.css', 'manifest.json')

foreach ($file in $filesToLink ) {
if ((Get-Item "$ObsidianPluginRoot/$PluginFolderName/$file" ).LinkType -ne 'SymbolicLink') {
Write-Output "Removing $file from plugin folder and linking"
Remove-Item "$ObsidianPluginRoot/$PluginFolderName/$file" -Force
New-Item -ItemType SymbolicLink -Path "$ObsidianPluginRoot/$PluginFolderName/$file" -Target "$repoRoot/$file"
} else {
(Get-Item "$ObsidianPluginRoot/$PluginFolderName/$file" ).LinkType
}
}

$hasHotReload = Test-Path "$ObsidianPluginRoot/$PluginFolderName/.hotreload"

if (!$hasHotReload) {
Write-Output 'Creating hotreload file'
'' | Set-Content "$ObsidianPluginRoot/$PluginFolderName/.hotreload"
}

yarn run dev

} else {
Write-Error 'Build failed'
}

Pop-Location
10 changes: 5 additions & 5 deletions src/Commands/CreateOrEdit.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { App, Editor, MarkdownView, View } from 'obsidian';
import { StatusRegistry } from 'StatusRegistry';
import { TaskModal } from '../TaskModal';
import { Priority, Status, Task } from '../Task';
import { Status } from '../Status';
import { Priority, Task } from '../Task';

export const createOrEdit = (
checking: boolean,
Expand Down Expand Up @@ -64,11 +66,10 @@ const taskFromLine = ({ line, path }: { line: string; path: string }): Task => {
// Should never happen; everything in the regex is optional.
console.error('Tasks: Cannot create task on line:', line);
return new Task({
status: Status.Todo,
status: Status.TODO,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For my own education, I'm curious as to why Todo -> TODO?

Asking as it increases the number of changes to review, not because I want to change it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consistency in code, as these are constants I have seen they should be upper case in TS/JS. Also makes it super easy to see when reading code that these are not a normal variable.

description: '',
path,
indentation: '',
originalStatusCharacter: ' ',
priority: Priority.None,
startDate: null,
scheduledDate: null,
Expand All @@ -86,7 +87,7 @@ const taskFromLine = ({ line, path }: { line: string; path: string }): Task => {

const indentation: string = nonTaskMatch[1];
const statusString: string = nonTaskMatch[3] ?? ' ';
const status = statusString === ' ' ? Status.Todo : Status.Done;
const status = StatusRegistry.getInstance().byIndicator(statusString);
let description: string = nonTaskMatch[4];

const blockLinkMatch = line.match(Task.blockLinkRegex);
Expand All @@ -101,7 +102,6 @@ const taskFromLine = ({ line, path }: { line: string; path: string }): Task => {
description,
path,
indentation,
originalStatusCharacter: statusString,
blockLink,
priority: Priority.None,
startDate: null,
Expand Down
120 changes: 120 additions & 0 deletions src/Commands/SelectStatus.ts.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// import { Editor, MarkdownView, View } from 'obsidian';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see elsewhere that you've introduced a Feature Toggle, and I do like that idea. I agree that it allows working code to be committed to main, to enable testing before it's ready for prime-time.

https://martinfowler.com/articles/feature-toggles.html

Sorry if this comment is premature (I did check that the PR is ready for review) but the usual behaviour of feature toggles is to allow experimental code to be turned on or off by changing the toggle.

As it's all commented out, I'm unsure how we can test it.

Would it be possible for all the UI code to be uncommented out, and then for the code at run-time to only enable the new features if the feature toggle is turned on?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Later: I see that the feature toggle is about whether to show a pop-up menu, and this code is about adding a command.

I still would suggest the new code be activated, even if it's not called, so that if there are any future refactorings, like renaming symbols, IDEs would update the inactivated code too, preventing the commented-out code later needing to be manually updated for any changes made in between.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coming in a separate PR. I added the feature capability in late in the cycle so it is skipped for the status change as the refactor is to large. However the UI will be behind the switch as it may be more problematic. All the UI code is just txt files for the moments as I think it through. This means that this PR can go in and the next one will enable that behind the feature flag.

When a feature is marked as stable we can have the settings automatically show it in the main body as well with a warning if the user has it disabled stating that it is now stable and available for use.


// import { Task } from '../Task';

// import { promptForMark } from '../ui/TaskMarkModal';
// export const selectStatus = (checking: boolean, editor: Editor, view: View) => {
// const mark = await promptForMark(this.app, this.plugin);
// if (mark) {
// this.markTaskOnLines(mark, this.getCurrentLinesFromEditor(editor));
// }

// if (checking) {
// if (!(view instanceof MarkdownView)) {
// // If we are not in a markdown view, the command shouldn't be shown.
// return false;
// }

// // The command should always trigger in a markdown view:
// // - Convert lines to list items.
// // - Convert list items to tasks.
// // - Toggle tasks' status.
// return true;
// }

// if (!(view instanceof MarkdownView)) {
// // Should never happen due to check above.
// return;
// }

// // We are certain we are in the editor due to the check above.
// const path = view.file?.path;
// if (path === undefined) {
// return;
// }

// const cursorPosition = editor.getCursor();
// const lineNumber = cursorPosition.line;
// const line = editor.getLine(lineNumber);

// const toggledLine = toggleLine({ line, path });
// editor.setLine(lineNumber, toggledLine);

// // The cursor is moved to the end of the line by default.
// // If there is text on the line, put the cursor back where it was on the line.
// if (/[^ [\]*-]/.test(toggledLine)) {
// editor.setCursor({
// line: cursorPosition.line,
// // Need to move the cursor by the distance we added to the beginning.
// ch: cursorPosition.ch + toggledLine.length - line.length,
// });
// }
// };

// const toggleLine = ({ line, path }: { line: string; path: string }): string => {
// let toggledLine: string = line;

// const task = Task.fromLine({
// line,
// path,
// sectionStart: 0, // We don't need this to toggle it here in the editor.
// sectionIndex: 0, // We don't need this to toggle it here in the editor.
// precedingHeader: null, // We don't need this to toggle it here in the editor.
// });
// if (task !== null) {
// toggledLine = toggleTask({ task });
// } else {
// // If the task is null this means that we have one of:
// // 1. a regular checklist item
// // 2. a list item
// // 3. a simple text line

// // The task regex will match checklist items.
// const regexMatch = line.match(Task.taskRegex);
// if (regexMatch !== null) {
// toggledLine = toggleChecklistItem({ regexMatch });
// } else {
// // This is not a checklist item. It is one of:
// // 1. a list item
// // 2. a simple text line

// const listItemRegex = /^([\s\t]*)([-*])/;
// if (listItemRegex.test(line)) {
// // Let's convert the list item to a checklist item.
// toggledLine = line.replace(listItemRegex, '$1$2 [ ]');
// } else {
// // Let's convert the line to a list item.
// toggledLine = line.replace(/^([\s\t]*)/, '$1- ');
// }
// }
// }

// return toggledLine;
// };

// const toggleTask = ({ task }: { task: Task }): string => {
// // Toggle a regular task.
// const toggledTasks = task.toggle();
// const serialized = toggledTasks
// .map((task: Task) => task.toFileLineString())
// .join('\n');

// return serialized;
// };

// const toggleChecklistItem = ({
// regexMatch,
// }: {
// regexMatch: RegExpMatchArray;
// }): string => {
// // It's a checklist item, let's toggle it.
// const indentation = regexMatch[1];
// const statusString = regexMatch[2].toLowerCase();
// const body = regexMatch[3];

// const toggledStatusString = statusString === ' ' ? 'x' : ' ';

// const toggledLine = `${indentation}- [${toggledStatusString}] ${body}`;

// return toggledLine;
// };
9 changes: 9 additions & 0 deletions src/Commands/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { App, Editor, Plugin, View } from 'obsidian';

import { createOrEdit } from './CreateOrEdit';
//import { selectStatus } from './SelectStatus';

import { toggleDone } from './ToggleDone';

Expand Down Expand Up @@ -32,5 +34,12 @@ export class Commands {
icon: 'check-in-circle',
editorCheckCallback: toggleDone,
});

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this check be activated if the feature toggle is on?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, the feature toggle is specific to the pop-up menu.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I removed it out to get a clean build and this PR in and will either add to this PR or will be in one to follow and under the flag. I have another draft PR in place using the feature flags based on this PR.

// plugin.addCommand({
// id: 'select-status-modal',
// name: 'Select Status',
// icon: 'check-in-circle',
// editorCheckCallback: selectStatus,
// });
}
}
4 changes: 2 additions & 2 deletions src/Events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ interface CacheUpdateData {
export class Events {
private obsidianEvents: ObsidianEvents;

constructor({ obsidianEents }: { obsidianEents: ObsidianEvents }) {
this.obsidianEvents = obsidianEents;
constructor({ obsidianEvents }: { obsidianEvents: ObsidianEvents }) {
this.obsidianEvents = obsidianEvents;
}

public onCacheUpdate(
Expand Down
Loading