Commit abf8c0c4 authored by ransome1's avatar ransome1
Browse files

Finalize v1.0.7

parent d2c0c132
......@@ -76,6 +76,7 @@ A prioritized backlog of new features and known issues can be found <a href="htt
- start dates
- <a href="https://github.com/ransome1/sleek/wiki/Recurring-todos">recurrences</a>
* Todo-List can be grouped and sorted by priorities, due dates, contexts or projects
* The sorting order can be defined on all 4 levels
* Todos can be filtered by contexts, projects and priorities
* Todos can be looked up by full-text search
* Autocomplete function suggests available contexts and projects
......@@ -101,6 +102,7 @@ A prioritized backlog of new features and known issues can be found <a href="htt
- French
- Simplified Chinese
- Brazilian Portugese
- Japanese
* sleek can be minimized to tray
* Existing todos can be used as templates for new ones
......
{
"name": "sleek",
"productName": "sleek",
"version": "1.0.8",
"version": "1.0.7",
"description": "Todo app based on todo.txt for Linux, Windows and MacOS, free and open-source",
"synopsis": "Todo app based on todo.txt for Linux, Windows and MacOS, free and open-source",
"category": "ProjectManagement",
......@@ -92,9 +92,9 @@
"artifactName": "${productName}-${version}-${arch}.${ext}"
},
"scripts": {
"build:windows": "yarn build:css && yarn build:pegjs && electron-builder -w --publish never",
"build:macos": "yarn build:css && yarn build:pegjs && electron-builder -m --publish never",
"build:linux": "yarn build:css && yarn build:pegjs && electron-builder -l --publish never",
"build:windows": "yarn build:css && electron-builder -w --publish never",
"build:macos": "yarn build:css && electron-builder -m --publish never",
"build:linux": "yarn build:css && electron-builder -l --publish never",
"build:appx": "electron-builder -w appx --publish never",
"build:pacman": "electron-builder -l pacman --publish never",
"build:appimage": "yarn build:css && yarn build:pegjs && electron-builder -l AppImage --publish never",
......@@ -103,7 +103,6 @@
"test": "mocha --timeout 10000",
"test1": "mocha ./test/onboarding.js --timeout 10000",
"build:css": "sass src/scss/style.scss:src/css/style.css",
"build:pegjs": "peggy --format es --output src/js/filterlang.mjs src/js/filterlang.pegjs",
"sass": "sass -w src/scss/style.scss:src/css/style.css",
"start": "yarn sass & electron ."
},
......@@ -122,10 +121,9 @@
},
"devDependencies": {
"electron": "12.0.10",
"electron-builder": "22.10.5",
"electron-builder": "^22.11.7",
"eslint": "^7.25.0",
"mocha": "^9.0.0",
"peggy": "^1.2.0",
"sass": "^1.34.1",
"spectron": "14.0.0"
}
......
name: sleek
base: core18
version: '1.0.6-2'
version: '1.0.7'
summary: Todo app based on the todo.txt format for Linux, free and open-source
description: |
sleek is an open-source todo app that makes use of the todo.txt format. sleeks GUI is modern and simple but still offers a decent set of functions which help users getting things done. sleek is available as a client for Windows, MacOS and Linux.
......
This diff is collapsed.
{{
import { addIntervalToDate } from "./recurrences.mjs";
}}
filterQuery
= _ left:orExpr _ { return left; }
/ _ { return []; }
orExpr
= left:andExpr _ OrOp _ right:orExpr { return left.concat(right, ["||"]); }
/ left:andExpr { return left; }
andExpr
= left:notExpr _ AndOp _ right:andExpr { return left.concat(right, ["&&"]); }
/ left:notExpr { return left; }
notExpr
= NotOp _ left:notExpr { return left.concat(["!!"]); }
/ left:boolExpr { return left; }
boolExpr
= left:project { return left; }
/ left:context { return left; }
/ "(" _ left:orExpr _ ")" { return left; }
/ left:comparison { return left; }
/ "complete" { return ["complete"]; }
/ left:StringLiteral { return ["string", left]; }
/ left:RegexLiteral { return ["regex", left]; }
project
= "+" left:name { return ["++", left]; }
/ "+" { return ["++", "*"]; }
context
= "@" left:name { return ["@@", left]; }
/ "@" { return ["@@", "*"]; }
OrOp
= "||"
/ "or"i
AndOp
= "&&"
/ "and"i
NotOp
= "!"
/ "not"i
comparison
= left:priorityComparison { return left; }
/ left:dueComparison { return left; }
priorityComparison
= "priority" _ op:compareOp _ right:priorityLiteral { return ["priority", right, op]; }
/ "priority" { return ["priority"]; }
priorityLiteral
= [A-Z] { return text(); }
dueComparison
= "due" _ op:compareOp _ right:dateExpr { return ["due"].concat(right, [op]); }
/ "due" { return ["due"]; }
dateExpr
= left:dateLiteral _ op:dateOp _ count:number unit:[dbwmy] {
if (op == "-") {
count = count * -1;
}
// we do our date math with the same code as we use for
// recurrence calculations. All dates are returned from
// the parser as millisec since epoch (getTime()) to
// simplify comparisons in the filter lang execution engine.
let d = addIntervalToDate(new Date(left), count, unit);
return d.getTime();
}
/ left:dateLiteral { return left; }
dateOp
= "+" { return text(); }
/ "-" { return text(); }
compareOp
= "==" { return text(); }
/ "!=" { return text(); }
/ ">=" { return text(); }
/ "<=" { return text(); }
/ ">" { return text(); }
/ "<" { return text(); }
dateLiteral
= year:number4 "-" month: number2 "-" day:number2 {
let d = new Date(year, month-1, day);
return d.getTime();
}
/ "today" {
let d = new Date(); // now, w current time of day
d = new Date(d.getFullYear(), d.getMonth(), d.getDate());
return d.getTime();
}
/ "tomorrow" {
let d = new Date(); // now, w current time of day
d = new Date(d.getFullYear(), d.getMonth(), d.getDate());
return d.getTime() + 24*60*60*1000;
}
number4
= [0-9][0-9][0-9][0-9] { return text(); }
number2
= [0-9][0-9] { return text(); }
number
= [0-9]+ { return text(); }
StringLiteral "string"
= '"' chars:DoubleStringCharacter* '"'? {
return chars.join("");
}
/ "'" chars:SingleStringCharacter* "'"? {
return chars.join("");
}
DoubleStringCharacter
= '\\' '"' { return '"'; }
/ !'"' SourceCharacter { return text(); }
SingleStringCharacter
= '\\' "'" { return "'"; }
/ !"'" SourceCharacter { return text(); }
RegexLiteral "regex"
= "/" chars:RegexCharacter* "/" "i" {
return new RegExp(chars.join(""), "i");
}
/ "/" chars:RegexCharacter* "/"? {
return new RegExp(chars.join(""));
}
RegexCharacter
= "\\" "/" { return "/"; }
/ !"/" SourceCharacter { return text(); }
SourceCharacter
= .
name
= '"' [a-zA-Z_][a-zA-Z_0-9]* '"' { return text(); }
/ [a-zA-Z_][a-zA-Z_0-9]* '"' { return '"' + text(); }
/ [a-zA-Z_][a-zA-Z_0-9]* { return text(); }
_ "whitespace"
= [ \t\n\r]*
// This is a simple stack machine that executes a filter language query
// compiled by filterlang.pegjs (which generates filterlang.mjs).
// The compiled query consists of a list of postfix opcodes designed
// specifically for todo.txt searching and filtering.
function runQuery(item, compiledQuery) {
if (!compiledQuery) {
return true; // a null query matches everything
}
let stack = [];
let operand1 = false;
let operand2 = false;
let next = 0;
let q = compiledQuery.slice(); // shallow copy
while (q.length > 0) {
const opcode = q.shift();
switch(opcode) {
case "priority":
stack.push(item.priority);
break;
case "due":
let d = item.due;
if (d) {
// normalize date to have time of midnight in local zone
// we represent dates as millisec from epoch to simplify comparison
d = new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
}
stack.push(d);
break;
case "complete":
stack.push(item.complete);
break;
case "string":
next = q.shift(); // the string value to match
stack.push(item.toString().toLowerCase().indexOf(next.toLowerCase()) !== -1);
break;
case "regex":
next = q.shift(); // the regex to match
stack.push(next.test(item.toString()));
break;
case "==":
operand2 = stack.pop();
operand1 = stack.pop();
stack.push(operand1 == operand2);
break;
case "!=":
operand2 = stack.pop();
operand1 = stack.pop();
stack.push(operand1 != operand2);
break;
case "<":
operand2 = stack.pop();
operand1 = stack.pop();
stack.push(operand1 < operand2);
break;
case "<=":
operand2 = stack.pop();
operand1 = stack.pop();
stack.push(operand1 <= operand2);
break;
case ">":
operand2 = stack.pop();
operand1 = stack.pop();
stack.push(operand1 > operand2);
break;
case ">=":
operand2 = stack.pop();
operand1 = stack.pop();
stack.push(operand1 >= operand2);
break;
case "++":
next = q.shift();
if (next == "*") {
stack.push(item.projects ? true : false);
} else if (next.startsWith('"')) {
stack.push(item.projects && item.projects.includes(next.slice(1,-1)));
} else {
// match the prefix of the project name
stack.push(item.projects && item.projects.findIndex(function(p) {
return p.startsWith(next);
}) > -1);
}
break;
case "@@":
next = q.shift();
if (next == "*") {
stack.push(item.contexts ? true : false);
} else if (next.startsWith('"')) {
stack.push(item.contexts && item.contexts.includes(next.slice(1,-1)));
} else {
// match the prefix of the context name
stack.push(item.contexts && item.contexts.findIndex(function(c) {
return c.startsWith(next);
}) > -1);
}
break;
case "||":
operand2 = stack.pop();
operand1 = stack.pop();
stack.push(operand1 || operand2);
break;
case "&&":
operand2 = stack.pop();
operand1 = stack.pop();
stack.push(operand1 && operand2);
break;
case "!!":
operand1 = stack.pop();
stack.push(!operand1);
break;
default:
// should be a data item like a string or date in millisec, ...
stack.push(opcode);
break;
}
}
return stack.pop();
}
export { runQuery };
......@@ -3,8 +3,6 @@ import { userData, handleError, translations, setUserData, startBuilding, getCon
import { _paq } from "./matomo.mjs";
import { items } from "./todos.mjs";
import { isToday, isPast, isFuture } from "./date.mjs";
import * as filterlang from "./filterlang.mjs";
import { runQuery } from "./filterquery.mjs";
const todoTableSearch = document.getElementById("todoTableSearch");
const autoCompleteContainer = document.getElementById("autoCompleteContainer");
......
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment